uniweb 0.31.1 → 0.32.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,14 +42,14 @@
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
44
  "@uniweb/core": "^0.13.0",
45
- "@uniweb/kit": "^0.13.7",
46
45
  "@uniweb/runtime": "^0.13.0",
46
+ "@uniweb/kit": "^0.13.7",
47
47
  "@uniweb/semantic-parser": "^1.3.1"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/build": "^0.27.1",
51
- "@uniweb/semantic-parser": "^1.3.1",
52
- "@uniweb/content-reader": "^1.2.4"
50
+ "@uniweb/build": "^0.28.0",
51
+ "@uniweb/content-reader": "^1.2.4",
52
+ "@uniweb/semantic-parser": "^1.3.1"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@uniweb/build": {
@@ -26,6 +26,7 @@ import {
26
26
  describeSiteDiff,
27
27
  computeUnitHashes,
28
28
  collectUnitUuids,
29
+ collectFolderItemUuids,
29
30
  readAssetMap
30
31
  } from '@uniweb/build/uwx'
31
32
 
@@ -457,6 +458,20 @@ export function mergeBaseVersions(siteDir, versions) {
457
458
  export function readItemUuids(siteDir) {
458
459
  return readMap(siteDir, 'itemUuids')
459
460
  }
461
+ /**
462
+ * Placement identity for the site's `@uniweb/folder` — path chain → `$uuid`.
463
+ *
464
+ * Kept separate from `itemUuids` (which is site-content units) because they key
465
+ * different trees: `pages/…/page.yml` there, `members/alice` here. One map with
466
+ * two key languages is a map nobody can validate.
467
+ */
468
+ export function readFolderItemUuids(siteDir) {
469
+ return readMap(siteDir, 'folderItemUuids')
470
+ }
471
+ export function writeFolderItemUuids(siteDir, map) {
472
+ if (!map || !Object.keys(map).length) return
473
+ updateSyncCache(siteDir, { folderItemUuids: map })
474
+ }
460
475
  export function writeItemUuids(siteDir, map) {
461
476
  if (!map || !Object.keys(map).length) return
462
477
  updateSyncCache(siteDir, { itemUuids: map })
@@ -1297,6 +1312,21 @@ export async function pushSyncPackages({
1297
1312
  for (const d of bf.deferred) note(`↷ ${d.id ?? `#${d.index}`}: ${d.reason}`)
1298
1313
  if (bf.updated.length)
1299
1314
  wrote.push(`wrote ${bf.updated.length} record file(s)`)
1315
+ // ⭐ BANK THE FOLDER'S PLACEMENT IDENTITY. The records back-fill their own
1316
+ // `$uuid` into their source files (above); the folder's ITEMS have nowhere to
1317
+ // be written, so they are banked here from the document the backend just
1318
+ // returned.
1319
+ //
1320
+ // ⛔ Without this a `publish` after a `push` is refused: send-only-changed
1321
+ // skips the unchanged records and re-sends the folder ALONE, its `contents`
1322
+ // items carry no `$uuid`, and a `multi` item without one reads as new — so
1323
+ // the backend refuses rather than replacing every placement. Reproduced on a
1324
+ // live manor 2026-08-27; the identities were on the wire all along.
1325
+ const folderDoc = finalized.find((f) => f?.document?.contents)?.document
1326
+ if (folderDoc) {
1327
+ const placements = collectFolderItemUuids(folderDoc)
1328
+ if (Object.keys(placements).length) writeFolderItemUuids(siteDir, placements)
1329
+ }
1300
1330
  finalizedTotal += finalized.length
1301
1331
  }
1302
1332
 
@@ -1323,6 +1353,26 @@ export async function pushSyncPackages({
1323
1353
  // Replaced wholesale: an item that no longer exists must not keep a uuid that
1324
1354
  // would re-target something else.
1325
1355
  if (theirs) writeItemUuids(siteDir, collectUnitUuids(theirs))
1356
+ else {
1357
+ // ⛔ BANKING IS BEST-EFFORT AND ITS FAILURE USED TO BE SILENT — say it here,
1358
+ // because the cost lands two commands away and names something else.
1359
+ //
1360
+ // Without `finalized[0].document` (carrying `pages`) this push banks NO item
1361
+ // identity. The push still reports success. The next `push`/`publish` then
1362
+ // emits with no `$uuid` per item, and the backend refuses — correctly, since
1363
+ // silently re-identifying every stored row is far worse. But that refusal
1364
+ // reads as a stale-token or a producer bug, with nothing pointing back at the
1365
+ // push that failed to bank.
1366
+ //
1367
+ // ⚠️ We cannot tell WHY it is absent from here — a response shape that
1368
+ // changed, a lane that shipped nothing, a backend that does not echo the
1369
+ // document. Report the observable fact and let the operator carry it.
1370
+ note(
1371
+ 'identity not banked: this push returned no post-write document, so no per-item ' +
1372
+ '$uuid was stored. The next push or publish will be identity-blind and the backend ' +
1373
+ 'may refuse it. `uniweb pull` re-arms identity if that happens.'
1374
+ )
1375
+ }
1326
1376
  }
1327
1377
  return { exitCode: 0, boundSiteUuid, finalizedTotal, wrote }
1328
1378
  }
@@ -77,6 +77,7 @@ import {
77
77
  readBaseVersions,
78
78
  readItemBaseVersions,
79
79
  ensureItemUuids,
80
+ readFolderItemUuids,
80
81
  ensureSiteExists,
81
82
  clearRemoteSyncStateIfUnbound,
82
83
  pushSyncPackages,
@@ -440,6 +441,8 @@ export async function publish(args = []) {
440
441
  let probe
441
442
  try {
442
443
  probe = await emitSyncPackages(siteDir, {
444
+ // Placement identity for the folder — see writeFolderItemUuids.
445
+ folderItemUuids: readFolderItemUuids(siteDir),
443
446
  // Resolves a foundation-relative `@/x` model ref into `@org/x`.
444
447
  ...(asOrg ? { org: asOrg } : {}),
445
448
  ...(foundationDir ? { foundationDir } : {}),
@@ -641,6 +644,8 @@ export async function publish(args = []) {
641
644
  let pkg
642
645
  try {
643
646
  pkg = await emitSyncPackages(siteDir, {
647
+ // Placement identity for the folder — see writeFolderItemUuids.
648
+ folderItemUuids: readFolderItemUuids(siteDir),
644
649
  // Resolves a foundation-relative `@/x` model ref into `@org/x`.
645
650
  ...(asOrg ? { org: asOrg } : {}),
646
651
  ...(foundationDir ? { foundationDir } : {}),
@@ -84,6 +84,7 @@ import {
84
84
  readBaseVersions,
85
85
  readItemBaseVersions,
86
86
  readItemUuids,
87
+ readFolderItemUuids,
87
88
  ensureItemUuids,
88
89
  ensureSiteExists,
89
90
  clearRemoteSyncStateIfUnbound,
@@ -373,6 +374,8 @@ export async function push(args = [], deps = {}) {
373
374
  let mediaRefs = []
374
375
  try {
375
376
  const probe = await emitSyncPackages(siteDir, {
377
+ // Placement identity for the folder — see writeFolderItemUuids.
378
+ folderItemUuids: readFolderItemUuids(siteDir),
376
379
  // Resolves a foundation-relative `@/x` model ref into `@org/x`.
377
380
  ...(asOrg ? { org: asOrg } : {}),
378
381
  ...(foundationDir ? { foundationDir } : {}),
@@ -469,6 +472,8 @@ export async function push(args = [], deps = {}) {
469
472
  let pkg
470
473
  try {
471
474
  pkg = await emitSyncPackages(siteDir, {
475
+ // Placement identity for the folder — see writeFolderItemUuids.
476
+ folderItemUuids: readFolderItemUuids(siteDir),
472
477
  // Resolves a foundation-relative `@/x` model ref into `@org/x`.
473
478
  ...(asOrg ? { org: asOrg } : {}),
474
479
  ...(foundationDir ? { foundationDir } : {}),
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-08-27T16:19:55.310Z",
3
+ "generatedAt": "2026-08-27T21:10:42.589Z",
4
4
  "packages": {
5
5
  "@uniweb/build": {
6
- "version": "0.27.1",
6
+ "version": "0.28.0",
7
7
  "path": "framework/build",
8
8
  "deps": [
9
9
  "@uniweb/content-reader",
@@ -68,7 +68,7 @@
68
68
  "deps": []
69
69
  },
70
70
  "@uniweb/projections": {
71
- "version": "0.3.7",
71
+ "version": "0.4.1",
72
72
  "path": "framework/projections",
73
73
  "deps": [
74
74
  "@uniweb/content-writer",