uniweb 0.48.6 → 0.50.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.50.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.1",
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.1"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/build": "^0.44.5",
50
+ "@uniweb/build": "^0.46.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
 
@@ -874,12 +874,12 @@ team:
874
874
 
875
875
  You can keep the same declarations under `queries:` in `site.yml` instead, if you would rather have one file.
876
876
 
877
- **Show a query on a page** with `data:` in `page.yml` (the whole result), or `fetch:` in a section's frontmatter (a subset). A list — `data: [team, articles]` — declares several, each arriving under its own `content.data` key:
877
+ **Show a query on a page** with `query:` in `page.yml` or a section's frontmatter (the whole result), or `fetch:` for anything more a `limit`, a `where`. A list — `query: [team, articles]` — declares several, each arriving under its own `content.data` key. `query:` takes names only; `data:`, its old name, is now an error.
878
878
 
879
879
  ```yaml
880
880
  # pages/blog/page.yml | # a section on the homepage
881
881
  title: Blog | ---
882
- data: recent | type: ArticleTeaser
882
+ query: recent | type: ArticleTeaser
883
883
  | fetch: { query: recent, limit: 3 }
884
884
  | ---
885
885
  ```
@@ -888,7 +888,7 @@ data: recent | type: ArticleTeaser
888
888
 
889
889
  ```
890
890
  pages/blog/
891
- ├── page.yml # title: Blog / data: recent
891
+ ├── page.yml # title: Blog / query: recent
892
892
  ├── list.md
893
893
  └── [slug]/
894
894
  ├── page.yml
@@ -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 `query:`, 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
 
@@ -975,7 +977,7 @@ function MyComponent({ content, params, block }) {
975
977
  }
976
978
  ```
977
979
 
978
- Frontmatter becomes `params`, minus the keys the framework consumes outright: `type`, `preset`, `input`, `props`, `fetch`, `data`, `id`. `props:` is the one that isn't dropped but merged *into* params.
980
+ Frontmatter becomes `params`, minus the keys the framework consumes outright: `type`, `preset`, `input`, `props`, `query`, `fetch`, `id` (a `data:` key is refused — it was `query:`'s old name). `props:` is the one that isn't dropped but merged *into* params.
979
981
 
980
982
  **Framework fields you'd expect to be stripped are not.** `background`, `theme`, `source`, `where`, and `vars` are acted on by the runtime *and* passed through — so `params.theme` is readable when a component needs logic beyond CSS tokens (a light vs. dark logo, say). Components ignore the keys they don't use, the same way they ignore unused `content.data` keys.
981
983
 
@@ -1606,7 +1608,7 @@ export default function Grid({ block, params }) {
1606
1608
 
1607
1609
  Each child is a regular section with its own type, params, and content — and you're in the middle: wrap each child, filter by type, reorder, add container classes. The author decides *what* goes in the grid; your component decides *how* it renders. Tomorrow the author can swap a child for a different section type with no code change, and your components stay reusable wherever child sections are accepted.
1608
1610
 
1609
- **Data and child blocks:** page-level `data:` is available to all blocks including children, and each child resolves data independently through the page → site hierarchy. If a child needs data, declare it in the child's `meta.js` or its frontmatter (`data: articles`).
1611
+ **Data and child blocks:** page-level `query:` (or `fetch:`) is available to all blocks including children, and each child resolves data independently through the page → site hierarchy. If a child needs data no ancestor declares, give it its own in its frontmatter (`query: articles`, or `fetch:`). Its `meta.js` `data:` declares the shape it reads, never where the data comes from — it fetches nothing.
1610
1612
 
1611
1613
  **SSG:** insets, `<ChildBlocks>`, and `<Visual>` all render correctly during prerender. Inset components using React hooks internally trigger prerender warnings — expected and harmless; the page renders correctly client-side.
1612
1614
 
@@ -1771,9 +1773,9 @@ Content-less containers appear as group nodes (`hasContent: false`) — use `nav
1771
1773
 
1772
1774
  ### Data
1773
1775
 
1774
- 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`.
1776
+ A component on a page with a `query:` 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 }) {
@@ -1829,7 +1831,7 @@ A backend with its own base URL, headers, wire or query language is a **transpor
1829
1831
  # site.yml
1830
1832
  fetcher:
1831
1833
  transports:
1832
- articles: acme # a foundation-registered transport handles `data: articles`
1834
+ articles: acme # a foundation-registered transport handles `query: articles`
1833
1835
  events: default # explicitly route back to the default fetcher
1834
1836
  acme: # binding config that transport reads
1835
1837
  apiKey: pk_public_123
@@ -772,12 +772,12 @@ export async function publish(args = []) {
772
772
  // released version on the wire is required when site.yml uses an unversioned
773
773
  // local ref; injectInfo overrides info.foundation. A registry/URL ref → fnd.ref
774
774
  // is null → the site.yml ref is forwarded verbatim (already pinned).
775
- // ⛔ DO NOT STAMP `info.data` HERE. The name is TAKEN: `uwx/site.js` already
776
- // emits `info.data` from `site.yml`'s top-level `data:`/`fetch:` block, and
777
- // `injectInfo` WINS the merge (`sync-package.js`: `{...siteDoc.info,
778
- // ...injectInfo}`), so stamping a file map here silently replaces the author's
779
- // fetch config on the wire. Both are `type: json`, so the store validator
780
- // accepts either and nothing errors at any layer.
775
+ // ⛔ DO NOT STAMP A FILE MAP INTO `info` HERE. `injectInfo` WINS the merge
776
+ // (`sync-package.js`: `{...siteDoc.info, ...injectInfo}`), so any name stamped
777
+ // here silently replaces what the author's document carries under it, and the
778
+ // store validator accepts it nothing errors at any layer. This was written
779
+ // about `info.data`, which carried the site's `fetch:` block until 2026-09-09;
780
+ // that moved to `settings.fetch`, and the warning holds for any name.
781
781
  //
782
782
  // A file map needs a name nothing else claims (`static_data` / `data_files`
783
783
  // were proposed) AND a consumer that reads it — neither settled. See
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-09-10T23:52:15.147Z",
3
+ "generatedAt": "2026-09-11T23:04:50.071Z",
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.46.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.1",
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.1",
86
86
  "path": "framework/runtime",
87
87
  "deps": [
88
88
  "@uniweb/core",
@@ -120,7 +120,7 @@
120
120
  "deps": []
121
121
  },
122
122
  "@uniweb/unipress": {
123
- "version": "0.9.12",
123
+ "version": "0.9.13",
124
124
  "path": "framework/unipress",
125
125
  "deps": [
126
126
  "@uniweb/build",
@@ -49,8 +49,8 @@ index: home
49
49
  # sort: date desc
50
50
  # limit: 10
51
51
  #
52
- # Pages and sections then name a query: `data: recent`, or
53
- # `fetch: { query: recent }`.
52
+ # Pages and sections then name a query: `query: recent`, or
53
+ # `fetch: { query: recent, limit: 3 }` for anything more.
54
54
  #
55
55
  # You can also keep queries here instead of in queries.yml:
56
56
  #
@@ -61,7 +61,7 @@ index: home
61
61
  #
62
62
  # fetch:
63
63
  # - url: https://api.example.com/team # Remote JSON
64
- # schema: team # Access as content.data.team
64
+ # as: team # Access as content.data.team
65
65
  # - path: /data/config.json # Local file from public/
66
66
 
67
67
  # ─── Extensions ────────────────────────────────────────────────────────────────