uniweb 0.65.0 → 0.67.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 +3 -3
- package/partials/agents.md +3 -3
- package/src/backend/foundation-bring-along.js +155 -97
- package/src/backend/site-sync.js +220 -14
- package/src/commands/clone.js +1 -1
- package/src/commands/deploy.js +9 -7
- package/src/commands/publish.js +15 -4
- package/src/commands/pull.js +219 -67
- package/src/commands/push.js +15 -5
- package/src/commands/status.js +17 -3
- package/src/commands/validate.js +18 -13
- package/src/framework-index.json +4 -4
- package/src/index.js +20 -13
- package/src/utils/conformance.js +68 -10
- package/src/utils/flag-guard.js +5 -4
- package/src/utils/git.js +48 -1
- package/src/utils/pull-written.js +5 -0
- package/src/utils/semver-precedence.js +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"@uniweb/content-writer": "^0.3.4",
|
|
45
45
|
"@uniweb/core": "^0.29.8",
|
|
46
46
|
"@uniweb/kit": "^0.19.4",
|
|
47
|
+
"@uniweb/schemas": "^0.6.0",
|
|
47
48
|
"@uniweb/runtime": "^0.27.4",
|
|
48
|
-
"@uniweb/schemas": "^0.4.0",
|
|
49
49
|
"@uniweb/semantic-parser": "^1.4.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@uniweb/build": "^0.
|
|
52
|
+
"@uniweb/build": "^0.61.0",
|
|
53
53
|
"@uniweb/content-reader": "^1.2.5",
|
|
54
54
|
"@uniweb/semantic-parser": "^1.4.1"
|
|
55
55
|
},
|
package/partials/agents.md
CHANGED
|
@@ -1870,7 +1870,7 @@ fetch:
|
|
|
1870
1870
|
|
|
1871
1871
|
**Component-side fetching.** When a component genuinely needs to fetch on its own (a search box, "load more", a lazy popover), use the kit hooks — `useFetched`, `useCacheEntry`, `useWholeRecord`. They share the framework's cache and dispatcher with declarative fetches; same-key requests dedupe automatically.
|
|
1872
1872
|
|
|
1873
|
-
**Validate before shipping.** `uniweb validate` checks file-based data against your declared schemas — missing required fields, type/enum/format mismatches, nested fields.
|
|
1873
|
+
**Validate before shipping.** `uniweb validate` checks file-based data against your declared schemas — missing required fields, type/enum/format mismatches, nested fields. A violation fails it (`--lax` only reports); it checks every record file in `records/`, and `push` / `publish` refuse the same findings before sending anything. Distinct from `uniweb doctor` (project structure): `validate` checks your *data* against the schemas you *declared*. External queries (`url:`) are reported as deferred — their data isn't in the project; the value of a `ref`/`options` field is not checked against the entity it names.
|
|
1874
1874
|
|
|
1875
1875
|
### Fetching from other sources (`fetcher:`)
|
|
1876
1876
|
|
|
@@ -2539,7 +2539,7 @@ uniweb i18n init-freeform / update-hash / move / rename / prune --freeform
|
|
|
2539
2539
|
|
|
2540
2540
|
uniweb -v # Installed CLI version — and whether a newer one exists
|
|
2541
2541
|
uniweb doctor # Diagnose project configuration (--fix to auto-repair)
|
|
2542
|
-
uniweb validate # Check file-based data against declared schemas (
|
|
2542
|
+
uniweb validate # Check file-based data against declared schemas (fails on a violation; --lax)
|
|
2543
2543
|
npx uniweb@latest update # Align @uniweb/* deps + AGENTS.md (--dry-run, --yes)
|
|
2544
2544
|
# bare `uniweb update` aligns to the CLI you ALREADY have
|
|
2545
2545
|
uniweb inspect <path> # Show parsed content for a section or page (--raw for the AST)
|
|
@@ -2609,7 +2609,7 @@ Foundations have their own free path too: `uniweb add ci --target foundation` pu
|
|
|
2609
2609
|
|
|
2610
2610
|
Either side can publish. Nothing about this changes how you build: the same foundation and the same site run under `uniweb dev`, `uniweb export`, or a CI deploy with no account at all.
|
|
2611
2611
|
|
|
2612
|
-
**Publishing vs registering.** Foundations on Uniweb Cloud live in the catalog as `@org/name@version` — `name` from the foundation's `main.js`. When a foundation powers a single site, **don't run `uniweb register` yourself** — `uniweb publish` from the site directory releases the local foundation to the catalog (when its code changed) and goes live in one step. Register deliberately only when the foundation is a product meant for multiple sites; consuming sites then pin `foundation: '@org/name@1.2.3'`. **The catalog is private and access-segregated, not a public package registry** — people see only the foundations licensed to sites they own or edit. The *site* carries the license, and it rides along with site ownership when a developer hands a site to a client. Don't describe publishing as making a foundation publicly discoverable. Schemas can also be registered on their own from a schemas-only package (`@uniweb/schemas`, any `@org/schemas`, or a bare folder of `schemas/*.{yml,json,js}`) — that's how `@std` schemas are published. Auth via `uniweb login` (`uniweb login --backend <url> --token <bearer>` without a terminal) or `UNIWEB_TOKEN`; preview with `--dry-run`.
|
|
2612
|
+
**Publishing vs registering.** Foundations on Uniweb Cloud live in the catalog as `@org/name@version` — `name` from the foundation's `main.js`. When a foundation powers a single site, **don't run `uniweb register` yourself** — `uniweb publish` from the site directory releases the local foundation to the catalog (when its code changed) and goes live in one step. A registered version is immutable, so when the foundation's code changed under a version already registered, `push` and `publish` release the change under the next version and write it into the foundation's `package.json` — commit that file (`--no-release` sends the content without the code). The version it picks is always the next patch: for a change that is not backwards compatible, set a higher minor or major version in the foundation's `package.json` yourself before you push. They stop when the registry holds a version newer than yours, released from another copy: pull that change first, or pass `--bump` to release yours above it. Register deliberately only when the foundation is a product meant for multiple sites; consuming sites then pin `foundation: '@org/name@1.2.3'`. **The catalog is private and access-segregated, not a public package registry** — people see only the foundations licensed to sites they own or edit. The *site* carries the license, and it rides along with site ownership when a developer hands a site to a client. Don't describe publishing as making a foundation publicly discoverable. Schemas can also be registered on their own from a schemas-only package (`@uniweb/schemas`, any `@org/schemas`, or a bare folder of `schemas/*.{yml,json,js}`) — that's how `@std` schemas are published. Auth via `uniweb login` (`uniweb login --backend <url> --token <bearer>` without a terminal) or `UNIWEB_TOKEN`; preview with `--dry-run`.
|
|
2613
2613
|
|
|
2614
2614
|
### Staying current
|
|
2615
2615
|
|
|
@@ -5,11 +5,30 @@
|
|
|
5
5
|
* A publish must never ship a site pointing at stale or missing foundation code
|
|
6
6
|
* (the footgun: a site goes live referencing a version the catalog doesn't
|
|
7
7
|
* have). So when the site references a LOCAL foundation, the verb fingerprints it
|
|
8
|
-
* and reconciles with the catalog
|
|
8
|
+
* and reconciles with the catalog (§4):
|
|
9
9
|
*
|
|
10
|
-
* | version not yet registered
|
|
11
|
-
* | registered, code unchanged
|
|
12
|
-
* | registered, code CHANGED
|
|
10
|
+
* | version not yet registered | release it |
|
|
11
|
+
* | registered, code unchanged | skip the release (digest match) |
|
|
12
|
+
* | registered, code CHANGED | release it under the next version |
|
|
13
|
+
* | a NEWER version is registered | stop — `--bump` releases above it |
|
|
14
|
+
*
|
|
15
|
+
* ⭐ **Changed code is released by default** *[Diego, 2026-09-24]*. A site whose project
|
|
16
|
+
* holds its own foundation is a content+code bundle — *"a developer is used to code
|
|
17
|
+
* going with the site, so they don't think of bumping the code of a site"* — and the
|
|
18
|
+
* version there decides nothing: the only site reading it is the one being pushed.
|
|
19
|
+
* Since a registered version is immutable, the change goes under the next version
|
|
20
|
+
* above the registered one, written into the package's `package.json` first. ⛔ This
|
|
21
|
+
* reverses the version-bump release gate of 2026-06-23, whose reasoning — a deliberate,
|
|
22
|
+
* npm-style release — fits a foundation other sites use as a product; those sites pin
|
|
23
|
+
* a catalog ref and never reach this code. Its author's demo site, which names it by
|
|
24
|
+
* path, does — and gets the same default, by ruling [Diego, 2026-09-24]. The release is
|
|
25
|
+
* always the next patch, so a change that is not backwards compatible needs a higher
|
|
26
|
+
* version set in `package.json` by hand.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ **A NEWER registered version still stops it.** It was released from another copy
|
|
29
|
+
* of the project, whose code this one may not have, and releasing above it would make
|
|
30
|
+
* the older code the newest — the code twin of push's content staleness gate. `--bump`
|
|
31
|
+
* releases above it anyway; `--no-release` ships the content bound to it.
|
|
13
32
|
*
|
|
14
33
|
* The freshness signal is the backend-stored, framework-computed digest (§4.1):
|
|
15
34
|
* no local state, multi-machine-safe. When the site references a published
|
|
@@ -44,7 +63,11 @@ import {
|
|
|
44
63
|
} from '@uniweb/build'
|
|
45
64
|
import { computeFoundationDigest } from '../utils/code-upload.js'
|
|
46
65
|
import { isNonInteractive } from '../utils/interactive.js'
|
|
47
|
-
import {
|
|
66
|
+
import { writeJsonPreservingStyle } from '../utils/json-file.js'
|
|
67
|
+
import {
|
|
68
|
+
compareSemverPrecedence,
|
|
69
|
+
nextVersionAbove
|
|
70
|
+
} from '../utils/semver-precedence.js'
|
|
48
71
|
|
|
49
72
|
/**
|
|
50
73
|
* Resolve the site's LOCAL foundation — the one publish should bring along — or
|
|
@@ -160,6 +183,14 @@ function readPkgField(dir, field) {
|
|
|
160
183
|
}
|
|
161
184
|
}
|
|
162
185
|
|
|
186
|
+
// Set the package's version, changing nothing else in the file — its key order,
|
|
187
|
+
// indentation and trailing newline stay as they were, so the diff is one line.
|
|
188
|
+
function writePkgVersion(dir, version) {
|
|
189
|
+
const path = join(dir, 'package.json')
|
|
190
|
+
const src = readFileSync(path, 'utf8')
|
|
191
|
+
writeJsonPreservingStyle(path, { ...JSON.parse(src), version }, src)
|
|
192
|
+
}
|
|
193
|
+
|
|
163
194
|
// What travels to the spawned `uniweb register` / `build` on the command line: only
|
|
164
195
|
// --non-interactive. The BACKEND travels in the child's environment
|
|
165
196
|
// (releaseFoundation), and the SESSION is the shared session file — or UNIWEB_TOKEN,
|
|
@@ -232,7 +263,9 @@ export async function bringFoundationAlong({
|
|
|
232
263
|
* @param {object} o
|
|
233
264
|
* @param {{dir: string, version: string|null}} o.local
|
|
234
265
|
* @param {'foundation'|'extension'} o.kind
|
|
235
|
-
* @returns {Promise<{ released: boolean, proceed: boolean, ref: string|null }>}
|
|
266
|
+
* @returns {Promise<{ released: boolean, proceed: boolean, ref: string|null, bumped?: string }>}
|
|
267
|
+
* `bumped` is the version written into package.json when changed code was released
|
|
268
|
+
* under the next version.
|
|
236
269
|
*/
|
|
237
270
|
async function bringLocalCodeAlong({
|
|
238
271
|
client,
|
|
@@ -271,6 +304,19 @@ async function bringLocalCodeAlong({
|
|
|
271
304
|
// says (see the `!reg` branch).
|
|
272
305
|
const noRelease = args.includes('--no-release')
|
|
273
306
|
|
|
307
|
+
// `--bump`: when the registry holds a version NEWER than the local one, release the
|
|
308
|
+
// local code above it instead of stopping. That is all it does — changed code under
|
|
309
|
+
// the registered version is released without it, and unchanged code never is — so a
|
|
310
|
+
// script can pass it on every run.
|
|
311
|
+
const bump = args.includes('--bump')
|
|
312
|
+
|
|
313
|
+
if (bump && noRelease) {
|
|
314
|
+
say.err(
|
|
315
|
+
'`--bump` releases your local changes and `--no-release` ships without them — pass one of the two.'
|
|
316
|
+
)
|
|
317
|
+
return { released: false, proceed: false, refused: true, ref: null }
|
|
318
|
+
}
|
|
319
|
+
|
|
274
320
|
// The pinned ref to stamp on the pushed site — read at RETURN time (after any
|
|
275
321
|
// release), so it reflects the released version, the scope register derived and
|
|
276
322
|
// the name it asked for. null when no scoped ref can be formed (then the site.yml
|
|
@@ -336,6 +382,36 @@ async function bringLocalCodeAlong({
|
|
|
336
382
|
}
|
|
337
383
|
}
|
|
338
384
|
|
|
385
|
+
// The next version above the registered one — what changed code is released under
|
|
386
|
+
// when the local version is taken. Null when the registered version is not SemVer,
|
|
387
|
+
// and then there is nothing to count up from.
|
|
388
|
+
const bumpTo = nextVersionAbove(reg.latest_version)
|
|
389
|
+
|
|
390
|
+
// Write it into package.json, then release. `register` rebuilds on its own when the
|
|
391
|
+
// built schema's version no longer matches package.json, so the release carries it.
|
|
392
|
+
// `optOut` names `--no-release` for a release nobody asked for by flag.
|
|
393
|
+
//
|
|
394
|
+
// ⚖️ A failed release leaves the new version in the file, and that is the useful
|
|
395
|
+
// state: the next run finds a local version above the registered one and releases
|
|
396
|
+
// it, with nothing to redo.
|
|
397
|
+
const releaseAsNext = async (why, { optOut = false } = {}) => {
|
|
398
|
+
if (!bumpTo) {
|
|
399
|
+
say.err(
|
|
400
|
+
`Cannot release ${label} under a new version: the registered ${reg.latest_version} is not a SemVer version.`
|
|
401
|
+
)
|
|
402
|
+
say.dim(
|
|
403
|
+
`Nothing was sent. Set a version above it in the ${kind}'s package.json, then re-run \`uniweb ${verb}\`; or \`uniweb ${verb} --no-release\` sends content bound to the released ${reg.latest_version}.`
|
|
404
|
+
)
|
|
405
|
+
return { released: false, proceed: false, refused: true, ref: null }
|
|
406
|
+
}
|
|
407
|
+
writePkgVersion(local.dir, bumpTo)
|
|
408
|
+
say.info(`Releasing the ${kind} ${scopedName || kind} as ${bumpTo} — ${why}…`)
|
|
409
|
+
const released = releaseFoundation(local, args, cliBin, say, client?.origin)
|
|
410
|
+
say.dim(`The ${kind}'s package.json now says ${bumpTo} — commit it.`)
|
|
411
|
+
if (optOut) say.dim('To send content without releasing code, pass `--no-release`.')
|
|
412
|
+
return { released, proceed: true, bumped: bumpTo, ref: await pinnedRef() }
|
|
413
|
+
}
|
|
414
|
+
|
|
339
415
|
// Registered — fingerprint the local build and compare. Build first so the
|
|
340
416
|
// digest reflects current source (idempotent: a no-op when already fresh).
|
|
341
417
|
buildFoundation(local, cliBin)
|
|
@@ -348,38 +424,70 @@ async function bringLocalCodeAlong({
|
|
|
348
424
|
return { released: false, proceed: true, ref: await pinnedRef() }
|
|
349
425
|
}
|
|
350
426
|
|
|
351
|
-
// A different version locally
|
|
427
|
+
// A different version locally.
|
|
352
428
|
if (local.version && local.version !== reg.latest_version) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
429
|
+
const order = compareSemverPrecedence(local.version, reg.latest_version)
|
|
430
|
+
// Equal in precedence (build metadata apart) is the registered version itself, and
|
|
431
|
+
// is treated as it, below.
|
|
432
|
+
if (order !== 0) {
|
|
433
|
+
if (noRelease) {
|
|
434
|
+
// ⚠️ Bind to the REGISTERED version, not the local one. `pinnedRef()` would
|
|
435
|
+
// return the bumped-but-unreleased `local.version` here — a ref no deployment
|
|
436
|
+
// can serve, which is the very failure this flag must not create.
|
|
437
|
+
say.info(
|
|
438
|
+
`Keeping the released ${kind} ${reg.latest_version} — local ${local.version} not released (\`--no-release\`).`
|
|
439
|
+
)
|
|
440
|
+
return { released: false, proceed: true, ref: registeredRef(reg) }
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ⛔ OLDER than the registered latest: that version was released from another
|
|
444
|
+
// copy of the project, and this one may not have its code. Releasing above it
|
|
445
|
+
// would make this code the newest and bind the site to it — a teammate's release
|
|
446
|
+
// undone by a push that never saw it. So it stops, and names the ways on.
|
|
447
|
+
//
|
|
448
|
+
// Until 2026-09-24 it was submitted, the registry deciding: it took a version it
|
|
449
|
+
// already held with this very code (a resume), and refused anything else. Only
|
|
450
|
+
// the latest version's digest is readable, and the digest folds in the version,
|
|
451
|
+
// so "this code, under an older version" cannot be told apart from here.
|
|
452
|
+
if (order === -1) {
|
|
453
|
+
if (bump) {
|
|
454
|
+
say.warn(
|
|
455
|
+
`The registry holds ${reg.latest_version}, newer than your ${label} — releasing your code above it (\`--bump\`). If ${reg.latest_version} is someone else's release, its changes are not in yours.`
|
|
456
|
+
)
|
|
457
|
+
return releaseAsNext(`above the registered ${reg.latest_version}`)
|
|
458
|
+
}
|
|
459
|
+
say.err(
|
|
460
|
+
`The registry holds ${kind} ${scopedName || kind} ${reg.latest_version}, newer than your ${local.version} — a release this copy does not have.`
|
|
461
|
+
)
|
|
462
|
+
say.dim(
|
|
463
|
+
`Nothing was sent. Pull the change that released it, then re-run \`uniweb ${verb}\`; or:`
|
|
464
|
+
)
|
|
465
|
+
if (bumpTo) {
|
|
466
|
+
say.dim(` • \`uniweb ${verb} --bump\` — releases your code as ${bumpTo}, above it`)
|
|
467
|
+
}
|
|
468
|
+
say.dim(
|
|
469
|
+
` • \`uniweb ${verb} --no-release\` — sends content bound to the released ${reg.latest_version}`
|
|
470
|
+
)
|
|
471
|
+
return { released: false, proceed: false, refused: true, ref: null }
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Newer than anything registered — a new version, released as it stands. Not
|
|
475
|
+
// SemVer (`order === null`) — nothing to compare, so the same, and the registry
|
|
476
|
+
// decides; `register` prints its answer if it is a no.
|
|
357
477
|
say.info(
|
|
358
|
-
|
|
478
|
+
order === 1
|
|
479
|
+
? `Releasing the ${kind} ${label} (new version; registered latest is ${reg.latest_version})…`
|
|
480
|
+
: `Releasing the ${kind} ${label} (registered latest is ${reg.latest_version})…`
|
|
359
481
|
)
|
|
360
|
-
return {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
// does not refuse: it submits, says what will decide, and `register` prints
|
|
366
|
-
// the registry's answer if it is a no.
|
|
367
|
-
const order = compareSemverPrecedence(local.version, reg.latest_version)
|
|
368
|
-
say.info(
|
|
369
|
-
order === 1
|
|
370
|
-
? `Releasing the ${kind} ${label} (new version; registered latest is ${reg.latest_version})…`
|
|
371
|
-
: order === null
|
|
372
|
-
? `Releasing the ${kind} ${label} (registered latest is ${reg.latest_version})…`
|
|
373
|
-
: `Releasing the ${kind} ${label} — not newer than the registered latest ${reg.latest_version}, so the registry takes it only if ${local.version} is already registered with this code…`
|
|
374
|
-
)
|
|
375
|
-
return {
|
|
376
|
-
released: releaseFoundation(local, args, cliBin, say, client?.origin),
|
|
377
|
-
proceed: true,
|
|
378
|
-
ref: await pinnedRef()
|
|
482
|
+
return {
|
|
483
|
+
released: releaseFoundation(local, args, cliBin, say, client?.origin),
|
|
484
|
+
proceed: true,
|
|
485
|
+
ref: await pinnedRef()
|
|
486
|
+
}
|
|
379
487
|
}
|
|
380
488
|
}
|
|
381
489
|
|
|
382
|
-
//
|
|
490
|
+
// The registered version — but the digest differs, or the backend can't confirm it.
|
|
383
491
|
if (!reg.digest) {
|
|
384
492
|
// Degrade: the backend doesn't return the stored digest yet, so we can't
|
|
385
493
|
// be sure the registered version matches local. Offer to re-deliver.
|
|
@@ -393,8 +501,10 @@ async function bringLocalCodeAlong({
|
|
|
393
501
|
`Can't verify the registered ${label} matches your local copy (backend returned no digest).`
|
|
394
502
|
)
|
|
395
503
|
if (skipPrompts || isNonInteractive(args)) {
|
|
504
|
+
// ⚖️ Not released: nothing says the code changed, and releasing on no evidence
|
|
505
|
+
// would mint a version on every push against such a backend.
|
|
396
506
|
say.dim(
|
|
397
|
-
|
|
507
|
+
`Proceeding without re-releasing — to release a change, set a higher version in the ${kind}'s package.json.`
|
|
398
508
|
)
|
|
399
509
|
return { released: false, proceed: true, ref: await pinnedRef() }
|
|
400
510
|
}
|
|
@@ -411,10 +521,8 @@ async function bringLocalCodeAlong({
|
|
|
411
521
|
return { released: false, proceed: true, ref: await pinnedRef() }
|
|
412
522
|
}
|
|
413
523
|
|
|
414
|
-
//
|
|
415
|
-
//
|
|
416
|
-
// the deliberate release gate is a version bump (§3.1).
|
|
417
|
-
// Asked for explicitly: the case this flag exists to name.
|
|
524
|
+
// The code changed and the version did not. A registered version is immutable, so
|
|
525
|
+
// the change is released under the next one.
|
|
418
526
|
if (noRelease) {
|
|
419
527
|
say.info(
|
|
420
528
|
`Keeping the released ${kind} ${reg.latest_version} — your local changes are not released (\`--no-release\`).`
|
|
@@ -422,67 +530,17 @@ async function bringLocalCodeAlong({
|
|
|
422
530
|
return { released: false, proceed: true, ref: registeredRef(reg) }
|
|
423
531
|
}
|
|
424
532
|
|
|
425
|
-
// ⭐
|
|
533
|
+
// ⭐ RELEASED BY DEFAULT — for a person and an agent alike, with no question.
|
|
426
534
|
//
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// cheap outcome is one more cycle and the expensive one is a silent wrong success
|
|
437
|
-
// it will not look at again. Refusing costs an agent a retry; proceeding costs it
|
|
438
|
-
// a false completion report and a live site nobody notices is stale.
|
|
439
|
-
|
|
440
|
-
// 1. Explicit consent — proceed, but at WARN. `dim` is what we print for things
|
|
441
|
-
// nobody needs to read, and "your changes are not live" is not that.
|
|
442
|
-
if (skipPrompts) {
|
|
443
|
-
say.warn(
|
|
444
|
-
`Local ${label} differs from the registered ${reg.latest_version} and the version wasn't bumped — ` +
|
|
445
|
-
`shipping against the registered code. Your local changes will NOT be live.`
|
|
446
|
-
)
|
|
447
|
-
return { released: false, proceed: true, ref: await pinnedRef() }
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// 2. Nobody to ask. Refuse, and name both real options as runnable commands so
|
|
451
|
-
// the caller's next step needs no interpretation. `refused` marks this as a
|
|
452
|
-
// hard stop rather than a human's "no", which callers map to different exits.
|
|
453
|
-
if (isNonInteractive(args)) {
|
|
454
|
-
say.err(
|
|
455
|
-
`Local ${kind} ${label} differs from the registered ${reg.latest_version}, and the version wasn't bumped.`
|
|
456
|
-
)
|
|
457
|
-
say.dim(`Nothing was sent. Either release the change, or ship without it:`)
|
|
458
|
-
say.dim(` • bump the ${kind}'s version in package.json, then re-run \`uniweb ${verb}\``)
|
|
459
|
-
// Teach the flag that NAMES this, not `--yes`. Both work, but `--yes` means "do
|
|
460
|
-
// not ask me" and only does this as a side effect — pointing a stuck user at it
|
|
461
|
-
// teaches a blunt instrument for a precise job.
|
|
462
|
-
say.dim(
|
|
463
|
-
` • \`uniweb ${verb} --no-release\` — sends content bound to the released ${reg.latest_version}`
|
|
464
|
-
)
|
|
465
|
-
return { released: false, proceed: false, refused: true, ref: null }
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// 3. A human is here. State it, then ask.
|
|
469
|
-
say.warn(
|
|
470
|
-
`Your local ${label} differs from the registered version ${reg.latest_version}, but the version wasn't bumped.`
|
|
471
|
-
)
|
|
472
|
-
say.dim(
|
|
473
|
-
`A registered version is immutable — bump the ${kind}'s version to release the change.`
|
|
474
|
-
)
|
|
475
|
-
const proceed = await confirm(
|
|
476
|
-
`Continue with the already-registered ${reg.latest_version} anyway?`,
|
|
477
|
-
false
|
|
478
|
-
)
|
|
479
|
-
if (!proceed) {
|
|
480
|
-
say.info(
|
|
481
|
-
`Aborted — nothing was sent. Bump the ${kind} version, then re-run \`uniweb ${verb}\`.`
|
|
482
|
-
)
|
|
483
|
-
return { released: false, proceed: false, ref: null }
|
|
484
|
-
}
|
|
485
|
-
return { released: false, proceed: true, ref: await pinnedRef() }
|
|
535
|
+
// Until 2026-09-24 this stopped: a terminal was asked, a run without one refused, and
|
|
536
|
+
// `--yes` shipped the REGISTERED code. That guarded against a site bound to code that
|
|
537
|
+
// was not the working tree's — which is exactly what releasing prevents: the site is
|
|
538
|
+
// bound to the code just released, as it is to the content pushed with it. `--yes`
|
|
539
|
+
// has nothing left to consent to here, and a question whose answer is nearly always
|
|
540
|
+
// yes teaches people to stop reading it.
|
|
541
|
+
return releaseAsNext(`its code changed since ${reg.latest_version} was registered`, {
|
|
542
|
+
optOut: true
|
|
543
|
+
})
|
|
486
544
|
}
|
|
487
545
|
|
|
488
546
|
// Build the foundation so its dist/ can be fingerprinted. Idempotent — the
|