uniweb 1.17.1 → 1.17.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "1.17.1",
3
+ "version": "1.17.2",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,12 +42,12 @@
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
44
  "@uniweb/content-writer": "~1.3.0",
45
- "@uniweb/core": "~1.4.0",
46
45
  "@uniweb/schemas": "~1.6.5",
46
+ "@uniweb/core": "~1.4.1",
47
47
  "@uniweb/semantic-parser": "~1.12.2"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/build": "~1.8.1",
50
+ "@uniweb/build": "~1.8.2",
51
51
  "@uniweb/content-reader": "~1.5.4",
52
52
  "@uniweb/semantic-parser": "~1.12.2"
53
53
  },
@@ -822,7 +822,7 @@ agents:
822
822
  | not declared | no check at all — nothing happens unless you ask for it |
823
823
  | your own site's origin | always fine, without listing it — declaring a partner must not take down your own chat box |
824
824
  | a request with **no** `Origin` | **allowed** — see below, this is deliberate |
825
- | matching | exact, per entry; no wildcards |
825
+ | matching | by origin: `https://app.example.com/` is `https://app.example.com`, and a path is ignored; no wildcards. An entry that is not an `http(s)` URL matches nothing |
826
826
 
827
827
  ⛔ **It is not a spend limit, and it cannot be.** `Origin` is set by the browser and enforced by the browser: anything that is not a browser sends whatever it likes, or nothing at all. So this stops **casual misuse and third-party embedding** — someone dropping your endpoint into their own page — and it does **not** stop anyone determined to run up your bill. Do not size your budget on it.
828
828
 
@@ -19,7 +19,7 @@ import {
19
19
  extensionNameOf,
20
20
  findSiteExtension
21
21
  } from '@uniweb/build'
22
- import { loadDeployYml, AGENTS_KEYS } from '@uniweb/build/site'
22
+ import { loadDeployYml, AGENTS_KEYS, originOf } from '@uniweb/build/site'
23
23
  import { listAdapters } from '@uniweb/build/hosts'
24
24
  import { RUNTIME_KEYS } from '@uniweb/build/uwx'
25
25
  import { getCliVersion } from '../versions.js'
