@uniweb/core 0.31.0 → 0.32.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/package.json +1 -1
- package/src/fetch-config.js +3 -3
- package/src/index.js +3 -3
- package/src/query-evaluation.js +7 -4
- package/src/route-match.js +14 -13
- package/src/scope.js +41 -8
package/package.json
CHANGED
package/src/fetch-config.js
CHANGED
|
@@ -688,9 +688,9 @@ function bindWhere(where, variables) {
|
|
|
688
688
|
|
|
689
689
|
/**
|
|
690
690
|
* ⭐ `scope:` IS ITS OWN FIELD, on both lanes. The service takes it natively; on
|
|
691
|
-
* the compiled file the evaluators apply it to each record
|
|
692
|
-
* the folder `records/folder.yml` put it in) — `@uniweb/core`'s `applyScope`,
|
|
693
|
-
* build uses too. ⛔ Until 2026-09-11 it was folded here into
|
|
691
|
+
* the compiled file the evaluators apply it to the branch each record is held with
|
|
692
|
+
* (`$branch`, the folder `records/folder.yml` put it in) — `@uniweb/core`'s `applyScope`,
|
|
693
|
+
* the one the build uses too. ⛔ Until 2026-09-11 it was folded here into
|
|
694
694
|
* `where: { path: { under } }`, an authored form that is now retired in its
|
|
695
695
|
* favour [Diego]: a branch is a scope, and `where` stays the author's predicate.
|
|
696
696
|
*
|
package/src/index.js
CHANGED
|
@@ -55,9 +55,9 @@ export {
|
|
|
55
55
|
isDataUrl
|
|
56
56
|
} from './data-paths.js'
|
|
57
57
|
export { evaluate as evaluateWhere, match as matchWhere, whereOutsideLanguage } from './where.js'
|
|
58
|
-
// A query's `scope:` over records
|
|
59
|
-
// default fetcher and by `@uniweb/build
|
|
60
|
-
export { applyScope, withinScope } from './scope.js'
|
|
58
|
+
// A query's `scope:` over compiled records, which hold their branch under `BRANCH_KEY` —
|
|
59
|
+
// read by the runtime's default fetcher and by `@uniweb/build`, which writes it.
|
|
60
|
+
export { applyScope, withinScope, withoutBranch, BRANCH_KEY } from './scope.js'
|
|
61
61
|
// The one sort evaluator and the one href encoder — both read by `@uniweb/build`
|
|
62
62
|
// (materialization, the `route:` bake) and by `@uniweb/runtime` (the fallback),
|
|
63
63
|
// which is what keeps the static and live lanes answering a query identically.
|
package/src/query-evaluation.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { match as matchWhere } from './where.js'
|
|
27
|
-
import { applyScope } from './scope.js'
|
|
27
|
+
import { applyScope, withoutBranch } from './scope.js'
|
|
28
28
|
import { sortRecords, parseSort } from './sort.js'
|
|
29
29
|
import { recordKeyValues } from './route-match.js'
|
|
30
30
|
|
|
@@ -42,7 +42,10 @@ import { recordKeyValues } from './route-match.js'
|
|
|
42
42
|
* @returns {Array<Object>|*} the records the config selects, in its order
|
|
43
43
|
*/
|
|
44
44
|
export function evaluateQuery(records, config, { locale = null, sort = sortRecords } = {}) {
|
|
45
|
-
if (!Array.isArray(records)
|
|
45
|
+
if (!Array.isArray(records)) return records
|
|
46
|
+
// ⛔ What this returns is an ANSWER, and no answer carries a record's branch: `scope` reads
|
|
47
|
+
// the one a compiled file holds, and it goes no further (`withoutBranch`, ruled 2026-09-27).
|
|
48
|
+
if (!config || typeof config !== 'object') return withoutBranch(records)
|
|
46
49
|
const order = (items, expr) => sort(items, expr, { locale })
|
|
47
50
|
|
|
48
51
|
let out = records
|
|
@@ -52,12 +55,12 @@ export function evaluateQuery(records, config, { locale = null, sort = sortRecor
|
|
|
52
55
|
if (isLimit(config.limit)) out = out.slice(0, config.limit)
|
|
53
56
|
|
|
54
57
|
const narrow = config.narrow
|
|
55
|
-
if (!narrow || typeof narrow !== 'object') return out
|
|
58
|
+
if (!narrow || typeof narrow !== 'object') return withoutBranch(out)
|
|
56
59
|
if (narrow.where) out = matchWhere(narrow.where, out)
|
|
57
60
|
if (narrow.match && typeof narrow.match === 'object') out = out.filter(matching(narrow.match))
|
|
58
61
|
if (narrow.sort) out = order(out, narrow.sort)
|
|
59
62
|
if (isLimit(narrow.limit)) out = out.slice(0, narrow.limit)
|
|
60
|
-
return out
|
|
63
|
+
return withoutBranch(out)
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/**
|
package/src/route-match.js
CHANGED
|
@@ -228,8 +228,10 @@ export function splitPathCapture(capture) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/**
|
|
231
|
-
* The inverse of `splitPathCapture` — a
|
|
232
|
-
* `
|
|
231
|
+
* The inverse of `splitPathCapture` — a capture from its directory and its last
|
|
232
|
+
* segment. `dir` may be empty. ⛔ Not a record's URL: a record carries no branch, so its
|
|
233
|
+
* own URL under `[...path]` is its handle alone (`fillRoutePattern`). Until 2026-09-27 this
|
|
234
|
+
* composed a record's placement and handle into one.
|
|
233
235
|
*
|
|
234
236
|
* @param {{ dir?: string|null, slug?: string|null }} parts
|
|
235
237
|
* @returns {string|null} null when there is no slug to name the record by
|
|
@@ -587,18 +589,17 @@ export function fillRoutePattern(pattern, values, { field = null } = {}) {
|
|
|
587
589
|
let tailHref = ''
|
|
588
590
|
const tail = pattern.match(CATCH_ALL)
|
|
589
591
|
if (tail) {
|
|
590
|
-
// A catch-all is filled
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
// `
|
|
592
|
+
// ⭐ A catch-all is filled with the record's handle ALONE — one segment, whatever it
|
|
593
|
+
// contains (a `/` inside it is a value). A record carries no branch [Diego,
|
|
594
|
+
// 2026-09-27], so its own URL under `[...path]` is the one it has under `[slug]`:
|
|
595
|
+
// `/logbook/<name>`. A URL that names a directory still reaches the page, and
|
|
596
|
+
// `scope: :dir` is what makes the directory count. ⛔ Until 2026-09-27 this prefixed the
|
|
597
|
+
// record's `path`, so one record was linked at `/logbook/field/river-survey` on a static
|
|
598
|
+
// site and at `/logbook/river-survey` where a records service, which answers no branch,
|
|
599
|
+
// served it.
|
|
595
600
|
const [handle] = routeParamValues(values, 'slug', field)
|
|
596
|
-
if (
|
|
597
|
-
|
|
598
|
-
const segments = dir.split('/').filter(Boolean).map((seg) => encodeURIComponent(seg))
|
|
599
|
-
// The handle is ONE segment whatever it contains: a `/` inside it is a value.
|
|
600
|
-
segments.push(encodeURIComponent(String(handle)))
|
|
601
|
-
tailHref = '/' + segments.join('/')
|
|
601
|
+
if (handle === undefined) return null
|
|
602
|
+
tailHref = '/' + encodeURIComponent(String(handle))
|
|
602
603
|
head = pattern.slice(0, tail.index)
|
|
603
604
|
}
|
|
604
605
|
const href = head.replace(new RegExp(`:(${PARAM_NAME})`, 'g'), (_, name) => {
|
package/src/scope.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A query's `scope:` — the folder branch it reads — evaluated over records
|
|
3
|
-
*
|
|
2
|
+
* A query's `scope:` — the folder branch it reads — evaluated over compiled records,
|
|
3
|
+
* which hold their branch.
|
|
4
4
|
*
|
|
5
|
-
* A record
|
|
6
|
-
* query-processor.js`). A scope
|
|
7
|
-
*
|
|
8
|
-
* The root (`''`) holds everything.
|
|
5
|
+
* A compiled record holds the folder `records/folder.yml` placed it in under
|
|
6
|
+
* `$branch` (`BRANCH_KEY`, written by `build/src/site/query-processor.js`). A scope
|
|
7
|
+
* contains that folder and every folder below it, at SEGMENT boundaries: `field`
|
|
8
|
+
* holds `field` and `field/2025`, never `fieldwork`. The root (`''`) holds everything.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ A RECORD DOES NOT CARRY ITS BRANCH (ruled 2026-09-27 [Diego]: *"branches are to
|
|
11
|
+
* organize the content pool … queries can filter by branch using `scope`"*). `$branch`
|
|
12
|
+
* is the compiled file's own key, there because the browser evaluates `scope` over that
|
|
13
|
+
* file. `evaluateQuery` reads it and answers no record with it (`withoutBranch`), just as
|
|
14
|
+
* a records service answers none. ⛔ Until then every compiled record carried `path`,
|
|
15
|
+
* which reached components, overwrote an authored field named `path`, and made a
|
|
16
|
+
* `[...path]` record's link `<branch>/<name>`.
|
|
9
17
|
*
|
|
10
18
|
* ⭐ Why this is its own field and not a `where` operator: a branch is a scope,
|
|
11
19
|
* and `where` stays the author's predicate over the records' own fields. Ruled
|
|
@@ -18,6 +26,31 @@
|
|
|
18
26
|
* Zero-dependency leaf.
|
|
19
27
|
*/
|
|
20
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The key a compiled record holds its branch under. The static lane's store only; no
|
|
31
|
+
* answer carries it.
|
|
32
|
+
*/
|
|
33
|
+
export const BRANCH_KEY = '$branch'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Records as a query answers them: without the branch their compiled file holds. A list
|
|
37
|
+
* in which no record holds one is returned as it is.
|
|
38
|
+
*
|
|
39
|
+
* @param {Array<Object>} records
|
|
40
|
+
* @returns {Array<Object>}
|
|
41
|
+
*/
|
|
42
|
+
export function withoutBranch(records) {
|
|
43
|
+
if (!Array.isArray(records)) return records
|
|
44
|
+
if (!records.some((r) => r && typeof r === 'object' && Object.prototype.hasOwnProperty.call(r, BRANCH_KEY))) {
|
|
45
|
+
return records
|
|
46
|
+
}
|
|
47
|
+
return records.map((r) => {
|
|
48
|
+
if (!r || typeof r !== 'object' || !Object.prototype.hasOwnProperty.call(r, BRANCH_KEY)) return r
|
|
49
|
+
const { [BRANCH_KEY]: _branch, ...rest } = r
|
|
50
|
+
return rest
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
21
54
|
function trimSlashes(s) {
|
|
22
55
|
return s.replace(/^\/+/, '').replace(/\/+$/, '')
|
|
23
56
|
}
|
|
@@ -29,7 +62,7 @@ function trimSlashes(s) {
|
|
|
29
62
|
* `2024`, the classic prefix bug, which silently returns records from a sibling
|
|
30
63
|
* folder the author never named.
|
|
31
64
|
*
|
|
32
|
-
* @param {*} placement - a record's `
|
|
65
|
+
* @param {*} placement - a compiled record's branch (`BRANCH_KEY`)
|
|
33
66
|
* @param {string} scope
|
|
34
67
|
* @returns {boolean}
|
|
35
68
|
*/
|
|
@@ -52,5 +85,5 @@ export function withinScope(placement, scope) {
|
|
|
52
85
|
export function applyScope(records, scope) {
|
|
53
86
|
if (!Array.isArray(records)) return records
|
|
54
87
|
if (typeof scope !== 'string' || trimSlashes(scope) === '') return records
|
|
55
|
-
return records.filter((r) => r && typeof r === 'object' && withinScope(r
|
|
88
|
+
return records.filter((r) => r && typeof r === 'object' && withinScope(r[BRANCH_KEY], scope))
|
|
56
89
|
}
|