@askrjs/askr 0.0.83 → 0.0.85

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 (64) hide show
  1. package/dist/{access-ePqdQqkV.js → access-Dpfoi4cA.js} +7 -1
  2. package/dist/actions/index.d.ts +1 -1
  3. package/dist/actions/index.js +1 -1
  4. package/dist/{activity-CXEaU-Al.d.ts → activity-B1I5HJQI.d.ts} +1 -1
  5. package/dist/{activity-CnSRFO3d.js → activity-Jd9CgEa3.js} +52 -15
  6. package/dist/{authoring-DbGEgg40.js → authoring-DdQ3pHqt.js} +6 -3
  7. package/dist/benchmark.js +8 -8
  8. package/dist/boot/index.d.ts +3 -3
  9. package/dist/boot/index.js +1 -1
  10. package/dist/{boot-uK1hzONI.js → boot-DWe880Et.js} +16 -15
  11. package/dist/{cleanup-CEF-cX49.js → cleanup-c837KVij.js} +17 -7
  12. package/dist/{component-cleanup-oWN45rfa.js → component-cleanup-BUfUleGZ.js} +2 -2
  13. package/dist/{component-internal-DsM75Gl1.js → component-internal-DcrLymUJ.js} +9 -6
  14. package/dist/components/index.d.ts +1 -1
  15. package/dist/components/index.js +1 -1
  16. package/dist/control/index.d.ts +1 -1
  17. package/dist/control/index.js +1 -1
  18. package/dist/{control-Cix3yjVf.d.ts → control-DhUZU4_V.d.ts} +2 -1
  19. package/dist/{control-C0X1zgrM.js → control-oLADku43.js} +4 -4
  20. package/dist/{csp-nonce-DDt0UNdt.js → csp-nonce-Bc52OIVc.js} +19 -2
  21. package/dist/data/index.js +1 -1
  22. package/dist/{data-G0hURNm_.js → data-Dkqb8_QP.js} +3 -3
  23. package/dist/{deferred-rdHAgyKe.js → deferred-vUOy-awa.js} +198 -16
  24. package/dist/{effect-2IIWvUjG.js → effect-lnxQIpJG.js} +2 -2
  25. package/dist/{fastlane-CkBXZ_3K.js → fastlane-CrfB3QG0.js} +2 -2
  26. package/dist/{for-internal-DRbV0h1d.js → for-internal-B_mnQhan.js} +55 -22
  27. package/dist/foundations/index.d.ts +1 -1
  28. package/dist/foundations/index.js +2 -2
  29. package/dist/foundations/state/index.d.ts +1 -1
  30. package/dist/foundations/state/index.js +1 -1
  31. package/dist/foundations/structures/index.d.ts +2 -2
  32. package/dist/foundations/structures/index.js +3 -3
  33. package/dist/fx/index.js +1 -1
  34. package/dist/{index-Vs1EeWEr.d.ts → index-Bkh9S9-t.d.ts} +6 -2
  35. package/dist/{index-TlEvex-C.d.ts → index-pIgGaXvS.d.ts} +21 -4
  36. package/dist/{index-ho0Htetw.d.ts → index-r-nt13FE.d.ts} +1 -1
  37. package/dist/index.d.ts +3 -3
  38. package/dist/index.js +10 -10
  39. package/dist/{lifecycle-batch-CwHCDseR.js → lifecycle-batch-Dfpid9-A.js} +45 -24
  40. package/dist/{lifecycle-operations-BJfxWUJr.js → lifecycle-operations-BrYrBsXY.js} +1 -1
  41. package/dist/{navigate-DfY1BUYc.d.ts → navigate-NyIjQUwI.d.ts} +1 -1
  42. package/dist/{navigate-Crvd5gUG.js → navigate-b4GGxO_l.js} +15 -14
  43. package/dist/{portal-BZ_rN17v.js → portal-BVWjAhNQ.js} +5 -5
  44. package/dist/{readable-B3VIOfd7.js → readable-B5NtV7V0.js} +3 -3
  45. package/dist/{renderer-VSw0KKDG.js → renderer-CZzNtoj2.js} +3355 -2813
  46. package/dist/resources/index.d.ts +2 -2
  47. package/dist/resources/index.js +5 -5
  48. package/dist/{route-matching-BUwz4kfl.js → route-matching-DHduNiGk.js} +49 -6
  49. package/dist/router/index.d.ts +7 -7
  50. package/dist/router/index.js +9 -7
  51. package/dist/{selector-DVwvof1M.js → selector-Ck_Pp9av.js} +2 -2
  52. package/dist/ssg/index.d.ts +10 -4
  53. package/dist/ssg/index.js +30 -15
  54. package/dist/ssr/index.d.ts +7 -3
  55. package/dist/ssr/index.js +1 -1
  56. package/dist/{ssr-C_Ql5_Kg.js → ssr-CpiBHMNv.js} +40 -26
  57. package/dist/ssr-DSHd_Q9K.js +25 -0
  58. package/dist/{state-BCl9DeiN.js → state-DCjONcHK.js} +4 -3
  59. package/dist/{structures-D3vO-WMN.js → structures-DSLF_iis.js} +4 -2
  60. package/dist/testing/index.d.ts +2 -2
  61. package/dist/testing/index.js +3 -3
  62. package/dist/{verify-hydration-CNLXUcuR.js → verify-hydration-C3QBBQqR.js} +2 -2
  63. package/package.json +3 -3
  64. package/dist/ssr-CbQO5xd3.js +0 -12
@@ -1,10 +1,10 @@
1
- import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-C3IECePB.js";
2
- import { g as readScope, u as defineScope } from "./component-internal-DsM75Gl1.js";
3
1
  import { d as getCurrentAppRenderRuntime } from "./component-scope-DzJEbYGE.js";
4
- import { p as isPromiseLike } from "./lifecycle-batch-CwHCDseR.js";
2
+ import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-C3IECePB.js";
3
+ import { g as readScope, u as defineScope } from "./component-internal-DcrLymUJ.js";
4
+ import { p as isPromiseLike } from "./lifecycle-batch-Dfpid9-A.js";
5
5
  import { n as getActiveRenderContext, r as getCurrentRenderData } from "./render-context-D-DFk2WN.js";
