uniweb 0.17.0 → 0.18.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",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,14 +41,14 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/kit": "^0.11.3",
45
44
  "@uniweb/core": "^0.8.5",
46
- "@uniweb/runtime": "^0.11.6"
45
+ "@uniweb/kit": "^0.11.3",
46
+ "@uniweb/runtime": "^0.11.7"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@uniweb/build": "^0.18.5",
50
- "@uniweb/semantic-parser": "^1.2.2",
51
- "@uniweb/content-reader": "^1.2.2"
50
+ "@uniweb/content-reader": "^1.2.2",
51
+ "@uniweb/semantic-parser": "^1.2.2"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@uniweb/build": {
@@ -1906,6 +1906,7 @@ uniweb add ci --target foundation # Publish a foundation for free at permanent v
1906
1906
  # (GitHub Pages → foundations/<name>/<version>/entry.js)
1907
1907
 
1908
1908
  uniweb push / pull / clone / status # Git-style content sync with the Uniweb backend
1909
+ uniweb push --org @acme # First push/publish of a site: who owns it (see below)
1909
1910
  uniweb register [--scope @org] # Register a foundation + its data schemas to the registry
1910
1911
  uniweb login / logout # Start or clear the backend session the verbs above reuse
1911
1912
  uniweb org list / create <handle> # Publish orgs you belong to — the @org in a scoped ref
@@ -1947,6 +1948,26 @@ Foundations have their own free path too: `uniweb add ci --target foundation` pu
1947
1948
 
1948
1949
  **Content authors work visually in the Uniweb App.** They compose the same extended markdown and set the same component params you defined, through a visual editor — never touching code, git, or the CLI. They see exactly the section types your foundation offers and exactly the knobs each one exposes, because **every `meta.js` is registered as the foundation's schema** when you publish. Your `meta.js` is the app's UI (see *meta.js* in Part 4).
1949
1950
 
1951
+ > ⚠️ **The FIRST push or publish of a site asks who owns it — and refuses if it cannot ask.**
1952
+ > That create decides which organization owns the site (and whose storage its assets are billed
1953
+ > to), it is the only moment the choice is made, and there is no CLI command to change it
1954
+ > afterwards. At a terminal you get a picker. **Without a terminal — CI, a script, or you, an
1955
+ > agent — the command exits non-zero instead of choosing**, and `--yes` refuses too, because
1956
+ > guessing an owner is not an answer. Name it and it never asks again:
1957
+ >
1958
+ > ```bash
1959
+ > uniweb publish --org @acme # an organization
1960
+ > uniweb publish --personal # your personal account, deliberately
1961
+ > ```
1962
+ >
1963
+ > The answer is recorded in `site.yml::$org` and committed, so it is a one-time choice per site,
1964
+ > not per machine. **Ask the human which one to use** rather than picking for them — a site in
1965
+ > the wrong org cannot be moved from here. Sites that already exist are unaffected: their
1966
+ > ownership is settled, so nothing is asked.
1967
+ >
1968
+ > Relatedly: these commands now **reject flags they do not recognize** instead of ignoring them.
1969
+ > If you get `Unknown flag`, read the suggestion — it is usually a near miss.
1970
+
1950
1971
  **Sync is developer-only and one-sided by design.** `uniweb push` and `uniweb pull` are your commands, not theirs:
1951
1972
 
1952
1973
  - **The app is the live source of truth for content** — where authors work and where your push lands live.
@@ -76,13 +76,13 @@ import {
76
76
  computeUnitHashes,
77
77
  collectUnitUuids
78
78
  } from '@uniweb/build/uwx'
79
- import { makeModelResolver } from './push.js'
80
79
  import {
81
80
  readWritten,
82
81
  recordWritten,
83
82
  isPullOutput
84
83
  } from '../utils/pull-written.js'
