@gradial/aci 0.2.0-canary.720 → 0.2.0-canary.726

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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  ### Fixed
6
6
 
7
+ - Page verification judges a Next.js image optimizer URL
8
+ (`/_next/image?url=…`, what `next/image` renders) by the image it wraps.
9
+ The optimizer is the host's runtime endpoint, not a file in the release,
10
+ so before staging it answered 404 and every Next site using `next/image`
11
+ was refused with `fetch.notFound` although its images load. A root-relative
12
+ source is fetched like any own file (a missing one is still an error, named
13
+ by its own path "via next/image"); a remote source is a third-party URL
14
+ (a warning). Each source is fetched and reported once per page, however
15
+ many srcset widths name it.
7
16
  - The staged and live checks fetch the stable collect script
8
17
  (`/_aci/aci.js`) at the judged release's own path,
9
18
  `/__r/<release>/_aci/aci.js`. The stable URL answers the active
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.2.0-canary.720` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.2.0-canary.726` is a pre-1.0 public-surface reset for the ACI
4
4
  client SDK. Breaking changes from the old `0.1.x` accidental surface are
5
5
  intentional: the root import is framework-neutral, framework runtime behavior is
6
6
  behind explicit subpaths, and the first required customer path is React/Next.
@@ -8,7 +8,7 @@ behind explicit subpaths, and the first required customer path is React/Next.
8
8
  ## Install
9
9
 
10
10
  ```bash
11
- npm install @gradial/aci@0.2.0-canary.720 zod@^4
11
+ npm install @gradial/aci@0.2.0-canary.726 zod@^4
12
12
  ```
13
13
 
14
14
  ## Root Import
@@ -141,13 +141,13 @@ host bundles select and serve immutable release artifacts.
141
141
  The first RC scaffold path is Next:
142
142
 
143
143
  ```bash
144
- npx @gradial/aci@0.2.0-canary.720 init my-site --template next-starter
144
+ npx @gradial/aci@0.2.0-canary.726 init my-site --template next-starter
145
145
  ```
146
146
 
147
147
  List available templates:
148
148
 
149
149
  ```bash
150
- npx @gradial/aci@0.2.0-canary.720 init --list
150
+ npx @gradial/aci@0.2.0-canary.726 init --list
151
151
  ```
152
152
 
153
153
  ## Personalization: The One-Tag Install
@@ -3,7 +3,8 @@
3
3
  // references answers 2xx. Each URL is fetched once per unit, and once per run
4
4
  // when the caller shares withFetchCache. A failed own URL is an error, a
5
5
  // failed third-party URL a warning (resource.ts). Same-origin links to routes
6
- // are the links check's.
6
+ // are the links check's. A next/image optimizer URL is judged by the image it
7
+ // wraps (html/refs.ts).
7
8
  import { isFilePath } from '../paths.js';
8
9
  import { fetchAll } from './resource.js';
