@gradial/aci 0.1.51-ssr.5 → 0.1.52-rc.10
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/README.md +4 -4
- package/dist/astro/assets.d.ts +8 -8
- package/dist/astro/assets.js +8 -8
- package/dist/astro/index.js +2 -2
- package/dist/audience-data-models.d.ts +9 -0
- package/dist/audience-data-models.js +3 -0
- package/dist/cli/compile-registry.d.mts +5 -1
- package/dist/cli/compile-registry.mjs +25 -10
- package/dist/cli/validate-content.d.mts +31 -1
- package/dist/cli/validate-content.mjs +50 -12
- package/dist/content/cache.d.ts +6 -1
- package/dist/content/cache.js +26 -0
- package/dist/content/provider.d.ts +1 -0
- package/dist/content/resolve-slots.d.ts +9 -2
- package/dist/content/resolve-slots.js +5 -3
- package/dist/content/types.d.ts +2 -0
- package/dist/dev/content-watch.js +3 -3
- package/dist/edge/personalization-core.d.ts +11 -0
- package/dist/edge/personalization-core.js +281 -0
- package/dist/internal/domain/edge/compose.d.ts +24 -0
- package/dist/internal/domain/edge/compose.js +89 -0
- package/dist/internal/domain/edge/index.d.ts +1 -0
- package/dist/internal/domain/edge/index.js +1 -0
- package/dist/internal/domain/edge/personalization-core.d.ts +4 -1
- package/dist/internal/domain/edge/personalization-core.js +25 -8
- package/dist/internal/domain/edge/resolve.js +6 -4
- package/dist/internal/domain/edge/types.d.ts +12 -0
- package/dist/internal/render-proof.d.ts +1 -0
- package/dist/internal/render-proof.js +197 -0
- package/dist/next/asset-route.d.ts +10 -0
- package/dist/next/asset-route.js +45 -0
- package/dist/next/collect-route.d.ts +19 -0
- package/dist/next/collect-route.js +459 -0
- package/dist/next/config.d.ts +9 -1
- package/dist/next/config.js +79 -4
- package/dist/next/content-watch.js +1 -1
- package/dist/next/edge-config.d.ts +1 -0
- package/dist/next/edge-config.js +92 -0
- package/dist/next/envelope-digest.d.ts +21 -0
- package/dist/next/envelope-digest.js +58 -0
- package/dist/next/error-pages.d.ts +3 -2
- package/dist/next/error-pages.js +9 -14
- package/dist/next/error-seat-payload.js +1 -1
- package/dist/next/error-seat.d.ts +3 -2
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +2 -1
- package/dist/next/middleware.d.ts +40 -0
- package/dist/next/middleware.js +475 -0
- package/dist/next/page.d.ts +13 -3
- package/dist/next/page.js +61 -82
- package/dist/next/personalization-host.d.ts +134 -0
- package/dist/next/personalization-host.js +270 -0
- package/dist/next/server.d.ts +33 -13
- package/dist/next/server.js +374 -60
- package/dist/next/vercel-collect-sdk-route.d.ts +27 -0
- package/dist/next/vercel-collect-sdk-route.js +181 -0
- package/dist/next/vercel-edge-projection-v1.d.ts +24 -0
- package/dist/next/vercel-edge-projection-v1.js +886 -0
- package/dist/next/vercel-personalization.d.ts +4 -0
- package/dist/next/vercel-personalization.js +4 -0
- package/dist/next/vercel-s3-personalization.d.ts +79 -0
- package/dist/next/vercel-s3-personalization.js +570 -0
- package/dist/providers/object.d.ts +3 -0
- package/dist/providers/object.js +18 -1
- package/dist/providers/s3.d.ts +47 -0
- package/dist/providers/s3.js +441 -0
- package/dist/providers/vercel-object-reader.d.ts +1 -1
- package/dist/providers/vercel-object-reader.js +10 -0
- package/dist/types/block-ref.d.ts +2 -0
- package/dist/types/block-ref.js +1 -0
- package/package.json +7 -7
- package/src/cli/compile-registry.mjs +24 -10
- package/src/cli/validate-content.mjs +52 -13
- package/src/types/block-ref.test.ts +45 -0
- package/src/types/block-ref.ts +2 -0
- package/templates/astro/starter-content/src/fixtures/atomicBlocks.ts +7 -6
- package/templates/astro/starter-content/src/fixtures/composition.ts +3 -2
- package/templates/astro/starter-content/src/fixtures/sections.ts +2 -1
- package/templates/common/.agents/skills/aci/SKILL.md +8 -7
- package/templates/common/.agents/skills/authoring/SKILL.md +11 -3
- package/templates/common/.agents/skills/authoring/references/block-registry.md +16 -5
- package/templates/common/.agents/skills/authoring/references/block-slots.md +3 -0
- package/templates/common/.agents/skills/authoring/references/contracts.md +4 -0
- package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +13 -7
- package/templates/common/.agents/skills/migration/references/canon-migration.md +7 -7
- package/templates/common/.content/pages/{home/_index.json → _index.json} +10 -0
- package/templates/common/starter-content/.content/.rules-baseline.json +8 -0
- package/templates/common/starter-content/.content/content-rules/no-public-resource-paths.json +6 -0
- package/templates/common/starter-content/.content/dam/blobs/sha256/58/09/5809142b357fca186bcb0fa41661ccaa142800570d2d4d2de2de77b6d8754773 +0 -0
- package/templates/common/starter-content/.content/dam/blobs/sha256/d7/df/d7dfcf7c59365632a408534cf56a9cc5ce848ccb5ce067fd655bbda7953ef5e8 +0 -0
- package/templates/common/starter-content/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +3 -3
- package/templates/common/starter-content/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +3 -3
- package/templates/common/starter-content/.content/fragments/navbar/_index.json +23 -20
- package/templates/common/starter-content/.content/fragments/site-footer/_index.json +59 -56
- package/templates/common/starter-content/.content/pages/404/_index.json +1 -0
- package/templates/common/starter-content/.content/pages/about/_index.json +3 -0
- package/templates/common/starter-content/.content/pages/error/_index.json +1 -0
- package/templates/common/starter-content/.content/pages/home/_index.json +9 -0
- package/templates/common/starter-content/.content/patterns/components/button.json +55 -0
- package/templates/common/starter-content/.content/patterns/components/container.json +62 -0
- package/templates/common/starter-content/.content/patterns/components/feature_card.json +30 -0
- package/templates/common/starter-content/.content/patterns/components/feature_grid.json +57 -0
- package/templates/common/starter-content/.content/patterns/components/home_hero.json +142 -0
- package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +42 -0
- package/templates/common/starter-content/.content/patterns/components/site-footer.json +203 -0
- package/templates/common/starter-content/.content/patterns/components/site-navigation.json +115 -0
- package/templates/common/starter-content/.content/patterns/components/stats_bar.json +45 -0
- package/templates/common/starter-content/.content/patterns/layouts/default.json +14 -0
- package/templates/nextjs/template/.aci.yaml +10 -0
- package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- /package/templates/common/.content/overlays/audience/preference-blue/pages/{home/_index.json → _index.json} +0 -0
- /package/templates/common/.content/overlays/audience/preference-red/pages/{home/_index.json → _index.json} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Content Infrastructure SDK
|
|
2
2
|
|
|
3
|
-
`@gradial/aci` `0.1.
|
|
3
|
+
`@gradial/aci` `0.1.52-rc.10` 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.
|
|
11
|
+
npm install @gradial/aci@0.1.52-rc.10 zod@^4
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Root Import
|
|
@@ -78,13 +78,13 @@ host bundles select and serve immutable release artifacts.
|
|
|
78
78
|
The first RC scaffold path is Next:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
npx @gradial/aci@0.1.
|
|
81
|
+
npx @gradial/aci@0.1.52-rc.10 init my-site --template next-starter
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
List available templates:
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
npx @gradial/aci@0.1.
|
|
87
|
+
npx @gradial/aci@0.1.52-rc.10 init --list
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
## Personalization: The One-Tag Install
|
package/dist/astro/assets.d.ts
CHANGED
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
* at the URL level. Instead:
|
|
6
6
|
*
|
|
7
7
|
* - In dev: the Vite `damAssetPlugin` serves DAM assets from compiled
|
|
8
|
-
* content at
|
|
8
|
+
* content at `/dam/{path}` (already handled by withAci).
|
|
9
9
|
*
|
|
10
|
-
* - In production:
|
|
11
|
-
*
|
|
12
|
-
* (
|
|
10
|
+
* - In production: CloudFront maps `/dam/*` to the S3 bucket
|
|
11
|
+
* containing the compiled assets. There is no app-level asset route
|
|
12
|
+
* (unlike Next.js which has `/gradial/assets/[releaseId]/[...path]`).
|
|
13
13
|
* The assets are content-addressed (hashed filenames) so the CDN
|
|
14
14
|
* can cache them with long TTLs.
|
|
15
15
|
*
|
|
16
16
|
* - At build time: `withAci`'s `astro:build:done` hook copies the
|
|
17
|
-
*
|
|
17
|
+
* `dam` directory from compiled content into the build output,
|
|
18
18
|
* so `astro build` produces a self-contained artifact.
|
|
19
19
|
*
|
|
20
20
|
* This module provides a helper to construct asset URLs that work in both
|
|
@@ -25,15 +25,15 @@
|
|
|
25
25
|
* Default asset URL prefix. Matches the Vite middleware path and the
|
|
26
26
|
* provider gateway's asset path.
|
|
27
27
|
*/
|
|
28
|
-
export declare const DAM_ASSET_PREFIX = "
|
|
28
|
+
export declare const DAM_ASSET_PREFIX = "/dam";
|
|
29
29
|
/**
|
|
30
30
|
* Construct a DAM asset URL from a logical asset path.
|
|
31
31
|
*
|
|
32
|
-
* In both dev and production, DAM assets live under
|
|
32
|
+
* In both dev and production, DAM assets live under `/dam/`.
|
|
33
33
|
* This function normalizes the path and prepends the prefix.
|
|
34
34
|
*
|
|
35
35
|
* @param logicalPath - The asset path as stored in content (e.g. "images/hero.webp")
|
|
36
|
-
* @returns The full URL path (e.g. "
|
|
36
|
+
* @returns The full URL path (e.g. "/dam/images/hero.webp")
|
|
37
37
|
*
|
|
38
38
|
* @example
|
|
39
39
|
* ```astro
|
package/dist/astro/assets.js
CHANGED
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
* at the URL level. Instead:
|
|
6
6
|
*
|
|
7
7
|
* - In dev: the Vite `damAssetPlugin` serves DAM assets from compiled
|
|
8
|
-
* content at
|
|
8
|
+
* content at `/dam/{path}` (already handled by withAci).
|
|
9
9
|
*
|
|
10
|
-
* - In production:
|
|
11
|
-
*
|
|
12
|
-
* (
|
|
10
|
+
* - In production: CloudFront maps `/dam/*` to the S3 bucket
|
|
11
|
+
* containing the compiled assets. There is no app-level asset route
|
|
12
|
+
* (unlike Next.js which has `/gradial/assets/[releaseId]/[...path]`).
|
|
13
13
|
* The assets are content-addressed (hashed filenames) so the CDN
|
|
14
14
|
* can cache them with long TTLs.
|
|
15
15
|
*
|
|
16
16
|
* - At build time: `withAci`'s `astro:build:done` hook copies the
|
|
17
|
-
*
|
|
17
|
+
* `dam` directory from compiled content into the build output,
|
|
18
18
|
* so `astro build` produces a self-contained artifact.
|
|
19
19
|
*
|
|
20
20
|
* This module provides a helper to construct asset URLs that work in both
|
|
@@ -25,15 +25,15 @@
|
|
|
25
25
|
* Default asset URL prefix. Matches the Vite middleware path and the
|
|
26
26
|
* provider gateway's asset path.
|
|
27
27
|
*/
|
|
28
|
-
export const DAM_ASSET_PREFIX = '
|
|
28
|
+
export const DAM_ASSET_PREFIX = '/dam';
|
|
29
29
|
/**
|
|
30
30
|
* Construct a DAM asset URL from a logical asset path.
|
|
31
31
|
*
|
|
32
|
-
* In both dev and production, DAM assets live under
|
|
32
|
+
* In both dev and production, DAM assets live under `/dam/`.
|
|
33
33
|
* This function normalizes the path and prepends the prefix.
|
|
34
34
|
*
|
|
35
35
|
* @param logicalPath - The asset path as stored in content (e.g. "images/hero.webp")
|
|
36
|
-
* @returns The full URL path (e.g. "
|
|
36
|
+
* @returns The full URL path (e.g. "/dam/images/hero.webp")
|
|
37
37
|
*
|
|
38
38
|
* @example
|
|
39
39
|
* ```astro
|
package/dist/astro/index.js
CHANGED
|
@@ -94,10 +94,10 @@ export function withAci(options = {}) {
|
|
|
94
94
|
'astro:build:done'({ dir }) {
|
|
95
95
|
const siteDir = process.cwd();
|
|
96
96
|
const compiledRoot = path.resolve(siteDir, options.compiledRoot || process.env.ACI_CONTENT_ROOT || '.aci/compiled');
|
|
97
|
-
const source = path.join(compiledRoot, '
|
|
97
|
+
const source = path.join(compiledRoot, 'dam');
|
|
98
98
|
if (!fs.existsSync(source))
|
|
99
99
|
return;
|
|
100
|
-
const target = path.join(dir.pathname, '
|
|
100
|
+
const target = path.join(dir.pathname, 'dam');
|
|
101
101
|
fs.rmSync(target, { recursive: true, force: true });
|
|
102
102
|
fs.cpSync(source, target, { recursive: true });
|
|
103
103
|
},
|
|
@@ -336,6 +336,9 @@ export declare const experimentDataModel: import("./index.js").DataModelContract
|
|
|
336
336
|
routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
337
337
|
fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
338
338
|
}, z.core.$strip>;
|
|
339
|
+
eligibility: z.ZodOptional<z.ZodObject<{
|
|
340
|
+
audienceRef: z.ZodString;
|
|
341
|
+
}, z.core.$strict>>;
|
|
339
342
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
340
343
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
341
344
|
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -426,6 +429,9 @@ export declare const experimentationDataModels: (import("./index.js").DataModelC
|
|
|
426
429
|
routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
427
430
|
fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
428
431
|
}, z.core.$strip>;
|
|
432
|
+
eligibility: z.ZodOptional<z.ZodObject<{
|
|
433
|
+
audienceRef: z.ZodString;
|
|
434
|
+
}, z.core.$strict>>;
|
|
429
435
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
430
436
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
431
437
|
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -794,6 +800,9 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
|
|
|
794
800
|
routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
795
801
|
fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
796
802
|
}, z.core.$strip>;
|
|
803
|
+
eligibility: z.ZodOptional<z.ZodObject<{
|
|
804
|
+
audienceRef: z.ZodString;
|
|
805
|
+
}, z.core.$strict>>;
|
|
797
806
|
primaryGoalRef: z.ZodOptional<z.ZodString>;
|
|
798
807
|
primaryGoalId: z.ZodOptional<z.ZodString>;
|
|
799
808
|
goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -127,6 +127,9 @@ export const experimentDataModel = defineDataModel({
|
|
|
127
127
|
routes: z.array(z.string().min(1)).optional(),
|
|
128
128
|
fragments: z.array(z.string().min(1)).optional(),
|
|
129
129
|
}),
|
|
130
|
+
eligibility: z.object({
|
|
131
|
+
audienceRef: z.string().min(1),
|
|
132
|
+
}).strict().optional(),
|
|
130
133
|
primaryGoalRef: z.string().optional(),
|
|
131
134
|
primaryGoalId: z.string().optional(),
|
|
132
135
|
goalRefs: z.array(z.string().min(1)).optional(),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
2
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import { ImageSchema } from '../types/image.ts';
|
|
7
8
|
import { VideoSchema } from '../types/video.ts';
|
|
@@ -18,14 +19,16 @@ try {
|
|
|
18
19
|
globalThis.React = React;
|
|
19
20
|
}
|
|
20
21
|
catch { /* react not available — component files without JSX will still work */ }
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
if (isMainModule()) {
|
|
23
|
+
const args = parseArgs(process.argv.slice(2));
|
|
24
|
+
try {
|
|
25
|
+
const result = await compileRegistry(args);
|
|
26
|
+
console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
async function compileRegistry(options) {
|
|
31
34
|
const required = ['components', 'layouts', 'output'];
|
|
@@ -440,9 +443,10 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
440
443
|
const defs = {
|
|
441
444
|
baseBlock: {
|
|
442
445
|
type: 'object',
|
|
443
|
-
required: ['id', 'component', 'props'],
|
|
446
|
+
required: ['$type', 'id', 'component', 'props'],
|
|
444
447
|
additionalProperties: true,
|
|
445
448
|
properties: {
|
|
449
|
+
$type: { const: 'component' },
|
|
446
450
|
id: { type: 'string', minLength: 1 },
|
|
447
451
|
component: { type: 'string' },
|
|
448
452
|
props: { type: 'object' },
|
|
@@ -757,3 +761,14 @@ function parseArgs(argv) {
|
|
|
757
761
|
}
|
|
758
762
|
return out;
|
|
759
763
|
}
|
|
764
|
+
function isMainModule() {
|
|
765
|
+
if (!process.argv[1])
|
|
766
|
+
return false;
|
|
767
|
+
try {
|
|
768
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
769
|
+
}
|
|
770
|
+
catch {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
export { compileRegistry, generatePageSchema };
|
|
@@ -1,2 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export {
|
|
2
|
+
export function validateContent(options: any): Promise<{
|
|
3
|
+
warnings?: any[] | undefined;
|
|
4
|
+
success: boolean;
|
|
5
|
+
pagesValidated: number;
|
|
6
|
+
blocksValidated: number;
|
|
7
|
+
errors: ({
|
|
8
|
+
page: string;
|
|
9
|
+
message: string;
|
|
10
|
+
fragment?: undefined;
|
|
11
|
+
component?: undefined;
|
|
12
|
+
path?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
fragment: string;
|
|
15
|
+
message: string;
|
|
16
|
+
page?: undefined;
|
|
17
|
+
component?: undefined;
|
|
18
|
+
path?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
fragment: string;
|
|
21
|
+
component: any;
|
|
22
|
+
message: string;
|
|
23
|
+
page?: undefined;
|
|
24
|
+
path?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
fragment: string;
|
|
27
|
+
component: any;
|
|
28
|
+
path: any;
|
|
29
|
+
message: any;
|
|
30
|
+
page?: undefined;
|
|
31
|
+
})[];
|
|
32
|
+
}>;
|
|
@@ -19,21 +19,25 @@
|
|
|
19
19
|
* Uses the JSON schemas generated by compile-registry — portable, no React/CSS
|
|
20
20
|
* imports needed, same schemas usable by Go backend or other tools.
|
|
21
21
|
*/
|
|
22
|
+
import { realpathSync } from 'node:fs';
|
|
22
23
|
import { readdir, readFile } from 'node:fs/promises';
|
|
23
24
|
import path from 'node:path';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
if (isMainModule()) {
|
|
27
|
+
const args = parseArgs(process.argv.slice(2));
|
|
28
|
+
try {
|
|
29
|
+
const result = await validateContent(args);
|
|
30
|
+
console.log(JSON.stringify(result, null, 2));
|
|
31
|
+
if (!result.success)
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error(JSON.stringify({
|
|
36
|
+
success: false,
|
|
37
|
+
errors: [{ message: String(error?.message ?? error) }],
|
|
38
|
+
}, null, 2));
|
|
29
39
|
process.exitCode = 1;
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
console.error(JSON.stringify({
|
|
33
|
-
success: false,
|
|
34
|
-
errors: [{ message: String(error?.message ?? error) }],
|
|
35
|
-
}, null, 2));
|
|
36
|
-
process.exitCode = 1;
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
// ---------------------------------------------------------------------------
|
|
39
43
|
// Main
|
|
@@ -86,6 +90,22 @@ async function validateContent(options) {
|
|
|
86
90
|
const blocks = collectBlocks(pageData);
|
|
87
91
|
for (const block of blocks) {
|
|
88
92
|
blocksValidated++;
|
|
93
|
+
if (!block || typeof block !== 'object') {
|
|
94
|
+
collector.push({
|
|
95
|
+
page: relativePath,
|
|
96
|
+
blockId: '<unknown>',
|
|
97
|
+
message: 'page region entry must be a component block',
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (block.$type !== 'component') {
|
|
102
|
+
collector.push({
|
|
103
|
+
page: relativePath,
|
|
104
|
+
blockId: block.id ?? '<unknown>',
|
|
105
|
+
path: '$type',
|
|
106
|
+
message: 'block "$type" must be "component"',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
89
109
|
if (!block.component) {
|
|
90
110
|
collector.push({
|
|
91
111
|
page: relativePath,
|
|
@@ -348,6 +368,13 @@ function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props
|
|
|
348
368
|
issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
|
|
349
369
|
continue;
|
|
350
370
|
}
|
|
371
|
+
if (nestedBlock.$type !== 'component') {
|
|
372
|
+
issues.push({
|
|
373
|
+
path: `${nestedPath}.$type`,
|
|
374
|
+
message: `${parentBlock.component}: block ref child "$type" must be "component"`,
|
|
375
|
+
});
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
351
378
|
if (!componentMap.has(nestedBlock.component)) {
|
|
352
379
|
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
|
|
353
380
|
continue;
|
|
@@ -424,3 +451,14 @@ function parseArgs(argv) {
|
|
|
424
451
|
}
|
|
425
452
|
return out;
|
|
426
453
|
}
|
|
454
|
+
function isMainModule() {
|
|
455
|
+
if (!process.argv[1])
|
|
456
|
+
return false;
|
|
457
|
+
try {
|
|
458
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
export { validateContent };
|
package/dist/content/cache.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import type { ContentProvider } from './provider.js';
|
|
1
|
+
import type { CompiledManifest, ContentProvider } from './provider.js';
|
|
2
2
|
export declare function getCacheStats(): {
|
|
3
|
+
manifest: {
|
|
4
|
+
hits: number;
|
|
5
|
+
misses: number;
|
|
6
|
+
};
|
|
3
7
|
siteConfig: {
|
|
4
8
|
hits: number;
|
|
5
9
|
misses: number;
|
|
6
10
|
};
|
|
7
11
|
};
|
|
8
12
|
export declare function resetCacheStats(): void;
|
|
13
|
+
export declare function getCachedManifest(provider: ContentProvider): Promise<CompiledManifest>;
|
|
9
14
|
/**
|
|
10
15
|
* Get site config with caching. Use this instead of calling provider.getSiteConfig() directly.
|
|
11
16
|
*/
|
package/dist/content/cache.js
CHANGED
|
@@ -5,19 +5,44 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Keyed by provider instance for automatic invalidation when its input changes.
|
|
7
7
|
*/
|
|
8
|
+
const manifestCache = new WeakMap();
|
|
8
9
|
const siteConfigCache = new WeakMap();
|
|
9
10
|
// Cache hit/miss stats for observability
|
|
11
|
+
let manifestHits = 0;
|
|
12
|
+
let manifestMisses = 0;
|
|
10
13
|
let siteConfigHits = 0;
|
|
11
14
|
let siteConfigMisses = 0;
|
|
12
15
|
export function getCacheStats() {
|
|
13
16
|
return {
|
|
17
|
+
manifest: { hits: manifestHits, misses: manifestMisses },
|
|
14
18
|
siteConfig: { hits: siteConfigHits, misses: siteConfigMisses },
|
|
15
19
|
};
|
|
16
20
|
}
|
|
17
21
|
export function resetCacheStats() {
|
|
22
|
+
manifestHits = 0;
|
|
23
|
+
manifestMisses = 0;
|
|
18
24
|
siteConfigHits = 0;
|
|
19
25
|
siteConfigMisses = 0;
|
|
20
26
|
}
|
|
27
|
+
// getCachedManifest is intentionally capability based: the general content
|
|
28
|
+
// interface does not require a manifest, while release-backed providers do.
|
|
29
|
+
export function getCachedManifest(provider) {
|
|
30
|
+
const cached = manifestCache.get(provider);
|
|
31
|
+
if (cached) {
|
|
32
|
+
manifestHits++;
|
|
33
|
+
return cached;
|
|
34
|
+
}
|
|
35
|
+
if (!('manifest' in provider) || typeof provider.manifest !== 'function') {
|
|
36
|
+
throw new Error('Provider does not support manifest()');
|
|
37
|
+
}
|
|
38
|
+
manifestMisses++;
|
|
39
|
+
const promise = provider.manifest().catch((error) => {
|
|
40
|
+
manifestCache.delete(provider);
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
manifestCache.set(provider, promise);
|
|
44
|
+
return promise;
|
|
45
|
+
}
|
|
21
46
|
/**
|
|
22
47
|
* Get site config with caching. Use this instead of calling provider.getSiteConfig() directly.
|
|
23
48
|
*/
|
|
@@ -44,5 +69,6 @@ export function getCachedSiteConfig(provider) {
|
|
|
44
69
|
* Useful for testing or manual cache invalidation.
|
|
45
70
|
*/
|
|
46
71
|
export function clearProviderCache(provider) {
|
|
72
|
+
manifestCache.delete(provider);
|
|
47
73
|
siteConfigCache.delete(provider);
|
|
48
74
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { LayoutEntry } from './provider.js';
|
|
2
2
|
import type { KernelPageBlock } from './types.js';
|
|
3
|
-
export interface
|
|
4
|
-
|
|
3
|
+
export interface FragmentBlock {
|
|
4
|
+
$type: 'component';
|
|
5
5
|
component: string;
|
|
6
6
|
props?: Record<string, unknown>;
|
|
7
7
|
}
|
|
8
|
+
export interface FragmentData {
|
|
9
|
+
fragmentId?: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
block?: FragmentBlock;
|
|
12
|
+
component?: string;
|
|
13
|
+
props?: Record<string, unknown>;
|
|
14
|
+
}
|
|
8
15
|
export type FragmentLoader = (id: string) => Promise<FragmentData>;
|
|
9
16
|
export declare function resolveSlots(layout: LayoutEntry, pageRegions: Record<string, KernelPageBlock[]>, getFragment: FragmentLoader): Promise<Record<string, KernelPageBlock[]>>;
|
|
@@ -11,10 +11,12 @@ export async function resolveSlots(layout, pageRegions, getFragment) {
|
|
|
11
11
|
const blocks = [];
|
|
12
12
|
for (const ref of defaults) {
|
|
13
13
|
const fragment = await getFragment(ref.fragmentId);
|
|
14
|
+
const block = fragment.block ?? { component: fragment.component ?? '', props: fragment.props };
|
|
14
15
|
blocks.push({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
$type: 'component',
|
|
17
|
+
id: fragment.id ?? fragment.fragmentId ?? ref.fragmentId,
|
|
18
|
+
component: block.component,
|
|
19
|
+
props: block.props || {},
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
resolved[slot.name] = blocks;
|
package/dist/content/types.d.ts
CHANGED
|
@@ -42,11 +42,13 @@ export interface KernelPageSeo {
|
|
|
42
42
|
canonical?: string;
|
|
43
43
|
}
|
|
44
44
|
export interface KernelPageBlock {
|
|
45
|
+
$type: 'component';
|
|
45
46
|
id: string;
|
|
46
47
|
component: string;
|
|
47
48
|
props: Record<string, unknown>;
|
|
48
49
|
}
|
|
49
50
|
export interface KernelAtomicBlock {
|
|
51
|
+
$type: 'component';
|
|
50
52
|
id: string;
|
|
51
53
|
component: string;
|
|
52
54
|
props: Record<string, unknown>;
|
|
@@ -95,7 +95,7 @@ export function damAssetPlugin(options = {}) {
|
|
|
95
95
|
configureServer(server) {
|
|
96
96
|
if (!server.middlewares)
|
|
97
97
|
return;
|
|
98
|
-
server.middlewares.use('
|
|
98
|
+
server.middlewares.use('/dam', async (req, res, next) => {
|
|
99
99
|
const root = path.resolve(options.compiledRoot || compiledContentRoot());
|
|
100
100
|
const requestPath = safeRequestPath(req.url || '');
|
|
101
101
|
if (!requestPath) {
|
|
@@ -103,7 +103,7 @@ export function damAssetPlugin(options = {}) {
|
|
|
103
103
|
res.end('asset not found');
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
|
-
const filePath = path.resolve(root, '
|
|
106
|
+
const filePath = path.resolve(root, 'dam', requestPath.replace(/^\/+/, ''));
|
|
107
107
|
if (!isInside(root, filePath)) {
|
|
108
108
|
res.statusCode = 404;
|
|
109
109
|
res.end('asset not found');
|
|
@@ -127,7 +127,7 @@ function safeRequestPath(url) {
|
|
|
127
127
|
const rawPath = url.split('?')[0] || '';
|
|
128
128
|
try {
|
|
129
129
|
const decoded = decodeURIComponent(rawPath);
|
|
130
|
-
return decoded.startsWith('
|
|
130
|
+
return decoded.startsWith('/dam/') ? decoded.slice('/dam/'.length) : decoded;
|
|
131
131
|
}
|
|
132
132
|
catch {
|
|
133
133
|
return '';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function selectPersonalizationVariant(request: any, route: any, claims: any, experiments: any, identity: any): any;
|
|
2
|
+
export function resolvePersonalizationDecision(request: any, route: any, claims: any, experiments: any, identity: any): {
|
|
3
|
+
variant: any;
|
|
4
|
+
experiment: {
|
|
5
|
+
experimentId: any;
|
|
6
|
+
armId: any;
|
|
7
|
+
allocationVersion: string;
|
|
8
|
+
variantKey: any;
|
|
9
|
+
bucket: number;
|
|
10
|
+
} | null;
|
|
11
|
+
};
|