uniweb 0.89.1 → 0.89.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.89.1",
3
+ "version": "0.89.2",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,17 +41,15 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "^0.38.1",
45
- "@uniweb/kit": "^0.20.0",
46
- "@uniweb/runtime": "^0.29.6",
47
44
  "@uniweb/semantic-parser": "^1.5.1",
48
45
  "@uniweb/content-writer": "^0.3.4",
46
+ "@uniweb/core": "^0.38.1",
49
47
  "@uniweb/schemas": "^0.13.0"
50
48
  },
51
49
  "peerDependencies": {
52
50
  "@uniweb/semantic-parser": "^1.5.1",
53
- "@uniweb/build": "^0.80.3",
54
- "@uniweb/content-reader": "^1.2.8"
51
+ "@uniweb/content-reader": "^1.2.8",
52
+ "@uniweb/build": "^0.80.3"
55
53
  },
56
54
  "peerDependenciesMeta": {
57
55
  "@uniweb/build": {
@@ -2580,6 +2580,29 @@ takes them there already signed in. ⛔ **Navigate to it, never `fetch` it**: th
2580
2580
  a redirect carrying a one-time credential no script on the site is to hold. It decides
2581
2581
  what to offer, not what is allowed — on the site the operator acts as a member.
2582
2582
 
2583
+ ### The links the backend mails
2584
+
2585
+ A sign-up's confirmation, a password reset and an invitation to an address with no
2586
+ account all arrive as a link to the site's root, a token in the query — `?verify=`,
2587
+ `?reset=`, `?offer=` — and the page it lands on finishes the step:
2588
+
2589
+ ```jsx
2590
+ const { step, status, result, error, confirm } = useMailedLink()
2591
+ // step: null | 'verify' | 'reset' | 'offer' status: 'idle' | 'working' | 'done' | 'error'
2592
+ ```
2593
+
2594
+ - `verify` finishes by itself; then the member signs in.
2595
+ - `reset` waits for `confirm({ new_password })`, and the hook supplies the token. A
2596
+ password the rule refuses is `error.extensions.reason === 'password_too_short'`, and
2597
+ the same link works again.
2598
+ - `offer` waits for a sign-in, then claims by itself. `result` is counts,
2599
+ `{ invitations, grants }`, never names. ⛔ `error.kind === 'absent'` means unknown,
2600
+ revoked or already used, alike: one sentence, no cause.
2601
+
2602
+ Render it where the root renders it — a header or a layout is on every page. ⛔ Never
2603
+ copy the token anywhere: it is a credential, it stays in the URL until the backend has
2604
+ judged it, and then the hook takes it off.
2605
+
2583
2606
  ### A backend on your machine
2584
2607
 
2585
2608
  You do not need a live backend to build against one. In `site.yml`:
@@ -2601,7 +2624,9 @@ supplies on its own origin, so cookies behave exactly as they will in production
2601
2624
  no address of your own for it: under `services:` an address means a backend you run, and
2602
2625
  asks your host to leave its own off. It **enforces** who
2603
2626
  may edit an entry and `append_only`, so a permission you are relying on fails on your
2604
- machine rather than in front of a user. State is in memory; restart to reset.
2627
+ machine rather than in front of a user. The mail it would send is printed in the dev
2628
+ server's terminal, its link on your site's root — follow it to try your page; seed
2629
+ `offers: [{ email }]` to try an invitation. State is in memory; restart to reset.
2605
2630
 
2606
2631
  `uniweb create my-event --template conference` is a worked example of all of this.
2607
2632
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-11T15:34:07.453Z",
3
+ "generatedAt": "2026-10-11T16:47:25.397Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "0.8.0"
6
+ "version": "0.9.0"
7
7
  },
8
8
  "@uniweb/build": {
9
9
  "version": "0.80.3"
package/src/versions.js CHANGED
@@ -9,26 +9,27 @@
9
9
  *
10
10
  * ## How versions are resolved
11
11
  *
12
- * 1. Start from the CLI's own `package.json` dependencies. When the CLI
13
- * was installed via npm, pnpm has already resolved every `workspace:*`
14
- * spec into a concrete version like `^0.9.1`, so this step is
15
- * usually enough.
16
- * 2. For any dep that is still `workspace:*` (i.e. the CLI is running
17
- * *from* the pnpm workspace — local dev, sandbox script, tests), fall
18
- * back to reading the real `package.json` of each sibling package
19
- * under `framework/<name>/` and using `^<that version>`. This is the
20
- * path that keeps local sandboxes and templates aligned with whatever
21
- * was last bumped in the monorepo.
22
- * 3. Scan `framework/*` to catch any additional `@uniweb/*` package that
23
- * wasn't explicitly listed as a CLI dep (press, loom, scholar, etc.).
24
- * New packages get picked up by the scaffolder automatically.
25
- *
26
- * The previous implementation shipped a hardcoded fallback table and
27
- * nothing kept it in sync with reality — see the audit note in
28
- * `framework/CLAUDE.md` under "Publishing". Every workspace-based scaffold
29
- * silently pinned against years-old versions. That table is gone. If this
30
- * function ever fails to resolve a package, it returns `^0.0.0` rather
31
- * than a stale best-guess, so the breakage is loud.
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.
15
+ * 2. For any dep that is still a `workspace:` spec (the CLI is running
16
+ * *from* a workspace checkout — local dev, sandboxes, tests), read the
17
+ * `package.json` of the sibling package under `framework/<name>/` and
18
+ * use `^<that version>`: the range a published CLI would carry.
19
+ * 3. Scan `framework/*` for every other `@uniweb/*` package (press, loom,
20
+ * scholar, etc.), so a new package reaches the scaffolder automatically.
21
+ * 4. A published CLI has no `framework/` beside it: it reads every other
22
+ * package's version from its bundled `framework-index.json`, refreshed
23
+ * when the CLI is published.
24
+ *
25
+ * ⭐ A version is not a reason to depend on a package. Steps 3 and 4 name
26
+ * every `@uniweb/*` package, so the CLI's `dependencies` list only what its
27
+ * code loads — each one is installed with every `npx uniweb`.
28
+ *
29
+ * The previous implementation shipped a hardcoded fallback table that
30
+ * nothing kept in sync, so every scaffold silently pinned years-old
31
+ * versions. That table is gone. A package none of these steps resolves gets
32
+ * the `{{version}}` helper's `^0.1.0` (templates/processor.js).
32
33
  */
33
34
 
34
35
  import { readFileSync, readdirSync, statSync } from 'node:fs'
@@ -198,16 +199,17 @@ function getFrameworkRoot() {
198
199
 
199
200
  /**
200
201
  * Read the current on-disk version of a specific `@uniweb/*` package by
201
- * looking up `framework/<last-segment>/package.json`. Returns the version
202
- * string verbatim (e.g. `0.7.11`), or null if the package isn't present
203
- * on disk (i.e. the CLI is running from npm, not from the workspace).
204
- *
205
- * Returns the version *exact*, not as a caret range, to match the shape
206
- * published-CLI direct deps take after pnpm resolves `workspace:*` at
207
- * publish time. Both modes converge on identical specs in
208
- * `getResolvedVersions()`, so `uniweb update` and the scaffolder produce
209
- * the same `package.json` whether the CLI ran from npm or from this
210
- * monorepo.
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
204
+ * running from npm, not from a workspace checkout).
205
+ *
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
208
+ * the scaffolder write the same `package.json` whether the CLI ran from npm
209
+ * or from a workspace. ⛔ This returned the version exact until 2026-10-11:
210
+ * 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`.
211
213
  */
212
214
  function readWorkspaceVersion(packageName) {
213
215
  const root = getFrameworkRoot()
@@ -219,7 +221,7 @@ function readWorkspaceVersion(packageName) {
219
221
  try {
220
222
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
221
223
  if (pkg.name === packageName && pkg.version) {
222
- return pkg.version
224
+ return `^${pkg.version}`
223
225
  }
224
226
  } catch {}
225
227
  return null
@@ -310,8 +312,8 @@ function discoverWorkspacePackages() {
310
312
 
311
313
  /**
312
314
  * Resolve a single version spec to something npm can install. A concrete
313
- * spec like `^0.9.0` passes through untouched; `workspace:*` is replaced
314
- * by the on-disk version of the named package; anything still unresolved
315
+ * spec like `^0.9.0` passes through untouched; a `workspace:` spec becomes
316
+ * `^<on-disk version>` of the named package; anything still unresolved
315
317
  * returns null so the caller can fall back.
316
318
  */
317
319
  function resolveVersionSpec(spec, packageName) {
@@ -328,8 +330,8 @@ function resolveVersionSpec(spec, packageName) {
328
330
  * Priority (highest first):
329
331
  *
330
332
  * 1. A concrete version spec already in the CLI's own `package.json`
331
- * (the state after an npm publish: `workspace:*` is resolved by
332
- * pnpm to a real version).
333
+ * (the state after an npm publish: pnpm rewrites `workspace:^` to
334
+ * `^<version>`), or a `workspace:` spec read as `^<on-disk version>`.
333
335
  * 2. Live workspace walk — every `@uniweb/*` package found under
334
336
  * `framework/*` at CLI invocation time. This is the path that
335
337
  * matters for local dev: a freshly-added package becomes