9
10
  const FETCH_CODES = {
@@ -8,7 +8,7 @@ function judgeFetched(input, ref, outcome, codes) {
8
8
  if ('status' in outcome && outcome.status >= 200 && outcome.status < 300)
9
9
  return null;
10
10
  const own = ref.url.origin === input.origin;
11
- const shown = own ? `${ref.url.pathname}${ref.url.search}` : ref.url.href;
11
+ const shown = `"${own ? `${ref.url.pathname}${ref.url.search}` : ref.url.href}"${ref.via ? ` (via ${ref.via})` : ''}`;
12
12
  const fix = own
13
13
  ? 'publish the file it points to, or change the reference to one that exists'
14
14
  : 'this third-party URL is outside the release; check it is still live';
@@ -16,7 +16,7 @@ function judgeFetched(input, ref, outcome, codes) {
16
16
  return judge(own ? 'error' : 'warning', input, {
17
17
  code: codes.failed,
18
18
  url: ref.raw,
19
- message: `the ${ref.source} "${shown}" could not be fetched (${outcome.error}); ${fix}`,
19
+ message: `the ${ref.source} ${shown} could not be fetched (${outcome.error}); ${fix}`,
20
20
  });
21
21
  }
22
22
  const notFound = outcome.status === 404 || outcome.status === 410;
@@ -28,7 +28,7 @@ function judgeFetched(input, ref, outcome, codes) {
28
28
  return judge(own ? 'error' : 'warning', input, {
29
29
  code: notFound ? codes.notFound : codes.failed,
30
30
  url: ref.raw,
31
- message: `the ${ref.source} "${shown}" ${answered}; ${fix}`,
31
+ message: `the ${ref.source} ${shown} ${answered}; ${fix}`,
32
32
  });
33
33
  }
34
34
  /** Fetches every target once through the context's cache and keeps the failures. */
@@ -8,6 +8,8 @@ export interface Ref {
8
8
  raw: string;
9
9
  /** Resolved against the page (or its <base>); null when it does not parse as a URL. */
10
10
  url: URL | null;
11
+ /** Set when `url` is the source a wrapper URL in `raw` names, e.g. a next/image optimizer URL. */
12
+ via?: 'next/image';
11
13
  }
12
14
  /** Every reference in `tags`, resolved against `pageURL`. Fragment-only and non-HTTP references are left out. */
13
15
  export declare function collectRefs(tags: readonly Tag[], pageURL: string): Ref[];
@@ -4,7 +4,13 @@
4
4
  // rendered HTML, not content fields, so links components generate count too.
5
5
  // An inline `style` attribute's `url()` images are resources; a fetched
6
6
  // stylesheet's `url()`s are not read (the suite fetches status only).
7
+ // A Next.js image optimizer URL (`/_next/image?url=…`) is the host's runtime
8
+ // endpoint, not a file in the release, so it stands for its wrapped source:
9
+ // the source is the reference, collected once per page however many widths
10
+ // and attributes name it.
7
11
  import { srcsetURLs } from './srcset.js';
12
+ /** The Next.js image optimizer's path; its `url` query parameter is the image it serves. */
13
+ const NEXT_IMAGE_PATH = '/_next/image';
8
14
  /** link rel values whose href the browser loads for the page itself. Other rels (canonical, alternate, preconnect) are not fetched. */
9
15
  const RESOURCE_RELS = new Set(['stylesheet', 'icon', 'apple-touch-icon', 'mask-icon', 'preload', 'modulepreload', 'manifest']);
10
16
  /** Resource attributes by tag. */
@@ -23,14 +29,23 @@ const SKIPPED = /^(?:#|mailto:|tel:|sms:|javascript:|data:|blob:|about:)/i;
23
29
  export function collectRefs(tags, pageURL) {
24
30
  const base = baseURL(tags, pageURL);
25
31
  const refs = [];
32
+ const nextImageSources = new Set();
26
33
  const add = (kind, source, raw) => {
27
34
  const value = raw?.trim();
28
35
  if (!value || SKIPPED.test(value))
29
36
  return;
30
37
  const url = parseURL(value, base);
31
- if (url && url.protocol !== 'http:' && url.protocol !== 'https:')
38
+ if (url && !isHTTP(url))
32
39
  return;
33
- refs.push({ kind, source, raw: value, url });
40
+ const wrapped = url && nextImageSource(url);
41
+ if (!wrapped) {
42
+ refs.push({ kind, source, raw: value, url });
43
+ return;
44
+ }
45
+ if (nextImageSources.has(wrapped.href))
46
+ return;
47
+ nextImageSources.add(wrapped.href);
48
+ refs.push({ kind, source, raw: value, url: wrapped, via: 'next/image' });
34
49
  };
35
50
  for (const tag of tags) {
36
51
  if (NAVIGATION_TAGS.has(tag.name))
@@ -62,6 +77,15 @@ function cssURLs(css) {
62
77
  }
63
78
  return urls;
64
79
  }
80
+ /** The image a next/image optimizer URL serves, resolved against it; null for any other URL, or a source that is not HTTP. */
81
+ function nextImageSource(url) {
82
+ const param = url.pathname === NEXT_IMAGE_PATH ? url.searchParams.get('url') : null;
83
+ const wrapped = param ? parseURL(param, url.href) : null;
84
+ return wrapped && isHTTP(wrapped) ? wrapped : null;
85
+ }
86
+ function isHTTP(url) {
87
+ return url.protocol === 'http:' || url.protocol === 'https:';
88
+ }
65
89
  function isResourceLink(tag) {
66
90
  const rels = (tag.attrs.get('rel') ?? '').toLowerCase().split(/\s+/);
67
91
  return rels.some((rel) => RESOURCE_RELS.has(rel));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradial/aci",
3
- "version": "0.2.0-canary.720",
3
+ "version": "0.2.0-canary.726",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/gradial/bare-metal-go.git",
@@ -179,11 +179,11 @@
179
179
  "astro": "^6.4.8"
180
180
  },
181
181
  "optionalDependencies": {
182
- "@gradial/aci-cli-darwin-arm64": "0.2.0-canary.720",
183
- "@gradial/aci-cli-darwin-x64": "0.2.0-canary.720",
184
- "@gradial/aci-cli-linux-arm64": "0.2.0-canary.720",
185
- "@gradial/aci-cli-linux-x64": "0.2.0-canary.720",
186
- "@gradial/aci-cli-win32-arm64": "0.2.0-canary.720",
187
- "@gradial/aci-cli-win32-x64": "0.2.0-canary.720"
182
+ "@gradial/aci-cli-darwin-arm64": "0.2.0-canary.726",
183
+ "@gradial/aci-cli-darwin-x64": "0.2.0-canary.726",
184
+ "@gradial/aci-cli-linux-arm64": "0.2.0-canary.726",
185
+ "@gradial/aci-cli-linux-x64": "0.2.0-canary.726",
186
+ "@gradial/aci-cli-win32-arm64": "0.2.0-canary.726",
187
+ "@gradial/aci-cli-win32-x64": "0.2.0-canary.726"
188
188
  }
189
189
  }