uniweb 0.89.2 → 0.89.3

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.89.2",
3
+ "version": "0.89.3",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,15 +41,15 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/semantic-parser": "^1.5.1",
45
- "@uniweb/content-writer": "^0.3.4",
46
- "@uniweb/core": "^0.38.1",
47
- "@uniweb/schemas": "^0.13.0"
44
+ "@uniweb/content-writer": "~0.3.4",
45
+ "@uniweb/semantic-parser": "~1.5.1",
46
+ "@uniweb/schemas": "~0.13.0",
47
+ "@uniweb/core": "~0.38.2"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/semantic-parser": "^1.5.1",
51
- "@uniweb/content-reader": "^1.2.8",
52
- "@uniweb/build": "^0.80.3"
50
+ "@uniweb/content-reader": "~1.2.8",
51
+ "@uniweb/build": "~0.80.4",
52
+ "@uniweb/semantic-parser": "~1.5.1"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@uniweb/build": {
@@ -1016,6 +1016,8 @@ src/ # the foundation package (folder name is `src`)
1016
1016
 
1017
1017
  **Discovery:** PascalCase files and folders at the root of `sections/` are auto-discovered. Nested levels require a `meta.js`. Lowercase directories are organizational only. `hidden: true` excludes a component entirely. Everything outside `sections/` and `layouts/` is ordinary React.
1018
1018
 
1019
+ **JSX goes in `.jsx` files** (or `.tsx`). Discovery also accepts a `.js` component, but only one without JSX: JSX compiles in no `.js` file, and the build stops on one, naming the file to rename.
1020
+
1019
1021
  **Source root.** The foundation package's source lives at the package root — the `src/` folder *is* the foundation. The build reads `package.json::main` (for new scaffolds, `main: "./_entry.generated.js"`). Older foundations nest source in `foundation/src/` with `main` pointing at `./src/_entry.generated.js`; both work through the same code path.
1020
1022
 
1021
1023
  **Import aliases.** Foundations include subpath imports for shared internals — use them instead of brittle relative paths:
@@ -1032,7 +1034,7 @@ import LessonHeader from '../../components/LessonHeader' // ❌ breaks if you re
1032
1034
 
1033
1035
  Within the same directory, use normal relative imports (`./AIFeedbackCard`).
1034
1036
 
