@uniweb/unipress 0.8.20 → 0.9.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/unipress",
3
- "version": "0.8.20",
3
+ "version": "0.9.0",
4
4
  "description": "Compile a content directory into a document (PDF, EPUB, Paged.js HTML, Typst source bundle, DOCX, XLSX) using a Uniweb foundation. Five built-in templates: book, monograph, report, data-report, directory.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -50,10 +50,10 @@
50
50
  "react": "^19.0.0",
51
51
  "react-dom": "^19.0.0",
52
52
  "@uniweb/build": "^0.33.0",
53
- "@uniweb/content-reader": "^1.2.4",
54
53
  "@uniweb/runtime": "^0.13.5",
55
- "@uniweb/core": "^0.16.0",
56
- "@uniweb/semantic-parser": "^1.4.0"
54
+ "@uniweb/semantic-parser": "^1.4.0",
55
+ "@uniweb/content-reader": "^1.2.4",
56
+ "@uniweb/core": "^0.16.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "vitest": "^4.1.7"
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { existsSync, readdirSync } from 'node:fs'
12
12
  import { resolve, join, basename } from 'node:path'
13
- import { collectSiteContent, processCollections } from '@uniweb/build/content'
13
+ import { collectSiteContent, processQueries } from '@uniweb/build/content'
14
14
  import { detectConfigFile, CONFIG_FILE_NAMES } from './document-yml.js'
15
15
  import { ContentDirectoryError, DocumentYmlError } from './errors.js'
16
16
 
@@ -32,7 +32,7 @@ function attachData(section, schema, data) {
32
32
  }
33
33
  }
34
34
 