6
- import { A as insertRecordSorted, D as getRouteRecords, E as getRouteList, F as snapshotRouteState, N as restoreRouteState, P as setDefaultRouteAuthOptions, T as getDefaultRouteAuthOptions, f as addRouteToStores, h as getActiveRouteAuthOptions, i as getMatchingRouteRecord, m as clearRouteState, o as buildRouteContext, s as buildRouteContextBase, z as resource } from "./route-matching-BUwz4kfl.js";
7
- import { i as ROUTE_ROOT_COMPONENT } from "./csp-nonce-DDt0UNdt.js";
6
+ import { A as getDefaultRouteBasePath, B as setDefaultRouteBasePath, F as insertRecordSorted, G as resource, M as getRouteRecords, R as restoreRouteState, V as snapshotRouteState, _ as clearRouteState, c as removeRouteBasePath, h as addRouteToStores, i as getMatchingRouteRecord, j as getRouteList, k as getDefaultRouteAuthOptions, l as buildRouteContext, o as addRouteBasePath, s as normalizeRouteBasePath, u as buildRouteContextBase, v as getActiveRouteAuthOptions, z as setDefaultRouteAuthOptions } from "./route-matching-DHduNiGk.js";
7
+ import { i as ROUTE_ROOT_COMPONENT } from "./csp-nonce-Bc52OIVc.js";
8
8
  import { c as withTelemetry, t as createQueryPrefetchContext } from "./query-registry-BL6rjGsO.js";
9
9
  //#region src/runtime/execution-model.ts
10
10
  const EXECUTION_MODEL_KEY = Symbol.for("__ASKR_EXECUTION_MODEL__");
@@ -173,10 +173,168 @@ function replacePageRoute(value, route) {
173
173
  framework: current.framework
174
174
  });
175
175
  }
176
+ function withPageFramework(value, framework) {
177
+ const current = pageRenderEnvelope(value);
178
+ return createPageRenderEnvelope({
179
+ resources: current.resources,
180
+ route: current.route,
181
+ framework
182
+ });
183
+ }
176
184
  function isEmptyPageRenderEnvelope(value) {
177
185
  return value.route === void 0 && Object.keys(value.resources).length === 0 && Object.keys(value.framework).length === 0;
178
186
  }
179
187
  //#endregion
