uniweb 1.17.0 → 1.17.1

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": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,15 +41,15 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/schemas": "~1.6.5",
45
- "@uniweb/semantic-parser": "~1.12.2",
44
+ "@uniweb/content-writer": "~1.3.0",
46
45
  "@uniweb/core": "~1.4.0",
47
- "@uniweb/content-writer": "~1.3.0"
46
+ "@uniweb/schemas": "~1.6.5",
47
+ "@uniweb/semantic-parser": "~1.12.2"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/build": "~1.8.0",
51
- "@uniweb/semantic-parser": "~1.12.2",
52
- "@uniweb/content-reader": "~1.5.4"
50
+ "@uniweb/build": "~1.8.1",
51
+ "@uniweb/content-reader": "~1.5.4",
52
+ "@uniweb/semantic-parser": "~1.12.2"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@uniweb/build": {
@@ -92,6 +92,17 @@ function entityDocFromUwx(buf) {
92
92
  return null
93
93
  }
94
94
 
95
+ // What a `.uwx`'s entries inflate to, summed — the quantity a backend's package limit counts.
96
+ // null for anything that is not a readable `.uwx`.
97
+ function inflatedSizeOfUwx(buf) {
98
+ if (!buf || buf.length < 2 || buf[0] !== 0x50 || buf[1] !== 0x4b) return null
99
+ try {
100
+ return [...readZip(buf).values()].reduce((n, data) => n + data.length, 0)
101
+ } catch {
102
+ return null
103
+ }
104
+ }
105
+
95
106
  // Every entity `$`-document of a `.uwx` in the order it was submitted — the manifest's, which is
96
107
  // what a push's `finalized[].index` counts in. null where a listed file is missing or unreadable.