85
84
  import {
85
+ makeModelResolver,
86
86
  mergeBaseVersions,
87
87
  mergeItemBaseVersions,
88
88
  writeUnitBases,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-08-12T22:56:55.282Z",
3
+ "generatedAt": "2026-08-12T23:11:50.036Z",
4
4
  "packages": {
5
5
  "@uniweb/build": {
6
6
  "version": "0.18.5",
@@ -73,7 +73,7 @@
73
73
  ]
74
74
  },
75
75
  "@uniweb/runtime": {
76
- "version": "0.11.6",
76
+ "version": "0.11.7",
77
77
  "path": "framework/runtime",
78
78
  "deps": [
79
79
  "@uniweb/core",
@@ -30,37 +30,81 @@ import { findUnknownFlags, didYouMean } from './args.js'
30
30
  /** Accepted by every command, wherever they are actually consumed. */
31
31
  const GLOBAL = ['--non-interactive', '--help', '-h']
32
32
 
33
+ /**
34
+ * Login-method flags. Any verb that can hit an unauthenticated backend may fall
35
+ * into `ensureRegistryAuth`, which hands `args` to the login picker — so these are
36
+ * genuinely reachable from all of them, not just from `uniweb login`.
37
+ */
38
+ const AUTH = ['--browser', '--password', '--token-paste']
39
+
40
+ /**
41
+ * Flags a verb inherits by importing `commands/deploy.js` for `resolveSiteDir` /
42
+ * `resolveSiteBackend`. Inert on these verbs, and listed rather than filtered:
43
+ * accepting a flag that does nothing is exactly the pre-guard behaviour, while
44
+ * rejecting one that works is a broken command. The guard's job is catching
45
+ * `--backed`, not policing inert-but-valid spellings.
46
+ */
47
+ const VIA_DEPLOY = ['--target', '--host', '--no-save']
48
+
33
49
  /**
34
50
  * Per-verb flag sets. Derived by scanning each command for dash-literals AND the
35
51
  * helpers it calls — not from the help text, which has drifted from the parser in
36
52
  * both directions (`--as-org` was implemented and undocumented; `--yes` is
37
53
  * documented on `publish` and consumed two files away).
38
54
  */
39
- export const VERB_FLAGS = {
55
+ const VERBS = {
40
56
  push: [
41
57
  '--all', '--as-org', '--org', '--backend', '--dry-run', '--force',
42
58
  '--foundation', '--output', '-o', '--personal', '--registry', '--token',
43
- '--no-validate'
59
+ // read in utils/conformance.js and backend/site-sync.js respectively —
60
+ // neither appears in push.js
61
+ '--no-validate', '--yes', ...VIA_DEPLOY
44
62
  ],
45
63
  publish: [
46
64
  '--as-org', '--org', '--backend', '--dry-run', '--force', '--foundation',
47
- '--no-save', '--personal', '--registry', '--token', '--no-validate', '--yes'
65
+ '--personal', '--registry', '--token',
66
+ // read in utils/conformance.js, backend/site-sync.js, and
67
+ // backend/foundation-bring-along.js — none appear in publish.js
68
+ '--no-validate', '--yes', '--no-verify', ...VIA_DEPLOY
48
69
  ],
49
70
  pull: [
50
71
  '--backend', '--content-only', '--dry-run', '--force', '--merge',
51
- '--no-collections', '--no-delete', '--no-prune', '--registry', '--token'
72
+ '--no-collections', '--no-delete', '--no-prune', '--registry', '--token',
73
+ // via backend/site-sync.js (the owner resolver) and utils/conformance.js
74
+ '--yes', '--org', '--as-org', '--no-validate', ...VIA_DEPLOY
52
75
  ],
53
76
  clone: [
54
77
  '--backend', '--content-only', '--no-collections', '--path', '--project',
55
- '--registry', '--token'
78
+ '--registry', '--token', '--org', '--as-org'
56
79
  ],
57
80
  register: [
58
81
  '--backend', '--dry-run', '--json', '--output', '-o', '--registry',
59
- '--schema-only', '--scope', '--token'
82
+ '--schema-only', '--scope', '--token', '--org', '--as-org'
60
83
  ],
61
- status: ['--backend', '--json', '--registry', '--remote', '--token']
84
+ status: [
85
+ '--backend', '--json', '--registry', '--remote', '--token', '--dry-run',
86
+ '--force', '--no-verify', '--no-validate', '--yes', '--org', '--as-org',
87
+ ...VIA_DEPLOY
88
+ ]
62
89
  }
63
90
 
91
+ /**
92
+ * The accepted set per verb: its own flags, plus the login-method flags every
93
+ * backend verb can reach, plus the globals.
94
+ *
95
+ * Derived from each verb's IMPORT GRAPH, not from its own source and not from the
96
+ * help text — `test/flag-guard-coverage.test.js` walks that graph and fails if a
97
+ * verb can honour a flag this list omits. Deliberately an over-approximation: a
98
+ * flag accepted here but inert costs nothing (it was ignored before the guard
99
+ * existed), while one rejected here breaks a working command.
100
+ */
101
+ export const VERB_FLAGS = Object.fromEntries(
102
+ Object.entries(VERBS).map(([verb, flags]) => [
103
+ verb,
104
+ [...new Set([...flags, ...AUTH])]
105
+ ])
106
+ )
107
+
64
108
  /**
65
109
  * Check `args` against the verb's accepted set. Returns null when everything is
66
110
  * recognized, or a ready-to-print message naming the first offender (plus a