uniweb 0.48.6 → 0.49.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.48.6",
3
+ "version": "0.49.0",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,13 +41,13 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "^0.24.4",
45
- "@uniweb/kit": "^0.18.2",
44
+ "@uniweb/core": "^0.25.0",
45
+ "@uniweb/kit": "^0.18.3",
46
46
  "@uniweb/semantic-parser": "^1.4.0",
47
- "@uniweb/runtime": "^0.19.5"
47
+ "@uniweb/runtime": "^0.20.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/build": "^0.44.5",
50
+ "@uniweb/build": "^0.45.0",
51
51
  "@uniweb/content-reader": "^1.2.4",
52
52
  "@uniweb/semantic-parser": "^1.4.0"
53
53
  },
@@ -857,7 +857,7 @@ A bare string is a path under `entities/`, naming one file or matching many.
857
857
  - article/2025-*.md
858
858
  ```
859
859
 
860
- A query then slices it with `where: { path: { under: 'archive' } }`. An entity belongs to one folder; if you want a computed subset, that is a query, not a second placement.
860
+ A query reads one branch with `scope: archive` — the folder and everything inside it (the older `where: { path: { under: } }` is refused by the build). An entity belongs to one folder; if you want a computed subset, that is a query, not a second placement.
861
861
 
862
862
  **`queries.yml` — how content is reached.** A bare map of name → query. A query names a schema and the published records of that schema are its rows:
863
863
 
@@ -897,9 +897,11 @@ pages/blog/
897
897
 
898
898
  `entities/article/design-tips.md` becomes `/blog/design-tips`. The section inside `[slug]/` needs no special markdown — the matched record is delivered to it. Generated pages are excluded from navigation menus.
899
899
 
900
+ **Which query the URL narrows — the page's route query:** the `[slug]` page's own `data:`, else its parent page's (the usual shape, above), else `site.yml`'s; if none declares one, the query its sections all declare. The first query of that level wins. Every section the route query reaches gets the one record; a section declaring a *different* query of its own gets that query as declared. The folder name says what the URL segment matches: `[slug]` the record's handle (`$name`, which compiled records carry — equal to their `slug`), `[uuid]` its `$uuid`, any other `[name]` the record's own field of that name. A folder inside `[slug]/` (`[slug]/cv/` → `/blog/:slug/cv`) is a parametric page too, reading the record when `[slug]/page.yml` declares the query. `[dir]` and `[path]` are refused as folder names, and so is any folder inside `[...path]/`.
901
+
900
902
  > **The record arrives as a single-element array under the query key** — `content.data.recent[0]`, not `content.data.article`. The runtime never coerces it to an object and never synthesizes a singular key. See *Data* in Part 4.
901
903
 
902
- **Records with URLs of their own shape — `[...path]/`.** A folder named exactly `[...path]` (one fixed spelling) captures the rest of the URL: `/blog/my-post` and `/blog/rust/2025/my-post` both reach it. The capture yields three standard variables — `:path` (the whole capture), `:dir` (everything before the last segment), `:slug` (the last segment, the record's handle) — and the record is still delivered by `slug`, so the section reads `content.data.recent[0]` as before. A record's URL is its folder placement plus its slug (`- folder: rust/2025` in `records.yml` → `/blog/rust/2025/my-post`). A query may bind a part — `scope: :dir` exposes the folder branch, `where: { tag: :dir }` keeps it private — and an unbound variable drops its clause, so one saved query serves the list page and the detail page. Reference: `reference/dynamic-routes.md`.
904
+ **Records with URLs of their own shape — `[...path]/`.** A folder named exactly `[...path]` (one fixed spelling) captures the rest of the URL: `/blog/my-post` and `/blog/rust/2025/my-post` both reach it. The capture yields three standard variables — `:path` (the whole capture), `:dir` (everything before the last segment), `:slug` (the last segment, the record's handle) — and the record is still delivered by its handle, so the section reads `content.data.recent[0]` as before. The same three variables exist under every parametric page: under `[slug]`, `:slug` and `:path` are the segment and `:dir` is empty. A record's URL is its folder placement plus its slug (`- folder: rust/2025` in `records.yml` → `/blog/rust/2025/my-post`). A query may bind a part — `scope: :dir` exposes the folder branch, `where: { tag: :dir }` keeps it private — and an unbound or empty variable drops its clause, so one saved query serves the list page and the parametric page, on a static site and a hosted one alike. Without `scope: :dir` the directory is decoration: the record is found by its handle wherever it sits. Reference: `reference/dynamic-routes.md`.
903
905
 
904
906
  **Two options for bigger sets:**
905
907
 
@@ -1773,7 +1775,7 @@ Content-less containers appear as group nodes (`hasContent: false`) — use `nav
1773
1775
 
