uniweb 0.70.1 → 0.70.2

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.70.1",
3
+ "version": "0.70.2",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,17 +41,17 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "^0.30.1",
44
+ "@uniweb/content-writer": "^0.3.4",
45
45
  "@uniweb/runtime": "^0.27.6",
46
46
  "@uniweb/kit": "^0.19.5",
47
- "@uniweb/schemas": "^0.7.1",
47
+ "@uniweb/core": "^0.30.1",
48
48
  "@uniweb/semantic-parser": "^1.4.1",
49
- "@uniweb/content-writer": "^0.3.4"
49
+ "@uniweb/schemas": "^0.7.2"
50
50
  },
51
51
  "peerDependencies": {
52
- "@uniweb/build": "^0.64.1",
53
52
  "@uniweb/content-reader": "^1.2.5",
54
- "@uniweb/semantic-parser": "^1.4.1"
53
+ "@uniweb/semantic-parser": "^1.4.1",
54
+ "@uniweb/build": "^0.64.2"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "@uniweb/build": {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * What `uniweb dev` asks the backend before previewing a site whose foundation is a catalog ref —
3
+ * every clone.
4
+ *
5
+ * Such a site has no build of its foundation, and a build does not turn a ref into a URL: where a
6
+ * version is served is the backend's to say. So the dev server is handed the backend's answer, read
7
+ * for it here through the site — the route push and pull read the version's section types from
8
+ * (`client.readRegisteredFoundation`) — and passed on in `UNIWEB_PREVIEW` (`@uniweb/build/site`'s
9
+ * `preview.js` has the contract). The backend it names is also where the site's kept media URLs are
10
+ * fetched from.
11
+ *
12
+ * ⛔ Nothing here is kept. The answer lives as long as the dev server: `site.yml` keeps the ref, and
13
+ * no file a push or pull reads gets the URL — the round trip never sees it.
14
+ *
15
+ * ⚠️ It may not be known. Whether a backend tells the CLI where a version is served is the backend's
16
+ * to decide, and may change, so an answer without a location is an ordinary outcome: the preview
17
+ * says so and does not start. Nothing else reads it — push, pull and publish work without it.
18
+ */
19
+ import { readBackendState, parseCatalogRef } from '@uniweb/build/uwx'
20
+ import { BackendClient } from './client.js'
21
+ import { resolveWorkspace } from './workspace.js'
22
+ import { readDeclaredFoundation } from '../utils/install-integrity.js'
23
+ import { syncedBackends } from '../utils/site-identity.js'
24
+
25
+ /**
26
+ * What the dev server of `siteDir` is handed, when its foundation is a catalog ref.
27
+ *
28
+ * @param {object} p
29
+ * @param {string} p.siteDir
30
+ * @param {string[]} [p.args]
31
+ * @param {BackendClient} [p.client] - injected in tests
32
+ * @returns {Promise<null
33
+ * | { preview: { backend: string, foundation: { ref: string, url: string, cssUrl: string|null } } }
34
+ * | { refused: string[] }>}
35
+ * null when there is nothing to ask — the foundation is not a catalog ref
36
+ */
37
+ export async function readSitePreview({ siteDir, args = [], client = null }) {
38
+ const ref = readDeclaredFoundation(siteDir)
39
+ if (!parseCatalogRef(ref)) return null
40
+
41
+ client = client || new BackendClient({ args, command: 'Previewing' })
42
+ const uuid = readBackendState(siteDir, client.origin).site?.uuid
43
+ if (!uuid) {
44
+ const known = syncedBackends(siteDir)
45
+ return {
46
+ refused: [
47
+ `This site's foundation is ${ref}, and the backend the site is on says where that version is served.`,
48
+ known.length
49
+ ? `The site is on ${known.join(', ')} — not on ${client.origin}, the backend you are logged in to. To preview it: uniweb login --backend <url>`
50
+ : `The site is on no backend yet: \`uniweb push\` puts it on ${client.origin}.`
51
+ ]
52
+ }
53
+ }
54
+
55
+ const ws = await resolveWorkspace({ client, args })
56
+ if (ws.refused) return { refused: ['A preview reads the site in one workspace, and none is chosen.', ws.reason] }
57
+ client.setWorkspace(ws.workspace, { source: ws.source })
58
+
59
+ const reply = await client.readRegisteredFoundation(uuid, ref)
60
+ const url = typeof reply?.module_url === 'string' && reply.module_url ? reply.module_url : null
61
+ if (!url) {
62
+ return {
63
+ refused: [
64
+ reply
65
+ ? `${client.origin} did not say where ${ref} is served, so this site cannot be previewed here.`
66
+ : `Could not read ${ref} through this site on ${client.origin}, so this site cannot be previewed here.`,
67
+ 'Push, pull and publish do not need it.'
68
+ ]
69
+ }
70
+ }
71
+ return {
72
+ preview: {
73
+ backend: client.origin,
74
+ foundation: { ref, url, cssUrl: typeof reply.css_url === 'string' && reply.css_url ? reply.css_url : null }
75
+ }
76
+ }
77
+ }
@@ -701,8 +701,13 @@ export async function ensureRegisteredFoundation({ client, siteDir, siteUuid, re
701
701
  const kept = readRegisteredFoundation(siteDir, ref)
702
702
  if (kept) return kept
703
703
  const reply = siteUuid ? await client.readRegisteredFoundation(siteUuid, ref) : null
704
- if (reply) writeRegisteredFoundation(siteDir, ref, reply)
705
- return reply
704
+ if (!reply) return null
705
+ // ⛔ ONLY WHAT WE READ — the section types. The reply carries more than we asked for, a serve
706
+ // location among it, and a serve location is decided at publish and read from the response that
707
+ // carries it, never kept. Until 2026-09-26 the whole reply was kept.
708
+ const sectionTypes = { schema: reply.schema }
709
+ writeRegisteredFoundation(siteDir, ref, sectionTypes)
710
+ return sectionTypes
706
711
  }
707
712
 
708
713
  // The Sections whose declared keys a push reads before it sends an optional one.
@@ -24,6 +24,14 @@
24
24
  * site by default (mirrors the `pnpm dev` shortcut `uniweb create` writes).
25
25
  * Use `--site` to pick a different one without editing the root scripts.
26
26
  *
27
+ * A site whose foundation is a catalog ref — every clone — has no build of it, and
28
+ * only the backend the site is on can say where that version is served. So `dev`
29
+ * asks it, through the site, and hands the answer to the dev server it starts in its
30
+ * environment (`UNIWEB_PREVIEW`), with the backend it asked, which is also where the
31
+ * site's kept media URLs are fetched from. Nothing is written: `site.yml` keeps the
32
+ * ref and the round trip never sees the URL. A backend that does not say is an
33
+ * ordinary answer — `dev` says so and starts nothing (`backend/site-preview.js`).
34
+ *
27
35
  * Implementation: shells out to the package manager that invoked the CLI
28
36
  * (detected via npm_config_user_agent), running the workspace-filtered
29
37
  * dev command (`pnpm --filter <name> dev` or `npm -w <name> run dev`).
@@ -121,12 +129,32 @@ export async function dev(args = []) {
121
129
  const [bin, ...rest] = command.split(' ')
122
130
  const sitePath = join(rootDir, site.path)
123
131
 
132
+ // ⭐ A SITE WHOSE FOUNDATION IS A CATALOG REF — a clone — is previewed with what the backend
133
+ // it is on says: where that version is served. Asked here, for this dev server alone, and
134
+ // handed to it in its environment; nothing is written (`backend/site-preview.js`). Any other
135
+ // site needs nothing from a backend, and this asks nothing.
136
+ const { readSitePreview } = await import('../backend/site-preview.js')
137
+ const asked = await readSitePreview({ siteDir: sitePath, args })
138
+ if (asked?.refused) {
139
+ console.error(`${RED}✗${RESET} ${asked.refused[0]}`)
140
+ for (const line of asked.refused.slice(1)) console.error(` ${line}`)
141
+ process.exit(1)
142
+ }
143
+ let env = process.env
144
+ if (asked?.preview) {
145
+ const { PREVIEW_ENV, encodePreview } = await import('@uniweb/build/site')
146
+ env = { ...process.env, [PREVIEW_ENV]: encodePreview(asked.preview) }
147
+ console.error(
148
+ `${DIM}→ Previewing with ${asked.preview.foundation.ref}, served by ${asked.preview.backend}${RESET}`
149
+ )
150
+ }
151
+
124
152
  console.error(
125
153
  `${DIM}→ ${command}${RESET} ${DIM}(site: ${site.name}, dir: ${sitePath})${RESET}`
126
154
  )
127
155
  console.error('')
128
156
 
129
- const child = spawn(bin, rest, { cwd: rootDir, stdio: 'inherit' })
157
+ const child = spawn(bin, rest, { cwd: rootDir, stdio: 'inherit', env })
130
158
  child.on('close', (code) => process.exit(code ?? 0))
131
159
  child.on('error', (err) => {
132
160
  console.error(`${RED}✗${RESET} Failed to start dev server: ${err.message}`)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-09-26T21:30:58.826Z",
3
+ "generatedAt": "2026-09-27T03:02:47.415Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
6
  "version": "0.6.3",
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "@uniweb/build": {
13
- "version": "0.64.1",
13
+ "version": "0.64.2",
14
14
  "path": "framework/build",
15
15
  "deps": [
16
16
  "@uniweb/content-reader",
@@ -96,7 +96,7 @@
96
96
  "deps": []
97
97
  },
98
98
  "@uniweb/schemas": {
99
- "version": "0.7.1",
99
+ "version": "0.7.2",
100
100
  "path": "framework/schemas",
101
101
  "deps": []
102
102
  },