188
+ //#region src/router/route-hydration.ts
189
+ const DEFERRED_VALUE$1 = Symbol.for("@askrjs/askr/deferred-value");
190
+ function isDeferredHydrationValue(value) {
191
+ if (!value || typeof value !== "object") return false;
192
+ const marker = Object.getOwnPropertyDescriptor(value, DEFERRED_VALUE$1);
193
+ return Boolean(marker && "value" in marker && marker.value === true);
194
+ }
195
+ const omissionGuards = /* @__PURE__ */ new WeakMap();
196
+ function propertyPath(parent, key) {
197
+ if (/^(?:0|[1-9]\d*)$/.test(key)) return `${parent}[${key}]`;
198
+ if (/^[A-Za-z_$][\w$]*$/.test(key)) return `${parent}.${key}`;
199
+ return `${parent}[${JSON.stringify(key)}]`;
200
+ }
201
+ function invalid(route, path, reason) {
202
+ throw new TypeError(`[Askr] Route hydration data for "${route}" at "${path}" is not JSON transport-safe: ${reason}. Return JSON-compatible data or use route(..., { dehydrate(data) { ... } }) to omit server-only values.`);
203
+ }
204
+ function objectLabel(value) {
205
+ if (value instanceof Date) return "Date";
206
+ if (value instanceof Map) return "Map";
207
+ if (value instanceof Set) return "Set";
208
+ const constructor = value.constructor;
209
+ return typeof constructor === "function" && constructor.name ? constructor.name : "class instance";
210
+ }
211
+ /** Validate the exact value that will enter the route hydration envelope. */
212
+ function validateRouteHydrationData(value, route, path = "$", ancestors = /* @__PURE__ */ new Set()) {
213
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
214
+ if (typeof value === "number") {
215
+ if (!Number.isFinite(value)) invalid(route, path, "non-finite numbers are not supported");
216
+ return;
217
+ }
218
+ if (typeof value === "undefined") invalid(route, path, "undefined is not supported");
219
+ if (typeof value === "bigint") invalid(route, path, "bigint is not supported");
220
+ if (typeof value === "symbol") invalid(route, path, "symbols are not supported");
221
+ if (typeof value === "function") invalid(route, path, "functions are not supported");
222
+ const object = value;
223
+ if (isDeferredHydrationValue(object)) {
224
+ if (object.state === "fulfilled") validateRouteHydrationData(object.value, route, `${path}.value`, ancestors);
225
+ return;
226
+ }
227
+ if (ancestors.has(object)) invalid(route, path, "cyclic references are not supported");
228
+ ancestors.add(object);
229
+ try {
230
+ if (Array.isArray(object)) {
231
+ for (let index = 0; index < object.length; index += 1) if (!Object.prototype.hasOwnProperty.call(object, index)) invalid(route, `${path}[${index}]`, "sparse arrays are not supported");
232
+ for (const key of Reflect.ownKeys(object)) {
233
+ if (key === "length") continue;
234
+ if (typeof key === "symbol") invalid(route, path, "symbol-keyed properties are not supported");
235
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
236
+ if (!descriptor.enumerable) invalid(route, propertyPath(path, key), "non-enumerable properties are not supported");
237
+ if (!("value" in descriptor)) invalid(route, propertyPath(path, key), "accessors are not supported");
238
+ if (!/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= object.length) invalid(route, propertyPath(path, key), "named array properties are not supported");
239
+ validateRouteHydrationData(descriptor.value, route, propertyPath(path, key), ancestors);
240
+ }
241
+ return;
242
+ }
243
+ const prototype = Object.getPrototypeOf(object);
244
+ if (prototype !== Object.prototype && prototype !== null) invalid(route, path, `${objectLabel(object)} instances are not supported; use a plain object`);
245
+ for (const key of Reflect.ownKeys(object)) {
246
+ if (typeof key === "symbol") invalid(route, path, "symbol-keyed properties are not supported");
247
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
248
+ const childPath = propertyPath(path, key);
249
+ if (!descriptor.enumerable) invalid(route, childPath, "non-enumerable properties are not supported");
250
+ if (!("value" in descriptor)) invalid(route, childPath, "accessors are not supported");
251
+ validateRouteHydrationData(descriptor.value, route, childPath, ancestors);
252
+ }
253
+ } finally {
254
+ ancestors.delete(object);
255
+ }
256
+ }
257
+ function omissionMap(complete, selected, ancestors = /* @__PURE__ */ new Set()) {
258
+ if (!complete || typeof complete !== "object" || isDeferredHydrationValue(complete)) return void 0;
259
+ if (ancestors.has(complete)) return void 0;
260
+ ancestors.add(complete);
261
+ const selectedObject = selected && typeof selected === "object" ? selected : void 0;
262
+ const output = Object.create(null);
263
+ try {
264
+ for (const key of Object.keys(complete)) {
265
+ if (!selectedObject || !Object.prototype.hasOwnProperty.call(selectedObject, key)) {
266
+ output[key] = 1;
267
+ continue;
268
+ }
269
+ const completeDescriptor = Object.getOwnPropertyDescriptor(complete, key);
270
+ const selectedDescriptor = Object.getOwnPropertyDescriptor(selectedObject, key);
271
+ if (!completeDescriptor || !selectedDescriptor) continue;
272
+ if (!("value" in completeDescriptor) || !("value" in selectedDescriptor)) continue;
273
+ const nested = omissionMap(completeDescriptor.value, selectedDescriptor.value, ancestors);
274
+ if (nested) output[key] = nested;
275
+ }
276
+ } finally {
277
+ ancestors.delete(complete);
278
+ }
279
+ return Object.keys(output).length > 0 ? Object.freeze(output) : void 0;
280
+ }
281
+ function prepareRouteHydrationData(complete, dehydrate, context, route = context.href || context.pathname) {
282
+ const selected = dehydrate ? dehydrate(complete, context) : complete;
283
+ if (isPromiseLike(selected)) throw new TypeError(`[Askr] Route dehydrate() for "${route}" returned a Promise. dehydrate(data, context) must be synchronous.`);
284
+ validateRouteHydrationData(selected, route);
285
+ return {
286
+ data: selected,
287
+ metadata: Object.freeze({
288
+ r: route,
289
+ ...dehydrate ? { o: omissionMap(complete, selected) } : {}
290
+ })
291
+ };
292
+ }
293
+ function readMetadata(framework) {
294
+ const value = framework["rh"];
295
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
296
+ const metadata = value;
297
+ return typeof metadata.r === "string" ? metadata : void 0;
298
+ }
299
+ function omittedRead(route, path) {
300
+ throw new Error(`[Askr] routeData() read "${path}" on the initial hydration of "${route}", but that branch was intentionally omitted by dehydrate(). Keep initial hydrated rendering within the selected data, or wait for a client navigation that reruns the loader with the complete result.`);
301
+ }
302
+ function guardOmissions(value, omissions, route, path = "$") {
303
+ if (!omissions || !value || typeof value !== "object" || isDeferredHydrationValue(value)) return value;
304
+ let guards = omissionGuards.get(value);
305
+ if (!guards) {
306
+ guards = /* @__PURE__ */ new WeakMap();
307
+ omissionGuards.set(value, guards);
308
+ }
309
+ const cached = guards.get(omissions);
310
+ if (cached) return cached;
311
+ const guarded = new Proxy(value, { get(target, property, receiver) {
312
+ if (typeof property === "string") {
313
+ const omitted = omissions[property];
314
+ if (omitted && !Object.prototype.hasOwnProperty.call(target, property)) omittedRead(route, propertyPath(path, property));
315
+ const child = Reflect.get(target, property, receiver);
316
+ return omitted === 1 ? child : guardOmissions(child, omitted, route, propertyPath(path, property));
317
+ }
318
+ return Reflect.get(target, property, receiver);
319
+ } });
320
+ guards.set(omissions, guarded);
321
+ return guarded;
322
+ }
323
+ function guardHydratedRouteData(value, framework) {
324
+ const metadata = readMetadata(framework);
325
+ return metadata ? guardOmissions(value, metadata.o, metadata.r) : value;
326
+ }
327
+ function getRouteHydrationMetadata(framework) {
328
+ return readMetadata(framework);
329
+ }
330
+ /** @internal Remove initial-only omission guards after a client loader reruns. */
331
+ function withoutRouteHydrationMetadata(framework) {
332
+ if (!framework || !("rh" in framework)) return framework;
333
+ const next = { ...framework };
334
+ delete next["rh"];
335
+ return next;
336
+ }
337
+ //#endregion
180
338
  //#region src/router/resolution.ts