1035
- **Foundation entry (`main.js`).** A single `export default { … }` whose top-level keys are the foundation's identity — `name` (what it registers as, `@org/<name>`) and `description` — and the capabilities it provides — `defaultLayout`, `defaultSection`, `viewTransitions`, `defaultInsets`, `xref`, `outputs`, `handlers` — plus an optional named `vars` export. The capabilities are read at render; the one thing a foundation declares that *isn't* — which host services it supports — lives in `package.json` instead (see [Declaring what your foundation supports](#declaring-what-your-foundation-supports)). Section types and layouts are auto-discovered and merged in by `@uniweb/build`. The build carries into `default.capabilities` in `dist/entry.js` only what a published page reads of your default export — the code it calls (`handlers`, `defaultInsets`, `xref`, `transports`, `outputs`) and its data, lean (`defaultLayout`, `defaultSection`, `viewTransitions`, `scroll`, each var's `default`, `type` and `applyTo`, each `data:` key's schema ref). `name`, `description`, a var's label or description and any key the framework does not read stay out of the published bundle: they are for the editor, which reads them from the foundation's schema. You never write that wrapper. The file is `main.js` — never `main.jsx`, which the build refuses; `main.js` may hold JSX. The one place it matters: when you import your **own** `main.js` from a component (e.g. a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1037
+ **Foundation entry (`main.js`).** A single `export default { … }` whose top-level keys are the foundation's identity — `name` (what it registers as, `@org/<name>`) and `description` — and the capabilities it provides — `defaultLayout`, `defaultSection`, `viewTransitions`, `defaultInsets`, `xref`, `outputs`, `handlers` — plus an optional named `vars` export. The capabilities are read at render; the one thing a foundation declares that *isn't* — which host services it supports — lives in `package.json` instead (see [Declaring what your foundation supports](#declaring-what-your-foundation-supports)). Section types and layouts are auto-discovered and merged in by `@uniweb/build`. The build carries into `default.capabilities` in `dist/entry.js` only what a published page reads of your default export — the code it calls (`handlers`, `defaultInsets`, `xref`, `transports`, `outputs`) and its data, lean (`defaultLayout`, `defaultSection`, `viewTransitions`, `scroll`, each var's `default`, `type` and `applyTo`, each `data:` key's schema ref). `name`, `description`, a var's label or description and any key the framework does not read stay out of the published bundle: they are for the editor, which reads them from the foundation's schema. You never write that wrapper. The file is `main.js` — never `main.jsx`, which the build refuses — and it is plain JavaScript: a component it declares (in `defaultInsets`, `xref`) lives in a `.jsx` file it imports. The one place it matters: when you import your **own** `main.js` from a component (e.g. a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1036
1038
 
1037
1039
  ### Props interface
1038
1040
 
@@ -260,8 +260,8 @@ export function printSurvey(
260
260
  } else if (row.status === 'behind') {
261
261
  icon = `${colors.yellow}✗${colors.reset}`
262
262
  // ⛔ `behind` alone was the whole message, in one colour, for every
263
- // distance. In 0.x the minor slot is where our breaking changes live
264
- // (`publish.js` derives it from a breaking marker and nothing else), so
263
+ // distance. The minor slot is where our breaking changes live, in 1.x as in
264
+ // 0.x (`publish.js` derives it from a breaking marker and nothing else), so
265
265
  // `^0.14.1 → ^0.16.0` is two releases a consumer must act on and
266
266
  // `^0.15.0 → ^0.15.2` is not — and the table said the same thing about
267
267
  // both. Naming the class is the difference between a list and a signal.
@@ -289,7 +289,7 @@ export function printSurvey(
289
289
  // artifact is a log. This block names the packages, so "what did that
290
290
  // upgrade cross?" is answerable afterwards from the log alone.
291
291
  //
292
- // ⚖️ It reports; it does not gate. Crossing a 0.x minor IS the ordinary way
292
+ // ⚖️ It reports; it does not gate. Crossing a minor IS the ordinary way
293
293
  // to take a Uniweb update — gating it would gate nearly every real upgrade
294
294
  // and make the verb useless. The decision stays the operator's; what changed
295
295
  // is that they can now make it knowingly.
@@ -305,7 +305,7 @@ export function printSurvey(
305
305
  log(` ${colors.red}${name}${colors.reset} ${r.current} → ${r.target} ${colors.dim}(${r.bump})${colors.reset}`)
306
306
  }
307
307
  log(
308
- ` ${colors.dim}In 0.x the minor slot is where breaking changes go, so these are releases${colors.reset}`
308
+ ` ${colors.dim}A minor version is where Uniweb's breaking changes go, so these are releases${colors.reset}`
309
309
  )
310
310
  log(
311
311
  ` ${colors.dim}you may need to act on. Read their changelogs before shipping.${colors.reset}`
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-11T16:47:25.397Z",
3
+ "generatedAt": "2026-10-11T19:38:43.125Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "0.9.0"
6
+ "version": "0.9.1"
7
7
  },
8
8
  "@uniweb/build": {
9
- "version": "0.80.3"
9
+ "version": "0.80.4"
10
10
  },
11
11
  "@uniweb/content-reader": {
12
12
  "version": "1.2.8"
@@ -15,16 +15,16 @@
15
15
  "version": "0.3.4"
16
16
  },
17
17
  "@uniweb/core": {
18
- "version": "0.38.1"
18
+ "version": "0.38.2"
19
19
  },
20
20
  "@uniweb/frame-bridge": {
21
21
  "version": "0.3.2"
22
22
  },
23
23
  "@uniweb/icons": {
24
- "version": "0.4.29"
24
+ "version": "0.4.30"
25
25
  },
26
26
  "@uniweb/kit": {
27
- "version": "0.20.0"
27
+ "version": "0.20.1"
28
28
  },
29
29
  "@uniweb/loom": {
30
30
  "version": "0.2.5"
@@ -33,10 +33,10 @@
33
33
  "version": "0.4.18"
34
34
  },
35
35
  "@uniweb/projections": {
36
- "version": "0.7.15"
36
+ "version": "0.7.16"
37
37
  },
38
38
  "@uniweb/runtime": {
39
- "version": "0.29.6"
39
+ "version": "0.29.7"
40
40
  },
41
41
  "@uniweb/scene": {
42
42
  "version": "0.1.3"
@@ -60,7 +60,7 @@
60
60
  "version": "0.2.0"
61
61
  },
62
62
  "@uniweb/unipress": {
63
- "version": "0.10.24"
63
+ "version": "0.10.25"
64
64
  }
65
65
  },
66
66
  "templates": {
@@ -36,8 +36,8 @@ export function stripVersionRange(spec) {
36
36
  /**
37
37
  * How far a bump moves, in the terms our versioning actually uses.
38
38
  *
39
- * ⭐ **In 0.x the MINOR slot is the breaking one, and it is the only channel we
40
- * have.** `scripts/framework/publish.js` derives a package's bump from its own
39
+ * ⭐ **The MINOR slot is the breaking one — in 1.x as in 0.x** *[ruled
40
+ * 2026-10-11]*. `scripts/framework/publish.js` derives a package's bump from its own
41
41
  * commits: a breaking marker means minor, everything else patch. So
42
42
  * `^0.14.1 → ^0.16.0` is two breaking releases and `^0.15.0 → ^0.15.2` is not —
43
43
  * and until 2026-09-02 `update` printed both as `behind`, in the same colour,
@@ -64,10 +64,11 @@ export function bumpClass(from, to) {
64
64
  /**
65
65
  * Is this crossing one a consumer must act on?
66
66
  *
67
- * A major always is. A minor is **when the major is 0**, because that is where
68
- * our scheme puts breaking changes — and npm agrees, which is the check that
69
- * makes this more than our own convention: `^0.14.1` admits `0.14.x` and
70
- * refuses `0.15.0`, so the range itself already treats the slot as a wall.
67
+ * A major, or a minor — at every major. Our scheme ships a breaking change as a minor
68
+ * bump, in 1.x as in 0.x *[ruled 2026-10-11]*, and the ranges a project carries (`~`)
69
+ * treat the minor slot as the wall: `~1.4.0` admits 1.4.x and refuses 1.5.0, as
70
+ * `^0.14.1` refuses 0.15.0. ⛔ Until then a minor counted only below 1.0, where a caret
71
+ * refuses it — so this would have stopped warning at the first 1.x minor.
71
72
  *
72
73
  * @param {string} from
73
74
  * @param {string} to
@@ -75,9 +76,7 @@ export function bumpClass(from, to) {
75
76
  */
76
77
  export function isBreakingBump(from, to) {
77
78
  const cls = bumpClass(from, to)
78
- if (cls === 'major') return true
79
- if (cls !== 'minor') return false
80
- return Number(stripVersionRange(to).split('.')[0]) === 0
79
+ return cls === 'major' || cls === 'minor'
81
80
  }
82
81
 
83
82
  /**
package/src/versions.js CHANGED
@@ -10,18 +10,24 @@
10
10
  * ## How versions are resolved
11
11
  *
12
12
  * 1. Start from the CLI's own `package.json` dependencies. A published CLI
13
- * carries concrete ranges like `^0.9.1` there — pnpm rewrites each
14
- * `workspace:^` spec at publish.
13
+ * carries concrete ranges like `~0.9.1` there — pnpm rewrites each
14
+ * `workspace:~` spec at publish.
15
15
  * 2. For any dep that is still a `workspace:` spec (the CLI is running
16
16
  * *from* a workspace checkout — local dev, sandboxes, tests), read the
17
17
  * `package.json` of the sibling package under `framework/<name>/` and
18
- * use `^<that version>`: the range a published CLI would carry.
18
+ * use `~<that version>`: the range a published CLI would carry.
19
19
  * 3. Scan `framework/*` for every other `@uniweb/*` package (press, loom,
20
20
  * scholar, etc.), so a new package reaches the scaffolder automatically.
21
21
  * 4. A published CLI has no `framework/` beside it: it reads every other
22
22
  * package's version from its bundled `framework-index.json`, refreshed
23
23
  * when the CLI is published.
24
24
  *
25
+ * ⭐ EVERY RANGE IS A TILDE. A breaking change ships as a minor bump, in 1.x as in
26
+ * 0.x *[ruled 2026-10-11]*, and `~1.4.0` admits 1.4.x alone, where `^1.4.0` would
27
+ * admit 1.5.0. So a project receives fixes and features (patches) by itself, and a
28
+ * breaking release through `uniweb update`, which says so. Below 1.0 a caret meant
29
+ * the same, which is why the table wrote one until then.
30
+ *
25
31
  * ⭐ A version is not a reason to depend on a package. Steps 3 and 4 name
26
32
  * every `@uniweb/*` package, so the CLI's `dependencies` list only what its
27
33
  * code loads — each one is installed with every `npx uniweb`.
@@ -199,17 +205,17 @@ function getFrameworkRoot() {
199
205
 
200
206
  /**
201
207
  * Read the current on-disk version of a specific `@uniweb/*` package by
202
- * looking up `framework/<last-segment>/package.json`, as a caret range
203
- * (e.g. `^0.7.11`) — or null if the package isn't on disk (the CLI is
208
+ * looking up `framework/<last-segment>/package.json`, as a tilde range
209
+ * (e.g. `~0.7.11`) — or null if the package isn't on disk (the CLI is
204
210
  * running from npm, not from a workspace checkout).
205
211
  *
206
- * ⭐ A caret, because that is what a published CLI carries: pnpm rewrites
207
- * its `workspace:^` deps to `^<version>` at publish. So `uniweb update` and
212
+ * ⭐ A tilde, because that is what a published CLI carries: pnpm rewrites
213
+ * its `workspace:~` deps to `~<version>` at publish. So `uniweb update` and
208
214
  * the scaffolder write the same `package.json` whether the CLI ran from npm
209
215
  * or from a workspace. ⛔ This returned the version exact until 2026-10-11:
210
216
  * right while the deps were `workspace:*`, which pnpm publishes exact, and
211
- * wrong once they became `workspace:^` — so a scaffold from a workspace
212
- * checkout pinned `0.20.0` where a user's got `^0.20.0`.
217
+ * wrong once they became a range — a scaffold from a workspace checkout
218
+ * pinned `0.20.0` where a user's got `^0.20.0`.
213
219
  */
214
220
  function readWorkspaceVersion(packageName) {
215
221
  const root = getFrameworkRoot()
@@ -221,7 +227,7 @@ function readWorkspaceVersion(packageName) {
221
227
  try {
222
228
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
223
229
  if (pkg.name === packageName && pkg.version) {
224
- return `^${pkg.version}`
230
+ return `~${pkg.version}`
225
231
  }
226
232
  } catch {}
227
233
  return null
@@ -260,7 +266,7 @@ export function loadFrameworkIndex() {
260
266
 
261
267
  /**
262
268
  * Pull `@uniweb/*` package versions out of the framework index's
263
- * `packages` field and format them as caret ranges. Used as the
269
+ * `packages` field and format them as tilde ranges. Used as the
264
270
  * published-CLI fallback source after the CLI's own deps and the live
265
271
  * workspace walk both come up empty.
266
272
  */
@@ -270,7 +276,7 @@ function readIndexPackages() {
270
276
  const result = {}
271
277
  for (const [name, entry] of Object.entries(index.packages)) {
272
278
  if (entry && entry.version) {
273
- result[name] = `^${entry.version}`
279
+ result[name] = `~${entry.version}`
274
280
  }
275
281
  }
276
282
  return result
@@ -303,7 +309,7 @@ function discoverWorkspacePackages() {
303
309
  try {
304
310
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
305
311
  if (pkg.name && pkg.version && pkg.name.startsWith('@uniweb/')) {
306
- found[pkg.name] = `^${pkg.version}`
312
+ found[pkg.name] = `~${pkg.version}`
307
313
  }
308
314
  } catch {}
309
315
  }
@@ -312,8 +318,8 @@ function discoverWorkspacePackages() {
312
318
 
313
319
  /**
314
320
  * Resolve a single version spec to something npm can install. A concrete
315
- * spec like `^0.9.0` passes through untouched; a `workspace:` spec becomes
316
- * `^<on-disk version>` of the named package; anything still unresolved
321
+ * spec like `~0.9.0` passes through untouched; a `workspace:` spec becomes
322
+ * `~<on-disk version>` of the named package; anything still unresolved
317
323
  * returns null so the caller can fall back.
318
324
  */
319
325
  function resolveVersionSpec(spec, packageName) {
@@ -330,8 +336,8 @@ function resolveVersionSpec(spec, packageName) {
330
336
  * Priority (highest first):
331
337
  *
332
338
  * 1. A concrete version spec already in the CLI's own `package.json`
333
- * (the state after an npm publish: pnpm rewrites `workspace:^` to
334
- * `^<version>`), or a `workspace:` spec read as `^<on-disk version>`.
339
+ * (the state after an npm publish: pnpm rewrites `workspace:~` to
340
+ * `~<version>`), or a `workspace:` spec read as `~<on-disk version>`.
335
341
  * 2. Live workspace walk — every `@uniweb/*` package found under
336
342
  * `framework/*` at CLI invocation time. This is the path that
337
343
  * matters for local dev: a freshly-added package becomes
@@ -384,10 +390,10 @@ export function getResolvedVersions() {
384
390
  if (!result[name]) result[name] = version
385
391
  }
386
392
 
387
- // CLI itself. Caret on the current version — templates referencing
388
- // `{{version "uniweb"}}` pick up whatever patch/minor ships in the
389
- // same publish cycle as the template.
390
- result['uniweb'] = `^${pkg.version}`
393
+ // CLI itself. Tilde on the current version, like every other entry: a
394
+ // project takes the CLI's patches by itself, and a minor — breaking —
395
+ // through `uniweb update`.
396
+ result['uniweb'] = `~${pkg.version}`
391
397
 
392
398
  resolvedVersions = result
393
399
  return resolvedVersions
@@ -397,7 +403,7 @@ export function getResolvedVersions() {
397
403
  * Get a specific package version
398
404
  *
399
405
  * @param {string} packageName - Package name (e.g., "@uniweb/build")
400
- * @returns {string} Version spec (e.g., "^0.1.4")
406
+ * @returns {string} Version spec (e.g., "~0.1.4")
401
407
  */
402
408
  export function getVersion(packageName) {
403
409
  const versions = getResolvedVersions()