97
108
  function submittedDocsFromUwx(buf) {
@@ -2169,12 +2180,13 @@ export async function pushSyncPackages({
2169
2180
  // `boundUuid` is set only for the lanes that address an EXISTING site by uuid
2170
2181
  // (content UPDATE, folder push) — never for the CREATE, which has no uuid to be
2171
2182
  // wrong about. It is what lets a 404 be read as "the site this clone is bound to
2172
- // is gone" rather than "the route is missing".
2183
+ // is gone" rather than "the route is missing". `sent` is the package the lane sends,
2184
+ // which a refusal for size measures.
2173
2185
  const postLane = async (
2174
2186
  label,
2175
2187
  doRequest,
2176
2188
  explainStale = async () => [],
2177
- { boundUuid = null, reportTemplate = false } = {}
2189
+ { boundUuid = null, reportTemplate = false, sent = null } = {}
2178
2190
  ) => {
2179
2191
  info(`Pushing ${label} to ${dim(client.origin)} …`)
2180
2192
  let res
@@ -2191,7 +2203,7 @@ export async function pushSyncPackages({
2191
2203
  // Two unrelated conflicts share HTTP 409, so branch on the machine-readable
2192
2204
  // `reason` — never on `detail`, which is prose the backend may reword.
2193
2205
  let problem = null
2194
- if ((res.status === 409 || res.status === 400 || res.status === 403 || res.status === 422) && body) {
2206
+ if ((res.status === 409 || res.status === 400 || res.status === 403 || res.status === 413 || res.status === 422) && body) {
2195
2207
  try {
2196
2208
  problem = JSON.parse(body)
2197
2209
  } catch {
@@ -2376,6 +2388,25 @@ export async function pushSyncPackages({
2376
2388
  ` deleted there → uniweb forget --server ${client.origin}, then push again: it creates a NEW site`
2377
2389
  )
2378
2390
  note('Deleting this folder removes only your local copy, either way.')
2391
+ } else if (res.status === 413) {
2392
+ // ⭐ TWO LIMITS, ON TWO QUANTITIES, BOTH THE BACKEND'S (backend, 2026-10-23): the
2393
+ // request as sent, and the package as its entries inflate. A package refusal names its
2394
+ // limit (`package_too_large`, `limit` in bytes), so quote it beside what this package
2395
+ // inflates to. A request refusal names none, so say what was sent and quote no limit —
2396
+ // a copy of theirs here would be a second constant to drift. The package is already
2397
+ // Deflated (`createZip`), so there is no smaller form of it to retry with.
2398
+ if (problem?.reason === 'package_too_large') {
2399
+ const inflated = humanBytes(inflatedSizeOfUwx(sent))
2400
+ const limit = humanBytes(problem.limit)
2401
+ note(
2402
+ `${inflated ? `This push's content is ${inflated} uncompressed` : "This push's content is too large"} — more than the ${limit ? `${limit} ` : ''}the backend at ${client.origin} takes in one push.`
2403
+ )
2404
+ } else {
2405
+ const size = humanBytes(sent?.length)
2406
+ note(
2407
+ `${size ? `This push sent ${size}` : 'This push was too large'} — more than the backend at ${client.origin} accepts in one request.`
2408
+ )
2409
+ }
2379
2410
  } else if (problem?.reason === 'template_records_not_copyable' && Array.isArray(problem.records)) {
2380
2411
  // ⭐ A push asking to designate the site a template is refused whole when a record in its
2381
2412
  // folder cannot be copied into the sites made from it — each named, with a `detail` written
@@ -2615,7 +2646,7 @@ export async function pushSyncPackages({
2615
2646
  localBuffer: siteContent.buffer,
2616
2647
  uuid: siteContentUuid
2617
2648
  }),
2618
- { boundUuid: siteContentUuid, reportTemplate: true }
2649
+ { boundUuid: siteContentUuid, reportTemplate: true, sent: siteContent.buffer }
2619
2650
  )
2620
2651
  if (!finalized) {
2621
2652
  mergeHarvested()
@@ -2631,7 +2662,7 @@ export async function pushSyncPackages({
2631
2662
  'site-content',
2632
2663
  () => client.createSiteContent(siteContent.buffer, { source }),
2633
2664
  undefined,
2634
- { reportTemplate: true }
2665
+ { reportTemplate: true, sent: siteContent.buffer }
2635
2666
  )
2636
2667
  if (payload === null) return { exitCode: 1, finalizedTotal, wrote }
2637
2668
  const minted = extractMintedSiteUuid(payload)
@@ -2679,7 +2710,7 @@ export async function pushSyncPackages({
2679
2710
  'records',
2680
2711
  () => client.pushFolder(boundSiteUuid, records.buffer, { source }),
2681
2712
  undefined,
2682
- { boundUuid: boundSiteUuid }
2713
+ { boundUuid: boundSiteUuid, sent: records.buffer }
2683
2714
  )
2684
2715
  if (!finalized) {
2685
2716
  mergeHarvested({ point: false })
@@ -338,8 +338,8 @@ export function describeUnplacedRecords({ docs, taken, skipped }) {
338
338
  }
339
339
 
340
340
  // Read a pull lane's bytes into entity `$`-documents. The backend serves a `.uwx`
341
- // (our Stored ZIP: `manifest.json` + `entities/<uuid>.json`); the entity files ARE the
342
- // documents. Falls back to a JSON body (a raw doc, a `{document}`/`{entity}` envelope,
341
+ // (a ZIP of `manifest.json` + `entities/<uuid>.json`, each entry Stored or Deflate —
342
+ // `readZip` reads both); the entity files ARE the documents. Falls back to a JSON body (a raw doc, a `{document}`/`{entity}` envelope,
343
343
  // or a list) so the lane survives a future envelope change. Returns an array (possibly
344
344
  // empty).
345
345
  export function readPullDocuments(buf) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-23T17:42:44.689Z",
3
+ "generatedAt": "2026-10-23T19:14:59.641Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
6
  "version": "1.6.4"
7
7
  },
8
8
  "@uniweb/build": {
9
- "version": "1.8.0"
9
+ "version": "1.8.1"
10
10
  },
11
11
  "@uniweb/content-reader": {
12
12
  "version": "1.5.4"
@@ -21,12 +21,14 @@
21
21
  *
22
22
  * ## Two things measured on a real payload, both of which would break a naive reader
23
23
  *
24
- * 1. ⛔ **The archive is NOT "stored".** `pull.js` describes the format as *"our
25
- * Stored ZIP"*, and that is true of `manifest.json` and false of the entity
26
- * files — measured on a live pull: `manifest.json` method 0 (STORED), the
27
- * entity JSON method 8 (DEFLATED). A stored-only reader silently yields the
24
+ * 1. ⛔ **The archive is NOT "stored".** `pull.js` described the format as *"our
25
+ * Stored ZIP"* until 2026-10-23, and that is true of `manifest.json` and false of
26
+ * the entity files — measured on a live pull: `manifest.json` method 0 (STORED),
27
+ * the entity JSON method 8 (DEFLATED). A stored-only reader silently yields the
28
28
  * manifest and drops the document, i.e. it returns success and no entities.
29
- * **Both methods are handled below and the tests cover a mixed archive.**
29
+ * **Both methods are handled below and the tests cover a mixed archive.** Our own
30
+ * writer (`@uniweb/build`'s `createZip`) has Deflated too since that day, so no
31
+ * `.uwx` in either direction is Stored-only.
30
32
  * 2. The entity payload is the *only* thing wanted; `manifest.json` is skipped, as
31
33
  * every other reader in the tree does.
32
34
  *