@uniweb/build 0.64.0 → 0.64.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.
@@ -359,6 +359,11 @@ export async function emitSyncPackages(siteRoot, opts = {}) {
359
359
  // …and the same data schema, where a name stands for the type of its data key
360
360
  // (`@/team` → `@/member`, `data-key-types.js`): decided once, by the records.
361
361
  keyTyped: col.keyTyped,
362
+ // …and whether this deployment takes the key that says so (`siteContent.queryFields`) —
363
+ // and the page and `settings` keys that carry Open Graph texts per language.
364
+ ...(Array.isArray(opts.queryFields) ? { queryFields: opts.queryFields } : {}),
365
+ ...(Array.isArray(opts.pageFields) ? { pageFields: opts.pageFields } : {}),
366
+ ...(Array.isArray(opts.settingsFields) ? { settingsFields: opts.settingsFields } : {}),
362
367
  // Withhold the `$services`/`$secrets` Sections when the caller has
363
368
  // determined the file is not asking for anything new by them. Passed
364
369
  // through rather than decided here: the last-agreed state is project
@@ -517,6 +522,21 @@ export async function emitSyncPackages(siteRoot, opts = {}) {
517
522
  }))
518
523
  const changedRecords = recordChanged.filter((r) => r.changed)
519
524
 
525
+ // ⛔ A RECORD THAT LOST ITS IDENTITY IS NOT UNCHANGED. Its hash was banked for this backend,
526
+ // so a push to it succeeded — and wrote the record's `$uuid` into its file — yet it goes with no
527
+ // `$uuid` now: the line is gone from the file. Skipped as unchanged, the folder still named it,
528
+ // by `$ref`, and the backend refused a reference to a record the package does not carry
529
+ // (measured 2026-09-26); sent, it would be a second record. A pull writes it back.
530
+ const refusals = [...(col.refusals || [])]
531
+ for (const r of recordChanged) {
532
+ if (r.changed || r.entity.document.$uuid) continue
533
+ refusals.push(
534
+ `${r.entity.id}: its \`$uuid\` is gone from its file, though it was pushed to this backend ` +
535
+ 'before — sent as it is, it would be a second record. Run `uniweb pull`, which writes it ' +
536
+ 'back, then push.'
537
+ )
538
+ }
539
+
520
540
  let records = null
521
541
  // ⭐ A RECORD THAT NAMES A RECORD THIS PUSH CREATES names it by `$ref`
522
542
  // (`records.js::encodeReference`), and the backend stores the uuid it mints for it. The
@@ -572,6 +592,9 @@ export async function emitSyncPackages(siteRoot, opts = {}) {
572
592
  // banked is then, by construction, what was hashed. A future injection is covered
573
593
  // by adding one line here — not by finding every reader.
574
594
  const applied = {
595
+ ...(Array.isArray(opts.queryFields) ? { queryFields: opts.queryFields } : {}),
596
+ ...(Array.isArray(opts.pageFields) ? { pageFields: opts.pageFields } : {}),
597
+ ...(Array.isArray(opts.settingsFields) ? { settingsFields: opts.settingsFields } : {}),
575
598
  ...(assetRewrite ? { assetRewrite } : {}),
576
599
  ...(assetIds ? { assetIds } : {}),
577
600
  ...(injectInfo ? { injectInfo } : {}),
@@ -582,7 +605,7 @@ export async function emitSyncPackages(siteRoot, opts = {}) {
582
605
  siteContent, records, siteContentUuid, hashes, warnings, skipped,
583
606
  // Records that name a record this push creates — see `namesNew` above.
584
607
  namesNew,
585
- refusals: col.refusals || [],
608
+ refusals,
586
609
  schemaless: col.schemaless, localAssets, applied,
587
610
  // { stamped, unknown } when identity was applied; null when the caller passed
588
611
  // no map. `unknown > 0` with `stamped === 0` on a site that has been pushed
@@ -206,10 +206,19 @@ export async function validateDataInputs({ siteRoot, foundationPath }) {
206
206
  // (pass 2). A list root was deferred here until then, though the query's records
207
207
  // are exactly that list.
208
208
 
209
+ // ⭐ WHAT THE QUERY'S RECORDS ARE, for a key typed by a schema whose root is a list: the
210
+ // list's ITEMS when the query selects other records (`@/link` records feeding `@/menu`), but
211
+ // ENTITIES of the list schema — each holding a whole list — when the query names that schema
212
+ // itself (`menus: { schema: '@/menu' }`). ⛔ Until 2026-09-26 both were checked as items, so
213
+ // every query over a list schema failed ("missing required field '[0].label'") and a push,
214
+ // which gates on this check, stopped.
215
+ const decl = typeof input.query === 'string' ? config.queries?.[input.query] : null
216
+ const querySchema = decl ? decl.schema ?? keyTypes.get(input.query) ?? `@/${input.query}` : undefined
217
+
209
218
  const pairKey = `${input.path} ${ref}`
210
219
  let entry = work.get(pairKey)
211
220
  if (!entry) {
212
- entry = { path: input.path, ref, schema, users: [] }
221
+ entry = { path: input.path, ref, schema, users: [], entities: querySchema === ref }
213
222
  work.set(pairKey, entry)
214
223
  }
215
224
  entry.users.push({ route: page.route, section: type, key })
@@ -234,10 +243,11 @@ export async function validateDataInputs({ siteRoot, foundationPath }) {
234
243
  schemasSeen.add(entry.ref)
235
244
  const items = Array.isArray(records) ? records : [records]
236
245
 
237
- // A schema whose ROOT IS A LIST describes the whole value the key receives, and the
238
- // query's records ARE that list — so they are checked once, as the list. Any other
239
- // schema describes one record, checked per record.
240
- if (rootListSection(entry.schema)) {
246
+ // A schema whose ROOT IS A LIST describes the whole value the key receives, and a query of
247
+ // other records delivers that list — so they are checked once, as the list. Any other
248
+ // schema describes one record, checked per record — and so are the entities of a list
249
+ // schema a query of that schema delivers, each holding its list (`validateItem`).
250
+ if (rootListSection(entry.schema) && !entry.entities) {
241
251
  recordCount += items.length
242
252
  const base = { file: entry.path, schema: entry.ref, users: entry.users }
243
253
  for (const finding of validateBound(entry.schema, items)) {