uniweb 1.0.0 → 1.0.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/LICENSE +201 -0
- package/README.md +532 -3
- package/package.json +60 -11
- package/partials/adding-pages.hbs +77 -0
- package/partials/agents.md +3000 -0
- package/partials/ai-assistance.hbs +28 -0
- package/partials/components-docs.hbs +13 -0
- package/partials/config-reference.hbs +66 -0
- package/partials/deployment.hbs +13 -0
- package/partials/learn-more.hbs +6 -0
- package/partials/quick-start.hbs +8 -0
- package/partials/search-docs.hbs +110 -0
- package/src/backend/asset-download.js +222 -0
- package/src/backend/client.js +986 -0
- package/src/backend/foundation-bring-along.js +692 -0
- package/src/backend/payment-handoff.js +362 -0
- package/src/backend/service-request.js +210 -0
- package/src/backend/site-media.js +255 -0
- package/src/backend/site-preview.js +77 -0
- package/src/backend/site-sync.js +2515 -0
- package/src/backend/workspace.js +234 -0
- package/src/commands/add.js +1900 -0
- package/src/commands/build.js +1214 -0
- package/src/commands/clone.js +466 -0
- package/src/commands/content.js +202 -0
- package/src/commands/deploy.js +537 -0
- package/src/commands/dev.js +202 -0
- package/src/commands/docs.js +530 -0
- package/src/commands/doctor.js +1982 -0
- package/src/commands/export.js +116 -0
- package/src/commands/families.js +82 -0
- package/src/commands/forget.js +197 -0
- package/src/commands/handoff.js +21 -0
- package/src/commands/i18n.js +1707 -0
- package/src/commands/inspect.js +375 -0
- package/src/commands/invite.js +24 -0
- package/src/commands/org.js +96 -0
- package/src/commands/publish.js +1087 -0
- package/src/commands/pull.js +1257 -0
- package/src/commands/push.js +663 -0
- package/src/commands/refresh.js +212 -0
- package/src/commands/register.js +1092 -0
- package/src/commands/rename.js +647 -0
- package/src/commands/site.js +358 -0
- package/src/commands/snapshot.js +461 -0
- package/src/commands/starter.js +206 -0
- package/src/commands/status.js +302 -0
- package/src/commands/sync.js +93 -0
- package/src/commands/template.js +107 -0
- package/src/commands/update.js +887 -0
- package/src/commands/validate.js +376 -0
- package/src/framework-index.json +248 -0
- package/src/index.js +2241 -0
- package/src/templates/fetchers/github.js +113 -0
- package/src/templates/fetchers/npm.js +85 -0
- package/src/templates/fetchers/release.js +92 -0
- package/src/templates/index.js +190 -0
- package/src/templates/processor.js +333 -0
- package/src/templates/resolver.js +232 -0
- package/src/templates/validator.js +155 -0
- package/src/utils/agents-stamp.js +110 -0
- package/src/utils/args.js +126 -0
- package/src/utils/asset-upload.js +270 -0
- package/src/utils/bytes.js +30 -0
- package/src/utils/code-upload.js +394 -0
- package/src/utils/config.js +417 -0
- package/src/utils/conformance.js +274 -0
- package/src/utils/dep-survey.js +176 -0
- package/src/utils/destination-prompt.js +146 -0
- package/src/utils/discover.js +75 -0
- package/src/utils/env.js +22 -0
- package/src/utils/exit.js +30 -0
- package/src/utils/fetch-retry.js +109 -0
- package/src/utils/flag-guard.js +246 -0
- package/src/utils/foundation-name.js +179 -0
- package/src/utils/git.js +342 -0
- package/src/utils/host-prompt.js +51 -0
- package/src/utils/install-integrity.js +210 -0
- package/src/utils/interactive.js +118 -0
- package/src/utils/json-file.js +72 -0
- package/src/utils/names.js +91 -0
- package/src/utils/placement.js +151 -0
- package/src/utils/pm.js +165 -0
- package/src/utils/preview.js +20 -0
- package/src/utils/pull-written.js +132 -0
- package/src/utils/records-guard.js +88 -0
- package/src/utils/registry-auth.js +994 -0
- package/src/utils/registry-orgs.js +290 -0
- package/src/utils/scaffold.js +483 -0
- package/src/utils/schemaless-report.js +30 -0
- package/src/utils/semver-precedence.js +80 -0
- package/src/utils/session-file.js +95 -0
- package/src/utils/site-content-refs.js +23 -0
- package/src/utils/site-copies.js +123 -0
- package/src/utils/site-data-upload.js +302 -0
- package/src/utils/site-identity.js +137 -0
- package/src/utils/snapshot-settings.js +112 -0
- package/src/utils/update-check.js +302 -0
- package/src/utils/uwx-read.js +157 -0
- package/src/utils/workspace.js +223 -0
- package/src/versions.js +467 -0
- package/starter/foundation/main.js +50 -0
- package/starter/foundation/sections/Section/index.jsx +95 -0
- package/starter/foundation/sections/Section/meta.js +44 -0
- package/starter/site/pages/about/1-about.md.hbs +13 -0
- package/starter/site/pages/about/page.yml +3 -0
- package/starter/site/pages/home/1-welcome.md.hbs +14 -0
- package/starter/site/pages/home/page.yml +3 -0
- package/templates/foundation/_gitignore +5 -0
- package/templates/foundation/components/.gitkeep +0 -0
- package/templates/foundation/main.js.hbs +33 -0
- package/templates/foundation/package.json.hbs +51 -0
- package/templates/foundation/sections/.gitkeep +0 -0
- package/templates/foundation/styles.css +6 -0
- package/templates/foundation/vite.config.js +3 -0
- package/templates/site/_gitignore +12 -0
- package/templates/site/entry.js +7 -0
- package/templates/site/index.html.hbs +13 -0
- package/templates/site/package.json.hbs +27 -0
- package/templates/site/public/favicon.svg +7 -0
- package/templates/site/site.yml.hbs +91 -0
- package/templates/site/theme.yml +99 -0
- package/templates/site/vite.config.js +7 -0
- package/templates/workspace/AGENTS.md.hbs +2 -0
- package/templates/workspace/README.md.hbs +87 -0
- package/templates/workspace/_gitignore +29 -0
- package/templates/workspace/_vscode/settings.json +6 -0
- package/templates/workspace/package.json.hbs +20 -0
- package/templates/workspace/pnpm-workspace.yaml.hbs +26 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* uniweb refresh — catch up with everything outside your working copy.
|
|
3
|
+
*
|
|
4
|
+
* A developer on a synced site has TWO independent external sources, and nothing
|
|
5
|
+
* in the tool used to connect them:
|
|
6
|
+
*
|
|
7
|
+
* - the git remote — teammates' commits;
|
|
8
|
+
* - the backend — content authors' edits, made in the app.
|
|
9
|
+
*
|
|
10
|
+
* Someone who runs only `git pull` silently misses every app edit; someone who
|
|
11
|
+
* runs only `uniweb pull` misses their teammates. The person most likely to be
|
|
12
|
+
* caught out is exactly the one starting their day believing they are current.
|
|
13
|
+
* This is the one command for that: run it in the morning, or before a milestone.
|
|
14
|
+
*
|
|
15
|
+
* READ-ONLY, and that is the load-bearing decision. It never pushes. Because it
|
|
16
|
+
* cannot ship anything, it can be run reflexively without weighing consequences —
|
|
17
|
+
* which is the whole point of a start-of-day command. A version that also pushed
|
|
18
|
+
* would be one you had to think about first, and so one people would stop running.
|
|
19
|
+
*
|
|
20
|
+
* Order is deliberate: git first, then the backend. The three-way merge's common
|
|
21
|
+
* ancestor is the COMMITTED version of each file, so taking teammates' commits
|
|
22
|
+
* first makes that ancestor fresher and the merge more accurate. And if git itself
|
|
23
|
+
* conflicts, refresh stops there — one source of conflict at a time.
|
|
24
|
+
*
|
|
25
|
+
* Exits NON-ZERO when a merge left conflicts, the way a conflicted `git merge`
|
|
26
|
+
* does. That is what makes `uniweb refresh && uniweb push` correct by
|
|
27
|
+
* construction: without it, the obvious one-liner ships conflict markers into live
|
|
28
|
+
* content. If a `sync` verb is ever wanted, it is that chain.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* uniweb refresh git pull, then merge the backend's content
|
|
32
|
+
* uniweb refresh --no-git skip the git remote; backend only
|
|
33
|
+
* uniweb refresh --no-backend skip the backend; git only
|
|
34
|
+
*
|
|
35
|
+
* The backend is the one you are logged in to, as for every backend verb (no flag picks it).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { readFileSync } from 'node:fs'
|
|
39
|
+
import { join } from 'node:path'
|
|
40
|
+
import yaml from 'js-yaml'
|
|
41
|
+
|
|
42
|
+
import { resolveSiteDir } from './deploy.js'
|
|
43
|
+
import {
|
|
44
|
+
isGitRepo,
|
|
45
|
+
hasRemote,
|
|
46
|
+
pullRemote,
|
|
47
|
+
headProvenance
|
|
48
|
+
} from '../utils/git.js'
|
|
49
|
+
import { probeUnpushed } from '../backend/site-sync.js'
|
|
50
|
+
import { resolveBackendOrigin } from '../backend/client.js'
|
|
51
|
+
import { readBackendState } from '@uniweb/build/uwx'
|
|
52
|
+
import { checkFlags } from '../utils/flag-guard.js'
|
|
53
|
+
|
|
54
|
+
const c = {
|
|
55
|
+
reset: '\x1b[0m',
|
|
56
|
+
bold: '\x1b[1m',
|
|
57
|
+
dim: '\x1b[2m',
|
|
58
|
+
cyan: '\x1b[36m',
|
|
59
|
+
green: '\x1b[32m',
|
|
60
|
+
yellow: '\x1b[33m',
|
|
61
|
+
red: '\x1b[31m'
|
|
62
|
+
}
|
|
63
|
+
const say = {
|
|
64
|
+
ok: (m) => console.log(`${c.green}✓${c.reset} ${m}`),
|
|
65
|
+
info: (m) => console.log(`${c.cyan}→${c.reset} ${m}`),
|
|
66
|
+
warn: (m) => console.log(`${c.yellow}⚠${c.reset} ${m}`),
|
|
67
|
+
err: (m) => console.error(`${c.red}✗${c.reset} ${m}`),
|
|
68
|
+
dim: (m) => console.log(` ${c.dim}${m}${c.reset}`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// A site is backend-synced once it has an identity to pull by — on THIS backend,
|
|
72
|
+
// from sync.json. It read `site.yml::$uuid`, so after step 4 refresh treated every
|
|
73
|
+
// project as unsynced and skipped the backend half of its check.
|
|
74
|
+
//
|
|
75
|
+
// ⛔ `backend` is the one the delegated pull will use: the backend the user is logged in
|
|
76
|
+
// to. (For part of 2026-09-21, `refresh --backend X` checked one backend's sync state and
|
|
77
|
+
// pulled from another; the flag is gone from the backend verbs since.)
|
|
78
|
+
function siteContentUuid(siteDir, backend) {
|
|
79
|
+
try {
|
|
80
|
+
return readBackendState(siteDir, backend).site?.uuid || null
|
|
81
|
+
} catch {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {string[]} args
|
|
88
|
+
* @param {object} [deps] - injectable seams for testing, mirroring pull.js:
|
|
89
|
+
* `resolveSiteDir`, and `pull` (so the backend step can be driven without a
|
|
90
|
+
* network or a real backend).
|
|
91
|
+
*/
|
|
92
|
+
export async function refresh(args = [], deps = {}) {
|
|
93
|
+
// An unrecognized flag is invisible to a literal scan, so it silently keeps the
|
|
94
|
+
// default — and for `--backend` the default can be production. This verb never
|
|
95
|
+
// sent data anywhere, which is why it went unguarded; but it DELEGATES to `pull`,
|
|
96
|
+
// and a mistyped `--backend` is not forwarded, so the pull runs against whatever
|
|
97
|
+
// the origin ladder resolves instead of the backend the user named.
|
|
98
|
+
//
|
|
99
|
+
// `sync` validates the UNION of both halves and forwards raw argv, so a legal
|
|
100
|
+
// `uniweb sync --force` would be rejected here. It passes `skipFlagCheck`.
|
|
101
|
+
if (!deps.skipFlagCheck) {
|
|
102
|
+
const bad = checkFlags('refresh', args)
|
|
103
|
+
if (bad) {
|
|
104
|
+
say.err(bad.message)
|
|
105
|
+
return { exitCode: 2 }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const skipGit = args.includes('--no-git')
|
|
109
|
+
const skipBackend = args.includes('--no-backend')
|
|
110
|
+
const resolveSite = deps.resolveSiteDir || resolveSiteDir
|
|
111
|
+
const siteDir = await resolveSite(args, 'refresh')
|
|
112
|
+
|
|
113
|
+
// Which sources this run actually consulted. Reported at the end, because
|
|
114
|
+
// "up to date" is a different claim from "up to date with the two things I
|
|
115
|
+
// happened to be able to check" — and the second is all we can ever honestly
|
|
116
|
+
// say. Silently skipping a source is how someone concludes they are current
|
|
117
|
+
// when they are not.
|
|
118
|
+
const consulted = []
|
|
119
|
+
const skipped = []
|
|
120
|
+
let conflicts = 0
|
|
121
|
+
|
|
122
|
+
// ── 1. the git remote ─────────────────────────────────────────────────────
|
|
123
|
+
if (skipGit) {
|
|
124
|
+
skipped.push('git (--no-git)')
|
|
125
|
+
} else if (!isGitRepo(siteDir)) {
|
|
126
|
+
skipped.push('git (not a repository)')
|
|
127
|
+
} else if (!hasRemote(siteDir)) {
|
|
128
|
+
skipped.push('git (no remote configured)')
|
|
129
|
+
} else {
|
|
130
|
+
say.info('Pulling from the git remote…')
|
|
131
|
+
const r = pullRemote(siteDir)
|
|
132
|
+
if (!r.ok) {
|
|
133
|
+
say.err('git pull failed — resolve this before continuing.')
|
|
134
|
+
for (const line of r.message.split('\n').slice(0, 6)) say.dim(line)
|
|
135
|
+
// Deliberately stop. Layering the backend's content on top of an unresolved
|
|
136
|
+
// git state gives the user two independent conflicts at once, and no clear
|
|
137
|
+
// order to address them in.
|
|
138
|
+
return { exitCode: 1 }
|
|
139
|
+
}
|
|
140
|
+
say.dim(
|
|
141
|
+
r.changed
|
|
142
|
+
? 'Took new commits from the remote.'
|
|
143
|
+
: 'Already up to date with the remote.'
|
|
144
|
+
)
|
|
145
|
+
consulted.push('git')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── 2. the backend ────────────────────────────────────────────────────────
|
|
149
|
+
// The backend the delegated pull talks to — the one you are logged in to
|
|
150
|
+
// (resolveBackendOrigin). Everything below asks about THAT one.
|
|
151
|
+
const backend = resolveBackendOrigin()
|
|
152
|
+
if (skipBackend) {
|
|
153
|
+
skipped.push('backend (--no-backend)')
|
|
154
|
+
} else if (!siteContentUuid(siteDir, backend)) {
|
|
155
|
+
skipped.push('backend (this site has never been synced)')
|
|
156
|
+
} else {
|
|
157
|
+
say.info("Merging the backend's content…")
|
|
158
|
+
const pull = deps.pull || (await import('./pull.js')).pull
|
|
159
|
+
// `--merge` rather than a plain pull: an author editing a different part of the
|
|
160
|
+
// same section is not a conflict, and should not be presented as one.
|
|
161
|
+
// Nothing of refresh's own argv is forwarded: the pull goes to the backend you are
|
|
162
|
+
// logged in to, as refresh does, and authenticates the same way.
|
|
163
|
+
const res = await pull(['--merge'])
|
|
164
|
+
conflicts = res?.merge?.conflicted?.length ?? 0
|
|
165
|
+
if (res?.exitCode && !conflicts) {
|
|
166
|
+
// Failed for a reason other than conflicts — say so plainly rather than
|
|
167
|
+
// reporting a clean refresh over a lane that never ran.
|
|
168
|
+
say.err('Could not merge the backend content.')
|
|
169
|
+
return { exitCode: res.exitCode }
|
|
170
|
+
}
|
|
171
|
+
consulted.push('backend')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── 3. where that leaves you ──────────────────────────────────────────────
|
|
175
|
+
console.log('')
|
|
176
|
+
if (consulted.length) say.dim(`Checked: ${consulted.join(', ')}`)
|
|
177
|
+
for (const s of skipped) say.dim(`Skipped: ${s}`)
|
|
178
|
+
|
|
179
|
+
const git = headProvenance(siteDir)
|
|
180
|
+
if (git)
|
|
181
|
+
say.dim(
|
|
182
|
+
`Commit : ${git.sha.slice(0, 8)}${git.dirty ? ' (working tree has changes)' : ''}`
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// What is still yours to send. The point of a milestone check is knowing both
|
|
186
|
+
// directions, not just that you took what was waiting.
|
|
187
|
+
if (!skipBackend && siteContentUuid(siteDir, backend)) {
|
|
188
|
+
try {
|
|
189
|
+
// Whose asset ids the comparison reads — see status.js. Offline, so the
|
|
190
|
+
// origin is resolved rather than taken from a client.
|
|
191
|
+
const probe = await probeUnpushed(siteDir, { backend })
|
|
192
|
+
if (probe.changed)
|
|
193
|
+
say.dim(
|
|
194
|
+
`Unpushed: ${probe.changed} entit${probe.changed === 1 ? 'y' : 'ies'} changed locally`
|
|
195
|
+
)
|
|
196
|
+
} catch {
|
|
197
|
+
/* the probe is a courtesy; never fail a refresh over it */
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log('')
|
|
202
|
+
if (conflicts) {
|
|
203
|
+
say.err(
|
|
204
|
+
`${conflicts} file(s) need you to resolve conflicts before pushing.`
|
|
205
|
+
)
|
|
206
|
+
return { exitCode: 1 }
|
|
207
|
+
}
|
|
208
|
+
say.ok('Up to date.')
|
|
209
|
+
return { exitCode: 0 }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export default refresh
|