uniweb 1.21.22 → 1.21.23

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": "1.21.22",
3
+ "version": "1.21.23",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -920,8 +920,9 @@ export class BackendClient {
920
920
  /**
921
921
  * GET /dev/site — one page of the sites in the workspace the client names
922
922
  * (`setWorkspace`; none ⇒ personal): `{ sites: [{ uuid, name?, updated_at,
923
- * deployment?: { status, published_url, … } }] }`. A page shorter than `limit` is
924
- * the last. `deployment` is absent for a site never published.
923
+ * deployment?: { status, published_url, visitor_url, … } }] }`. A page shorter than
924
+ * `limit` is the last. `deployment` is absent for a site never published. `uniweb site`
925
+ * shows `visitor_url`, else `published_url` (`liveAddress`).
925
926
  *
926
927
  * @param {{ limit?: number, offset?: number }} [opts]
927
928
  * @returns {Promise<Response>}
@@ -951,11 +952,14 @@ export class BackendClient {
951
952
  * who has the site open in the app right now, and on what — `uniweb status` shows
952
953
  * it (`describeEditors`, commands/status.js).
953
954
  *
954
- * ⭐ **The backend also serves a LIVE-SITE record here, and nothing in this CLI reads
955
- * it yet** (shipped 2026-08-29; documented here so it is not lost twice):
955
+ * ⭐ **The backend also serves a LIVE-SITE record here** (shipped 2026-08-29), and
956
+ * `uniweb status` reads part of it: the address it shows (`visitor_url`, else
957
+ * `last_published_url` — `liveAddress`), and the foundation and runtime its `--json`
958
+ * detail carries. Nothing reads the rest yet:
956
959
  *
957
960
  * last_published_url · last_published_foundation · last_published_extensions
958
961
  * last_published_runtime · last_published_runtime_floor · last_published_runtime_resolution
962
+ * visitor_url — where visitors go: the custom domain while one is active (2026-10-28)
959
963
  *
960
964
  * Three things about it that a reader will otherwise get wrong:
961
965
  *
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Where a site's visitors go — the address the CLI shows wherever it says where a site is live:
3
+ * `uniweb publish`, `uniweb status`, `uniweb site list` and `uniweb site unpublish`.
4
+ *
5
+ * A backend names two addresses for a published site. `visitor_url` is where its visitors go: the
6
+ * site's custom domain while one is active, else the address it was delivered to. The other — `url`
7
+ * on a publish, `last_published_url` on a site's status, `deployment.published_url` on a list row —
8
+ * is where the last publish delivered it, which may be a subdomain that only redirects to that
9
+ * domain. The first is shown; the second only where the first is absent or null, as from a backend
10
+ * that does not send it. Both are complete addresses, read verbatim and never composed onto.
11
+ *
12
+ * ⛔ Not a liveness claim. Whether a site IS live is `published` (status) or `deployment.status`
13
+ * (list): an unpublished site keeps both addresses.
14
+ *
15
+ * @param {unknown} visitorUrl - the answer's `visitor_url`
16
+ * @param {unknown} deliveredUrl - the address the last publish delivered the site to
17
+ * @returns {string|null}
18
+ */
19
+ export function liveAddress(visitorUrl, deliveredUrl) {
20
+ for (const url of [visitorUrl, deliveredUrl]) if (typeof url === 'string' && url) return url
21
+ return null
22
+ }
@@ -118,6 +118,7 @@ import { repoOf } from '../utils/sync-point.js'
118
118
  import { commitBeforeSending } from '../backend/commit-before-sending.js'
119
119
  import { resolveWorkspace, describeWorkspace, previewWorkspace, SOURCE_LABEL } from '../backend/workspace.js'
120
120
  import { uploadSiteMedia, describeAssetRefusal } from '../backend/site-media.js'
121
+ import { liveAddress } from '../backend/live-address.js'
121
122
  import {
122
123
  updateBackendMap,
123
124
  readBackendState,
@@ -1064,6 +1065,10 @@ export async function publish(args = []) {
1064
1065
  // prefixed with our own origin; the reply is absolute now, so a path from an
1065
1066
  // older backend is simply shown as one.
1066
1067
  const serveUrl = typeof result.url === 'string' && result.url ? result.url : null
1068
+ // …and where its visitors go, which is what is shown: its custom domain while one is
1069
+ // active (`liveAddress`). The backend reads it on every publish and stores it nowhere, so
1070
+ // deploy.yml keeps the address this publish delivered to.
1071
+ const shownUrl = liveAddress(result.visitor_url, serveUrl)
1067
1072
 
1068
1073
  // 8. Persist deploy.yml memory — a record of what went live (and so a re-run
1069
1074
  // reuses the resolved target without re-asking). Identity is sync.json's,
@@ -1145,7 +1150,7 @@ export async function publish(args = []) {
1145
1150
  )
1146
1151
  say.dim(` site.yml lists ${unserved.join(', ')}, and ${unserved.length === 1 ? 'it was' : 'they were'} not published.`)
1147
1152
  }
1148
- if (serveUrl) console.log(` ${c.cyan}${serveUrl}${c.reset}`)
1153
+ if (shownUrl) console.log(` ${c.cyan}${shownUrl}${c.reset}`)
1149
1154
  // ⛔ No site.yml write for where it went live. That is a fact about this deploy,
1150
1155
  // not about the site, and it is recorded where deploy facts live: deploy.yml's
1151
1156
  // `lastDeploy.<target>.url` (step 8 above). site.yml carried it as `$url` ⇄
@@ -27,6 +27,7 @@ import { join, resolve } from 'node:path'
27
27
 
28
28
  import { BackendClient, WorkspaceMismatchError, describeWorkspace } from '../backend/client.js'
29
29
  import { resolveWorkspace } from '../backend/workspace.js'
30
+ import { liveAddress } from '../backend/live-address.js'
30
31
  import { checkFlags } from '../utils/flag-guard.js'
31
32
  import { confirm, isNonInteractive } from '../utils/interactive.js'
32
33
  import { readSiteIdentity } from '../utils/site-identity.js'
@@ -135,7 +136,7 @@ async function list(args) {
135
136
  name: s.name ?? null,
136
137
  status: s.deployment?.status ?? null,
137
138
  published: s.deployment?.status === 'published',
138
- url: s.deployment?.published_url ?? null,
139
+ url: liveAddress(s.deployment?.visitor_url, s.deployment?.published_url),
139
140
  updated_at: s.updated_at ?? null
140
141
  }))
