@gradial/aci 0.1.45 → 0.1.46
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/dist/audience-data-models.d.ts +142 -2
- package/dist/audience-data-models.js +2 -0
- package/dist/cli/compile-registry.d.mts +1 -5
- package/dist/cli/compile-registry.mjs +10 -25
- package/dist/cli/validate-content.d.mts +1 -31
- package/dist/cli/validate-content.mjs +12 -50
- package/dist/cli/verify-next-cdn-routes.mjs +1 -0
- package/dist/content/provider.d.ts +1 -1
- package/dist/content/resolve-slots.js +0 -1
- package/dist/content/types.d.ts +0 -2
- package/dist/internal/render-proof.d.ts +1 -0
- package/dist/internal/render-proof.js +197 -0
- package/dist/next/middleware.js +9 -54
- package/dist/next/page.js +9 -4
- package/dist/next/personalization-host.d.ts +5 -0
- package/dist/next/server.js +88 -75
- package/dist/next/vercel-collect-sdk-route.js +6 -30
- package/dist/next/vercel-edge-projection-v1.d.ts +24 -0
- package/dist/next/vercel-edge-projection-v1.js +880 -0
- package/dist/next/vercel-s3-personalization.js +7 -11
- package/dist/providers/s3.d.ts +2 -4
- package/dist/providers/s3.js +173 -19
- package/dist/types/block-ref.d.ts +0 -2
- package/dist/types/block-ref.js +0 -1
- package/package.json +2 -2
- package/src/cli/compile-registry.mjs +10 -24
- package/src/cli/validate-content.mjs +13 -52
- package/src/cli/verify-next-cdn-routes.mjs +6 -0
- package/src/types/block-ref.ts +0 -2
- package/templates/astro/starter-content/src/fixtures/atomicBlocks.ts +6 -7
- package/templates/astro/starter-content/src/fixtures/composition.ts +2 -3
- package/templates/astro/starter-content/src/fixtures/sections.ts +1 -2
- package/templates/common/.agents/skills/authoring/SKILL.md +3 -11
- package/templates/common/.agents/skills/authoring/references/block-slots.md +0 -3
- package/templates/common/.content/pages/home/_index.json +0 -10
- package/templates/common/starter-content/.content/pages/about/_index.json +6 -6
- package/templates/common/starter-content/.content/pages/home/_index.json +38 -22
- package/src/types/block-ref.test.ts +0 -45
- package/templates/nextjs/template/src/app/collect/route.ts +0 -18
|
@@ -80,6 +80,41 @@ export declare const segmentDataModel: import("./index.js").DataModelContract<z.
|
|
|
80
80
|
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
81
81
|
type: z.ZodLiteral<"event_latest">;
|
|
82
82
|
}, z.core.$strict>], "type">>>;
|
|
83
|
+
none: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
84
|
+
minCount: z.ZodNumber;
|
|
85
|
+
event: z.ZodString;
|
|
86
|
+
path: z.ZodOptional<z.ZodString>;
|
|
87
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
88
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
90
|
+
experimentId: z.ZodString;
|
|
91
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strict>>;
|
|
93
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
94
|
+
type: z.ZodLiteral<"event_count">;
|
|
95
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
96
|
+
event: z.ZodString;
|
|
97
|
+
path: z.ZodOptional<z.ZodString>;
|
|
98
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
99
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
100
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
101
|
+
experimentId: z.ZodString;
|
|
102
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strict>>;
|
|
104
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
105
|
+
type: z.ZodLiteral<"event_exists">;
|
|
106
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
107
|
+
event: z.ZodString;
|
|
108
|
+
path: z.ZodOptional<z.ZodString>;
|
|
109
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
110
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
111
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
experimentId: z.ZodString;
|
|
113
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
114
|
+
}, z.core.$strict>>;
|
|
115
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
116
|
+
type: z.ZodLiteral<"event_latest">;
|
|
117
|
+
}, z.core.$strict>], "type">>>;
|
|
83
118
|
}, z.core.$strict>, z.ZodObject<{
|
|
84
119
|
all: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
85
120
|
minCount: z.ZodNumber;
|
|
@@ -151,6 +186,41 @@ export declare const segmentDataModel: import("./index.js").DataModelContract<z.
|
|
|
151
186
|
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
152
187
|
type: z.ZodLiteral<"event_latest">;
|
|
153
188
|
}, z.core.$strict>], "type">>;
|
|
189
|
+
none: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
190
|
+
minCount: z.ZodNumber;
|
|
191
|
+
event: z.ZodString;
|
|
192
|
+
path: z.ZodOptional<z.ZodString>;
|
|
193
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
194
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
195
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
196
|
+
experimentId: z.ZodString;
|
|
197
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
198
|
+
}, z.core.$strict>>;
|
|
199
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
200
|
+
type: z.ZodLiteral<"event_count">;
|
|
201
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
202
|
+
event: z.ZodString;
|
|
203
|
+
path: z.ZodOptional<z.ZodString>;
|
|
204
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
205
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
206
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
207
|
+
experimentId: z.ZodString;
|
|
208
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
209
|
+
}, z.core.$strict>>;
|
|
210
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
211
|
+
type: z.ZodLiteral<"event_exists">;
|
|
212
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
213
|
+
event: z.ZodString;
|
|
214
|
+
path: z.ZodOptional<z.ZodString>;
|
|
215
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
216
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
217
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
218
|
+
experimentId: z.ZodString;
|
|
219
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
220
|
+
}, z.core.$strict>>;
|
|
221
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
222
|
+
type: z.ZodLiteral<"event_latest">;
|
|
223
|
+
}, z.core.$strict>], "type">>>;
|
|
154
224
|
}, z.core.$strict>]>>;
|
|
155
225
|
}, z.core.$strip>, "segment">;
|
|
156
226
|
export declare const audienceDataModel: import("./index.js").DataModelContract<z.ZodObject<{
|
|
@@ -163,11 +233,11 @@ export declare const audienceDataModel: import("./index.js").DataModelContract<z
|
|
|
163
233
|
}>;
|
|
164
234
|
priority: z.ZodNumber;
|
|
165
235
|
membershipSource: z.ZodEnum<{
|
|
236
|
+
none: "none";
|
|
166
237
|
segment: "segment";
|
|
167
238
|
rule: "rule";
|
|
168
239
|
manual: "manual";
|
|
169
240
|
external: "external";
|
|
170
|
-
none: "none";
|
|
171
241
|
}>;
|
|
172
242
|
ruleRef: z.ZodOptional<z.ZodString>;
|
|
173
243
|
segmentRef: z.ZodOptional<z.ZodString>;
|
|
@@ -473,6 +543,41 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
|
|
|
473
543
|
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
474
544
|
type: z.ZodLiteral<"event_latest">;
|
|
475
545
|
}, z.core.$strict>], "type">>>;
|
|
546
|
+
none: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
547
|
+
minCount: z.ZodNumber;
|
|
548
|
+
event: z.ZodString;
|
|
549
|
+
path: z.ZodOptional<z.ZodString>;
|
|
550
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
551
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
552
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
553
|
+
experimentId: z.ZodString;
|
|
554
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
555
|
+
}, z.core.$strict>>;
|
|
556
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
557
|
+
type: z.ZodLiteral<"event_count">;
|
|
558
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
559
|
+
event: z.ZodString;
|
|
560
|
+
path: z.ZodOptional<z.ZodString>;
|
|
561
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
562
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
563
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
564
|
+
experimentId: z.ZodString;
|
|
565
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
566
|
+
}, z.core.$strict>>;
|
|
567
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
568
|
+
type: z.ZodLiteral<"event_exists">;
|
|
569
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
570
|
+
event: z.ZodString;
|
|
571
|
+
path: z.ZodOptional<z.ZodString>;
|
|
572
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
573
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
574
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
575
|
+
experimentId: z.ZodString;
|
|
576
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
577
|
+
}, z.core.$strict>>;
|
|
578
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
579
|
+
type: z.ZodLiteral<"event_latest">;
|
|
580
|
+
}, z.core.$strict>], "type">>>;
|
|
476
581
|
}, z.core.$strict>, z.ZodObject<{
|
|
477
582
|
all: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
478
583
|
minCount: z.ZodNumber;
|
|
@@ -544,6 +649,41 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
|
|
|
544
649
|
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
545
650
|
type: z.ZodLiteral<"event_latest">;
|
|
546
651
|
}, z.core.$strict>], "type">>;
|
|
652
|
+
none: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
653
|
+
minCount: z.ZodNumber;
|
|
654
|
+
event: z.ZodString;
|
|
655
|
+
path: z.ZodOptional<z.ZodString>;
|
|
656
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
657
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
658
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
659
|
+
experimentId: z.ZodString;
|
|
660
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
661
|
+
}, z.core.$strict>>;
|
|
662
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
663
|
+
type: z.ZodLiteral<"event_count">;
|
|
664
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
665
|
+
event: z.ZodString;
|
|
666
|
+
path: z.ZodOptional<z.ZodString>;
|
|
667
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
668
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
669
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
670
|
+
experimentId: z.ZodString;
|
|
671
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
672
|
+
}, z.core.$strict>>;
|
|
673
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
674
|
+
type: z.ZodLiteral<"event_exists">;
|
|
675
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
676
|
+
event: z.ZodString;
|
|
677
|
+
path: z.ZodOptional<z.ZodString>;
|
|
678
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
679
|
+
withinDays: z.ZodOptional<z.ZodNumber>;
|
|
680
|
+
experiment: z.ZodOptional<z.ZodObject<{
|
|
681
|
+
experimentId: z.ZodString;
|
|
682
|
+
armId: z.ZodOptional<z.ZodString>;
|
|
683
|
+
}, z.core.$strict>>;
|
|
684
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
685
|
+
type: z.ZodLiteral<"event_latest">;
|
|
686
|
+
}, z.core.$strict>], "type">>>;
|
|
547
687
|
}, z.core.$strict>]>>;
|
|
548
688
|
}, z.core.$strip>, "segment"> | import("./index.js").DataModelContract<z.ZodObject<{
|
|
549
689
|
id: z.ZodString;
|
|
@@ -555,11 +695,11 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
|
|
|
555
695
|
}>;
|
|
556
696
|
priority: z.ZodNumber;
|
|
557
697
|
membershipSource: z.ZodEnum<{
|
|
698
|
+
none: "none";
|
|
558
699
|
segment: "segment";
|
|
559
700
|
rule: "rule";
|
|
560
701
|
manual: "manual";
|
|
561
702
|
external: "external";
|
|
562
|
-
none: "none";
|
|
563
703
|
}>;
|
|
564
704
|
ruleRef: z.ZodOptional<z.ZodString>;
|
|
565
705
|
segmentRef: z.ZodOptional<z.ZodString>;
|
|
@@ -43,10 +43,12 @@ const segmentCriteriaSchema = z.union([
|
|
|
43
43
|
z.object({
|
|
44
44
|
all: z.array(segmentCriteriaConditionSchema).min(1),
|
|
45
45
|
any: z.array(segmentCriteriaConditionSchema).optional(),
|
|
46
|
+
none: z.array(segmentCriteriaConditionSchema).optional(),
|
|
46
47
|
}).strict(),
|
|
47
48
|
z.object({
|
|
48
49
|
all: z.array(segmentCriteriaConditionSchema).optional(),
|
|
49
50
|
any: z.array(segmentCriteriaConditionSchema).min(1),
|
|
51
|
+
none: z.array(segmentCriteriaConditionSchema).optional(),
|
|
50
52
|
}).strict(),
|
|
51
53
|
]);
|
|
52
54
|
const requestRuleConditionSchema = z.object({
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { realpathSync } from 'node:fs';
|
|
3
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
3
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
6
5
|
import { z } from 'zod';
|
|
7
6
|
import { ImageSchema } from '../types/image.ts';
|
|
8
7
|
import { VideoSchema } from '../types/video.ts';
|
|
@@ -19,16 +18,14 @@ try {
|
|
|
19
18
|
globalThis.React = React;
|
|
20
19
|
}
|
|
21
20
|
catch { /* react not available — component files without JSX will still work */ }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
process.exitCode = 1;
|
|
31
|
-
}
|
|
21
|
+
const args = parseArgs(process.argv.slice(2));
|
|
22
|
+
try {
|
|
23
|
+
const result = await compileRegistry(args);
|
|
24
|
+
console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
28
|
+
process.exitCode = 1;
|
|
32
29
|
}
|
|
33
30
|
async function compileRegistry(options) {
|
|
34
31
|
const required = ['components', 'layouts', 'output'];
|
|
@@ -443,10 +440,9 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
443
440
|
const defs = {
|
|
444
441
|
baseBlock: {
|
|
445
442
|
type: 'object',
|
|
446
|
-
required: ['
|
|
443
|
+
required: ['id', 'component', 'props'],
|
|
447
444
|
additionalProperties: true,
|
|
448
445
|
properties: {
|
|
449
|
-
$type: { const: 'component' },
|
|
450
446
|
id: { type: 'string', minLength: 1 },
|
|
451
447
|
component: { type: 'string' },
|
|
452
448
|
props: { type: 'object' },
|
|
@@ -761,14 +757,3 @@ function parseArgs(argv) {
|
|
|
761
757
|
}
|
|
762
758
|
return out;
|
|
763
759
|
}
|
|
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,32 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
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
|
-
}>;
|
|
2
|
+
export {};
|
|
@@ -19,25 +19,21 @@
|
|
|
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';
|
|
23
22
|
import { readdir, readFile } from 'node:fs/promises';
|
|
24
23
|
import path from 'node:path';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
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));
|
|
24
|
+
const args = parseArgs(process.argv.slice(2));
|
|
25
|
+
try {
|
|
26
|
+
const result = await validateContent(args);
|
|
27
|
+
console.log(JSON.stringify(result, null, 2));
|
|
28
|
+
if (!result.success)
|
|
39
29
|
process.exitCode = 1;
|
|
40
|
-
|
|
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;
|
|
41
37
|
}
|
|
42
38
|
// ---------------------------------------------------------------------------
|
|
43
39
|
// Main
|
|
@@ -90,22 +86,6 @@ async function validateContent(options) {
|
|
|
90
86
|
const blocks = collectBlocks(pageData);
|
|
91
87
|
for (const block of blocks) {
|
|
92
88
|
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
|
-
}
|
|
109
89
|
if (!block.component) {
|
|
110
90
|
collector.push({
|
|
111
91
|
page: relativePath,
|
|
@@ -368,13 +348,6 @@ function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props
|
|
|
368
348
|
issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
|
|
369
349
|
continue;
|
|
370
350
|
}
|
|
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
|
-
}
|
|
378
351
|
if (!componentMap.has(nestedBlock.component)) {
|
|
379
352
|
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
|
|
380
353
|
continue;
|
|
@@ -451,14 +424,3 @@ function parseArgs(argv) {
|
|
|
451
424
|
}
|
|
452
425
|
return out;
|
|
453
426
|
}
|
|
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 };
|
|
@@ -32,6 +32,7 @@ checkMissing('legacy required catch-all is removed', 'src/app/[...slug]/page.tsx
|
|
|
32
32
|
checkFile('canonical asset route exists', 'src/app/gradial/assets/[...path]/route.ts', (content) => content.includes('@gradial/aci/next/asset-route'), 'Expected the canonical asset route to use @gradial/aci/next/asset-route.');
|
|
33
33
|
checkMissing('legacy release-scoped asset route is removed', 'src/app/gradial/assets/[releaseId]/[...path]/route.ts', 'Release-scoped asset URLs are produced by withAci() rewrites into the canonical src/app/gradial/assets/[...path]/route.ts; do not add a [releaseId] route.');
|
|
34
34
|
checkMissing('no separate __r route folder', 'src/app/__r/[releaseId]/routes/[[...slug]]/page.tsx', 'The root catch-all handles release-scoped rewrites. Do not create a separate __r route folder.');
|
|
35
|
+
checkMissing('platform-owned collect route is absent', 'src/app/collect/route.ts', 'ACI injects the reserved /collect function during Vercel Build Output composition; customer source must not own it.');
|
|
35
36
|
checkFile('next.config uses withAci', 'next.config.mjs', (content) => content.includes('withAci'), 'Expected next.config.mjs to use withAci() from @gradial/aci/next.');
|
|
36
37
|
for (const line of checks) {
|
|
37
38
|
console.log(line);
|
|
@@ -12,7 +12,6 @@ export async function resolveSlots(layout, pageRegions, getFragment) {
|
|
|
12
12
|
for (const ref of defaults) {
|
|
13
13
|
const fragment = await getFragment(ref.fragmentId);
|
|
14
14
|
blocks.push({
|
|
15
|
-
$type: 'component',
|
|
16
15
|
id: fragment.id,
|
|
17
16
|
component: fragment.component,
|
|
18
17
|
props: fragment.props || {},
|
package/dist/content/types.d.ts
CHANGED
|
@@ -42,13 +42,11 @@ export interface KernelPageSeo {
|
|
|
42
42
|
canonical?: string;
|
|
43
43
|
}
|
|
44
44
|
export interface KernelPageBlock {
|
|
45
|
-
$type: 'component';
|
|
46
45
|
id: string;
|
|
47
46
|
component: string;
|
|
48
47
|
props: Record<string, unknown>;
|
|
49
48
|
}
|
|
50
49
|
export interface KernelAtomicBlock {
|
|
51
|
-
$type: 'component';
|
|
52
50
|
id: string;
|
|
53
51
|
component: string;
|
|
54
52
|
props: Record<string, unknown>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const RENDER_PROOF_VERSION = 'gradial.aci.renderProof.v1';
|
|
2
|
+
const RENDER_PROOF_KEYS = [
|
|
3
|
+
'version',
|
|
4
|
+
'releaseId',
|
|
5
|
+
'route',
|
|
6
|
+
'contentRef',
|
|
7
|
+
'contentSha256',
|
|
8
|
+
'variantKey',
|
|
9
|
+
];
|
|
10
|
+
const MAX_RENDER_PROOF_CONTENT_BYTES = 4096;
|
|
11
|
+
const receiptBrand = Symbol('aci.verified-page-receipt');
|
|
12
|
+
const renderProofReceipts = new WeakMap();
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const selectVerifiedPage = Symbol('aci.select-verified-page');
|
|
15
|
+
/** @internal */
|
|
16
|
+
export function createVerifiedPageReceipt(value) {
|
|
17
|
+
// Selection itself must fail if the coupled marker cannot be emitted.
|
|
18
|
+
// `getPage` also uses this capability and intentionally discards the
|
|
19
|
+
// receipt, so deferring the size check to React would permit markerless
|
|
20
|
+
// success on that public server loader.
|
|
21
|
+
validateVerifiedPageReceipt(value);
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
...value,
|
|
24
|
+
[receiptBrand]: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** @internal */
|
|
28
|
+
export function validateVerifiedPageReceipt(value) {
|
|
29
|
+
encodeRenderProofContent(toRenderProof(value));
|
|
30
|
+
}
|
|
31
|
+
/** @internal */
|
|
32
|
+
export function attachRenderProofReceipt(input, receipt) {
|
|
33
|
+
if (receipt[receiptBrand] !== true) {
|
|
34
|
+
throw new Error('ACI render proof integrity error: unverified receipt');
|
|
35
|
+
}
|
|
36
|
+
renderProofReceipts.set(input, receipt);
|
|
37
|
+
}
|
|
38
|
+
/** @internal */
|
|
39
|
+
export function renderProofContentFor(input) {
|
|
40
|
+
const receipt = renderProofReceipts.get(input);
|
|
41
|
+
return receipt ? encodeRenderProofContent(toRenderProof(receipt)) : null;
|
|
42
|
+
}
|
|
43
|
+
/** @internal */
|
|
44
|
+
export function encodeRenderProofContent(proof) {
|
|
45
|
+
validateRenderProof(proof);
|
|
46
|
+
const canonicalJSON = JSON.stringify({
|
|
47
|
+
version: proof.version,
|
|
48
|
+
releaseId: proof.releaseId,
|
|
49
|
+
route: proof.route,
|
|
50
|
+
contentRef: proof.contentRef,
|
|
51
|
+
contentSha256: proof.contentSha256,
|
|
52
|
+
variantKey: proof.variantKey,
|
|
53
|
+
});
|
|
54
|
+
const content = Buffer.from(canonicalJSON, 'utf8').toString('base64url');
|
|
55
|
+
if (content.length < 1 || content.length > MAX_RENDER_PROOF_CONTENT_BYTES) {
|
|
56
|
+
throw new Error(`ACI render proof integrity error: marker content is ${content.length} bytes`);
|
|
57
|
+
}
|
|
58
|
+
return content;
|
|
59
|
+
}
|
|
60
|
+
/** @internal */
|
|
61
|
+
export function decodeRenderProofContent(content) {
|
|
62
|
+
if (content.length < 1
|
|
63
|
+
|| content.length > MAX_RENDER_PROOF_CONTENT_BYTES
|
|
64
|
+
|| !/^[A-Za-z0-9_-]+$/.test(content)) {
|
|
65
|
+
throw new Error('ACI render proof integrity error: invalid marker encoding');
|
|
66
|
+
}
|
|
67
|
+
const bytes = Buffer.from(content, 'base64url');
|
|
68
|
+
if (bytes.toString('base64url') !== content) {
|
|
69
|
+
throw new Error('ACI render proof integrity error: non-canonical base64url');
|
|
70
|
+
}
|
|
71
|
+
let canonicalJSON = '';
|
|
72
|
+
try {
|
|
73
|
+
canonicalJSON = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
throw integrityError('marker is not valid UTF-8', cause);
|
|
77
|
+
}
|
|
78
|
+
let decoded;
|
|
79
|
+
try {
|
|
80
|
+
decoded = JSON.parse(canonicalJSON);
|
|
81
|
+
}
|
|
82
|
+
catch (cause) {
|
|
83
|
+
throw integrityError('marker is not valid JSON', cause);
|
|
84
|
+
}
|
|
85
|
+
if (!isRecord(decoded)) {
|
|
86
|
+
throw new Error('ACI render proof integrity error: marker must be an object');
|
|
87
|
+
}
|
|
88
|
+
if (!sameKeys(Object.keys(decoded), RENDER_PROOF_KEYS)) {
|
|
89
|
+
throw new Error('ACI render proof integrity error: marker keys are not canonical');
|
|
90
|
+
}
|
|
91
|
+
const proof = {
|
|
92
|
+
version: decoded.version,
|
|
93
|
+
releaseId: decoded.releaseId,
|
|
94
|
+
route: decoded.route,
|
|
95
|
+
contentRef: decoded.contentRef,
|
|
96
|
+
contentSha256: decoded.contentSha256,
|
|
97
|
+
variantKey: decoded.variantKey,
|
|
98
|
+
};
|
|
99
|
+
validateRenderProof(proof);
|
|
100
|
+
if (JSON.stringify(proof) !== canonicalJSON) {
|
|
101
|
+
throw new Error('ACI render proof integrity error: marker JSON is not canonical');
|
|
102
|
+
}
|
|
103
|
+
return Object.freeze(proof);
|
|
104
|
+
}
|
|
105
|
+
function toRenderProof(receipt) {
|
|
106
|
+
return {
|
|
107
|
+
version: RENDER_PROOF_VERSION,
|
|
108
|
+
releaseId: receipt.releaseId,
|
|
109
|
+
route: receipt.route,
|
|
110
|
+
contentRef: receipt.contentRef,
|
|
111
|
+
contentSha256: receipt.contentSha256,
|
|
112
|
+
variantKey: receipt.variantKey,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function validateRenderProof(proof) {
|
|
116
|
+
if (!isRecord(proof)) {
|
|
117
|
+
throw new Error('ACI render proof integrity error: proof must be an object');
|
|
118
|
+
}
|
|
119
|
+
for (const key of RENDER_PROOF_KEYS) {
|
|
120
|
+
if (typeof proof[key] !== 'string') {
|
|
121
|
+
throw new Error(`ACI render proof integrity error: ${key} must be a string`);
|
|
122
|
+
}
|
|
123
|
+
if (hasUnpairedSurrogate(proof[key])) {
|
|
124
|
+
throw new Error(`ACI render proof integrity error: ${key} has invalid Unicode`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (proof.version !== RENDER_PROOF_VERSION) {
|
|
128
|
+
throw new Error('ACI render proof integrity error: unsupported version');
|
|
129
|
+
}
|
|
130
|
+
if (Buffer.byteLength(proof.releaseId, 'ascii') > 128
|
|
131
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(proof.releaseId)) {
|
|
132
|
+
throw new Error('ACI render proof integrity error: invalid releaseId');
|
|
133
|
+
}
|
|
134
|
+
if (Buffer.byteLength(proof.route, 'utf8') > 2048
|
|
135
|
+
|| !isCanonicalRoute(proof.route)) {
|
|
136
|
+
throw new Error('ACI render proof integrity error: invalid route');
|
|
137
|
+
}
|
|
138
|
+
if (Buffer.byteLength(proof.contentRef, 'utf8') > 2048
|
|
139
|
+
|| !isCleanContentRef(proof.contentRef)) {
|
|
140
|
+
throw new Error('ACI render proof integrity error: invalid contentRef');
|
|
141
|
+
}
|
|
142
|
+
if (!/^[0-9a-f]{64}$/.test(proof.contentSha256)) {
|
|
143
|
+
throw new Error('ACI render proof integrity error: invalid contentSha256');
|
|
144
|
+
}
|
|
145
|
+
if (Buffer.byteLength(proof.variantKey, 'utf8') > 1024
|
|
146
|
+
|| /[\u0000-\u001f\u007f]/.test(proof.variantKey)) {
|
|
147
|
+
throw new Error('ACI render proof integrity error: invalid variantKey');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function isCanonicalRoute(route) {
|
|
151
|
+
if (route === '/')
|
|
152
|
+
return true;
|
|
153
|
+
if (!route.startsWith('/')
|
|
154
|
+
|| route.startsWith('//')
|
|
155
|
+
|| route.endsWith('/')
|
|
156
|
+
|| /[\u0000-\u001f\u007f]/.test(route)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
return route.slice(1).split('/').every((segment) => (segment !== '' && segment !== '.' && segment !== '..'));
|
|
160
|
+
}
|
|
161
|
+
function isCleanContentRef(ref) {
|
|
162
|
+
if (!ref
|
|
163
|
+
|| ref.startsWith('/')
|
|
164
|
+
|| ref.endsWith('/')
|
|
165
|
+
|| ref.includes('\\')
|
|
166
|
+
|| /[\u0000-\u001f\u007f]/.test(ref)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return ref.split('/').every((segment) => (segment !== '' && segment !== '.' && segment !== '..'));
|
|
170
|
+
}
|
|
171
|
+
function isRecord(value) {
|
|
172
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
173
|
+
}
|
|
174
|
+
function sameKeys(actual, expected) {
|
|
175
|
+
return actual.length === expected.length
|
|
176
|
+
&& actual.every((key, index) => key === expected[index]);
|
|
177
|
+
}
|
|
178
|
+
function hasUnpairedSurrogate(value) {
|
|
179
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
180
|
+
const code = value.charCodeAt(index);
|
|
181
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
182
|
+
const next = value.charCodeAt(index + 1);
|
|
183
|
+
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
184
|
+
return true;
|
|
185
|
+
index += 1;
|
|
186
|
+
}
|
|
187
|
+
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
function integrityError(message, cause) {
|
|
194
|
+
const error = new Error(`ACI render proof integrity error: ${message}`);
|
|
195
|
+
error.cause = cause;
|
|
196
|
+
return error;
|
|
197
|
+
}
|