uniweb 0.12.28 → 0.12.30
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 +5 -5
- package/src/commands/push.js +13 -5
- package/src/commands/register.js +64 -8
- package/src/framework-index.json +8 -8
- package/src/index.js +1 -1
- package/src/versions.js +16 -0
- package/templates/foundation/package.json.hbs +4 -4
- package/templates/site/package.json.hbs +2 -2
- package/templates/workspace/package.json.hbs +0 -3
- package/templates/workspace/pnpm-workspace.yaml.hbs +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.30",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/
|
|
45
|
-
"@uniweb/
|
|
46
|
-
"@uniweb/
|
|
44
|
+
"@uniweb/kit": "0.9.17",
|
|
45
|
+
"@uniweb/runtime": "0.8.18",
|
|
46
|
+
"@uniweb/core": "0.7.13"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@uniweb/build": "0.14.
|
|
49
|
+
"@uniweb/build": "0.14.13",
|
|
50
50
|
"@uniweb/content-reader": "1.1.12",
|
|
51
51
|
"@uniweb/semantic-parser": "1.1.17"
|
|
52
52
|
},
|
package/src/commands/push.js
CHANGED
|
@@ -122,11 +122,17 @@ function changedSummary(finalized) {
|
|
|
122
122
|
// (the `@uniweb/data-schema` form) from the backend via the client. Cached per run;
|
|
123
123
|
// HTTP 404 → null (the emitter then says "register it first"). The bearer is acquired
|
|
124
124
|
// lazily by the client, so a fully-local sync never authenticates.
|
|
125
|
-
|
|
125
|
+
//
|
|
126
|
+
// `offline` (set for `-o` / `--dry-run`) forces every non-local Model to null WITHOUT
|
|
127
|
+
// touching the backend — an offline emit must never authenticate. The collections
|
|
128
|
+
// emitter then soft-skips a convention-defaulted schema with a warning ("not synced")
|
|
129
|
+
// and still emits the site-content lane; an EXPLICIT non-local schema surfaces as a
|
|
130
|
+
// clear "could not be resolved" error rather than an auth prompt.
|
|
131
|
+
export function makeModelResolver({ client, offline = false }) {
|
|
126
132
|
const cache = new Map()
|
|
127
133
|
return async (modelName) => {
|
|
128
134
|
if (cache.has(modelName)) return cache.get(modelName)
|
|
129
|
-
const decl = await client.readDataSchema(modelName)
|
|
135
|
+
const decl = offline ? null : await client.readDataSchema(modelName)
|
|
130
136
|
cache.set(modelName, decl)
|
|
131
137
|
return decl
|
|
132
138
|
}
|
|
@@ -161,8 +167,10 @@ export async function push(args = []) {
|
|
|
161
167
|
const foundationDir = flagValue(args, '--foundation')
|
|
162
168
|
const sendAll = args.includes('--all') // bypass the send-only-changed cache
|
|
163
169
|
// One front door. The bearer is resolved lazily on first need (a non-local Model
|
|
164
|
-
// read during the build, or the submit)
|
|
165
|
-
//
|
|
170
|
+
// read during the build, or the submit). Offline emit (--dry-run / -o) is fully
|
|
171
|
+
// offline: it never submits, and its Model resolver never reads from the backend
|
|
172
|
+
// (the `offline` flag below), so it never authenticates — even when a collection
|
|
173
|
+
// references a Model the local foundation doesn't define.
|
|
166
174
|
const client = new BackendClient({
|
|
167
175
|
originFlag: flagValue(args, '--registry'),
|
|
168
176
|
token: tokenFlag,
|
|
@@ -181,7 +189,7 @@ export async function push(args = []) {
|
|
|
181
189
|
try {
|
|
182
190
|
pkg = await emitSyncPackages(siteDir, {
|
|
183
191
|
...(foundationDir ? { foundationDir } : {}),
|
|
184
|
-
resolveModel: makeModelResolver({ client }),
|
|
192
|
+
resolveModel: makeModelResolver({ client, offline: Boolean(output) || dryRun }),
|
|
185
193
|
priorHashes,
|
|
186
194
|
sendAll,
|
|
187
195
|
})
|
package/src/commands/register.js
CHANGED
|
@@ -27,10 +27,13 @@
|
|
|
27
27
|
* uniweb register --schema-only Skip the code delivery (schemas land, no dist upload)
|
|
28
28
|
* uniweb register --dry-run Print the .uwx + the code file plan; submit nothing
|
|
29
29
|
* uniweb register -o foundation.uwx Write the .uwx to a file; submit nothing
|
|
30
|
-
* uniweb register --
|
|
30
|
+
* uniweb register --json Porcelain: ONE compact JSON line on stdout
|
|
31
|
+
* ({ok,scope,origin,entities:[{name,uuid,version,unchanged}]}),
|
|
32
|
+
* all human output to stderr — for scripted callers
|
|
33
|
+
* uniweb register --registry <url> Override the submit endpoint (alias: --backend)
|
|
31
34
|
* uniweb register --token <bearer> Submit with this bearer; skips `uniweb login`
|
|
32
35
|
*
|
|
33
|
-
* Endpoint resolution: --registry <url> > UNIWEB_REGISTER_URL > the local default.
|
|
36
|
+
* Endpoint resolution: --backend / --registry <url> > UNIWEB_REGISTER_URL > the local default.
|
|
34
37
|
* Auth (submit only): --token <bearer> > UNIWEB_TOKEN > `uniweb login` session.
|
|
35
38
|
*/
|
|
36
39
|
|
|
@@ -70,10 +73,18 @@ const colors = {
|
|
|
70
73
|
reset: '\x1b[0m', bright: '\x1b[1m', dim: '\x1b[2m',
|
|
71
74
|
red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[36m',
|
|
72
75
|
}
|
|
73
|
-
|
|
76
|
+
// Porcelain (`--json`) mode: stdout carries ONLY the final compact JSON line, so
|
|
77
|
+
// all human/colored output diverts to stderr. `emitJson` writes to the REAL
|
|
78
|
+
// stdout (bypassing the redirect). `jsonMode`/`jsonEmitted`/`lastError` are reset
|
|
79
|
+
// per run by the exported `register` wrapper.
|
|
80
|
+
let jsonMode = false
|
|
81
|
+
let jsonEmitted = false
|
|
82
|
+
let lastError = null
|
|
83
|
+
const log = (...a) => (jsonMode ? console.error(...a) : console.log(...a))
|
|
74
84
|
const success = (m) => log(`${colors.green}✓${colors.reset} ${m}`)
|
|
75
|
-
const error = (m) => console.error(`${colors.red}✗${colors.reset} ${m}`)
|
|
85
|
+
const error = (m) => { lastError = String(m); console.error(`${colors.red}✗${colors.reset} ${m}`) }
|
|
76
86
|
const info = (m) => log(`${colors.blue}→${colors.reset} ${m}`)
|
|
87
|
+
const emitJson = (obj) => { jsonEmitted = true; process.stdout.write(JSON.stringify(obj) + '\n') }
|
|
77
88
|
|
|
78
89
|
function flagValue(args, name) {
|
|
79
90
|
const eq = args.find((a) => a.startsWith(`${name}=`))
|
|
@@ -195,11 +206,27 @@ export function foundationNeedsBuild(targetDir) {
|
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
export async function register(args = []) {
|
|
209
|
+
jsonMode = args.includes('--json')
|
|
210
|
+
jsonEmitted = false
|
|
211
|
+
lastError = null
|
|
212
|
+
const result = await runRegister(args)
|
|
213
|
+
// Guarantee a porcelain line on stdout for every --json exit: the success path
|
|
214
|
+
// emits its own; here we cover the error / early-return paths so a scripted
|
|
215
|
+
// caller can always JSON.parse(stdout).
|
|
216
|
+
if (jsonMode && !jsonEmitted) {
|
|
217
|
+
emitJson(result?.exitCode === 0 ? { ok: true, entities: [] } : { ok: false, error: lastError || `register failed (exit ${result?.exitCode ?? 1})` })
|
|
218
|
+
}
|
|
219
|
+
return result
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function runRegister(args = []) {
|
|
198
223
|
const dryRun = args.includes('--dry-run')
|
|
199
224
|
const output = flagValue(args, '-o') || flagValue(args, '--output')
|
|
200
225
|
const scopeFlag = flagValue(args, '--scope')
|
|
201
226
|
const tokenFlag = flagValue(args, '--token')
|
|
202
|
-
|
|
227
|
+
// Origin: --backend and --registry are aliases (matches deploy/publish + the
|
|
228
|
+
// origin-selection convention); either overrides UNIWEB_REGISTER_URL / default.
|
|
229
|
+
const client = new BackendClient({ originFlag: flagValue(args, '--backend') || flagValue(args, '--registry'), token: tokenFlag, args, command: 'Registering' })
|
|
203
230
|
|
|
204
231
|
// Target: a schemas-only package (standalone data-schema register) or a
|
|
205
232
|
// foundation (foundation + the schemas it renders). A schemas package is only
|
|
@@ -340,12 +367,16 @@ export async function register(args = []) {
|
|
|
340
367
|
res = await client.register(json)
|
|
341
368
|
} catch (err) {
|
|
342
369
|
error(`Could not reach the registry at ${client.origin}: ${err.message}`)
|
|
343
|
-
log(` ${colors.dim}Set the endpoint with --registry <url> or UNIWEB_REGISTER_URL.${colors.reset}`)
|
|
370
|
+
log(` ${colors.dim}Set the endpoint with --backend/--registry <url> or UNIWEB_REGISTER_URL.${colors.reset}`)
|
|
344
371
|
return { exitCode: 2 }
|
|
345
372
|
}
|
|
373
|
+
// Read the response body once: the --json success path needs it for the minted
|
|
374
|
+
// entity ids; the error path shows it.
|
|
375
|
+
const rawBody = await res.text().catch(() => '')
|
|
376
|
+
let parsedBody = null
|
|
377
|
+
try { parsedBody = rawBody ? JSON.parse(rawBody) : null } catch { parsedBody = null }
|
|
346
378
|
let alreadyRegistered = false
|
|
347
379
|
if (!res.ok) {
|
|
348
|
-
const body = await res.text().catch(() => '')
|
|
349
380
|
// Resume path: a registered version is immutable, so re-running after a
|
|
350
381
|
// partial code delivery hits the duplicate rejection here — a STRUCTURED
|
|
351
382
|
// 409 (problem+json, title "Conflict") — and proceeds to phase 2 (the
|
|
@@ -361,7 +392,7 @@ export async function register(args = []) {
|
|
|
361
392
|
log(` ${colors.dim}The registry didn't accept your credentials — it may use different ones than \`uniweb login\`.${colors.reset}`)
|
|
362
393
|
log(` ${colors.dim}Supply a registry bearer with --token <bearer> (or UNIWEB_TOKEN); an existing one may be wrong or expired.${colors.reset}`)
|
|
363
394
|
}
|
|
364
|
-
if (
|
|
395
|
+
if (rawBody) log(` ${colors.dim}${rawBody.slice(0, 500)}${colors.reset}`)
|
|
365
396
|
return { exitCode: 1 }
|
|
366
397
|
}
|
|
367
398
|
}
|
|
@@ -415,5 +446,30 @@ export async function register(args = []) {
|
|
|
415
446
|
return { exitCode: 1 }
|
|
416
447
|
}
|
|
417
448
|
}
|
|
449
|
+
if (jsonMode) {
|
|
450
|
+
// Join my authoritative submitted names with the backend's minted ids. Each
|
|
451
|
+
// response entry is `{ registered: { name, version, payload_model_uuid, … },
|
|
452
|
+
// unchanged }` (symmetric on the new-version + unchanged branches); a flat
|
|
453
|
+
// shape is tolerated as a fallback. Names from doc.entities are the spine, so
|
|
454
|
+
// the porcelain always reports WHICH names landed even if a field is absent.
|
|
455
|
+
const minted = {}
|
|
456
|
+
const addMint = (e) => {
|
|
457
|
+
if (!e || typeof e !== 'object') return
|
|
458
|
+
const reg = e.registered ?? e
|
|
459
|
+
if (reg.name) minted[reg.name] = { uuid: reg.payload_model_uuid ?? null, version: reg.version ?? null, unchanged: e.unchanged === true }
|
|
460
|
+
}
|
|
461
|
+
if (parsedBody && typeof parsedBody === 'object') {
|
|
462
|
+
if (Array.isArray(parsedBody.data_schemas)) parsedBody.data_schemas.forEach(addMint)
|
|
463
|
+
if (parsedBody.foundation_schema) addMint(parsedBody.foundation_schema)
|
|
464
|
+
}
|
|
465
|
+
const names = [
|
|
466
|
+
...doc.entities.filter((e) => e.model === '@uniweb/data-schema').map((e) => e.name),
|
|
467
|
+
// The foundation-schema entity carries its `@scope/name` under `info`, not a
|
|
468
|
+
// top-level `name` (that's the foundation-schema shape).
|
|
469
|
+
...doc.entities.filter((e) => e.model === '@uniweb/foundation-schema').map((e) => e.info?.name ?? e.name),
|
|
470
|
+
].filter(Boolean)
|
|
471
|
+
const entities = names.map((name) => ({ name, ...(minted[name] || { uuid: null, version: null, unchanged: false }) }))
|
|
472
|
+
emitJson({ ok: true, scope: scope || null, origin: client.origin, entities })
|
|
473
|
+
}
|
|
418
474
|
return { exitCode: 0 }
|
|
419
475
|
}
|
package/src/framework-index.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-06-
|
|
3
|
+
"generatedAt": "2026-06-16T23:17:46.927Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/build": {
|
|
6
|
-
"version": "0.14.
|
|
6
|
+
"version": "0.14.13",
|
|
7
7
|
"path": "framework/build",
|
|
8
8
|
"deps": [
|
|
9
9
|
"@uniweb/content-reader",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"deps": []
|
|
26
26
|
},
|
|
27
27
|
"@uniweb/core": {
|
|
28
|
-
"version": "0.7.
|
|
28
|
+
"version": "0.7.13",
|
|
29
29
|
"path": "framework/core",
|
|
30
30
|
"deps": [
|
|
31
31
|
"@uniweb/semantic-parser",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"deps": []
|
|
44
44
|
},
|
|
45
45
|
"@uniweb/kit": {
|
|
46
|
-
"version": "0.9.
|
|
46
|
+
"version": "0.9.17",
|
|
47
47
|
"path": "framework/kit",
|
|
48
48
|
"deps": [
|
|
49
49
|
"@uniweb/core",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"deps": []
|
|
62
62
|
},
|
|
63
63
|
"@uniweb/runtime": {
|
|
64
|
-
"version": "0.8.
|
|
64
|
+
"version": "0.8.18",
|
|
65
65
|
"path": "framework/runtime",
|
|
66
66
|
"deps": [
|
|
67
67
|
"@uniweb/core",
|
|
@@ -89,17 +89,17 @@
|
|
|
89
89
|
"deps": []
|
|
90
90
|
},
|
|
91
91
|
"@uniweb/templates": {
|
|
92
|
-
"version": "0.7.
|
|
92
|
+
"version": "0.7.41",
|
|
93
93
|
"path": "framework/templates",
|
|
94
94
|
"deps": []
|
|
95
95
|
},
|
|
96
96
|
"@uniweb/theming": {
|
|
97
|
-
"version": "0.1.
|
|
97
|
+
"version": "0.1.4",
|
|
98
98
|
"path": "framework/theming",
|
|
99
99
|
"deps": []
|
|
100
100
|
},
|
|
101
101
|
"@uniweb/unipress": {
|
|
102
|
-
"version": "0.4.
|
|
102
|
+
"version": "0.4.18",
|
|
103
103
|
"path": "framework/unipress",
|
|
104
104
|
"deps": [
|
|
105
105
|
"@uniweb/build",
|
package/src/index.js
CHANGED
|
@@ -1105,7 +1105,7 @@ ${colors.bright}Options:${colors.reset}
|
|
|
1105
1105
|
--dry-run Resolve site.yml + foundation/runtime; print summary; no writes
|
|
1106
1106
|
--no-auto-publish Don't auto-publish workspace-local foundation as part of deploy
|
|
1107
1107
|
--no-save Skip the auto-save of lastDeploy in deploy.yml
|
|
1108
|
-
--
|
|
1108
|
+
--backend <url> Override the default backend origin (\$UNIWEB_REGISTER_URL or built-in)
|
|
1109
1109
|
--non-interactive Fail with usage info instead of prompting
|
|
1110
1110
|
|
|
1111
1111
|
${colors.bright}Auth:${colors.reset}
|
package/src/versions.js
CHANGED
|
@@ -40,6 +40,18 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
40
40
|
// Cache for resolved versions
|
|
41
41
|
let resolvedVersions = null
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* The React major the framework standardizes on.
|
|
45
|
+
*
|
|
46
|
+
* React 19 changed the element marker symbol, so mixing React 18 and 19
|
|
47
|
+
* across the foundation ↔ runtime SSR boundary breaks rendering. The whole
|
|
48
|
+
* toolchain therefore pins a single major. This is the one place that value
|
|
49
|
+
* lives: scaffolded `package.json` files reference it through the
|
|
50
|
+
* `{{version "react"}}` / `{{version "react-dom"}}` template helpers instead
|
|
51
|
+
* of hardcoding a range.
|
|
52
|
+
*/
|
|
53
|
+
export const REACT_VERSION = '^19.0.0'
|
|
54
|
+
|
|
43
55
|
/**
|
|
44
56
|
* Get the CLI's own package.json
|
|
45
57
|
*/
|
|
@@ -285,6 +297,10 @@ export function getVersionsForTemplates() {
|
|
|
285
297
|
// Full package names
|
|
286
298
|
...versions,
|
|
287
299
|
|
|
300
|
+
// React pinned to the framework's official major (single source: REACT_VERSION above).
|
|
301
|
+
react: REACT_VERSION,
|
|
302
|
+
'react-dom': REACT_VERSION,
|
|
303
|
+
|
|
288
304
|
// Simplified names for templates (e.g., {{versions.build}})
|
|
289
305
|
build: versions['@uniweb/build'],
|
|
290
306
|
runtime: versions['@uniweb/runtime'],
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"preview": "vite preview"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"react": "
|
|
26
|
-
"react-dom": "
|
|
25
|
+
"react": "{{version "react"}}",
|
|
26
|
+
"react-dom": "{{version "react-dom"}}"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@uniweb/core": "{{version "@uniweb/core"}}",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"@tailwindcss/vite": "^4.0.0",
|
|
34
34
|
"@uniweb/build": "{{version "@uniweb/build"}}",
|
|
35
35
|
"@vitejs/plugin-react": "^5.0.0",
|
|
36
|
-
"react": "
|
|
37
|
-
"react-dom": "
|
|
36
|
+
"react": "{{version "react"}}",
|
|
37
|
+
"react-dom": "{{version "react-dom"}}",
|
|
38
38
|
"tailwindcss": "^4.0.0",
|
|
39
39
|
"vite": "^7.0.0",
|
|
40
40
|
"vite-plugin-svgr": "^4.2.0"
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"@tailwindcss/vite": "^4.0.0",
|
|
18
18
|
"@uniweb/build": "{{version "@uniweb/build"}}",
|
|
19
19
|
"@vitejs/plugin-react": "^5.0.0",
|
|
20
|
-
"react": "
|
|
21
|
-
"react-dom": "
|
|
20
|
+
"react": "{{version "react"}}",
|
|
21
|
+
"react-dom": "{{version "react-dom"}}",
|
|
22
22
|
"react-router-dom": "^7.0.0",
|
|
23
23
|
"tailwindcss": "^4.0.0",
|
|
24
24
|
"vite": "^7.0.0",
|