@@ -337,6 +337,11 @@ export function findFormContent(sitePath, siteYml) {
337
337
  *
338
338
  * `agents: false` turns the capability off in one word and is not an object —
339
339
  * flagging its absence of keys would be nonsense.
340
+ *
341
+ * ⭐ **An `expectedOrigins` entry that names no origin matches nothing**, and that is silent
342
+ * too. `app.example.com`, with no scheme, reads as no origin, so a list of only such entries
343
+ * declines every third-party caller. The entry is read by the rule the host checks by
344
+ * (`originOf`, `@uniweb/projections`).
340
345
  */
341
346
  export function checkAgentsBlock({ siteName, siteYml, issues }) {
342
347
  const agents = siteYml?.agents
@@ -353,6 +358,8 @@ export function checkAgentsBlock({ siteName, siteYml, issues }) {
353
358
  return
354
359
  }
355
360
 
361
+ checkExpectedOrigins({ siteName, agents, issues })
362
+
356
363
  const known = new Set(AGENTS_KEYS)
357
364
  const unknown = Object.keys(agents).filter((k) => !known.has(k))
358
365
  if (unknown.length === 0) return
@@ -381,6 +388,24 @@ export function checkAgentsBlock({ siteName, siteYml, issues }) {
381
388
  )
382
389
  }
383
390
 
391
+ /** `agents.expectedOrigins` entries that name no origin, each named. */
392
+ function checkExpectedOrigins({ siteName, agents, issues }) {
393
+ const declared = agents.expectedOrigins
394
+ if (declared === undefined || declared === null) return
395
+ const unreadable = (Array.isArray(declared) ? declared : [declared]).filter((entry) => originOf(entry) === null)
396
+ if (unreadable.length === 0) return
397
+ const id = 'agents-origin-unreadable'
398
+ const shown = unreadable.map((entry) => `'${String(entry)}'`).join(', ')
399
+ issues.push({
400
+ id,
401
+ type: 'warning',
402
+ site: siteName,
403
+ message: `site.yml: \`agents.expectedOrigins\` ${unreadable.length === 1 ? 'entry' : 'entries'} ${shown} name${unreadable.length === 1 ? 's' : ''} no origin, so ${unreadable.length === 1 ? 'it matches' : 'they match'} nothing`
404
+ })
405
+ warn(`[${id}] ${siteName}: \`agents.expectedOrigins\` ${unreadable.length === 1 ? 'entry' : 'entries'} ${shown} name${unreadable.length === 1 ? 's' : ''} no origin.`)
406
+ log(` ${colors.dim}Write each as an http(s) URL with its scheme, e.g. https://app.example.com. An entry that names no origin matches nothing.${colors.reset}`)
407
+ }
408
+
384
409
  /**
385
410
  * The closest known key within a small edit distance, or null.
386
411
  *
@@ -78,6 +78,7 @@ import {
78
78
  readBackendState,
79
79
  siteContentDocumentToProject,
80
80
  dataBlockModels,
81
+ siteDefaultSectionType,
81
82
  recordsToProject,
82
83
  siteSelfScope,
83
84
  readZip,
@@ -829,6 +830,7 @@ export async function pull(args = [], deps = {}) {
829
830
  // resolves.
830
831
  let keepAuthoredFoundation = false
831
832
  let dataModel = null
833
+ let sectionType
832
834
  // The Models the pulled site's queries name, as the push qualified them — the re-bank
833
835
  // resolves with them too.
834
836
  let pulledQueryModels = []
@@ -854,6 +856,12 @@ export async function pull(args = [], deps = {}) {
854
856
  // A translated data block is read back by what its section's type declares it is, as the
855
857
  // push translated it — after the registered foundation above, whose section types say it.
856
858
  dataModel = await dataBlockModels(siteDir)
859
+ // The type a section that names none renders with — what the push filled in, and what a
860
+ // section's file leaves out again. A clone's foundation is the one the document pins.
861
+ sectionType = await siteDefaultSectionType(
862
+ siteDir,
863
+ keepAuthoredFoundation ? null : { foundation: siteDoc?.info?.foundation }
864
+ )
857
865
  }
858
866
  // ⭐ THE SCOPE THE PULLED RECORDS AND QUERIES WERE QUALIFIED WITH — the pushed site's
859
867
  // FOUNDATION's: the scope of the pinned ref it carries (`info.foundation`), else the
@@ -998,7 +1006,8 @@ export async function pull(args = [], deps = {}) {
998
1006
  keepAuthoredFoundation,
999
1007
  // So a query's `@acme/member` comes back as the author's `@/member`.
1000
1008
  scope: pullScope,
1001
- dataModel
1009
+ dataModel,
1010
+ sectionType
1002
1011
  })
1003
1012
  wrote.push(...report.pages, ...report.sections, ...report.layout)
1004
1013
  removed.push(
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-23T19:14:59.641Z",
3
+ "generatedAt": "2026-10-24T15:06:07.442Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
6
  "version": "1.6.4"
7
7
  },
8
8
  "@uniweb/build": {
9
- "version": "1.8.1"
9
+ "version": "1.8.2"
10
10
  },
11
11
  "@uniweb/content-reader": {
12
12
  "version": "1.5.4"
@@ -15,7 +15,7 @@
15
15
  "version": "1.3.0"
16
16
  },
17
17
  "@uniweb/core": {
18
- "version": "1.4.0"
18
+ "version": "1.4.1"
19
19
  },
20
20
  "@uniweb/frame-bridge": {
21
21
  "version": "1.0.2"
@@ -24,7 +24,7 @@
24
24
  "version": "1.0.5"
25
25
  },
26
26
  "@uniweb/kit": {
27
- "version": "1.6.0"
27
+ "version": "1.6.1"
28
28
  },
29
29
  "@uniweb/loom": {
30
30
  "version": "1.1.2"
@@ -33,10 +33,10 @@
33
33
  "version": "1.1.4"
34
34
  },
35
35
  "@uniweb/projections": {
36
- "version": "1.1.6"
36
+ "version": "1.1.7"
37
37
  },
38
38
  "@uniweb/runtime": {
39
- "version": "1.1.15"
39
+ "version": "1.1.16"
40
40
  },
41
41
  "@uniweb/scene": {
42
42
  "version": "1.0.1"