@gradial/aci 0.1.53-rc.5 → 0.1.53-rc.7
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 +8 -0
- package/README.md +38 -4
- package/dist/cli/emit-error-pages.mjs +46 -5
- package/dist/content/error-page-kind.d.ts +6 -0
- package/dist/content/error-page-kind.js +2 -1
- package/dist/content/error-pages.d.ts +1 -1
- package/dist/platform-tests/adapters/astro.js +4 -1
- package/package.json +7 -7
- package/src/cli/emit-error-pages.mjs +40 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
### Fixed
|
|
6
6
|
|
|
7
|
+
- Astro root-identity conformance checks now load the site-configured UI renderers,
|
|
8
|
+
allowing nested React and other integration components to render in the isolated
|
|
9
|
+
container.
|
|
10
|
+
|
|
7
11
|
- Astro authored 404/500 helpers use the shared `getPageData` pipeline and
|
|
8
12
|
consume the selected render input during ACI
|
|
9
13
|
preview/publish, preserving authored content and styling in code artifacts
|
|
@@ -12,6 +16,10 @@
|
|
|
12
16
|
|
|
13
17
|
### Added
|
|
14
18
|
|
|
19
|
+
- Optional Node-only `src/aci/hosting-error-page.mjs` policy adds site-specific
|
|
20
|
+
browser bootstrap code to the SDK-owned static 500 fallback, with syntax
|
|
21
|
+
validation, script-data escaping, and the shared output-size budget.
|
|
22
|
+
|
|
15
23
|
- Public `aci schemas compile` and `aci error-pages prepare|emit` commands let
|
|
16
24
|
site build scripts compile code-owned artifacts without invoking SDK internals.
|
|
17
25
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Content Infrastructure SDK
|
|
2
2
|
|
|
3
|
-
`@gradial/aci` `0.1.53-rc.
|
|
3
|
+
`@gradial/aci` `0.1.53-rc.7` 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.1.53-rc.
|
|
11
|
+
npm install @gradial/aci@0.1.53-rc.7 zod@^4
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Root Import
|
|
@@ -137,13 +137,13 @@ host bundles select and serve immutable release artifacts.
|
|
|
137
137
|
The first RC scaffold path is Next:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
npx @gradial/aci@0.1.53-rc.
|
|
140
|
+
npx @gradial/aci@0.1.53-rc.7 init my-site --template next-starter
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
List available templates:
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
npx @gradial/aci@0.1.53-rc.
|
|
146
|
+
npx @gradial/aci@0.1.53-rc.7 init --list
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
## Personalization: The One-Tag Install
|
|
@@ -245,3 +245,37 @@ recompiles contracts without reading authored content. The error commands prepar
|
|
|
245
245
|
code-owned analytics policy and emit the default hosting fallback (preserving a
|
|
246
246
|
site-owned `public/500.html`). All three honor `--site-dir`; helper failures exit
|
|
247
247
|
nonzero. Local development/content validation must explicitly compile content.
|
|
248
|
+
|
|
249
|
+
## Hosting error-page policy
|
|
250
|
+
|
|
251
|
+
`aci error-pages emit` owns the static `public/500.html` fallback. To add a
|
|
252
|
+
site-specific analytics/consent bootstrap without recreating its HTML emitter,
|
|
253
|
+
create a **Node-only build module** at `src/aci/hosting-error-page.mjs`:
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
/** @type {import('@gradial/aci/content').HostingErrorPageScript} */
|
|
257
|
+
export const hostingErrorPageScript = async ({ kind, pageType }) => {
|
|
258
|
+
// Return bundled browser JavaScript, or an empty string for no bootstrap.
|
|
259
|
+
return '';
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The hook receives `{ kind: 'catchAll', pageType: 'error' }`. It runs at build
|
|
264
|
+
time, may import build helpers, and must return self-contained classic browser
|
|
265
|
+
JavaScript. It must not read authored content or select an authored error page.
|
|
266
|
+
Keep consent decisions and vendor-specific behavior in the site policy. It
|
|
267
|
+
must not repeat the error-view push: the SDK emits that first from the existing
|
|
268
|
+
client-safe `src/aci/error-page-instrumentation.mjs` policy.
|
|
269
|
+
|
|
270
|
+
The SDK checks script syntax, escapes HTML script-data delimiters, appends a
|
|
271
|
+
separate `data-aci-error-page-bootstrap="error"` script, and enforces a combined
|
|
272
|
+
150 KiB HTML budget. Invalid present policies fail the build before replacing
|
|
273
|
+
the previous generated fallback. A missing module adds no bootstrap. An existing
|
|
274
|
+
unmarked site-owned `public/500.html` is left untouched. This hook affects the
|
|
275
|
+
native hosting fallback only; it does not add loaders to authored CDN errors or
|
|
276
|
+
change the instrumentation capsule consumed by publish.
|
|
277
|
+
|
|
278
|
+
When replacing a site-local generator, delete its old ignored generated
|
|
279
|
+
`public/500.html` once so it can become SDK-managed. Remove the generator from
|
|
280
|
+
build scripts, retain any shared consent-bundle preparation needed by other
|
|
281
|
+
static routes, and run `aci error-pages emit` as the final build step.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Usage: emit-error-pages.mjs <prepare|emit>
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
+
import { Script } from 'node:vm';
|
|
8
9
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
10
|
const MARKER = '<!-- aci:error-pages generated -->';
|
|
10
11
|
const SIZE_BUDGET = 150 * 1024;
|
|
@@ -15,11 +16,12 @@ function log(message) {
|
|
|
15
16
|
console.log(`[aci error-pages] ${message}`);
|
|
16
17
|
}
|
|
17
18
|
function fail(message) {
|
|
18
|
-
|
|
19
|
-
process.exit(1);
|
|
19
|
+
throw new Error(`[aci error-pages] ${message}`);
|
|
20
20
|
}
|
|
21
21
|
async function sdkContent() {
|
|
22
|
-
|
|
22
|
+
const content = await import(pathToFileURL(path.join(packageRoot, 'dist', 'content', 'index.js')).href);
|
|
23
|
+
const { escapeInlineScriptSource } = await import(pathToFileURL(path.join(packageRoot, 'dist', 'content', 'error-page-kind.js')).href);
|
|
24
|
+
return { ...content, escapeInlineScriptSource };
|
|
23
25
|
}
|
|
24
26
|
// Keep the existing capsule transport path for SDK-first upgrades. This file
|
|
25
27
|
// is executable policy, not a compiled content snapshot.
|
|
@@ -126,6 +128,43 @@ export function stampInstrumentation(html, instrumentationTag) {
|
|
|
126
128
|
}
|
|
127
129
|
return stamped;
|
|
128
130
|
}
|
|
131
|
+
// Node-only build hook. Keep loaders/bundlers out of the client policy module.
|
|
132
|
+
const HOSTING_POLICY_MODULE = path.join('src', 'aci', 'hosting-error-page.mjs');
|
|
133
|
+
async function hostingBootstrapTag(root, sdk) {
|
|
134
|
+
const modulePath = path.join(root, HOSTING_POLICY_MODULE);
|
|
135
|
+
if (!fs.existsSync(modulePath))
|
|
136
|
+
return '';
|
|
137
|
+
let policy;
|
|
138
|
+
try {
|
|
139
|
+
policy = await import(pathToFileURL(modulePath).href);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
throw new Error(`${HOSTING_POLICY_MODULE} failed to import: ${error.message}`);
|
|
143
|
+
}
|
|
144
|
+
if (typeof policy.hostingErrorPageScript !== 'function') {
|
|
145
|
+
throw new Error(`${HOSTING_POLICY_MODULE} must export a hostingErrorPageScript function`);
|
|
146
|
+
}
|
|
147
|
+
let source;
|
|
148
|
+
try {
|
|
149
|
+
source = await policy.hostingErrorPageScript(Object.freeze({ kind: 'catchAll', pageType: 'error' }));
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript failed: ${error.message}`);
|
|
153
|
+
}
|
|
154
|
+
if (typeof source !== 'string')
|
|
155
|
+
throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript must return JavaScript source as a string`);
|
|
156
|
+
if (!source.trim())
|
|
157
|
+
return '';
|
|
158
|
+
const escaped = sdk.escapeInlineScriptSource(source);
|
|
159
|
+
try {
|
|
160
|
+
new Script(escaped);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript returned invalid JavaScript: ${error.message}`);
|
|
164
|
+
}
|
|
165
|
+
log(`hosting bootstrap source: site module ${HOSTING_POLICY_MODULE}`);
|
|
166
|
+
return `<script data-aci-error-page-bootstrap="error">${escaped}</script>`;
|
|
167
|
+
}
|
|
129
168
|
export async function emit(root = siteRoot, content) {
|
|
130
169
|
const publicDir = path.join(root, 'public');
|
|
131
170
|
const target = path.join(publicDir, '500.html');
|
|
@@ -135,11 +174,13 @@ export async function emit(root = siteRoot, content) {
|
|
|
135
174
|
}
|
|
136
175
|
const sdk = content ?? (await sdkContent());
|
|
137
176
|
const instrumentationTag = await resolveInstrumentationTag(root, sdk);
|
|
138
|
-
const
|
|
177
|
+
const bootstrapTag = await hostingBootstrapTag(root, sdk);
|
|
178
|
+
const tags = bootstrapTag ? `${instrumentationTag}\n${bootstrapTag}` : instrumentationTag;
|
|
179
|
+
const html = stampInstrumentation(sdk.defaultErrorPageHtml(), tags);
|
|
139
180
|
const size = Buffer.byteLength(html);
|
|
140
181
|
if (size > SIZE_BUDGET) {
|
|
141
182
|
fail(`public/500.html is ${size} bytes, over the ${SIZE_BUDGET}-byte budget (REQ-14). `
|
|
142
|
-
+ '
|
|
183
|
+
+ 'Reduce the hosting bootstrap policy output or provide a smaller site-owned hosting fallback.');
|
|
143
184
|
}
|
|
144
185
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
145
186
|
fs.writeFileSync(target, html);
|
|
@@ -3,6 +3,12 @@ export type ErrorPageKind = 'notFound' | 'catchAll';
|
|
|
3
3
|
export type ErrorPageType = 'not_found' | 'error';
|
|
4
4
|
/** Maps a binding kind to its D-14 page_type. */
|
|
5
5
|
export declare function errorPageType(kind: ErrorPageKind): ErrorPageType;
|
|
6
|
+
/** The Node-only hook used to augment the SDK-owned native hosting fallback. */
|
|
7
|
+
export interface HostingErrorPageContext {
|
|
8
|
+
readonly kind: 'catchAll';
|
|
9
|
+
readonly pageType: 'error';
|
|
10
|
+
}
|
|
11
|
+
export type HostingErrorPageScript = (context: HostingErrorPageContext) => string | Promise<string>;
|
|
6
12
|
declare global {
|
|
7
13
|
interface Window {
|
|
8
14
|
dataLayer?: Array<Record<string, unknown>>;
|
|
@@ -65,7 +65,8 @@ export function serializeErrorPageInstrumentation(push, kind) {
|
|
|
65
65
|
* `\s` and `\!` decode to the original characters, so payload values
|
|
66
66
|
* survive byte-for-byte on every surface.
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
/** @internal Shared by SDK build-time hosting policy emission. */
|
|
69
|
+
export function escapeInlineScriptSource(source) {
|
|
69
70
|
return source.replace(/<(\/script|script|!--)/gi, (match) => `<\\${match.slice(1)}`);
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
@@ -19,7 +19,7 @@ import type { KernelSiteConfig, SiteErrorPages } from './types.js';
|
|
|
19
19
|
/** D-5 migration posture for absent bindings; default is 'warn' today. */
|
|
20
20
|
export type ErrorPagePosture = 'warn' | 'enforce';
|
|
21
21
|
export { errorPageType, errorPageInstrumentationTag, defaultPushErrorPageView, serializeErrorPageInstrumentation, } from './error-page-kind.js';
|
|
22
|
-
export type { ErrorPageKind, ErrorPageType, ErrorPagePush } from './error-page-kind.js';
|
|
22
|
+
export type { ErrorPageKind, ErrorPageType, ErrorPagePush, HostingErrorPageContext, HostingErrorPageScript } from './error-page-kind.js';
|
|
23
23
|
import type { ErrorPageKind } from './error-page-kind.js';
|
|
24
24
|
/**
|
|
25
25
|
* The one D-5 posture config surface, shared by every stack: the same
|
|
@@ -54,7 +54,10 @@ export async function openAstroSite(siteDir, explicit = []) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
const registry = mergeRegistries(registries);
|
|
57
|
-
|
|
57
|
+
// Use the same renderer modules Astro registered from the site config,
|
|
58
|
+
// loaded through Vite so they share the components' module environment.
|
|
59
|
+
const { renderers } = await vite.ssrLoadModule('virtual:astro:renderers');
|
|
60
|
+
const container = await experimental_AstroContainer.create({ renderers });
|
|
58
61
|
return {
|
|
59
62
|
registry,
|
|
60
63
|
adapter: createAstroAdapter(registry, container, siteDir),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.53-rc.
|
|
3
|
+
"version": "0.1.53-rc.7",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/gradial/bare-metal-go.git",
|
|
@@ -164,11 +164,11 @@
|
|
|
164
164
|
"astro": "^6.4.8"
|
|
165
165
|
},
|
|
166
166
|
"optionalDependencies": {
|
|
167
|
-
"@gradial/aci-cli-darwin-arm64": "0.1.53-rc.
|
|
168
|
-
"@gradial/aci-cli-darwin-x64": "0.1.53-rc.
|
|
169
|
-
"@gradial/aci-cli-linux-arm64": "0.1.53-rc.
|
|
170
|
-
"@gradial/aci-cli-linux-x64": "0.1.53-rc.
|
|
171
|
-
"@gradial/aci-cli-win32-arm64": "0.1.53-rc.
|
|
172
|
-
"@gradial/aci-cli-win32-x64": "0.1.53-rc.
|
|
167
|
+
"@gradial/aci-cli-darwin-arm64": "0.1.53-rc.7",
|
|
168
|
+
"@gradial/aci-cli-darwin-x64": "0.1.53-rc.7",
|
|
169
|
+
"@gradial/aci-cli-linux-arm64": "0.1.53-rc.7",
|
|
170
|
+
"@gradial/aci-cli-linux-x64": "0.1.53-rc.7",
|
|
171
|
+
"@gradial/aci-cli-win32-arm64": "0.1.53-rc.7",
|
|
172
|
+
"@gradial/aci-cli-win32-x64": "0.1.53-rc.7"
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
import { Script } from 'node:vm';
|
|
9
10
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
11
|
|
|
11
12
|
const MARKER = '<!-- aci:error-pages generated -->';
|
|
@@ -19,12 +20,13 @@ function log(message) {
|
|
|
19
20
|
console.log(`[aci error-pages] ${message}`);
|
|
20
21
|
}
|
|
21
22
|
function fail(message) {
|
|
22
|
-
|
|
23
|
-
process.exit(1);
|
|
23
|
+
throw new Error(`[aci error-pages] ${message}`);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async function sdkContent() {
|
|
27
|
-
|
|
27
|
+
const content = await import(pathToFileURL(path.join(packageRoot, 'dist', 'content', 'index.js')).href);
|
|
28
|
+
const { escapeInlineScriptSource } = await import(pathToFileURL(path.join(packageRoot, 'dist', 'content', 'error-page-kind.js')).href);
|
|
29
|
+
return { ...content, escapeInlineScriptSource };
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// Keep the existing capsule transport path for SDK-first upgrades. This file
|
|
@@ -142,6 +144,37 @@ export function stampInstrumentation(html, instrumentationTag) {
|
|
|
142
144
|
return stamped;
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
// Node-only build hook. Keep loaders/bundlers out of the client policy module.
|
|
148
|
+
const HOSTING_POLICY_MODULE = path.join('src', 'aci', 'hosting-error-page.mjs');
|
|
149
|
+
|
|
150
|
+
async function hostingBootstrapTag(root, sdk) {
|
|
151
|
+
const modulePath = path.join(root, HOSTING_POLICY_MODULE);
|
|
152
|
+
if (!fs.existsSync(modulePath)) return '';
|
|
153
|
+
let policy;
|
|
154
|
+
try {
|
|
155
|
+
policy = await import(pathToFileURL(modulePath).href);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
throw new Error(`${HOSTING_POLICY_MODULE} failed to import: ${error.message}`);
|
|
158
|
+
}
|
|
159
|
+
if (typeof policy.hostingErrorPageScript !== 'function') {
|
|
160
|
+
throw new Error(`${HOSTING_POLICY_MODULE} must export a hostingErrorPageScript function`);
|
|
161
|
+
}
|
|
162
|
+
let source;
|
|
163
|
+
try {
|
|
164
|
+
source = await policy.hostingErrorPageScript(Object.freeze({ kind: 'catchAll', pageType: 'error' }));
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript failed: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
if (typeof source !== 'string') throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript must return JavaScript source as a string`);
|
|
169
|
+
if (!source.trim()) return '';
|
|
170
|
+
const escaped = sdk.escapeInlineScriptSource(source);
|
|
171
|
+
try { new Script(escaped); } catch (error) {
|
|
172
|
+
throw new Error(`${HOSTING_POLICY_MODULE}: hostingErrorPageScript returned invalid JavaScript: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
log(`hosting bootstrap source: site module ${HOSTING_POLICY_MODULE}`);
|
|
175
|
+
return `<script data-aci-error-page-bootstrap="error">${escaped}</script>`;
|
|
176
|
+
}
|
|
177
|
+
|
|
145
178
|
export async function emit(root = siteRoot, content) {
|
|
146
179
|
const publicDir = path.join(root, 'public');
|
|
147
180
|
const target = path.join(publicDir, '500.html');
|
|
@@ -152,13 +185,15 @@ export async function emit(root = siteRoot, content) {
|
|
|
152
185
|
|
|
153
186
|
const sdk = content ?? (await sdkContent());
|
|
154
187
|
const instrumentationTag = await resolveInstrumentationTag(root, sdk);
|
|
155
|
-
const
|
|
188
|
+
const bootstrapTag = await hostingBootstrapTag(root, sdk);
|
|
189
|
+
const tags = bootstrapTag ? `${instrumentationTag}\n${bootstrapTag}` : instrumentationTag;
|
|
190
|
+
const html = stampInstrumentation(sdk.defaultErrorPageHtml(), tags);
|
|
156
191
|
|
|
157
192
|
const size = Buffer.byteLength(html);
|
|
158
193
|
if (size > SIZE_BUDGET) {
|
|
159
194
|
fail(
|
|
160
195
|
`public/500.html is ${size} bytes, over the ${SIZE_BUDGET}-byte budget (REQ-14). `
|
|
161
|
-
+ '
|
|
196
|
+
+ 'Reduce the hosting bootstrap policy output or provide a smaller site-owned hosting fallback.',
|
|
162
197
|
);
|
|
163
198
|
}
|
|
164
199
|
|