1774
1776
  A component on a page with a `data:` or `fetch:` declaration automatically receives that data in `content.data.{key}` — no opt-in in `meta.js`.
1775
1777
 
1776
- **Bound collections always arrive as arrays.** On a list page, `content.data.articles` is the full collection. On a template page (`[slug]/`), the matched record is delivered under the *same* key as a single-element array — the detail section reads `content.data.articles[0]`. When nothing matches, the key is `[]`. The runtime never coerces to a single object and never synthesizes a singular key.
1778
+ **Bound collections always arrive as arrays.** On a list page, `content.data.articles` is the full collection. On a parametric page (`[slug]/`), the matched record is delivered under the *same* key as a single-element array — the detail section reads `content.data.articles[0]`. When nothing matches, the key is `[]`. The runtime never coerces to a single object and never synthesizes a singular key.
1777
1779
 
1778
1780
  ```jsx
1779
1781
  function Article({ content, block }) {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-09-10T23:52:15.147Z",
3
+ "generatedAt": "2026-09-11T18:41:11.718Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "0.3.1",
6
+ "version": "0.3.2",
7
7
  "path": "framework/api",
8
8
  "deps": [
9
9
  "@uniweb/core"
10
10
  ]
11
11
  },
12
12
  "@uniweb/build": {
13
- "version": "0.44.5",
13
+ "version": "0.45.0",
14
14
  "path": "framework/build",
15
15
  "deps": [
16
16
  "@uniweb/content-reader",
@@ -34,7 +34,7 @@
34
34
  "deps": []
35
35
  },
36
36
  "@uniweb/core": {
37
- "version": "0.24.4",
37
+ "version": "0.25.0",
38
38
  "path": "framework/core",
39
39
  "deps": [
40
40
  "@uniweb/semantic-parser",
@@ -47,14 +47,14 @@
47
47
  "deps": []
48
48
  },
49
49
  "@uniweb/icons": {
50
- "version": "0.4.15",
50
+ "version": "0.4.16",
51
51
  "path": "framework/icons",
52
52
  "deps": [
53
53
  "@uniweb/core"
54
54
  ]
55
55
  },
56
56
  "@uniweb/kit": {
57
- "version": "0.18.2",
57
+ "version": "0.18.3",
58
58
  "path": "framework/kit",
59
59
  "deps": [
60
60
  "@uniweb/core",
@@ -74,7 +74,7 @@
74
74
  "deps": []
75
75
  },
76
76
  "@uniweb/projections": {
77
- "version": "0.6.0",
77
+ "version": "0.6.1",
78
78
  "path": "framework/projections",
79
79
  "deps": [
80
80
  "@uniweb/content-writer",
@@ -82,7 +82,7 @@
82
82
  ]
83
83
  },
84
84
  "@uniweb/runtime": {
85
- "version": "0.19.5",
85
+ "version": "0.20.0",
86
86
  "path": "framework/runtime",
87
87
  "deps": [
88
88
  "@uniweb/core",