181
339
  const anonymous = () => ({
182
340
  authenticated: false,
@@ -208,14 +366,21 @@ function createRouteRenderResult(record, params, context, handler) {
208
366
  function getDefaultRouteMode() {
209
367
  return typeof window !== "undefined" ? "spa" : "ssr";
210
368
  }
369
+ function withoutInitialRouteHydrationMetadata(envelope) {
370
+ return withPageFramework(envelope, withoutRouteHydrationMetadata(envelope.framework) ?? {});
371
+ }
211
372
  /** @internal Bind resolved loader output to one route subtree and its hydration envelope. */
212
- function bindResolvedRouteData(handler, data) {
373
+ function bindResolvedRouteData(handler, data, hydration) {
213
374
  return (params, context) => {
214
375
  const renderContext = getActiveRenderContext();
215
376
  if (renderContext) {
216
377
  const envelope = replacePageRoute(renderContext.renderData, data);
217
- renderContext.renderData = envelope;
218
- renderContext.hydrationData = envelope;
378
+ renderContext.renderData = hydration ? envelope : withoutInitialRouteHydrationMetadata(envelope);
379
+ const hydrationEnvelope = replacePageRoute(renderContext.hydrationData, hydration?.data ?? data);
380
+ renderContext.hydrationData = hydration ? withPageFramework(hydrationEnvelope, {
381
+ ...hydrationEnvelope.framework,
382
+ ["rh"]: hydration.metadata
383
+ }) : withoutInitialRouteHydrationMetadata(hydrationEnvelope);
219
384
  }
220
385
  return handler(params, context);
221
386
  };
@@ -254,7 +419,12 @@ function buildLoadedRenderResult(record, params, context, request, telemetry, lo
254
419
  }));
255
420
  const loaded = runPreload ? runPreload.then(load) : load();
256
421
  const finalize = (data) => {
257
- const result = createRouteRenderResult(record, params, context, bindResolvedRouteData(renderHandler, data));
422
+ const hydration = context.mode === "ssr" ? prepareRouteHydrationData(data, record.options?.dehydrate, {
423
+ ...context,
424
+ params,
425
+ request
426
+ }, context.href || context.pathname || record.path) : void 0;
427
+ const result = createRouteRenderResult(record, params, context, bindResolvedRouteData(renderHandler, data, hydration));
258
428
  routeRenderData.set(result, data);
259
429
  return result;
260
430
  };
@@ -308,24 +478,33 @@ function resolveMatchedRoute(record, params, context, authOptions, request, tele
308
478
  }
309
479
  function resolveRouteRequest(target, options) {
310
480
  if (!options?.registry) throw new TypeError("resolveRouteRequest requires options.registry.");
481
+ const basePath = normalizeRouteBasePath(options.registry.manifest.basePath);
482
+ const logicalTarget = removeRouteBasePath(target, basePath);
483
+ if (logicalTarget === void 0) return null;
311
484
  return withTelemetry(options.telemetry?.routeMatch, {}, () => {
312
485
  const records = options.registry.manifest.records;
313
- const match = getMatchingRouteRecord(target, records);
486
+ const match = getMatchingRouteRecord(logicalTarget, records);
314
487
  if (!match) return null;
315
488
  const mode = options.mode ?? getDefaultRouteMode();
316
489
  const signal = options.signal ?? getActiveRenderContext()?.signal ?? new AbortController().signal;
317
490
  const authOptions = getActiveRouteAuthOptions(options.auth ?? options.registry.manifest.auth);
318
- const base = buildRouteContextBase(target, match.params, {
491
+ const base = buildRouteContextBase(logicalTarget, match.params, {
319
492
  mode,
320
493
  signal
321
494
  });
495
+ const exposeRedirect = (result) => result?.kind === "redirect" ? {
496
+ ...result,
497
+ to: addRouteBasePath(result.to, basePath)
498
+ } : result;
322
499
  const finalize = (authContext) => {
323
500
  setCurrentAuth(authContext);
324
- return resolveMatchedRoute(match.record, match.params, buildRouteContext(target, match.params, {
501
+ const result = resolveMatchedRoute(match.record, match.params, buildRouteContext(logicalTarget, match.params, {
325
502
  mode,
326
503
  signal,
327
504
  authContext
328
505
  }), authOptions, options.request, options.telemetry, options.load !== false);
506
+ if (!basePath) return result;
507
+ return isPromiseLike(result) ? Promise.resolve(result).then(exposeRedirect) : exposeRedirect(result);
329
508
  };
330
509
  if (options.authContext) return finalize(options.authContext);
331
510
  if (!authOptions?.resolve) return finalize(anonymous());
@@ -339,11 +518,13 @@ function createRouteManifest() {
339
518
  const auth = getDefaultRouteAuthOptions();
340
519
  return {
341
520
  records: [...getRouteRecords()],
342
- ...auth ? { auth } : {}
521
+ ...auth ? { auth } : {},
522
+ ...getDefaultRouteBasePath() ? { basePath: getDefaultRouteBasePath() } : {}
343
523
  };
344
524
  }
345
525
  function _applyManifest(manifest) {
346
526
  setDefaultRouteAuthOptions(manifest.auth);
527
+ setDefaultRouteBasePath(normalizeRouteBasePath(manifest.basePath));
347
528
  for (const record of manifest.records) {
348
529
  insertRecordSorted(record);
349
530
  addRouteToStores({
@@ -359,6 +540,7 @@ function createRouteRegistry(definition, options = {}) {
359
540
  clearRouteState();
360
541
  try {
361
542
  setDefaultRouteAuthOptions(options.auth);
543
+ setDefaultRouteBasePath(normalizeRouteBasePath(options.basePath));
362
544
  definition();
363
545
  const manifest = createRouteManifest();
364
546
  return Object.freeze({
@@ -465,10 +647,10 @@ async function resolveDeferredValues(input, signal) {
465
647
  }
466
648
  function routeData() {
467
649
  const envelope = getCurrentRenderData();
468
- if (envelope) return envelope.route;
650
+ if (envelope) return guardHydratedRouteData(envelope.route, envelope.framework);
469
651
  const runtime = getCurrentAppRenderRuntime();
470
652
  if (!runtime?.hasRoute) throw new Error("routeData() can only be read while rendering a route with loader data.");
471
- return runtime.route;
653
+ return guardHydratedRouteData(runtime.route, runtime.framework);
472
654
  }
473
655
  function rejectedChild(props, error) {
474
656
  return typeof props.rejected === "function" ? props.rejected(error) : props.rejected ?? null;
@@ -499,4 +681,4 @@ function Resolve(props) {
499
681
  return props.children(result.value);
500
682
  }
501
683
  //#endregion
502
- export { _preloadRouteHandler as C, getExecutionModel as E, getRenderHandler as S, assertExecutionModel as T, isPageRenderEnvelope as _, resolveDeferredValues as a, Outlet as b, _applyManifest as c, getRouteRenderContext as d, getRouteRenderData as f, isEmptyPageRenderEnvelope as g, createPageRenderEnvelope as h, isDeferred as i, createRouteRegistry as l, resolveRouteRequest as m, Resolve as n, reviveDeferredValue as o, hasRouteRenderData as p, defer as r, routeData as s, DEFERRED_BOUNDARY as t, bindResolvedRouteData as u, pageRenderEnvelope as v, lazy as w, createRouteHandler as x, currentAuth as y };
684
+ export { getExecutionModel as A, currentAuth as C, _preloadRouteHandler as D, getRenderHandler as E, lazy as O, pageRenderEnvelope as S, createRouteHandler as T, validateRouteHydrationData as _, resolveDeferredValues as a, isEmptyPageRenderEnvelope as b, _applyManifest as c, getRouteRenderContext as d, getRouteRenderData as f, prepareRouteHydrationData as g, getRouteHydrationMetadata as h, isDeferred as i, assertExecutionModel as k, createRouteRegistry as l, resolveRouteRequest as m, Resolve as n, reviveDeferredValue as o, hasRouteRenderData as p, defer as r, routeData as s, DEFERRED_BOUNDARY as t, bindResolvedRouteData as u, withoutRouteHydrationMetadata as v, Outlet as w, isPageRenderEnvelope as x, createPageRenderEnvelope as y };
@@ -1,5 +1,5 @@
1
- import { t as enqueueRuntimeLane } from "./access-ePqdQqkV.js";
2
- import { m as withFineGrainedReadTracking } from "./readable-B3VIOfd7.js";
1
+ import { t as enqueueRuntimeLane } from "./access-Dpfoi4cA.js";
2
+ import { m as withFineGrainedReadTracking } from "./readable-B5NtV7V0.js";
3
3
  //#region src/runtime/effect.ts
4
4
  const SOURCE_EFFECTS = Symbol("askr.source-effects");
5
5
  const effectSources = /* @__PURE__ */ new WeakMap();
@@ -1,6 +1,6 @@
1
- import { b as isDevelopmentEnvironment, l as runRuntimeWithSyncProgress, o as getRuntimeRenderer, u as setRuntimeBulkCommitProbe } from "./access-ePqdQqkV.js";
1
+ import { b as isDevelopmentEnvironment, l as runRuntimeWithSyncProgress, o as getRuntimeRenderer, u as setRuntimeBulkCommitProbe } from "./access-Dpfoi4cA.js";
2
2
  import { i as isFragmentType } from "./jsx-C3IECePB.js";
3
- import { i as finalizeReadableSubscriptions } from "./readable-B3VIOfd7.js";
3
+ import { i as finalizeReadableSubscriptions } from "./readable-B5NtV7V0.js";
4
4
  //#region src/runtime/dev-namespace.ts
5
5
  /**
6
6
  * Set a value in the dev namespace (no-op in production).
@@ -1,10 +1,10 @@
1
1
  import { n as _isDOMElement } from "./vnode-D24p6IW2.js";
2
- import { b as isDevelopmentEnvironment, o as getRuntimeRenderer, v as logger } from "./access-ePqdQqkV.js";
3
- import { _ as rebaseVNodeTreeWithContextFrame, m as haveEquivalentContextFrames, n as createComponentInstance, s as renderScopedComponent } from "./component-internal-DsM75Gl1.js";
4
- import { d as shouldCoalesceFineGrainedItemReads, l as notifyReadableReaders, o as markReactivePropsDirtySource, s as markReadableDerivedSubscribersDirty, u as recordReadableRead } from "./readable-B3VIOfd7.js";
2
+ import { b as isDevelopmentEnvironment, o as getRuntimeRenderer, v as logger } from "./access-Dpfoi4cA.js";
5
3
  import { h as getCurrentStateIndex, i as claimHookIndex, o as clearRenderTracking, p as getCurrentInstance } from "./component-scope-DzJEbYGE.js";
6
- import { i as unregisterOwnedChildScope, r as registerOwnedChildScope, t as cleanupComponent } from "./component-cleanup-oWN45rfa.js";
7
- import { o as finalizeInlineReadSubscriptions, u as registerLifecycleTransaction } from "./lifecycle-batch-CwHCDseR.js";
4
+ import { _ as rebaseVNodeTreeWithContextFrame, m as haveEquivalentContextFrames, n as createComponentInstance, s as renderScopedComponent } from "./component-internal-DcrLymUJ.js";
5
+ import { d as shouldCoalesceFineGrainedItemReads, l as notifyReadableReaders, o as markReactivePropsDirtySource, s as markReadableDerivedSubscribersDirty, u as recordReadableRead } from "./readable-B5NtV7V0.js";
6
+ import { i as unregisterOwnedChildScope, r as registerOwnedChildScope, t as cleanupComponent } from "./component-cleanup-BUfUleGZ.js";
7
+ import { o as finalizeInlineReadSubscriptions, u as registerLifecycleTransaction } from "./lifecycle-batch-Dfpid9-A.js";
8
8
  //#region src/runtime/child-scope.ts
9
9
  const EMPTY_CHILD_SCOPE_PROPS = {};
10
10
  const childScopesByInstance = /* @__PURE__ */ new WeakMap();
@@ -200,9 +200,10 @@ function stageBranchRemoval(state, scope) {
200
200
  const transaction = state._transaction;
201
201
  if (transaction && !transaction.removedScopes.includes(scope)) transaction.removedScopes.push(scope);
202
202
  else if (!transaction) disposeChildScope(scope);
203
- const removedDom = scope.dom;
203
+ const removedRange = getRuntimeRenderer().resolveChildScopeRange?.(scope) ?? scope.range;
204
+ const removedDom = removedRange?.single ? removedRange.start : scope.dom;
204
205
  if (removedDom) state.lastRemovedNodes.push(removedDom);
205
- if (scope.range) state.lastRemovedRanges.push(scope.range);
206
+ if (removedRange) state.lastRemovedRanges.push(removedRange);
206
207
  }
207
208
  function commitControlTransaction(state, transaction) {
208
209
  if (state._transaction !== transaction) return;
@@ -603,11 +604,30 @@ const REACTIVE_FOR_ITEM_PROXY_HANDLER = {
603
604
  metadata.wholeItemRead = true;
604
605
  recordReadableRead(getWholeItemSignal(metadata));
605
606
  return Object.getPrototypeOf(Object(metadata.currentItem));
607
+ },
608
+ set(target, prop, value) {
609
+ if (!(prop in target) && typeof prop !== "symbol" && isDevelopmentEnvironment()) {
610
+ const metadata = getReactiveForItemState(target);
611
+ if (prop in Object(metadata.currentItem)) logger.warn(`[Askr] Assigning to "${String(prop)}" on a <For> item shadows a property from the source data - it will not update when the source item changes and does not trigger a re-render. Use a different property name for cached/derived per-row values, or a state() cell for values that should be reactive.`);
612
+ }
613
+ return Reflect.defineProperty(target, prop, {
614
+ value,
615
+ writable: true,
616
+ enumerable: true,
617
+ configurable: true
618
+ });
606
619
  }
607
620
  };
608
621
  const recordBenchEvent = (_event) => {};
609
622
  //#endregion
610
623
  //#region src/runtime/for-scopes.ts
624
+ function resolveScopeBoundary(scope) {
625
+ const range = getRuntimeRenderer().resolveChildScopeRange?.(scope) ?? scope.range;
626
+ return {
627
+ dom: range?.single ? range.start : scope.dom,
628
+ range
629
+ };
630
+ }
611
631
  function recordRemovedBoundary(forState, dom, range) {
612
632
  if (range && !range.single) forState.lastRemovedRanges.push(range);
613
633
  else if (dom) forState.lastRemovedNodes.push(dom);
@@ -616,8 +636,11 @@ function captureRemovedScopeNodes(forState, dom, range) {
616
636
  const transaction = forState._transaction;
617
637
  if (!transaction) return;
618
638
  const nodes = transaction.removedScopeNodes ??= [];
619
- if (range && !range.single) for (let node = range.start.nextSibling; node && node !== range.end; node = node.nextSibling) nodes.push(node);
620
- else if (dom) nodes.push(dom);
639
+ if (range && !range.single) {
640
+ nodes.push(range.start);
641
+ for (let node = range.start.nextSibling; node && node !== range.end; node = node.nextSibling) nodes.push(node);
642
+ nodes.push(range.end);
643
+ } else if (dom) nodes.push(dom);
621
644
  }
622
645
  function enqueueForScopeUpdate(parent) {
623
646
  parent?._enqueueRun?.();
@@ -734,8 +757,7 @@ function renderItemScope(forState, scope, item, indexSignal, key) {
734
757
  return vnode;
735
758
  }
736
759
  function disposeItemInstance(forState, itemInstance, domCleanup) {
737
- const removedDom = itemInstance.scope.dom;
738
- const removedRange = itemInstance.scope.range;
760
+ const { dom: removedDom, range: removedRange } = resolveScopeBoundary(itemInstance.scope);
739
761
  const transaction = forState._transaction;
740
762
  if (transaction) {
741
763
  (transaction.removedScopes ??= []).push(itemInstance.scope);
@@ -748,7 +770,7 @@ function disposeItemInstance(forState, itemInstance, domCleanup) {
748
770
  } catch (err) {
749
771
  if (isDevelopmentEnvironment()) console.error("[For] Cleanup error:", err);
750
772
  }
751
- if (!removedDom) return;
773
+ if (!removedDom && !removedRange) return;
752
774
  if (removedDom instanceof Element) {
753
775
  if (domCleanup === "teardown") getRuntimeRenderer().teardownNodeSubtree(removedDom);
754
776
  }
@@ -842,15 +864,14 @@ const FOR_FALLBACK_SCOPE_KEY = "__for-fallback__";
842
864
  function disposeFallbackScope(forState, domCleanup) {
843
865
  const fallbackScope = forState.fallbackScope;
844
866
  if (!fallbackScope) return;
845
- const removedDom = fallbackScope.dom;
846
- const removedRange = fallbackScope.range;
867
+ const { dom: removedDom, range: removedRange } = resolveScopeBoundary(fallbackScope);
847
868
  const transaction = forState._transaction;
848
869
  if (transaction) {
849
870
  (transaction.removedScopes ??= []).push(fallbackScope);
850
871
  captureRemovedScopeNodes(forState, removedDom, removedRange);
851
872
  } else disposeChildScope(fallbackScope);
852
873
  forState.fallbackScope = null;
853
- if (!removedDom) return;
874
+ if (!removedDom && !removedRange) return;
854
875
  if (removedDom instanceof Element) {
855
876
  if (domCleanup === "teardown") getRuntimeRenderer().teardownNodeSubtree(removedDom);
856
877
  }
@@ -880,8 +901,10 @@ function disposeAllItems(forState, domCleanup) {
880
901
  transaction.removeAllItems = true;
881
902
  for (let index = 0; index < forState.orderedItems.length; index++) {
882
903
  const scope = forState.orderedItems[index]?.scope;
883
- const removedDom = scope?.dom;
884
- const range = scope?.range;
904
+ const { dom: removedDom, range } = scope ? resolveScopeBoundary(scope) : {
905
+ dom: void 0,
906
+ range: void 0
907
+ };
885
908
  if (scope) captureRemovedScopeNodes(forState, removedDom, range);
886
909
  recordRemovedBoundary(forState, removedDom, range);
887
910
  }
@@ -901,6 +924,12 @@ function disposeAllItems(forState, domCleanup) {
901
924
  }
902
925
  //#endregion
903
926
  //#region src/runtime/for-reconcile.ts
927
+ function hasUnmountedComponentHost(node) {
928
+ if (!node) return false;
929
+ const host = node;
930
+ if (host.__ASKR_INSTANCE?.mounted === false) return true;
931
+ return host.__ASKR_INSTANCES?.some((instance) => !instance.mounted) ?? false;
932
+ }
904
933
  function failForValidation(message) {
905
934
  throw new Error(message);
906
935
  }
@@ -1087,11 +1116,15 @@ function reconcileForItems(forState, newArray) {
1087
1116
  resultVNodes.length = newLen;
1088
1117
  resultItems.length = newLen;
1089
1118
  const isFullClear = newLen === 0;
1119
+ const dirtyIndices = [];
1090
1120
  for (let i = 0; i < newLen; i++) {
1091
1121
  const item = newArray[i];
1092
1122
  const key = orderedKeys[i];
1093
1123
  const existing = items.get(key);
1094
- updateItemInstance(forState, existing, item);
1124
+ const itemChanged = existing.item !== item;
1125
+ const scopeNeedsDomUpdate = existing.scope.needsDomUpdate || hasUnmountedComponentHost(existing.scope.dom);
1126
+ if (itemChanged) updateItemInstance(forState, existing, item);
1127
+ if (itemChanged || scopeNeedsDomUpdate) dirtyIndices.push(i);
1095
1128
  resultItems[i] = existing;
1096
1129
  resultVNodes[i] = existing.scope.vnode;
1097
1130
  }
@@ -1107,7 +1140,7 @@ function reconcileForItems(forState, newArray) {
1107
1140
  forState.orderedKeys = orderedKeys;
1108
1141
  forState.orderedItems.length = newLen;
1109
1142
  forState.orderedVNodes = resultVNodes;
1110
- forState.pendingDirtyIndices = null;
1143
+ forState.pendingDirtyIndices = dirtyIndices;
1111
1144
  forState.pendingSwapIndices = null;
1112
1145
  forState.pendingMoveOnly = false;
1113
1146
  return resultVNodes;
@@ -1151,7 +1184,7 @@ function reconcileForItems(forState, newArray) {
1151
1184
  const key = byFn(newArray[i], i);
1152
1185
  if (key === orderedKeys[i]) {
1153
1186
  const existing = items.get(key);
1154
- if (existing && existing.item !== item) {
1187
+ if (existing && (existing.item !== item || existing.scope.needsDomUpdate)) {
1155
1188
  canUseSwapPath = false;
1156
1189
  break;
1157
1190
  }
@@ -1182,8 +1215,8 @@ function reconcileForItems(forState, newArray) {
1182
1215
  const firstItem = newArray[firstMismatch];
1183
1216
  const secondItem = newArray[secondMismatch];
1184
1217
  const nextOrderedItems = forState.orderedItems.slice();
1185
- nextOrderedItems[firstMismatch] = secondExisting;
1186
- nextOrderedItems[secondMismatch] = firstExisting;
1218
+ nextOrderedItems[firstMismatch] = firstExisting;
1219
+ nextOrderedItems[secondMismatch] = secondExisting;
1187
1220
  if (firstExisting.item !== firstItem) updateItemInstance(forState, firstExisting, firstItem);
1188
1221
  if (secondExisting.item !== secondItem) updateItemInstance(forState, secondExisting, secondItem);
1189
1222
  syncForItemIndex(forState, firstExisting, firstMismatch);
@@ -1,3 +1,3 @@
1
1
  import { r as JSXElement } from "../jsx-Dvr2SGRG.js";
2
- import { _ as definePortal, c as Presence, d as SlotProps, f as LayoutComponent, g as PortalProps, h as Portal, l as PresenceProps, m as DefaultPortal, p as layout, u as Slot } from "../index-Vs1EeWEr.js";
2
+ import { _ as Portal, c as Presence, d as SlotProps, g as DefaultPortal, h as layout, l as PresenceProps, m as LayoutComponent, u as Slot, v as PortalProps, y as definePortal } from "../index-Bkh9S9-t.js";
3
3
  export { DefaultPortal, type JSXElement, type LayoutComponent, Portal, type PortalProps, Presence, type PresenceProps, Slot, type SlotProps, definePortal, layout };
@@ -1,3 +1,3 @@
1
- import { a as layout, i as Slot, r as Presence } from "../structures-D3vO-WMN.js";
2
- import { i as definePortal, n as Portal, t as DefaultPortal } from "../portal-BZ_rN17v.js";
1
+ import { a as layout, i as Slot, r as Presence } from "../structures-DSLF_iis.js";
2
+ import { i as definePortal, n as Portal, t as DefaultPortal } from "../portal-BVWjAhNQ.js";
3
3
  export { DefaultPortal, Portal, Presence, Slot, definePortal, layout };
@@ -1,4 +1,4 @@
1
- import { h as State } from "../../control-Cix3yjVf.js";
1
+ import { v as State } from "../../control-DhUZU4_V.js";
2
2
  //#region src/foundations/state/controllable.d.ts
3
3
  declare function isControlled<T>(value: T | undefined): value is T;
4
4
  declare function resolveControllable<T>(value: T | undefined, defaultValue: T): {
@@ -1,4 +1,4 @@
1
- import { t as state } from "../../state-BCl9DeiN.js";
1
+ import { t as state } from "../../state-DCjONcHK.js";
2
2
  //#region src/foundations/state/controllable.ts
3
3
  /**
4
4
  * controllable
@@ -1,3 +1,3 @@
1
1
  import { r as JSXElement } from "../../jsx-Dvr2SGRG.js";
2
- import { _ as definePortal, a as Collection, c as Presence, d as SlotProps, f as LayoutComponent, g as PortalProps, h as Portal, i as createLayer, l as PresenceProps, m as DefaultPortal, n as LayerManager, o as CollectionItem, p as layout, r as LayerOptions, s as createCollection, t as Layer, u as Slot } from "../../index-Vs1EeWEr.js";
3
- export { type Collection, type CollectionItem, DefaultPortal, type JSXElement, type Layer, type LayerManager, type LayerOptions, type LayoutComponent, Portal, type PortalProps, Presence, type PresenceProps, Slot, type SlotProps, createCollection, createLayer, definePortal, layout };
2
+ import { _ as Portal, a as Collection, c as Presence, d as SlotProps, f as cloneElement, g as DefaultPortal, h as layout, i as createLayer, l as PresenceProps, m as LayoutComponent, n as LayerManager, o as CollectionItem, p as isElement, r as LayerOptions, s as createCollection, t as Layer, u as Slot, v as PortalProps, y as definePortal } from "../../index-Bkh9S9-t.js";
3
+ export { type Collection, type CollectionItem, DefaultPortal, type JSXElement, type Layer, type LayerManager, type LayerOptions, type LayoutComponent, Portal, type PortalProps, Presence, type PresenceProps, Slot, type SlotProps, cloneElement, createCollection, createLayer, definePortal, isElement, layout };
@@ -1,3 +1,3 @@
1
- import { a as layout, i as Slot, n as createCollection, r as Presence, t as createLayer } from "../../structures-D3vO-WMN.js";
2
- import { i as definePortal, n as Portal, t as DefaultPortal } from "../../portal-BZ_rN17v.js";
3
- export { DefaultPortal, Portal, Presence, Slot, createCollection, createLayer, definePortal, layout };
1
+ import { a as layout, i as Slot, n as createCollection, o as cloneElement, r as Presence, s as isElement, t as createLayer } from "../../structures-DSLF_iis.js";
2
+ import { i as definePortal, n as Portal, t as DefaultPortal } from "../../portal-BVWjAhNQ.js";
3
+ export { DefaultPortal, Portal, Presence, Slot, cloneElement, createCollection, createLayer, definePortal, isElement, layout };
package/dist/fx/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { h as scheduleEventHandler, n as enqueueRuntimeTask, v as logger } from "../access-ePqdQqkV.js";
1
+ import { h as scheduleEventHandler, n as enqueueRuntimeTask, v as logger } from "../access-Dpfoi4cA.js";
2
2
  import { f as getCurrentComponentInstance } from "../component-scope-DzJEbYGE.js";
3
3
  //#region src/fx/timing.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { c as RenderableChild } from "./control-Cix3yjVf.js";
1
+ import { u as RenderableChild } from "./control-DhUZU4_V.js";
2
2
  import { r as JSXElement } from "./jsx-Dvr2SGRG.js";
3
3
  import "./types-C9YcqVpk.js";
4
4
  //#region src/runtime/portal.d.ts
@@ -44,6 +44,10 @@ type LayoutComponent<P = object> = (props: P & {
44
44
  }) => unknown;
45
45
  declare function layout<P = object>(Layout: LayoutComponent<P>): (children?: RenderableChild, props?: P) => unknown;
46
46
  //#endregion
47
+ //#region src/jsx/utils.d.ts
48
+ declare function isElement(value: unknown): value is JSXElement;
49
+ declare function cloneElement(element: JSXElement, props: Record<string, unknown>): JSXElement;
50
+ //#endregion
47
51
  //#region src/foundations/structures/slot.d.ts
48
52
  type SlotProps = {
49
53
  asChild: true;
@@ -276,4 +280,4 @@ declare function createLayer(): LayerManager;
276
280
  * }
277
281
  */
278
282
  //#endregion
279
- export { definePortal as _, Collection as a, Presence as c, SlotProps as d, LayoutComponent as f, PortalProps as g, Portal as h, createLayer as i, PresenceProps as l, DefaultPortal as m, LayerManager as n, CollectionItem as o, layout as p, LayerOptions as r, createCollection as s, Layer as t, Slot as u };
283
+ export { Portal as _, Collection as a, Presence as c, SlotProps as d, cloneElement as f, DefaultPortal as g, layout as h, createLayer as i, PresenceProps as l, LayoutComponent as m, LayerManager as n, CollectionItem as o, isElement as p, LayerOptions as r, createCollection as s, Layer as t, Slot as u, PortalProps as v, definePortal as y };
@@ -1,4 +1,4 @@
1
- import { _ as StateTuple, c as RenderableChild, i as Scope, n as ReadableSource, r as ComponentInstance } from "./control-Cix3yjVf.js";
1
+ import { a as ComponentInstance, b as StateTuple, i as ReadableSource, n as ChildScope, o as Scope, r as DOMRange, u as RenderableChild } from "./control-DhUZU4_V.js";
2
2
  import { r as JSXElement } from "./jsx-Dvr2SGRG.js";
3
3
  import { t as Scheduler } from "./scheduler-CmLO4Dfm.js";
4
4
  import { D as QueryPrefetchContext, _ as CoreTelemetry } from "./index-5S-fi2xy.js";
@@ -30,7 +30,8 @@ interface RuntimeKeyedReorderDecision {
30
30
  interface RuntimeRendererHost {
31
31
  evaluate(node: unknown, target: Element | null, context?: object, retainedOwner?: ComponentInstance): void;
32
32
  cleanupInstancesUnder(node: Node): void;
33
- replaceComponentRange(instance: ComponentInstance, result: unknown, placeholder: Comment): Node | null;
33
+ replaceComponentRange(instance: ComponentInstance, result: unknown, host: Element | Comment): Node | null;
34
+ resolveChildScopeRange?(scope: ChildScope): DOMRange | null;
34
35
  teardownNodeSubtree(root: Node): void;
35
36
  populateKeyMapForElement(parent: Element): void;
36
37
  getKeyMapForElement(parent: Element): Map<string | number, Element> | undefined;
@@ -157,10 +158,20 @@ type RouteMetaSource<TParams extends RouteParams = RouteParams> = RouteMeta | ((
157
158
  * - `title`: page title hint used by SSG and document-meta integrations
158
159
  * - `namespace`: MFE namespace key for grouped route management
159
160
  */
160
- interface RouteOptions<TParams extends RouteParams = RouteParams, TSearchSchema extends ObjectSchema<RouteSearch> | undefined = ObjectSchema<RouteSearch> | undefined> extends CommonAccessOptions {
161
+ interface RouteOptions<TParams extends RouteParams = RouteParams, TSearchSchema extends ObjectSchema<RouteSearch> | undefined = ObjectSchema<RouteSearch> | undefined, TLoaderData = unknown, TDehydratedData = TLoaderData> extends CommonAccessOptions {
161
162
  loader?: (context: RouteContext<TParams> & {
162
163
  request?: Request;
163
- }) => unknown;
164
+ }) => TLoaderData | PromiseLike<TLoaderData>;
165
+ /**
166
+ * Select the loader data transported to the browser for initial hydration.
167
+ *
168
+ * Server rendering still receives the complete loader value. The selector
169
+ * must be synchronous; client navigations rerun the loader and receive its
170
+ * complete result.
171
+ */
172
+ dehydrate?: (data: TLoaderData, context: RouteContext<TParams> & {
173
+ request?: Request;
174
+ }) => TDehydratedData extends PromiseLike<unknown> ? never : TDehydratedData;
164
175
  preload?: (context: RouteContext<TParams> & {
165
176
  request?: Request;
166
177
  data: QueryPrefetchContext;
@@ -198,6 +209,8 @@ interface RouteRef<TParams extends RouteParams = RouteParams, TSearch = RouteSea
198
209
  readonly path: string;
199
210
  /** @internal Executable schema retained for destination validation. */
200
211
  readonly searchSchema?: ObjectSchema<TSearch & RouteSearch>;
212
+ /** @internal Public mount point captured by createRouteRegistry(). */
213
+ readonly basePath?: string;
201
214
  readonly __params?: TParams;
202
215
  readonly __search?: TSearch;
203
216
  }
@@ -238,6 +251,8 @@ interface PageScopeRecord {
238
251
  }
239
252
  interface RouteRegistryOptions {
240
253
  auth?: RouteAuthOptions;
254
+ /** Public pathname prefix for applications mounted below the origin root. */
255
+ basePath?: string;
241
256
  }
242
257
  type RouteDefinition = () => void;
243
258
  interface RouteRequestOptions {
@@ -313,6 +328,8 @@ interface RouteRecord {
313
328
  interface RouteManifest {
314
329
  records: RouteRecord[];
315
330
  auth?: RouteAuthOptions;
331
+ /** Normalized public pathname prefix. Empty and root mounts omit it. */
332
+ basePath?: string;
316
333
  }
317
334
  declare const routeRegistryBrand: unique symbol;
318
335
  interface RouteHandler<TParams extends RouteParams = RouteParams> {