uniweb 0.12.7 → 0.12.8

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/src/index.js CHANGED
@@ -539,6 +539,13 @@ async function main() {
539
539
  return
540
540
  }
541
541
 
542
+ // Handle export command (dynamic import — depends on @uniweb/build)
543
+ if (command === 'export') {
544
+ const { exportSite } = await importProjectCommand('./commands/export.js')
545
+ await exportSite(args.slice(1))
546
+ return
547
+ }
548
+
542
549
  // Handle login command
543
550
  if (command === 'login') {
544
551
  await login(args.slice(1))
@@ -825,7 +832,8 @@ ${colors.bright}Commands:${colors.reset}
825
832
  rename <type> Rename a workspace package (foundation today)
826
833
  build Build the current project
827
834
  deploy Deploy a site to Uniweb hosting
828
- publish Publish a foundation to the Uniweb Registry
835
+ export Export a self-contained site for third-party hosting
836
+ publish Publish a foundation to the Uniweb catalog (deliberate; for site-bound foundations, use deploy)
829
837
  invite <email> Create a foundation invite for a client
830
838
  handoff <email> Hand off a site to a client
831
839
  inspect <path> Inspect parsed content shape of a markdown file or folder
@@ -855,10 +863,20 @@ ${colors.bright}Global Options:${colors.reset}
855
863
  Auto-detected when CI=true or no TTY (pipes, agents)
856
864
 
857
865
  ${colors.bright}Publish Options:${colors.reset}
866
+ --catalog Confirm publish to the public catalog (required in CI)
867
+ --propagate Walk trusting sites' policy waves (default: silent)
868
+ --name <id> Foundation id (overrides package.json::uniweb.id)
869
+ --namespace <ns> Force org-scope namespace (overrides package.json)
858
870
  --local Publish to the local registry (.unicloud/) instead of Uniweb Registry
871
+ --registry <url> Use a specific registry URL
859
872
  --edit-access <p> Set edit access policy: "open" or "restricted" (default: restricted)
860
873
  --dry-run Show what would be published without uploading
861
874
 
875
+ uniweb publish is for cataloging a foundation as a product. For
876
+ site-bound foundations (one foundation, one site), use uniweb deploy
877
+ instead — it auto-publishes under a site-scoped slot, no naming
878
+ ceremony.
879
+
862
880
  ${colors.bright}Invite Options:${colors.reset}
863
881
  --uses <n> Max sites per invite (default: 1)
864
882
  --expires <days> Days until expiry (default: 30)
@@ -878,16 +896,16 @@ ${colors.bright}Template Options:${colors.reset}
878
896
  --registry <url> Registry URL (default: http://localhost:4001)
879
897
 
880
898
  ${colors.bright}Deploy Options:${colors.reset}
881
- --dry-run Resolve site.yml + foundation/runtime but skip the Worker POST
882
- --skip-build Don't rebuild, use existing dist/ as-is
883
- --skip-assets Skip binary asset upload (content-only deploy)
899
+ --dry-run Resolve site.yml + foundation/runtime; print summary; no writes
900
+ --no-auto-publish Don't auto-publish workspace-local foundation as part of deploy
901
+
902
+ ${colors.bright}Export Options:${colors.reset}
903
+ --no-prerender Skip per-page prerendered HTML
884
904
 
885
905
  ${colors.bright}Build Options:${colors.reset}
886
906
  --target <type> Build target (foundation, site) - auto-detected if not specified
887
- --link Site: data-only pipeline (Uniweb-edge hosting)
888
- --bundle Site: vite-built static-host artifact (default)
889
- --prerender Force pre-rendering (bundle mode only; overrides site.yml)
890
- --no-prerender Skip pre-rendering (bundle mode only; overrides site.yml)
907
+ --prerender Force pre-rendering (overrides site.yml)
908
+ --no-prerender Skip pre-rendering (overrides site.yml)
891
909
  --foundation-dir Path to foundation directory (for prerendering)
892
910
  --platform <name> Deployment platform (e.g., vercel) for platform-specific output
893
911
 
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Internal env-var helpers.
3
+ *
4
+ * UNIWEB_* env vars are escape hatches for developers, operators, and the
5
+ * platform test team — not user-facing settings. Documented in
6
+ * `framework/cli/docs/env-vars.md`. Anything truly user-facing should be a
7
+ * flag, not an env var.
8
+ */
9
+
10
+ /**
11
+ * Parse a boolean-shaped env var. Returns true for "1", "true", "yes" (any
12
+ * case); false otherwise (including unset, empty, or any other string).
13
+ *
14
+ * @param {string} name - Env var name (e.g., "UNIWEB_SKIP_BUILD").
15
+ * @returns {boolean}
16
+ */
17
+ export function parseBoolEnv(name) {
18
+ const raw = process.env[name]
19
+ if (!raw) return false
20
+ const v = String(raw).trim().toLowerCase()
21
+ return v === '1' || v === 'true' || v === 'yes'
22
+ }
@@ -144,9 +144,8 @@ export class LocalRegistry {
144
144
 
145
145
  /**
146
146
  * Get the full version entry for `name@version`, or null if absent.
147
- * Used by `publish` and `deploy` to refill `dist/publish.json` from
148
- * server-of-record state when the local cache is missing — see
149
- * `kb/framework/build/workspace-ergonomics.md` (receipt-as-cache).
147
+ * Used by `publish` (pre-flight duplicate check) and `deploy`
148
+ * (staleness check via git provenance comparison).
150
149
  *
151
150
  * @param {string} name
152
151
  * @param {string} version
@@ -270,8 +269,8 @@ export class RemoteRegistry {
270
269
 
271
270
  /**
272
271
  * Get the full version entry for `name@version`, or null if absent.
273
- * Used by `publish` and `deploy` to refill `dist/publish.json` from
274
- * server-of-record state when the local cache is missing.
272
+ * Used by `publish` (pre-flight duplicate check) and `deploy`
273
+ * (staleness check via git provenance comparison).
275
274
  *
276
275
  * @param {string} name
277
276
  * @param {string} version
@@ -1,91 +0,0 @@
1
- /**
2
- * `dist/publish.json` receipt — shared shape used by `publish` and `deploy`.
3
- *
4
- * The receipt is a per-checkout cache of the last publish; it lets the
5
- * deploy verb decide whether a workspace-local foundation needs republishing
6
- * without a network round-trip on the happy path. It's gitignored, so it
7
- * never travels with the source — fresh clones, CI runs, and teammates all
8
- * start with no cache. Both verbs refill the cache lazily by reading the
9
- * registry's index when the local file is missing.
10
- *
11
- * See `kb/framework/build/workspace-ergonomics.md` for the full rationale.
12
- */
13
-
14
- /**
15
- * Compose the canonical six-field receipt body. All callers MUST go through
16
- * this helper so the runbook's pp-10 schema check stays meaningful.
17
- *
18
- * @param {Object} params
19
- * @param {string|null} params.gitSha
20
- * @param {boolean} params.gitDirty
21
- * @param {string} params.url
22
- * @param {string} params.publishedAt
23
- * @param {string} params.classification 'propagate' | 'silent'
24
- * @returns {Object}
25
- */
26
- export function composeReceipt({ gitSha, gitDirty, url, publishedAt, classification }) {
27
- return {
28
- schemaVersion: 1,
29
- publishedFromGitSha: gitSha,
30
- publishedFromGitDirty: gitDirty,
31
- url,
32
- publishedAt,
33
- classification,
34
- }
35
- }
36
-
37
- /**
38
- * Resolve the canonical receipt URL given (in priority order):
39
- * 1. A `publishResult.url` from a fresh upload — server-rendered, handles
40
- * empty-scope rewrites the CLI can't synthesize.
41
- * 2. An `existingEntry.url` recorded by a previous publish (refill path).
42
- * 3. A synthesized canonical form — `file://` for local registries,
43
- * `<apiUrl>/foundations/<name>@<version>/foundation.js` for remote.
44
- * The remote form mirrors the path the worker returns in `publishResult.url`,
45
- * which keeps the receipt's URL parseable by the regex in
46
- * `deploy.js::deriveLocalFoundationRef` even when the registry's index
47
- * entry doesn't carry an explicit `url` field. (Unicloud's index entries
48
- * don't; uniweb-edge's index entries don't either — both rely on the
49
- * response shape, not the index shape.)
50
- *
51
- * Path-shaped candidates (e.g. `/foundations/...`) are joined with the
52
- * registry's `apiUrl` so the receipt always carries an absolute URL.
53
- */
54
- export function deriveReceiptUrl({ publishResult, existingEntry, registry, name, version, isLocal }) {
55
- const candidate = publishResult?.url || existingEntry?.url
56
- if (candidate) {
57
- if (candidate.startsWith('http') || candidate.startsWith('file://')) return candidate
58
- if (registry?.apiUrl) return new URL(candidate, registry.apiUrl).toString()
59
- }
60
- if (isLocal) return `file://${registry.getPackagePath(name, version)}/`
61
- return `${registry.apiUrl.replace(/\/$/, '')}/foundations/${name}@${version}/foundation.js`
62
- }
63
-
64
- /**
65
- * Build a receipt from an existing registry version entry, used to refill a
66
- * missing `dist/publish.json` from server-of-record state. Returns null if
67
- * the entry doesn't carry enough provenance to make the receipt useful for
68
- * staleness checks (the only field that strictly must be present is
69
- * `publishedFromGitSha` — without it, the deploy verb can't compare against
70
- * HEAD, and refilling would just re-trigger the auto-publish next run).
71
- */
72
- export function receiptFromRegistryEntry({ existingEntry, registry, name, version, isLocal, isPropagateDefault }) {
73
- if (!existingEntry || !existingEntry.publishedFromGitSha) return null
74
- return composeReceipt({
75
- gitSha: existingEntry.publishedFromGitSha,
76
- gitDirty: existingEntry.publishedFromGitDirty ?? false,
77
- url: deriveReceiptUrl({ existingEntry, registry, name, version, isLocal }),
78
- publishedAt: existingEntry.publishedAt || new Date().toISOString(),
79
- classification: existingEntry.classification || (isPropagateDefault ? 'propagate' : 'silent'),
80
- })
81
- }
82
-
83
- /**
84
- * Split `@ns/name@ver`, `~user/name@ver`, or `name@ver` into name + version.
85
- * Returns null on any shape we don't recognize.
86
- */
87
- export function splitRegistryRef(ref) {
88
- if (typeof ref !== 'string') return null
89
- const m = /^(@[^/]+\/[^@]+|~[^/]+\/[^@]+|[^@]+)@(.+)$/.exec(ref)
90
- return m ? { name: m[1], version: m[2] } : null
91
- }