@uniweb/core 0.26.0 → 0.26.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/core",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
@@ -44,8 +44,8 @@
44
44
  "vitest": "^4.1.7"
45
45
  },
46
46
  "dependencies": {
47
- "@uniweb/theming": "^0.1.15",
48
- "@uniweb/semantic-parser": "^1.4.0"
47
+ "@uniweb/semantic-parser": "^1.4.0",
48
+ "@uniweb/theming": "^0.1.15"
49
49
  },
50
50
  "scripts": {
51
51
  "test": "vitest run"
package/src/index.js CHANGED
@@ -71,6 +71,7 @@ export {
71
71
  routeParamName,
72
72
  parentRouteOf,
73
73
  recordRouteBase,
74
+ findPageForRoute,
74
75
  } from './route-match.js'
75
76
  export { isRichSchema } from './schemas.js'
76
77
  // ⛔ `Tracker` is NOT on the package entry. It is a FEATURE, not part of the
@@ -556,3 +556,49 @@ export function stripLocalePrefix(route, activeLocale, defaultLocale) {
556
556
  if (route.startsWith(`${prefix}/`)) return route.slice(prefix.length)
557
557
  return route
558
558
  }
559
+
560
+ /**
561
+ * ⭐ **THE PAGE A ROUTE NAMES — one rule, for every lane that asks the question.**
562
+ *
563
+ * Exact match first, then the parametric pages in page order, which is the order
564
+ * `Website#getPage` uses in the SPA. Captured params are decoded by
565
+ * `matchDynamicRoute` (a catch-all per segment), so the values are what a site's
566
+ * query binds against.
567
+ *
568
+ * ⛔ **Why this is a leaf rather than a thing each lane composes.** More than one
569
+ * lane asks *which page does this path name* — the SPA, the runtime's server-side
570
+ * prefetch, and a host's router. Our leaves (`normalizeRoute`, `isDynamicRoute`,
571
+ * `routePatternToRegex`) were importable and the RULE was not, so a consumer
572
+ * imported the parts and wrote the composition itself. **A renamed export breaks
573
+ * loudly; a re-implemented rule drifts silently.** A hand-written matcher spelled
574
+ * `:(\w+)` accepts a different param alphabet than our `[A-Za-z0-9_-]+`, so a
575
+ * hyphenated slug resolves in one copy and 404s in the other, with nothing failing
576
+ * anywhere. Import this; do not rebuild it.
577
+ *
578
+ * ⚠️ **A trailing slash is the same route, on BOTH branches** (`normalizeRoute`),
579
+ * which is what `Website#getPage` has always done. The runtime's own prefetch copy
580
+ * compared raw strings until 2026-09-12 and so disagreed with the SPA about
581
+ * `/about/` — the exact class of split this function exists to end.
582
+ *
583
+ * ⚖️ Matching is not resolving: a hit means the ROUTE exists. Whether the record
584
+ * behind a parametric page exists is a data question answered later.
585
+ *
586
+ * @param {Array<Object>|{pages?: Array<Object>}} source - the pages, or a payload holding them
587
+ * @param {string} route - the concrete path
588
+ * @returns {{ page: Object|null, params: Record<string,string> }} `page` is null when nothing matches
589
+ */
590
+ export function findPageForRoute(source, route) {
591
+ const pages = Array.isArray(source) ? source : source?.pages
592
+ const list = Array.isArray(pages) ? pages : []
593
+ const wanted = normalizeRoute(route)
594
+
595
+ for (const page of list) {
596
+ if (typeof page?.route === 'string' && normalizeRoute(page.route) === wanted) return { page, params: {} }
597
+ }
598
+ for (const page of list) {
599
+ if (typeof page?.route !== 'string' || !isDynamicRoute(page.route)) continue
600
+ const hit = matchDynamicRoute(page.route, wanted)
601
+ if (hit) return { page, params: hit.params }
602
+ }
603
+ return { page: null, params: {} }
604
+ }