35
- function findCollectionRecords(fetchConfig, resolved) {
35
+ function findQueryRecords(fetchConfig, resolved) {
36
36
  if (!fetchConfig?.path || !fetchConfig?.schema) return null
37
37
  const m = COLLECTION_PATH_RE.exec(fetchConfig.path)
38
38
  if (!m) return null
@@ -51,7 +51,7 @@ function findCollectionRecords(fetchConfig, resolved) {
51
51
  function attachSectionFetches(sections, resolved, cascade = null) {
52
52
  if (!Array.isArray(sections)) return
53
53
  for (const section of sections) {
54
- const records = findCollectionRecords(section.fetch, resolved)
54
+ const records = findQueryRecords(section.fetch, resolved)
55
55
  if (records) attachData(section, section.fetch.schema, records)
56
56
  if (cascade) attachData(section, cascade.schema, cascade.records)
57
57
  if (Array.isArray(section.subsections) && section.subsections.length) {
@@ -65,7 +65,7 @@ function attachSectionFetches(sections, resolved, cascade = null) {
65
65
  * `parsedContent.data` so the SSR render pipeline reads populated data
66
66
  * synchronously (no `useFetched` round-trip; no `public/` directory).
67
67
  *
68
- * In a regular Uniweb site build, the Vite plugin runs `processCollections`
68
+ * In a regular Uniweb site build, the Vite plugin runs `processQueries`
69
69
  * + `writeCollectionFiles` and the runtime resolves `fetch:` declarations
70
70
  * over HTTP at render time. Under `unipress compile` neither of those
71
71
  * happens — there's no public dir, and SSR skips effects. We close the
@@ -81,15 +81,36 @@ function attachSectionFetches(sections, resolved, cascade = null) {
81
81
  * fetches, refine configs, and array-form `fetch: [...]` declarations
82
82
  * are left untouched (those have their own gaps; out of scope here).
83
83
  */
84
- async function resolveLocalCollections(siteContent, sitePath) {
85
- const collectionsConfig = siteContent?.config?.collections
86
- if (!collectionsConfig || typeof collectionsConfig !== 'object') return
87
- if (Object.keys(collectionsConfig).length === 0) return
88
-
89
- const resolved = await processCollections(
90
- sitePath,
91
- collectionsConfig,
84
+ async function resolveLocalQueries(siteContent, sitePath) {
85
+ // **`config.queries`, not `config.collections`.** The build renamed both the
86
+ // payload key and the function on 2026-08-29 (`@uniweb/build` e442738, "no
87
+ // identifier in build says `collection` any more") and this file was not
88
+ // carried across — the same shape that left `hosting` reading a key nothing
89
+ // emits, noted in `framework/CLAUDE.md` § *Decoupling is the architecture*.
90
+ //
91
+ // ⚠️ **The crash was the lucky half.** `processCollections` being undefined
92
+ // only threw because our own fixture still declared the retired
93
+ // `site.yml::collections`, which is passed through verbatim as an unrecognised
94
+ // key. A CURRENT site declares `queries:`, so this read returned undefined,
95
+ // the guard below returned early, and unipress compiled documents with **no
96
+ // query data and no error** — silent, which is why four days passed.
97
+ //
98
+ // Local identifiers keep the older word deliberately: `framework/CLAUDE.md`
99
+ // says renaming those is churn. Only the two names that cross a package
100
+ // boundary had to move.
101
+ const queriesConfig = siteContent?.config?.queries
102
+ if (!queriesConfig || typeof queriesConfig !== 'object') return
103
+ if (Object.keys(queriesConfig).length === 0) return
104
+
105
+ // ⛔ Third arg is the ENTITIES POOL override, not the site root. Passing
106
+ // `sitePath` made the pool resolve to `<site>/{schema}/` instead of
107
+ // `<site>/entities/{schema}/`, so every query matched nothing. `null` takes
108
+ // the default, which is what a site without `paths.entities` wants — the
109
+ // same value `@uniweb/build`'s own plugin computes (`paths.entities || null`).
110
+ const resolved = await processQueries(
92
111
  sitePath,
112
+ queriesConfig,
113
+ siteContent?.config?.paths?.entities || null,
93
114
  '/',
94
115
  )
95
116
 
@@ -97,7 +118,7 @@ async function resolveLocalCollections(siteContent, sitePath) {
97
118
  // Page-level fetch cascades to every section on the page — top-level
98
119
  // and nested alike. attachSectionFetches threads it through the whole
99
120
  // section tree; a section's own fetch still takes priority.
100
- const pageRecords = findCollectionRecords(page.fetch, resolved)
121
+ const pageRecords = findQueryRecords(page.fetch, resolved)
101
122
  const cascade = pageRecords
102
123
  ? { schema: page.fetch.schema, records: pageRecords }
103
124
  : null
@@ -108,16 +129,29 @@ async function resolveLocalCollections(siteContent, sitePath) {
108
129
  // (regardless of its own page's fetch declaration) can self-bootstrap
109
130
  // — e.g., a Cite inset rendering inside a Chapter on page A needs the
110
131
  // bibliography records that the Bibliography section declared on page
111
- // B. Foundations read this via `block.website.config.collections.<name>.records`
132
+ // B. Foundations read this via `block.website.config.recordsByQuery.<name>`
112
133
  // as a synchronous fallback.
134
+ //
135
+ // ⭐ **`recordsByQuery`, because "collections" named nothing.** This key is
136
+ // unipress's own — written here, read only by unipress foundations, never
137
+ // emitted or read by `@uniweb/build`. It was called `collections`, which
138
+ // collided with the build's payload key of the same name and said nothing
139
+ // about what it held. *[Diego, 2026-09-02]* — "collect" as an act is fine;
140
+ // as a set of things it is useless, because it gives no sense of what KIND of
141
+ // things, where `records`, `queries` and `entities` each carry meaning.
142
+ //
143
+ // This holds **records, keyed by the query that resolved them**, and the name
144
+ // now says exactly that. ⚠️ Not `config.records`, which the framework already
145
+ // uses for live record URL patterns (`core/src/query-address.js`).
146
+ //
147
+ // The value is the record ARRAY directly, not `{ records: [...] }`. The
148
+ // wrapper existed to merge with a pre-existing `config.collections` from the
149
+ // build — which cannot happen now that the build emits `queries` — and
150
+ // `recordsByQuery.<name>.records` would have said "records" twice.
113
151
  if (!siteContent.config) siteContent.config = {}
114
- if (!siteContent.config.collections) siteContent.config.collections = {}
152
+ if (!siteContent.config.recordsByQuery) siteContent.config.recordsByQuery = {}
115
153
  for (const name of Object.keys(resolved)) {
116
- const existing = siteContent.config.collections[name]
117
- siteContent.config.collections[name] = {
118
- ...(existing && typeof existing === 'object' ? existing : {}),
119
- records: resolved[name],
120
- }
154
+ siteContent.config.recordsByQuery[name] = resolved[name]
121
155
  }
122
156
  }
123
157
 
@@ -196,7 +230,7 @@ export async function loadContent(dir, options = {}) {
196
230
  throw err
197
231
  }
198
232
 
199
- await resolveLocalCollections(content, sitePath)
233
+ await resolveLocalQueries(content, sitePath)
200
234
 
201
235
  // Cross-reference registry is built AFTER the foundation loads, in
202
236
  // orchestrator.loadAndInit, so foundation-declared `xref.kinds`