@uniweb/build 0.20.1 → 0.22.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/build",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -59,16 +59,16 @@
59
59
  "js-yaml": "^4.1.0",
60
60
  "sharp": "^0.35.3",
61
61
  "yaml": "^2.5.0",
62
- "@uniweb/schemas": "^0.2.10",
63
- "@uniweb/theming": "^0.1.15",
62
+ "@uniweb/projections": "^0.3.1",
64
63
  "@uniweb/content-writer": "^0.3.3",
65
- "@uniweb/projections": "^0.2.8"
64
+ "@uniweb/theming": "^0.1.15",
65
+ "@uniweb/schemas": "^0.2.10"
66
66
  },
67
67
  "optionalDependencies": {
68
- "@uniweb/runtime": "^0.11.7",
68
+ "@uniweb/content-reader": "^1.2.2",
69
69
  "@uniweb/schemas": "^0.2.10",
70
- "@uniweb/semantic-parser": "^1.2.2",
71
- "@uniweb/content-reader": "^1.2.2"
70
+ "@uniweb/runtime": "^0.11.7",
71
+ "@uniweb/semantic-parser": "^1.2.2"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
package/src/site/index.js CHANGED
@@ -9,6 +9,10 @@
9
9
  export { siteContentPlugin } from './plugin.js'
10
10
  export { defineSiteConfig, readSiteConfig, default } from './config.js'
11
11
  export { collectSiteContent } from './content-collector.js'
12
+ // The `agents:` vocabulary lives in @uniweb/projections (which owns the block);
13
+ // re-exported so the CLI can validate an author's spelling without taking a
14
+ // direct dependency on it. Same shape as the search re-exports.
15
+ export { AGENTS_KEYS } from '@uniweb/projections'
12
16
  export { buildSiteData } from './build-site-data.js'
13
17
  export { shouldSplitContent } from './split-content.js'
14
18
  export {
@@ -43,6 +43,7 @@ import {
43
43
  pageMarkdownFilename,
44
44
  branchIndexFilename,
45
45
  applyRouteTranslation,
46
+ partitionKnowledgePages,
46
47
  INDEX_FILENAME
47
48
  } from '@uniweb/projections'
48
49
  import { collectSiteContent, mountEntriesOf } from './content-collector.js'
@@ -505,6 +506,58 @@ export function siteContentPlugin(options = {}) {
505
506
  robots: seo.robots || {}
506
507
  }
507
508
 
509
+ // Warn once per build, not once per collection — the dev server re-collects
510
+ // on every content change and would otherwise repeat this on every keystroke.
511
+ let warnedAboutKnowledge = false
512
+
513
+ /**
514
+ * Collect the site, then drop `knowledge:` pages.
515
+ *
516
+ * ⛔ **This is the bundle lane, and the bundle lane has no agent.**
517
+ * `uniweb export` and `uniweb deploy --host <adapter>` both run this pipeline
518
+ * (`cli/src/commands/export.js`, `deploy.js`), and their destinations are
519
+ * static hosts — files served as files, with no service running over the
520
+ * content. A `knowledge:` page is source material for such a service: prose
521
+ * written for it to reason with, addressed to it rather than to a person.
522
+ * Here there is no service to give it to, so publishing it would turn prose
523
+ * written for a machine reader into pages for visitors — the only readers
524
+ * this host has.
525
+ *
526
+ * ⚠️ **Not a confidentiality argument.** [Diego, 2026-08-13] *"It is not the
527
+ * case that it's private in the sense of sensitive."* The service quotes this
528
+ * material back to whoever prompts it, by design. The reason to drop it here
529
+ * is that it has no reader on this lane, not that it must be kept from one.
530
+ *
531
+ * Measured 2026-08-13, before this existed: a knowledge page became
532
+ * `dist/kb/index.html` — a public route — and its body rode in the
533
+ * `__SITE_CONTENT__` of *every* prerendered page including `404.html`,
534
+ * because that script carries the whole site.
535
+ *
536
+ * ⚠️ **Deliberately NOT inside `collectSiteContent`.** The link lane
537
+ * (`build-site-data.js`) calls that same collector and **must keep** these
538
+ * pages: it hands `site-content.json` to a backend that derives an agent
539
+ * corpus from it and gates access to that corpus. Same flag, two correct
540
+ * answers, decided by what is behind the destination — so the subtraction
541
+ * belongs to the pipeline, not to the collector.
542
+ */
543
+ async function collectForBundle(path, options) {
544
+ const collected = await collectSiteContent(path, options)
545
+ const { knowledgePages, renderedPages } = partitionKnowledgePages(collected.pages || [])
546
+ if (!knowledgePages.length) return collected
547
+
548
+ if (!warnedAboutKnowledge) {
549
+ warnedAboutKnowledge = true
550
+ const routes = knowledgePages.map(page => page.route).join(', ')
551
+ console.warn(
552
+ `[site-content] Dropped ${knowledgePages.length} knowledge page(s) from this build: ${routes}\n` +
553
+ ` This build targets a host that serves files only, so nothing here reads them — their prose is ` +
554
+ `written for a site assistant, not for visitors. Use \`uniweb deploy\` to make them agent-readable.`
555
+ )
556
+ }
557
+
558
+ return { ...collected, pages: renderedPages }
559
+ }
560
+
508
561
  let siteContent = null
509
562
  let resolvedSitePath = null
510
563
  let resolvedPublicDir = null
@@ -738,7 +791,7 @@ export function siteContentPlugin(options = {}) {
738
791
  if (!isProduction) {
739
792
  try {
740
793
  // Do an early content collection to get the collections config
741
- const earlyContent = await collectSiteContent(resolvedSitePath, { foundationPath })
794
+ const earlyContent = await collectForBundle(resolvedSitePath, { foundationPath })
742
795
  collectionsConfig = earlyContent.config?.collections
743
796
 
744
797
  // Resolve content directory paths from site.yml paths: group
@@ -788,7 +841,7 @@ export function siteContentPlugin(options = {}) {
788
841
  // pages stay in the graph so in-progress drafts remain previewable.
789
842
  // strict on a production build: a mount that contributes no pages is a
790
843
  // warning while you author and a broken deploy once you ship.
791
- siteContent = await collectSiteContent(resolvedSitePath, { foundationPath, dropUnpublished: isProduction, base: basePath, strict: isProduction })
844
+ siteContent = await collectForBundle(resolvedSitePath, { foundationPath, dropUnpublished: isProduction, base: basePath, strict: isProduction })
792
845
  headHtml = await loadHeadHtml()
793
846
  console.log(`[site-content] Collected ${siteContent.pages?.length || 0} pages`)
794
847
 
@@ -841,7 +894,7 @@ export function siteContentPlugin(options = {}) {
841
894
  rebuildTimeout = setTimeout(async () => {
842
895
  console.log('[site-content] Content changed, rebuilding...')
843
896
  try {
844
- siteContent = await collectSiteContent(resolvedSitePath, { foundationPath, base: basePath })
897
+ siteContent = await collectForBundle(resolvedSitePath, { foundationPath, base: basePath })
845
898
  headHtml = await loadHeadHtml()
846
899
  // Execute fetches for the updated content
847
900
  await executeDevFetches(siteContent, resolvedSitePath)
@@ -358,7 +358,7 @@ export function pageSectionsToFiles({ pageDir, pageSections, ctx, pageContext })
358
358
  // and preserved. Keep in sync with pageRecordToYml below.
359
359
  const PAGE_YML_MANAGED_KEYS = new Set([
360
360
  'id', 'title', 'description', 'label', 'keywords', 'index', 'hidden',
361
- 'hideIn', 'redirect', 'rewrite', 'layout', 'seo',
361
+ 'hideIn', 'knowledge', 'redirect', 'rewrite', 'layout', 'seo',
362
362
  'fetch', 'sections',
363
363
  ])
364
364
 
@@ -380,6 +380,7 @@ function pageRecordToYml(record, sectionsArray, sourceLocale) {
380
380
  if (record.is_index) y.index = true
381
381
  if (record.hidden !== undefined) y.hidden = record.hidden
382
382
  if (record.hide_in !== undefined) y.hideIn = record.hide_in
383
+ if (record.knowledge !== undefined) y.knowledge = record.knowledge
383
384
  if (record.redirect !== undefined) y.redirect = record.redirect
384
385
  if (record.rewrite !== undefined) y.rewrite = record.rewrite
385
386
  if (record.layout !== undefined) y.layout = record.layout
package/src/uwx/site.js CHANGED
@@ -201,6 +201,11 @@ function buildPageData(config, ctx) {
201
201
  setIf(data, 'hidden', config.hidden)
202
202
  const hideIn = normalizeHideIn(config)
203
203
  if (hideIn.length) data.hide_in = hideIn
204
+ // Agent-only content. Sent as authored — only the marked page carries it, and
205
+ // the cascade to descendants is the CONSUMER's to compute (by route prefix, or
206
+ // equivalently by walking the page tree). A reader that tests this field alone
207
+ // honours the branch root and silently misses every child.
208
+ setIf(data, 'knowledge', config.knowledge)
204
209
  setIf(data, 'redirect', config.redirect)
205
210
  setIf(data, 'rewrite', config.rewrite)
206
211
  setIf(data, 'layout', config.layout)