uniweb 0.84.0 → 0.86.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 +6 -6
- package/partials/agents.md +126 -80
- package/src/backend/client.js +59 -6
- package/src/backend/foundation-bring-along.js +5 -3
- package/src/backend/service-request.js +91 -145
- package/src/backend/site-preview.js +1 -1
- package/src/backend/site-sync.js +161 -54
- package/src/commands/build.js +6 -2
- package/src/commands/clone.js +1 -1
- package/src/commands/deploy.js +3 -3
- package/src/commands/doctor.js +32 -36
- package/src/commands/forget.js +7 -7
- package/src/commands/publish.js +28 -30
- package/src/commands/pull.js +22 -11
- package/src/commands/push.js +19 -28
- package/src/commands/refresh.js +2 -1
- package/src/commands/register.js +6 -6
- package/src/commands/rename.js +6 -6
- package/src/commands/site.js +358 -0
- package/src/commands/snapshot.js +14 -5
- package/src/commands/status.js +1 -1
- package/src/framework-index.json +11 -11
- package/src/index.js +41 -11
- package/src/utils/config.js +13 -13
- package/src/utils/flag-guard.js +30 -16
- package/src/utils/site-identity.js +3 -3
- package/src/utils/yaml-edit.js +0 -115
package/src/commands/publish.js
CHANGED
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
* Distinct from `uniweb deploy` (third-party hosts) and `uniweb register`
|
|
23
23
|
* (foundation code → catalog). For a self-contained artifact, see `uniweb export`.
|
|
24
24
|
*
|
|
25
|
-
* Backend: the one you are logged in to —
|
|
25
|
+
* Backend: the one you are logged in to — UNIWEB_SERVER overrides it for a script
|
|
26
26
|
* (resolveBackendOrigin). Auth: UNIWEB_TOKEN > the stored session > `uniweb login`.
|
|
27
|
-
* No `--
|
|
27
|
+
* No `--server` or `--token`: switching and signing in are `uniweb login`.
|
|
28
28
|
*
|
|
29
29
|
* Usage:
|
|
30
30
|
* uniweb publish Bring the foundation along, sync, and go live
|
|
@@ -57,13 +57,16 @@ import { emitSyncPackages } from '@uniweb/build/uwx'
|
|
|
57
57
|
import {
|
|
58
58
|
bankLanguages,
|
|
59
59
|
reconcile,
|
|
60
|
-
|
|
60
|
+
announceServices,
|
|
61
|
+
foundationSupports,
|
|
62
|
+
recordsNotAsked
|
|
61
63
|
} from '../backend/service-request.js'
|
|
62
64
|
import { isSiteRelativeExtensionUrl } from '@uniweb/build'
|
|
63
65
|
import { resolveDefaultLocale } from '@uniweb/core/locale-config'
|
|
64
66
|
|
|
65
67
|
import {
|
|
66
68
|
BackendClient,
|
|
69
|
+
BackendUnreachableError,
|
|
67
70
|
describeRequestError,
|
|
68
71
|
WorkspaceMismatchError
|
|
69
72
|
} from '../backend/client.js'
|
|
@@ -268,10 +271,10 @@ export async function publish(args = []) {
|
|
|
268
271
|
}
|
|
269
272
|
const siteYml = readSiteYml(join(siteDir, 'site.yml'))
|
|
270
273
|
// ⭐ THE BACKEND YOU ARE LOGGED IN TO is where this goes *[Diego, 2026-09-21]*, for every
|
|
271
|
-
// backend verb (resolveBackendOrigin). Only
|
|
274
|
+
// backend verb (resolveBackendOrigin). Only UNIWEB_SERVER — the automation
|
|
272
275
|
// override — outranks it, and nothing talks to a backend the user is not logged in to:
|
|
273
276
|
// logged in nowhere, the origin is the default backend and the first request asks for
|
|
274
|
-
// that login. ⛔ There is no `--
|
|
277
|
+
// that login. ⛔ There is no `--server` here: switching is `uniweb login --server`.
|
|
275
278
|
// ⛔ The project's own record — its synced backend, deploy.yml's default target — routes
|
|
276
279
|
// NOTHING. For part of 2026-09-21 it answered a "logged in nowhere" tier, which cannot
|
|
277
280
|
// be reached: *"We do not allow any communication with backend if the user is not
|
|
@@ -301,8 +304,8 @@ export async function publish(args = []) {
|
|
|
301
304
|
// ⚠️ Logged in to a backend where this project has no site, while it has one
|
|
302
305
|
// elsewhere: following the login CREATES a second site. Say so before the owner
|
|
303
306
|
// question, which otherwise arrives with no reason attached. Not when
|
|
304
|
-
//
|
|
305
|
-
if (!process.env.
|
|
307
|
+
// UNIWEB_SERVER chose the backend — that is already a decision.
|
|
308
|
+
if (!process.env.UNIWEB_SERVER) {
|
|
306
309
|
const known = syncedElsewhere(siteDir, client.origin)
|
|
307
310
|
if (known) {
|
|
308
311
|
const [headline, ...rest] = describeSyncedElsewhere(known, client.origin, 'publish')
|
|
@@ -478,6 +481,12 @@ export async function publish(args = []) {
|
|
|
478
481
|
cliBin: process.argv[1]
|
|
479
482
|
})
|
|
480
483
|
} catch (err) {
|
|
484
|
+
// A backend that did not answer is said as that — not as a release that failed.
|
|
485
|
+
if (err instanceof BackendUnreachableError) {
|
|
486
|
+
say.err(err.message)
|
|
487
|
+
say.dim('Nothing was released or sent. Check that the backend is running, then re-run `uniweb publish`.')
|
|
488
|
+
return { exitCode: 1 }
|
|
489
|
+
}
|
|
481
490
|
// A scope you are not a member of is said as that, with the ways on (`explainReleaseFailure`).
|
|
482
491
|
say.err(err.notMember ? err.message : `Foundation release failed: ${err.message}`)
|
|
483
492
|
for (const line of err.notMember ? err.ways : ['Fix the foundation, then re-run `uniweb publish`.']) say.dim(line)
|
|
@@ -804,26 +813,16 @@ export async function publish(args = []) {
|
|
|
804
813
|
const injectInfo = {
|
|
805
814
|
...(fnd.ref ? { foundation: fnd.ref } : {})
|
|
806
815
|
}
|
|
807
|
-
// ⭐ WHAT THE SITE HAS — read before the push, for the
|
|
808
|
-
//
|
|
809
|
-
// this
|
|
810
|
-
// It reads this backend's site, from sync.json; a never-synced site has none.
|
|
816
|
+
// ⭐ WHAT THE SITE HAS — read before the push, for the language selection. Only this
|
|
817
|
+
// read sees a decision the owner made in the app since this clone last synced. It
|
|
818
|
+
// reads this backend's site, from sync.json; a never-synced site has none.
|
|
811
819
|
const boundUuid = readBackendState(siteDir, client.origin).site?.uuid || null
|
|
812
820
|
const status = boundUuid ? await client.siteStatus(boundUuid) : null
|
|
813
821
|
|
|
814
|
-
// ⭐ THE SERVICES
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
|
|
818
|
-
const services = await settleServices({
|
|
819
|
-
client,
|
|
820
|
-
siteDir,
|
|
821
|
-
siteYml,
|
|
822
|
-
status,
|
|
823
|
-
interactive: !isNonInteractive(args),
|
|
824
|
-
confirm,
|
|
825
|
-
say
|
|
826
|
-
})
|
|
822
|
+
// ⭐ THE SERVICES are stated by the push itself — the file's, and off for each held
|
|
823
|
+
// one it no longer lists (`statedServices`) — and the backend decides per service.
|
|
824
|
+
// Said here: what the file asks that will not be sent as written.
|
|
825
|
+
announceServices({ siteYml, say, supports: await foundationSupports(siteDir, siteYml) })
|
|
827
826
|
|
|
828
827
|
// ⭐ THE LANGUAGE SELECTION IS A REQUEST TOO, and it is the one that costs.
|
|
829
828
|
//
|
|
@@ -873,8 +872,6 @@ export async function publish(args = []) {
|
|
|
873
872
|
backend: client.origin,
|
|
874
873
|
// The keys this deployment's Sections take — see deploymentFields.
|
|
875
874
|
...fields,
|
|
876
|
-
// The `services` Section as settled above — or withheld.
|
|
877
|
-
...services.emit,
|
|
878
875
|
// Placement identity for the folder — see writeFolderItemUuids.
|
|
879
876
|
folderItemUuids: readFolderItemUuids(siteDir, client.origin),
|
|
880
877
|
// Identity for the records' list items — see readRecordItemUuids.
|
|
@@ -908,6 +905,10 @@ export async function publish(args = []) {
|
|
|
908
905
|
if (refuseUnsendableRecords(pkg.refusals, { error: say.err, note: say.dim })) {
|
|
909
906
|
return { exitCode: 1 }
|
|
910
907
|
}
|
|
908
|
+
// ⭐ `records` is what delivers the records the pages show on the published site — said
|
|
909
|
+
// here, at publish, and never at push: syncing records does not depend on it.
|
|
910
|
+
const notAsked = recordsNotAsked({ siteYml, shown: pkg.recordsShown })
|
|
911
|
+
if (notAsked) say.warn(notAsked)
|
|
911
912
|
const report = {
|
|
912
913
|
info: (m) => say.info(m),
|
|
913
914
|
note: (m) => say.dim(m),
|
|
@@ -921,9 +922,6 @@ export async function publish(args = []) {
|
|
|
921
922
|
report
|
|
922
923
|
})
|
|
923
924
|
if (pushResult.exitCode !== 0) return { exitCode: pushResult.exitCode }
|
|
924
|
-
// The push stored what it sent: the services agreed on are recorded now, whether or
|
|
925
|
-
// not the site then goes live.
|
|
926
|
-
services.after()
|
|
927
925
|
const siteUuid = pushResult.boundSiteUuid
|
|
928
926
|
if (!siteUuid) {
|
|
929
927
|
say.err('Push did not yield a site uuid — cannot go live.')
|
|
@@ -942,7 +940,7 @@ export async function publish(args = []) {
|
|
|
942
940
|
} catch (err) {
|
|
943
941
|
say.err(describeRequestError(err, client.origin))
|
|
944
942
|
if (!(err instanceof WorkspaceMismatchError))
|
|
945
|
-
say.dim('Is that the backend you meant? Switch with: uniweb login --
|
|
943
|
+
say.dim('Is that the backend you meant? Switch with: uniweb login --server <url>')
|
|
946
944
|
return { exitCode: 1 }
|
|
947
945
|
}
|
|
948
946
|
// The body of a publish that did not go through — read once, and kept for the report below.
|
package/src/commands/pull.js
CHANGED
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
*
|
|
45
45
|
* The content + folder pull lanes are both keyed by the site's uuid on that backend
|
|
46
46
|
* (sync.json).
|
|
47
|
-
* Backend: the one you are logged in to —
|
|
47
|
+
* Backend: the one you are logged in to — UNIWEB_SERVER overrides it for a script
|
|
48
48
|
* (resolveBackendOrigin). Auth: UNIWEB_TOKEN > the stored session > `uniweb login`.
|
|
49
|
-
* No `--
|
|
49
|
+
* No `--server` or `--token`: switching and signing in are `uniweb login`.
|
|
50
50
|
*
|
|
51
51
|
* A project that never pushed has no `$uuid` to pull by — pull is a no-op with a
|
|
52
52
|
* clear message. The backend serves each lane as a `.uwx` (ZIP: `manifest.json` +
|
|
@@ -95,7 +95,8 @@ import {
|
|
|
95
95
|
rebankSyncHashes,
|
|
96
96
|
writeQueryUuids,
|
|
97
97
|
mergeBaseVersions,
|
|
98
|
-
|
|
98
|
+
writeItemBaseVersions,
|
|
99
|
+
pulledItemVersions,
|
|
99
100
|
writeUnitBases,
|
|
100
101
|
writeItemUuids,
|
|
101
102
|
writeFolderItemUuids
|
|
@@ -701,10 +702,10 @@ export async function pull(args = [], deps = {}) {
|
|
|
701
702
|
if (blocked) return blocked
|
|
702
703
|
}
|
|
703
704
|
// ⭐ THE BACKEND YOU ARE LOGGED IN TO is where this goes *[Diego, 2026-09-21]*, for every
|
|
704
|
-
// backend verb (resolveBackendOrigin). Only
|
|
705
|
+
// backend verb (resolveBackendOrigin). Only UNIWEB_SERVER — the automation
|
|
705
706
|
// override — outranks it, and nothing talks to a backend the user is not logged in to:
|
|
706
707
|
// logged in nowhere, the origin is the default backend and the first request asks for
|
|
707
|
-
// that login. ⛔ There is no `--
|
|
708
|
+
// that login. ⛔ There is no `--server` here: switching is `uniweb login --server`.
|
|
708
709
|
// ⛔ The project's own record — its synced backend, deploy.yml's default target — routes
|
|
709
710
|
// NOTHING. For part of 2026-09-21 it answered a "logged in nowhere" tier, which cannot
|
|
710
711
|
// be reached: *"We do not allow any communication with backend if the user is not
|
|
@@ -746,14 +747,14 @@ export async function pull(args = [], deps = {}) {
|
|
|
746
747
|
if (!siteContentUuid) {
|
|
747
748
|
// Following the login can land on a backend with no site for this project while it
|
|
748
749
|
// has one elsewhere — say where, rather than only "push first", which would create a
|
|
749
|
-
// second site. Not when
|
|
750
|
-
const known = !process.env.
|
|
750
|
+
// second site. Not when UNIWEB_SERVER chose the backend: that is a decision.
|
|
751
|
+
const known = !process.env.UNIWEB_SERVER
|
|
751
752
|
? syncedElsewhere(siteDir, client.origin)
|
|
752
753
|
: null
|
|
753
754
|
if (known) {
|
|
754
755
|
info(`Nothing to pull — this project has no site on ${client.origin}.`)
|
|
755
756
|
note(
|
|
756
|
-
`Its ${known.length === 1 ? `site is on ${known[0]}` : `sites are on ${known.join(', ')}`}. To pull from there: uniweb login --
|
|
757
|
+
`Its ${known.length === 1 ? `site is on ${known[0]}` : `sites are on ${known.join(', ')}`}. To pull from there: uniweb login --server <url>`
|
|
757
758
|
)
|
|
758
759
|
return { exitCode: 0 }
|
|
759
760
|
}
|
|
@@ -798,7 +799,7 @@ export async function pull(args = [], deps = {}) {
|
|
|
798
799
|
} catch (err) {
|
|
799
800
|
error(describeRequestError(err, client.origin))
|
|
800
801
|
if (err instanceof WorkspaceMismatchError) return { refused: true }
|
|
801
|
-
note('Is that the backend you meant? Switch with: uniweb login --
|
|
802
|
+
note('Is that the backend you meant? Switch with: uniweb login --server <url>')
|
|
802
803
|
return null
|
|
803
804
|
}
|
|
804
805
|
if (res.status === 404) {
|
|
@@ -815,7 +816,7 @@ export async function pull(args = [], deps = {}) {
|
|
|
815
816
|
if (detail) note(detail)
|
|
816
817
|
if (res.status === 401 || res.status === 403)
|
|
817
818
|
note(
|
|
818
|
-
"Credentials weren't accepted — log in again (`uniweb login --
|
|
819
|
+
"Credentials weren't accepted — log in again (`uniweb login --server <url>`), or check UNIWEB_TOKEN."
|
|
819
820
|
)
|
|
820
821
|
return null
|
|
821
822
|
}
|
|
@@ -887,6 +888,9 @@ export async function pull(args = [], deps = {}) {
|
|
|
887
888
|
// The Models the pulled site's queries name, as the push qualified them — the re-bank
|
|
888
889
|
// below resolves with them too (see there).
|
|
889
890
|
let pulledQueryModels = []
|
|
891
|
+
// The versions this copy holds after the pull: every site-content item the pulled
|
|
892
|
+
// document carries (`pulledItemVersions`), banked below once the files have taken it.
|
|
893
|
+
let pulledVersions = null
|
|
890
894
|
if (content && !content.notModified) {
|
|
891
895
|
const siteDoc =
|
|
892
896
|
content.docs &&
|
|
@@ -905,6 +909,7 @@ export async function pull(args = [], deps = {}) {
|
|
|
905
909
|
// The next push re-establishes it; until then our side reports as unknown,
|
|
906
910
|
// which is honest rather than wrong.
|
|
907
911
|
writeUnitBases(siteDir, client.origin, { remote: computeUnitHashes(siteDoc), local: {} })
|
|
912
|
+
pulledVersions = pulledItemVersions(siteDoc, content.itemVersions)
|
|
908
913
|
// Per-item identity for the next push. Without it the backend reads our
|
|
909
914
|
// records as new and re-mints every page and section row.
|
|
910
915
|
writeItemUuids(siteDir, client.origin, collectUnitUuids(siteDoc))
|
|
@@ -1216,7 +1221,13 @@ export async function pull(args = [], deps = {}) {
|
|
|
1216
1221
|
// Records this pull did not place were not taken, so neither is their lane.
|
|
1217
1222
|
if (lane === folderLane && recordsNotPlaced) continue
|
|
1218
1223
|
mergeBaseVersions(siteDir, client.origin, lane.versions)
|
|
1219
|
-
|
|
1224
|
+
}
|
|
1225
|
+
// ⭐ The site-content items' versions REPLACE what this copy held: a pull is how it
|
|
1226
|
+
// sees the site, and a version kept for an item the site no longer has would be sent
|
|
1227
|
+
// again, asking the backend to delete it. The folder lane's are not kept — that lane
|
|
1228
|
+
// is gated by its entity version. (Both were merged into one map until 2026-10-07.)
|
|
1229
|
+
if (pulledVersions && content && !content.notModified && !content.refused) {
|
|
1230
|
+
writeItemBaseVersions(siteDir, client.origin, pulledVersions)
|
|
1220
1231
|
}
|
|
1221
1232
|
// Persist the ETags so the next pull is conditional (304 when unchanged). The
|
|
1222
1233
|
// folder's is DROPPED when records were not placed, so the next pull fetches
|
package/src/commands/push.js
CHANGED
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
* backend's reconcile deletes items absent from the package, so an author's NEW page
|
|
49
49
|
* would be hard-deleted). `--force` omits the token and restores last-push-wins.
|
|
50
50
|
*
|
|
51
|
-
* Backend: the one you are logged in to —
|
|
51
|
+
* Backend: the one you are logged in to — UNIWEB_SERVER overrides it for a script
|
|
52
52
|
* (resolveBackendOrigin). Auth: UNIWEB_TOKEN > the stored session > `uniweb login`.
|
|
53
|
-
* No `--
|
|
53
|
+
* No `--server` or `--token`: switching and signing in are `uniweb login`.
|
|
54
54
|
*
|
|
55
55
|
* The two-lane SUBMISSION (POST both lanes, back-fill uuids, persist the
|
|
56
56
|
* send-only-changed cache) lives in `../backend/site-sync.js` so `uniweb publish`
|
|
@@ -65,7 +65,7 @@ import { emitSyncPackages, readBackendState } from '@uniweb/build/uwx'
|
|
|
65
65
|
import { findSiteCopies, describeSiteCopies } from '../utils/site-copies.js'
|
|
66
66
|
import { uploadSiteMedia, describeAssetRefusal } from '../backend/site-media.js'
|
|
67
67
|
import { updateBackendMap, carryServed, SYNC_STORE_FILE } from '@uniweb/build/uwx'
|
|
68
|
-
import { BackendClient } from '../backend/client.js'
|
|
68
|
+
import { BackendClient, BackendUnreachableError } from '../backend/client.js'
|
|
69
69
|
import { resolveSiteDir } from './deploy.js'
|
|
70
70
|
import { refuseIfContentDoesNotConform } from '../utils/conformance.js'
|
|
71
71
|
import { reportSchemalessQueries } from '../utils/schemaless-report.js'
|
|
@@ -75,7 +75,7 @@ import {
|
|
|
75
75
|
describeSyncedElsewhere
|
|
76
76
|
} from '../utils/site-identity.js'
|
|
77
77
|
import { confirm, isNonInteractive } from '../utils/interactive.js'
|
|
78
|
-
import {
|
|
78
|
+
import { announceServices, foundationSupports } from '../backend/service-request.js'
|
|
79
79
|
import { guardEmptyRecords } from '../utils/records-guard.js'
|
|
80
80
|
import { bringFoundationAlong } from '../backend/foundation-bring-along.js'
|
|
81
81
|
import {
|
|
@@ -162,10 +162,10 @@ export async function push(args = [], deps = {}) {
|
|
|
162
162
|
// arrival or lose a value on the way. See utils/conformance.js.
|
|
163
163
|
if (await refuseIfContentDoesNotConform(siteDir, { args })) return { exitCode: 1 }
|
|
164
164
|
// ⭐ THE BACKEND YOU ARE LOGGED IN TO is where this goes *[Diego, 2026-09-21]*, for every
|
|
165
|
-
// backend verb (resolveBackendOrigin). Only
|
|
165
|
+
// backend verb (resolveBackendOrigin). Only UNIWEB_SERVER — the automation
|
|
166
166
|
// override — outranks it, and nothing talks to a backend the user is not logged in to:
|
|
167
167
|
// logged in nowhere, the origin is the default backend and the first request asks for
|
|
168
|
-
// that login. ⛔ There is no `--
|
|
168
|
+
// that login. ⛔ There is no `--server` here: switching is `uniweb login --server`.
|
|
169
169
|
// ⛔ The project's own record — its synced backend, deploy.yml's default target — routes
|
|
170
170
|
// NOTHING. For part of 2026-09-21 it answered a "logged in nowhere" tier, which cannot
|
|
171
171
|
// be reached: *"We do not allow any communication with backend if the user is not
|
|
@@ -209,8 +209,8 @@ export async function push(args = [], deps = {}) {
|
|
|
209
209
|
// ⚠️ Logged in to a backend where this project has no site, while it has one
|
|
210
210
|
// elsewhere: following the login CREATES a second site. Say so before the owner
|
|
211
211
|
// question, which otherwise arrives with no reason attached. Not for `-o` (nothing is
|
|
212
|
-
// created) and not when
|
|
213
|
-
if (!output && !process.env.
|
|
212
|
+
// created) and not when UNIWEB_SERVER chose the backend — that is a decision.
|
|
213
|
+
if (!output && !process.env.UNIWEB_SERVER) {
|
|
214
214
|
const known = syncedElsewhere(siteDir, client.origin)
|
|
215
215
|
if (known) {
|
|
216
216
|
const [headline, ...rest] = describeSyncedElsewhere(known, client.origin, 'push')
|
|
@@ -278,6 +278,12 @@ export async function push(args = [], deps = {}) {
|
|
|
278
278
|
verb: 'push'
|
|
279
279
|
})
|
|
280
280
|
} catch (err) {
|
|
281
|
+
// A backend that did not answer is said as that — not as a release that failed.
|
|
282
|
+
if (err instanceof BackendUnreachableError) {
|
|
283
|
+
error(err.message)
|
|
284
|
+
note('Nothing was released or sent. Check that the backend is running, then re-run `uniweb push`.')
|
|
285
|
+
return { exitCode: 1 }
|
|
286
|
+
}
|
|
281
287
|
// A scope you are not a member of is said as that, with the ways on (`explainReleaseFailure`).
|
|
282
288
|
error(err.notMember ? err.message : `Foundation release failed: ${err.message}`)
|
|
283
289
|
for (const line of err.notMember ? err.ways : ['Fix the foundation, then re-run `uniweb push`.']) note(line)
|
|
@@ -521,20 +527,10 @@ export async function push(args = [], deps = {}) {
|
|
|
521
527
|
ref: siteYml?.foundation
|
|
522
528
|
})
|
|
523
529
|
}
|
|
524
|
-
// ⭐ THE SERVICES `site.yml` ASKS FOR
|
|
525
|
-
//
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
const offline = Boolean(output) || dryRun
|
|
529
|
-
const services = await settleServices({
|
|
530
|
-
client,
|
|
531
|
-
siteDir,
|
|
532
|
-
siteYml,
|
|
533
|
-
offline,
|
|
534
|
-
interactive: !offline && !isNonInteractive(args),
|
|
535
|
-
confirm,
|
|
536
|
-
say: { info, warn, dim: note, ok: success }
|
|
537
|
-
})
|
|
530
|
+
// ⭐ THE SERVICES `site.yml` ASKS FOR are stated by the emit — the file's, and off for
|
|
531
|
+
// each held one it no longer lists (`statedServices`) — and the backend decides per
|
|
532
|
+
// service. Said here: what the file asks that will not be sent as written.
|
|
533
|
+
announceServices({ siteYml, say: { warn }, supports: await foundationSupports(siteDir, siteYml) })
|
|
538
534
|
const emitOptions = {
|
|
539
535
|
backend: client.origin,
|
|
540
536
|
// Placement identity for the folder — see writeFolderItemUuids.
|
|
@@ -570,9 +566,7 @@ export async function push(args = [], deps = {}) {
|
|
|
570
566
|
itemBaseVersions: readItemBaseVersions(siteDir, client.origin)
|
|
571
567
|
}),
|
|
572
568
|
...(assetRewrite ? { assetRewrite } : {}),
|
|
573
|
-
...(assetIds ? { assetIds } : {})
|
|
574
|
-
// The `services` Section as settled above — or withheld.
|
|
575
|
-
...services.emit
|
|
569
|
+
...(assetIds ? { assetIds } : {})
|
|
576
570
|
}
|
|
577
571
|
let pkg
|
|
578
572
|
try {
|
|
@@ -601,8 +595,6 @@ export async function push(args = [], deps = {}) {
|
|
|
601
595
|
|
|
602
596
|
// Nothing changed since the last push — the backend is already up to date.
|
|
603
597
|
if (totalEntities === 0) {
|
|
604
|
-
// The site already holds what this copy has, so what was settled is agreed.
|
|
605
|
-
if (!offline) services.after()
|
|
606
598
|
success(
|
|
607
599
|
`Nothing to push — ${skipped} entit${skipped === 1 ? 'y' : 'ies'} unchanged since the last push.`
|
|
608
600
|
)
|
|
@@ -663,7 +655,6 @@ export async function push(args = [], deps = {}) {
|
|
|
663
655
|
}
|
|
664
656
|
})
|
|
665
657
|
if (result.exitCode !== 0) return { exitCode: result.exitCode }
|
|
666
|
-
services.after()
|
|
667
658
|
success(
|
|
668
659
|
`Pushed ${result.finalizedTotal} entit${result.finalizedTotal === 1 ? 'y' : 'ies'}` +
|
|
669
660
|
(result.wrote.length ? ` — ${result.wrote.join(', ')}` : '')
|
package/src/commands/refresh.js
CHANGED
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
* uniweb refresh git pull, then merge the backend's content
|
|
32
32
|
* uniweb refresh --no-git skip the git remote; backend only
|
|
33
33
|
* uniweb refresh --no-backend skip the backend; git only
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* The backend is the one you are logged in to, as for every backend verb (no flag picks it).
|
|
35
36
|
*/
|
|
36
37
|
|
|
37
38
|
import { readFileSync } from 'node:fs'
|
package/src/commands/register.js
CHANGED
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
* ({ok,scope,origin,entities:[{name,uuid,version,unchanged}]}),
|
|
36
36
|
* all human output to stderr — for scripted callers
|
|
37
37
|
*
|
|
38
|
-
* Endpoint:
|
|
39
|
-
* (~/.uniweb/config.json, else uniweb.app) — resolveBackendOrigin. No
|
|
40
|
-
* switching is `uniweb login --
|
|
38
|
+
* Endpoint: UNIWEB_SERVER > the backend you are logged in to > the default
|
|
39
|
+
* (~/.uniweb/config.json, else uniweb.app) — resolveBackendOrigin. No backend flag:
|
|
40
|
+
* switching is `uniweb login --server <url>`.
|
|
41
41
|
* Auth (submit only): UNIWEB_TOKEN > the stored session > `uniweb login`.
|
|
42
42
|
*/
|
|
43
43
|
|
|
@@ -595,7 +595,7 @@ async function runRegister(args = []) {
|
|
|
595
595
|
const dryRun = args.includes('--dry-run')
|
|
596
596
|
const output = flagValue(args, '-o') || flagValue(args, '--output')
|
|
597
597
|
const scopeFlag = flagValue(args, '--scope')
|
|
598
|
-
// The backend you are logged in to (
|
|
598
|
+
// The backend you are logged in to (UNIWEB_SERVER overrides it for a script) —
|
|
599
599
|
// resolveBackendOrigin, like every backend command.
|
|
600
600
|
const client = new BackendClient({
|
|
601
601
|
args,
|
|
@@ -845,7 +845,7 @@ async function runRegister(args = []) {
|
|
|
845
845
|
} catch (err) {
|
|
846
846
|
error(`Could not reach the registry at ${client.origin}: ${err.message}`)
|
|
847
847
|
log(
|
|
848
|
-
` ${colors.dim}Is that the backend you meant? Switch with: uniweb login --
|
|
848
|
+
` ${colors.dim}Is that the backend you meant? Switch with: uniweb login --server <url>${colors.reset}`
|
|
849
849
|
)
|
|
850
850
|
return { exitCode: 2 }
|
|
851
851
|
}
|
|
@@ -905,7 +905,7 @@ async function runRegister(args = []) {
|
|
|
905
905
|
` ${colors.dim}The registry didn't accept your credentials — it may use different ones than \`uniweb login\`.${colors.reset}`
|
|
906
906
|
)
|
|
907
907
|
log(
|
|
908
|
-
` ${colors.dim}Log in again (uniweb login --
|
|
908
|
+
` ${colors.dim}Log in again (uniweb login --server <url>), or check UNIWEB_TOKEN — an existing one may be wrong or expired.${colors.reset}`
|
|
909
909
|
)
|
|
910
910
|
}
|
|
911
911
|
// ⭐ **Surface `detail` as a sentence, not as a JSON dump.** The registry
|
package/src/commands/rename.js
CHANGED
|
@@ -62,7 +62,7 @@ import { writeJsonPreservingStyleAsync } from '../utils/json-file.js'
|
|
|
62
62
|
import { getExistingPackageNames, validatePackageName } from '../utils/names.js'
|
|
63
63
|
import { detectPackageManager, installCmd } from '../utils/pm.js'
|
|
64
64
|
import { getCliPrefix } from '../utils/interactive.js'
|
|
65
|
-
import {
|
|
65
|
+
import { replaceInYamlList, setYamlKey } from '@uniweb/build/uwx'
|
|
66
66
|
|
|
67
67
|
const colors = {
|
|
68
68
|
reset: '\x1b[0m',
|
|
@@ -307,9 +307,9 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
307
307
|
pkgSrc,
|
|
308
308
|
sitePkgPath,
|
|
309
309
|
siteYmlPath,
|
|
310
|
-
// Edited in place —
|
|
310
|
+
// Edited in place — `setYamlKey`, @uniweb/build — and computed HERE, before
|
|
311
311
|
// anything moves, so a file it cannot edit stops the rename cleanly.
|
|
312
|
-
newYmlText: ymlMatches ?
|
|
312
|
+
newYmlText: ymlMatches ? setYamlKey(ymlText, 'foundation', newName) : null,
|
|
313
313
|
hasDep,
|
|
314
314
|
ymlMatches
|
|
315
315
|
})
|
|
@@ -318,7 +318,7 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
318
318
|
refuseUneditableSiteYml(
|
|
319
319
|
affectedSites.filter((s) => s.ymlMatches && s.newYmlText === null).map((s) => s.path),
|
|
320
320
|
'foundation',
|
|
321
|
-
`Write
|
|
321
|
+
`Write \`foundation: ${oldName}\` as a plain value — not through a YAML alias (\`*name\`) or merge key (\`<<:\`).`
|
|
322
322
|
)
|
|
323
323
|
|
|
324
324
|
// ─── Print plan, then execute ────────────────────────────────
|
|
@@ -530,14 +530,14 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
530
530
|
siteYmlPath,
|
|
531
531
|
hits,
|
|
532
532
|
// Edited in place, and computed before anything moves — as for foundations.
|
|
533
|
-
newYmlText:
|
|
533
|
+
newYmlText: replaceInYamlList(ymlText, 'extensions', replacements)
|
|
534
534
|
})
|
|
535
535
|
}
|
|
536
536
|
}
|
|
537
537
|
refuseUneditableSiteYml(
|
|
538
538
|
affectedSites.filter((a) => a.newYmlText === null).map((a) => a.site.path),
|
|
539
539
|
'extensions',
|
|
540
|
-
|
|
540
|
+
'Write `extensions:` as a plain list — not through a YAML alias (`*name`) or merge key (`<<:`).'
|
|
541
541
|
)
|
|
542
542
|
|
|
543
543
|
log('')
|