141
142
  if (json) {
@@ -270,7 +271,7 @@ async function unpublish(args) {
270
271
  if (!target) return { exitCode: 2 }
271
272
  const site = await findSite(client, target.uuid)
272
273
  const label = site?.name ? `“${site.name}” (${target.uuid})` : target.uuid
273
- const url = site?.deployment?.published_url
274
+ const url = liveAddress(site?.deployment?.visitor_url, site?.deployment?.published_url)
274
275
  const ok = await confirmed(
275
276
  args,
276
277
  `Unpublish ${label} on ${client.origin}?${url ? ` Visitors get nothing at ${url} until it is published again.` : ''}`,
@@ -60,6 +60,7 @@ import {
60
60
  import { describeWorkspace, resolveWorkspace, SOURCE_LABEL } from '../backend/workspace.js'
61
61
  import { BackendClient, resolveBackendOrigin } from '../backend/client.js'
62
62
  import { readAppChanges } from '../backend/app-changes.js'
63
+ import { liveAddress } from '../backend/live-address.js'
63
64
  import { probePull } from './pull.js'
64
65
  import { readOrgFlag } from '../utils/args.js'
65
66
  import { listSyncedBackends, readBackendState } from '@uniweb/build/uwx'
@@ -649,9 +650,10 @@ async function appChecks(siteDir, siteYml, { offline, args, git }) {
649
650
  if (site) {
650
651
  // ⭐ WHERE THE SITE IS LIVE IS THE BACKEND'S TO SAY, never deploy.yml's: a receipt is
651
652
  // what this copy published, and a publish from the app or another copy does not move it.
652
- // ⛔ `last_published_url` outlives an unpublish (`client.siteStatus`), so it is said as
653
- // where the site WAS live when it is not live now — never as where it is.
654
- const url = typeof site.last_published_url === 'string' && site.last_published_url ? site.last_published_url : null
653
+ // It is where visitors go: the custom domain while one is active (`liveAddress`).
654
+ // ⛔ Both addresses outlive an unpublish (`client.siteStatus`), so the one shown is said
655
+ // as where the site WAS live when it is not live now — never as where it is.
656
+ const url = liveAddress(site.visitor_url, site.last_published_url)
655
657
  const when = site.last_published_at ? sinceLabel(site.last_published_at, Date.now()) : null
656
658
  const at = `${url ? ` at ${url}` : ''}${when ? `, published ${when}` : ''}`
657
659
  const detail = {