uniweb 0.10.11 → 0.10.13
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 +4 -4
- package/src/commands/deploy.js +78 -2
- package/src/framework-index.json +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.13",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/kit": "0.9.8",
|
|
45
44
|
"@uniweb/core": "0.7.8",
|
|
45
|
+
"@uniweb/kit": "0.9.8",
|
|
46
46
|
"@uniweb/runtime": "0.8.9"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@uniweb/build": "0.11.
|
|
50
|
-
"@uniweb/content-reader": "1.1.
|
|
49
|
+
"@uniweb/build": "0.11.9",
|
|
50
|
+
"@uniweb/content-reader": "1.1.9",
|
|
51
51
|
"@uniweb/semantic-parser": "1.1.15"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
package/src/commands/deploy.js
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
import { createServer } from 'node:http'
|
|
39
39
|
import { existsSync } from 'node:fs'
|
|
40
40
|
import { readFile, writeFile, readdir, stat } from 'node:fs/promises'
|
|
41
|
-
import { resolve, join, basename, sep } from 'node:path'
|
|
41
|
+
import { resolve, join, basename, relative, sep } from 'node:path'
|
|
42
42
|
import { execSync } from 'node:child_process'
|
|
43
43
|
import yaml from 'js-yaml'
|
|
44
44
|
|
|
@@ -178,6 +178,7 @@ export async function deploy(args = []) {
|
|
|
178
178
|
// and the whole object for the publish payload.
|
|
179
179
|
const siteContent = JSON.parse(await readFile(contentPath, 'utf8'))
|
|
180
180
|
const languages = extractLanguages(siteContent)
|
|
181
|
+
const languageLabels = extractLanguageLabels(siteContent)
|
|
181
182
|
const defaultLanguage = siteContent?.config?.defaultLanguage || languages[0] || 'en'
|
|
182
183
|
const theme = await readTheme(siteDir, siteContent)
|
|
183
184
|
|
|
@@ -223,6 +224,11 @@ export async function deploy(args = []) {
|
|
|
223
224
|
foundation,
|
|
224
225
|
runtimeVersion,
|
|
225
226
|
languages,
|
|
227
|
+
// Optional `{ code: label }` map from site.yml's object-form
|
|
228
|
+
// languages. PHP stamps this into the session JWT so CliDeployReview
|
|
229
|
+
// can use real labels (English, Français, …) when provisioning the
|
|
230
|
+
// site, instead of falling back to `lang.toUpperCase()`.
|
|
231
|
+
...(languageLabels ? { languageLabels } : {}),
|
|
226
232
|
// `name` from site.yml is a hint for the create-flow review page so
|
|
227
233
|
// the handle input is pre-filled. Ignored by authorize in other
|
|
228
234
|
// branches (fast path, intent=authorize).
|
|
@@ -312,6 +318,20 @@ export async function deploy(args = []) {
|
|
|
312
318
|
loopback.close()
|
|
313
319
|
}
|
|
314
320
|
|
|
321
|
+
// Write site.id / handle to site.yml AS SOON as we have them, before any
|
|
322
|
+
// step that can fail (validate, asset upload, publish). On first deploy
|
|
323
|
+
// the user has already paid by this point — losing the link to the
|
|
324
|
+
// server's site row would force a duplicate-create on the next attempt
|
|
325
|
+
// (and a second subscription). The features write happens later after
|
|
326
|
+
// publish; this early write only covers id/handle.
|
|
327
|
+
if (siteIdResolved && !siteYml.site?.id) {
|
|
328
|
+
await writeSiteYmlUpdates(siteYmlPath, siteYml, {
|
|
329
|
+
site: { id: siteIdResolved, handle: handleResolved },
|
|
330
|
+
})
|
|
331
|
+
siteYml.site = { ...(siteYml.site || {}), id: siteIdResolved, handle: handleResolved }
|
|
332
|
+
say.dim(`Linked site.yml to site.id=${siteIdResolved}`)
|
|
333
|
+
}
|
|
334
|
+
|
|
315
335
|
// Pre-flight against the Worker. Surfaces "foundation not published" /
|
|
316
336
|
// "runtime not found" / namespace mismatch BEFORE we ship content.
|
|
317
337
|
say.info('Validating foundation + runtime…')
|
|
@@ -349,6 +369,16 @@ export async function deploy(args = []) {
|
|
|
349
369
|
say.dim('Skipping asset upload (--skip-assets).')
|
|
350
370
|
}
|
|
351
371
|
|
|
372
|
+
// Collect compiled collection JSON files from dist/data/. The framework
|
|
373
|
+
// emits these for `collection:` data sources — `<name>.json` cascade
|
|
374
|
+
// payloads plus per-record `<name>/<slug>.json` files when `deferred:` is
|
|
375
|
+
// declared. Editor publish has no equivalent (collections live in the DB);
|
|
376
|
+
// CLI sites need them shipped as static R2 objects.
|
|
377
|
+
const dataFiles = await collectDataFiles(distDir)
|
|
378
|
+
if (Object.keys(dataFiles).length > 0) {
|
|
379
|
+
say.dim(`Data files : ${Object.keys(dataFiles).length} (collection JSON)`)
|
|
380
|
+
}
|
|
381
|
+
|
|
352
382
|
say.info('Publishing…')
|
|
353
383
|
const publishPayload = {
|
|
354
384
|
foundation,
|
|
@@ -356,6 +386,10 @@ export async function deploy(args = []) {
|
|
|
356
386
|
theme,
|
|
357
387
|
languages,
|
|
358
388
|
defaultLanguage,
|
|
389
|
+
// Compiled collection JSON files (relative-path → utf8 content). Worker
|
|
390
|
+
// publish writes each to ${sitePrefix}/data/<key>; worker serve allows
|
|
391
|
+
// /data/* paths from R2 alongside _pages/*.
|
|
392
|
+
...(Object.keys(dataFiles).length > 0 ? { dataFiles } : {}),
|
|
359
393
|
// Same shape as Editor publish — one entry per language. Single-locale
|
|
360
394
|
// sites end up with `{ [defaultLanguage]: siteContent }`; multi-locale
|
|
361
395
|
// sites carry per-locale translated content emitted by buildLocalizedContent.
|
|
@@ -530,6 +564,40 @@ function extractLanguages(siteContent) {
|
|
|
530
564
|
return langs.map((l) => (typeof l === 'string' ? l : l?.value || l?.code)).filter(Boolean)
|
|
531
565
|
}
|
|
532
566
|
|
|
567
|
+
// Collect compiled collection JSON files from dist/data/ recursively.
|
|
568
|
+
// Returns `{ '<relPath>': '<utf8-content>' }` keyed by the path under data/
|
|
569
|
+
// so the worker can write each to `${sitePrefix}/data/<relPath>` in R2.
|
|
570
|
+
// Empty object when the site has no `collection:` data sources.
|
|
571
|
+
async function collectDataFiles(distDir) {
|
|
572
|
+
const dataDir = join(distDir, 'data')
|
|
573
|
+
if (!existsSync(dataDir)) return {}
|
|
574
|
+
const files = {}
|
|
575
|
+
const entries = await readdir(dataDir, { withFileTypes: true, recursive: true })
|
|
576
|
+
for (const entry of entries) {
|
|
577
|
+
if (!entry.isFile()) continue
|
|
578
|
+
if (!entry.name.endsWith('.json')) continue
|
|
579
|
+
const fullPath = join(entry.parentPath || entry.path, entry.name)
|
|
580
|
+
const relPath = relative(dataDir, fullPath)
|
|
581
|
+
files[relPath] = await readFile(fullPath, 'utf8')
|
|
582
|
+
}
|
|
583
|
+
return files
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Optional per-language labels from site.yml's object form. Returns null when
|
|
587
|
+
// site.yml uses the plain-string form (no labels declared) — server falls back
|
|
588
|
+
// to its own defaults in that case.
|
|
589
|
+
function extractLanguageLabels(siteContent) {
|
|
590
|
+
const langs = siteContent?.config?.languages
|
|
591
|
+
if (!Array.isArray(langs)) return null
|
|
592
|
+
const labels = {}
|
|
593
|
+
for (const l of langs) {
|
|
594
|
+
if (typeof l === 'string') continue
|
|
595
|
+
const code = l?.value || l?.code
|
|
596
|
+
if (code && l?.label) labels[code] = l.label
|
|
597
|
+
}
|
|
598
|
+
return Object.keys(labels).length > 0 ? labels : null
|
|
599
|
+
}
|
|
600
|
+
|
|
533
601
|
/**
|
|
534
602
|
* Resolve theme config.
|
|
535
603
|
*
|
|
@@ -742,6 +810,7 @@ async function uploadAssetsAndRewriteContent({ siteDir, localeContents, siteYml,
|
|
|
742
810
|
const failed = []
|
|
743
811
|
await runInPool(queue, ASSET_UPLOAD_CONCURRENCY, async ({ f, plan }) => {
|
|
744
812
|
if (!plan) {
|
|
813
|
+
say.warn(`Server didn't return an upload plan for ${f.filename} — skipping.`)
|
|
745
814
|
failed.push(f.filename)
|
|
746
815
|
return
|
|
747
816
|
}
|
|
@@ -1017,9 +1086,16 @@ async function putToS3WithRetry(file, presigned, maxRetries) {
|
|
|
1017
1086
|
const res = await fetch(presigned.url, { method: 'POST', body: form })
|
|
1018
1087
|
if (res.ok || res.status === 204) return true
|
|
1019
1088
|
if (res.status >= 500 && attempt < maxRetries) continue
|
|
1089
|
+
// Surface the server's response so failures are diagnosable. S3
|
|
1090
|
+
// returns XML with a useful <Code>/<Message> on rejection (e.g.
|
|
1091
|
+
// AccessDenied + reason); silently retrying without surfacing it
|
|
1092
|
+
// hides real config issues like bucket-permission mismatches.
|
|
1093
|
+
const errBody = await res.text().catch(() => '')
|
|
1094
|
+
say.warn(`Upload of ${file.filename} rejected by S3 (HTTP ${res.status}):\n ${errBody.slice(0, 500)}`)
|
|
1020
1095
|
return false
|
|
1021
|
-
} catch {
|
|
1096
|
+
} catch (err) {
|
|
1022
1097
|
if (attempt < maxRetries) continue
|
|
1098
|
+
say.warn(`Upload of ${file.filename} failed: ${err?.message || err}`)
|
|
1023
1099
|
return false
|
|
1024
1100
|
}
|
|
1025
1101
|
}
|
package/src/framework-index.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-04-
|
|
3
|
+
"generatedAt": "2026-04-27T20:54:30.546Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/build": {
|
|
6
|
-
"version": "0.11.
|
|
6
|
+
"version": "0.11.9",
|
|
7
7
|
"path": "framework/build",
|
|
8
8
|
"deps": [
|
|
9
9
|
"@uniweb/content-reader",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
16
|
"@uniweb/content-reader": {
|
|
17
|
-
"version": "1.1.
|
|
17
|
+
"version": "1.1.9",
|
|
18
18
|
"path": "framework/content-reader",
|
|
19
19
|
"deps": []
|
|
20
20
|
},
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"deps": []
|
|
93
93
|
},
|
|
94
94
|
"@uniweb/unipress": {
|
|
95
|
-
"version": "0.2.
|
|
95
|
+
"version": "0.2.9",
|
|
96
96
|
"path": "framework/unipress",
|
|
97
97
|
"deps": [
|
|
98
98
|
"@uniweb/build",
|