@uniweb/build 0.33.0 → 0.35.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/build",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -59,15 +59,15 @@
59
59
  "js-yaml": "^4.1.0",
60
60
  "sharp": "^0.35.3",
61
61
  "yaml": "^2.5.0",
62
- "@uniweb/schemas": "^0.2.13",
63
- "@uniweb/projections": "^0.5.4",
62
+ "@uniweb/projections": "^0.5.6",
64
63
  "@uniweb/theming": "^0.1.15",
65
- "@uniweb/content-reader": "^1.2.4",
66
64
  "@uniweb/semantic-parser": "^1.4.0",
67
- "@uniweb/content-writer": "^0.3.4"
65
+ "@uniweb/content-writer": "^0.3.4",
66
+ "@uniweb/content-reader": "^1.2.4",
67
+ "@uniweb/schemas": "^0.2.13"
68
68
  },
69
69
  "optionalDependencies": {
70
- "@uniweb/runtime": "^0.13.5"
70
+ "@uniweb/runtime": "^0.13.7"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
@@ -76,7 +76,7 @@
76
76
  "@tailwindcss/vite": "^4.0.0",
77
77
  "@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
78
78
  "vite-plugin-svgr": "^4.0.0",
79
- "@uniweb/core": "^0.16.0"
79
+ "@uniweb/core": "^0.18.0"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "vite": {
@@ -253,11 +253,11 @@ export function applyPostProcessing(data, config) {
253
253
  const RECOGNIZED_FETCH_KEYS = {
254
254
  refine: new Set(['refine', 'inherit', 'detail', 'limit', 'sort', 'where', 'filter']),
255
255
  query: new Set([
256
- 'query', 'schema', 'prerender', 'merge', 'transform',
256
+ 'query', 'as', 'schema', 'prerender', 'merge', 'transform',
257
257
  'where', 'limit', 'sort', 'detailPage', 'filter',
258
258
  ]),
259
259
  source: new Set([
260
- 'path', 'url', 'schema', 'prerender', 'merge', 'transform', 'detail',
260
+ 'path', 'url', 'as', 'schema', 'prerender', 'merge', 'transform', 'detail',
261
261
  'detailPage', 'where', 'limit', 'sort', 'filter',
262
262
  ]),
263
263
  }
@@ -288,11 +288,13 @@ export function parseFetchConfig(fetch) {
288
288
 
289
289
  // Simple string: "/data/team.json"
290
290
  if (typeof fetch === 'string') {
291
- const schema = inferSchemaFromPath(fetch)
291
+ const inferred = inferSchemaFromPath(fetch)
292
292
  return {
293
293
  path: fetch,
294
294
  url: undefined,
295
- schema,
295
+ // Both spellings — see the note in the named-query branch below.
296
+ as: inferred,
297
+ schema: inferred,
296
298
  prerender: true,
297
299
  merge: false,
298
300
  transform: undefined,
@@ -349,9 +351,46 @@ export function parseFetchConfig(fetch) {
349
351
  warnUnknownFetchKeys(fetch, 'query')
350
352
  if (fetch.filter !== undefined) warnFilterDeprecated()
351
353
  return {
354
+ // ⭐ **`query` IS EMITTED, and that is what makes the two producers agree.**
355
+ // The sync lane has always emitted it (`uwx/site.js`) and this one did not,
356
+ // for the same declaration — so `resolveQuerySource` fired on a published
357
+ // site and never on a `--link`-deployed one. Measured 2026-09-02 against a
358
+ // host declaring `config.records`:
359
+ //
360
+ // --link endpoint undefined, path /data/articles.json ← the STATIC file
361
+ // publish endpoint /_api/q/articles ← the live lane
362
+ //
363
+ // Same site, same declaration, two verbs, two data sources. Publishing to a
364
+ // platform that declares a live lane is supposed to READ from it — the
365
+ // compiled `/data/*.json` is the escape hatch for entities with no known
366
+ // data schema, which never sync, not a second way to serve the ones that do.
367
+ //
368
+ // ⚠️ Not in the cache key: `deriveCacheKey` hashes {path,url,endpoint,schema,
369
+ // transform}, so adding this moves no cached entry.
370
+ query: fetch.query,
352
371
  path: queryDataUrl(fetch.query),
353
372
  url: undefined,
354
- schema: fetch.schema || fetch.query,
373
+ // ⭐ **`as`, not `schema`.** The BINDING KEY — the `content.data.<key>` a
374
+ // component reads — defaults to the query name. It was called `schema`
375
+ // until 2026-09-02, which collided with the MODEL REF of the same name on a
376
+ // `queries` declaration; `fetch.schema` is still accepted as input so
377
+ // existing content keeps working, and is never emitted.
378
+ // ⛔ **BOTH SPELLINGS ARE EMITTED, and this is not the overload coming back.**
379
+ // `bindingKey()` reads `as ?? schema`, so NEW core needs only `as`. But a
380
+ // published site renders at ITS OWN pinned runtime version, and every
381
+ // runtime shipped to date bundles a core whose resolver does
382
+ // `if (!cfg?.schema) continue` — so an `as`-only payload served by an older
383
+ // runtime is SKIPPED ENTIRELY. No data, nothing thrown, nothing logged.
384
+ //
385
+ // ⭐ The compatibility runs the OTHER WAY from `bindingKey`'s: that one is
386
+ // old payloads meeting new code, this one is new payloads meeting old code,
387
+ // and only the first was covered. Caught by frontend before it shipped.
388
+ //
389
+ // ⇒ Emit both until every serving runtime carries `bindingKey`, then drop
390
+ // `schema` here. Until then a reader may see this as redundant; it is a
391
+ // compatibility duplicate and the comment is what tells them apart.
392
+ as: fetch.as || fetch.schema || fetch.query,
393
+ schema: fetch.as || fetch.schema || fetch.query,
355
394
  prerender: fetch.prerender ?? true,
356
395
  merge: fetch.merge ?? false,
357
396
  transform: fetch.transform,
@@ -371,6 +410,7 @@ export function parseFetchConfig(fetch) {
371
410
  const {
372
411
  path,
373
412
  url,
413
+ as,
374
414
  schema,
375
415
  prerender = url ? false : true,
376
416
  merge = false,
@@ -393,7 +433,9 @@ export function parseFetchConfig(fetch) {
393
433
  return {
394
434
  path,
395
435
  url,
396
- schema: schema || inferSchemaFromPath(path || url),
436
+ // Both spellings see the note in the named-query branch above.
437
+ as: as ?? schema ?? inferSchemaFromPath(path || url),
438
+ schema: as ?? schema ?? inferSchemaFromPath(path || url),
397
439
  prerender,
398
440
  merge,
399
441
  transform,
package/src/uwx/site.js CHANGED
@@ -251,7 +251,11 @@ function buildPageData(config, ctx) {
251
251
  //
252
252
  // `schema` (the query name) is BOTH the content.data key and part of the
253
253
  // dataStore cache key (deriveCacheKey hashes {path,url,endpoint,schema,…};
254
- // the shorthand is ignored). Mirrors the static build's parseFetchConfig.
254
+ // the shorthand is ignored). Mirrors the static build's parseFetchConfig
255
+ // ⚠️ which it did NOT until 2026-09-02. This line emitted `query` and that
256
+ // one did not, for the same declaration, so `resolveQuerySource` fired on a
257
+ // published site and never on a `--link`-deployed one: same site, two verbs,
258
+ // two data sources. The claim was here the whole time; the mirroring was not.
255
259
  // ⭐ `query`, END TO END — no crossing. An earlier version emitted `collection`
256
260
  // here on the belief that this field was the backend's to name. MEASURED
257
261
  // otherwise: framework already ships `transform`, `detailPage`, `merge` and
@@ -259,7 +263,14 @@ function buildPageData(config, ctx) {
259
263
  // validating — so `fetch` is a blob they carry and framework owns its
260
264
  // vocabulary. ⇒ There was nothing to coordinate, and inventing a coordination
261
265
  // is how a name stays wrong.
262
- fetch = { query, path: queryDataUrl(query), schema: query, ...rest }
266
+ // `as`, the BINDING KEY not to be confused with `schema` at
267
+ // DECL_EMITTED_ABOVE / `setIf(data,'schema',d.schema)` below, which is a
268
+ // queries decl's MODEL REF and keeps its name. One word meant both until
269
+ // 2026-09-02; this is the half that moved.
270
+ // Both spellings, deliberately — an `as`-only payload is skipped entirely by
271
+ // any runtime older than `bindingKey` (`if (!cfg?.schema) continue`), with no
272
+ // data and no error. Drop `schema` when every serving runtime carries it.
273
+ fetch = { query, path: queryDataUrl(query), as: query, schema: query, ...rest }
263
274
  }
264
275
  setIf(data, 'fetch', fetch)
265
276
  if (isDynamic) {
@@ -104,7 +104,7 @@ export async function validateDataInputs({ siteRoot, foundationPath }) {
104
104
  if (!type) return
105
105
  const bindings = foundation[type]?.data
106
106
  for (const input of collectInputs(section, page.fetch, config.fetch)) {
107
- const key = input.schema // the content.data KEY (the `fetch.schema` field is mis-named)
107
+ const key = input.as ?? input.schema // the content.data KEY; `schema` is its pre-2026-09-02 name
108
108
 
109
109
  if (input.url) {
110
110
  deferred.push({ route: page.route, section: type, key, reason: 'remote url: source', url: input.url })
@@ -437,8 +437,14 @@ async function loadStandardSchemas() {
437
437
  function collectInputs(section, pageFetch, siteFetch) {
438
438
  const byKey = new Map()
439
439
  for (const f of [siteFetch, pageFetch, section.fetch]) {
440
- if (f && (f.path || f.url) && typeof f.schema === 'string') {
441
- byKey.set(f.schema, f)
440
+ // The binding key is `as`; `schema` is its pre-2026-09-02 name and still
441
+ // arrives on stored payloads. A gate on the old name alone silently yields
442
+ // NOTHING here — no inputs collected, no violations found, a green run — which
443
+ // is exactly how this was caught: the integration test went from flagging the
444
+ // seeded violations to flagging none.
445
+ const key = f?.as ?? f?.schema
446
+ if (f && (f.path || f.url) && typeof key === 'string') {
447
+ byKey.set(key, f)
442
448
  }
443
449
  }
444
450
  return [...byKey.values()]