@gradial/aci 0.1.45 → 0.1.47
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/error-page.d.ts +56 -0
- package/dist/astro/error-page.js +79 -0
- package/dist/astro/index.d.ts +4 -0
- package/dist/astro/index.js +9 -0
- 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/emit-error-pages.d.mts +39 -0
- package/dist/cli/emit-error-pages.mjs +402 -0
- 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/default-error-page-html.d.ts +2 -0
- package/dist/content/default-error-page-html.js +6 -0
- package/dist/content/default-error-page.d.ts +23 -0
- package/dist/content/default-error-page.js +34 -0
- package/dist/content/error-page-kind.d.ts +57 -0
- package/dist/content/error-page-kind.js +106 -0
- package/dist/content/error-pages.d.ts +88 -0
- package/dist/content/error-pages.js +120 -0
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.js +2 -0
- package/dist/content/provider.d.ts +1 -1
- package/dist/content/resolve-slots.js +0 -1
- package/dist/content/types.d.ts +12 -2
- package/dist/internal/render-proof.d.ts +1 -0
- package/dist/internal/render-proof.js +197 -0
- package/dist/next/config.js +17 -0
- package/dist/next/error-page-instrumentation.d.ts +23 -0
- package/dist/next/error-page-instrumentation.js +36 -0
- package/dist/next/error-page-marker.d.ts +11 -0
- package/dist/next/error-page-marker.js +20 -0
- package/dist/next/error-pages.d.ts +52 -0
- package/dist/next/error-pages.js +94 -0
- package/dist/next/error-seat-payload.d.ts +33 -0
- package/dist/next/error-seat-payload.js +143 -0
- package/dist/next/error-seat.d.ts +57 -0
- package/dist/next/error-seat.js +83 -0
- package/dist/next/index.d.ts +4 -0
- package/dist/next/index.js +8 -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 +15 -9
- package/src/cli/compile-registry.mjs +10 -24
- package/src/cli/emit-error-pages.mjs +424 -0
- 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/astro/template/package.json.tmpl +1 -1
- package/templates/astro/template/src/aci/error-page-instrumentation.mjs +33 -0
- package/templates/astro/template/src/pages/404.astro +54 -0
- package/templates/astro/template/src/pages/500.astro +53 -0
- package/templates/astro/template/src/pages/[...slug].astro +15 -2
- package/templates/common/.agents/skills/authoring/SKILL.md +74 -11
- package/templates/common/.agents/skills/authoring/references/block-slots.md +0 -3
- package/templates/common/.content/config/robots.txt +4 -0
- package/templates/common/.content/config/site.json.tmpl +4 -0
- package/templates/common/.content/pages/404/_index.json +31 -0
- package/templates/common/.content/pages/error/_index.json +31 -0
- package/templates/common/.content/pages/home/_index.json +0 -10
- package/templates/common/.content/schemas/site.schema.json +9 -0
- package/templates/common/starter-content/.content/config/robots.txt +2 -0
- package/templates/common/starter-content/.content/config/site.json +4 -0
- package/templates/common/starter-content/.content/pages/404/_index.json +31 -0
- package/templates/common/starter-content/.content/pages/about/_index.json +0 -3
- package/templates/common/starter-content/.content/pages/error/_index.json +31 -0
- package/templates/common/starter-content/.content/pages/home/_index.json +21 -14
- package/templates/common/starter-content/.content/schemas/site.schema.json +9 -0
- package/templates/nextjs/template/package.json.tmpl +1 -1
- package/templates/nextjs/template/src/aci/error-page-instrumentation.d.mts +4 -0
- package/templates/nextjs/template/src/aci/error-page-instrumentation.mjs +38 -0
- package/templates/nextjs/template/src/app/error.tsx +18 -0
- package/templates/nextjs/template/src/app/not-found.tsx +13 -0
- package/src/types/block-ref.test.ts +0 -45
- package/templates/nextjs/template/src/app/collect/route.ts +0 -18
|
@@ -2,6 +2,7 @@ import { GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client, } from
|
|
|
2
2
|
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
|
|
3
3
|
import { getUncachedEdgeConfigValue } from './edge-config.js';
|
|
4
4
|
import { IdentityErasureFencedError } from './personalization-host.js';
|
|
5
|
+
import { decodeEdgeRouteProjectionV1, edgeRoutesV1Key, } from './vercel-edge-projection-v1.js';
|
|
5
6
|
const ENVELOPE_DIGEST_METADATA_KEY = 'aci-envelope-sha256';
|
|
6
7
|
const COLLECTION_GATE_ETAG_METADATA_KEY = 'aci-collection-gate-etag';
|
|
7
8
|
const COLLECTION_GATE_STATE_METADATA_KEY = 'aci-collection-gate-state';
|
|
@@ -406,12 +407,13 @@ export class VercelEdgeConfigRouteStore {
|
|
|
406
407
|
this.siteId = siteId;
|
|
407
408
|
}
|
|
408
409
|
async readEdgeManifest(releaseId) {
|
|
409
|
-
const value = await getUncachedEdgeConfigValue(this.edgeConfig,
|
|
410
|
-
if (
|
|
410
|
+
const value = await getUncachedEdgeConfigValue(this.edgeConfig, edgeRoutesV1Key(this.siteId, releaseId));
|
|
411
|
+
if (value == null)
|
|
411
412
|
return null;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
413
|
+
return decodeEdgeRouteProjectionV1(value, {
|
|
414
|
+
siteId: this.siteId,
|
|
415
|
+
releaseId,
|
|
416
|
+
});
|
|
415
417
|
}
|
|
416
418
|
}
|
|
417
419
|
export function createVercelS3PersonalizationStore(config) {
|
|
@@ -531,12 +533,6 @@ function cleanKey(value) {
|
|
|
531
533
|
function joinKey(...parts) {
|
|
532
534
|
return parts.map(cleanKey).filter(Boolean).join('/');
|
|
533
535
|
}
|
|
534
|
-
function edgeRoutesKey(siteId, releaseId) {
|
|
535
|
-
return `edgeRoutes_${edgeKeySegment(siteId)}_${edgeKeySegment(releaseId)}`;
|
|
536
|
-
}
|
|
537
|
-
function edgeKeySegment(value) {
|
|
538
|
-
return value.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
539
|
-
}
|
|
540
536
|
function isNotFound(error) {
|
|
541
537
|
const value = error;
|
|
542
538
|
return value?.$metadata?.httpStatusCode === 404 || value?.name === 'NoSuchKey' || value?.name === 'NotFound';
|
package/dist/providers/s3.d.ts
CHANGED
|
@@ -32,10 +32,8 @@ export declare class S3ContentProvider implements ContentProvider {
|
|
|
32
32
|
getSiteConfig<T = unknown>(): Promise<T>;
|
|
33
33
|
getPage<T = unknown>(route: string): Promise<T>;
|
|
34
34
|
/**
|
|
35
|
-
* getPageByRef serves
|
|
36
|
-
*
|
|
37
|
-
* release manifest declares are served — the header names published
|
|
38
|
-
* content, it cannot address arbitrary keys.
|
|
35
|
+
* getPageByRef serves an exact payload the manifest declares. The stricter
|
|
36
|
+
* route-bound selection used by server rendering stays package-internal.
|
|
39
37
|
*/
|
|
40
38
|
getPageByRef<T = unknown>(payloadRef: string): Promise<T>;
|
|
41
39
|
getFragment<T = unknown>(id: string): Promise<T>;
|
package/dist/providers/s3.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
3
3
|
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
|
|
4
|
-
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
4
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
|
|
5
5
|
import { normalizeRoute } from '../content/routes.js';
|
|
6
|
+
import { createVerifiedPageReceipt, selectVerifiedPage, validateVerifiedPageReceipt, } from '../internal/render-proof.js';
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// S3 content provider — reads compiled content files from S3
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
@@ -23,29 +24,21 @@ export class S3ContentProvider {
|
|
|
23
24
|
return await this.getJSON(manifest.siteConfigRef);
|
|
24
25
|
}
|
|
25
26
|
async getPage(route) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return await this.getJSON(entry.payloadRef);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
throw new PageNotFoundError(route, error);
|
|
37
|
-
}
|
|
27
|
+
return (await this.#selectPage({
|
|
28
|
+
route,
|
|
29
|
+
contentRef: '',
|
|
30
|
+
contentRefPresent: false,
|
|
31
|
+
variantKey: '',
|
|
32
|
+
variantKeyPresent: false,
|
|
33
|
+
})).page;
|
|
38
34
|
}
|
|
39
35
|
/**
|
|
40
|
-
* getPageByRef serves
|
|
41
|
-
*
|
|
42
|
-
* release manifest declares are served — the header names published
|
|
43
|
-
* content, it cannot address arbitrary keys.
|
|
36
|
+
* getPageByRef serves an exact payload the manifest declares. The stricter
|
|
37
|
+
* route-bound selection used by server rendering stays package-internal.
|
|
44
38
|
*/
|
|
45
39
|
async getPageByRef(payloadRef) {
|
|
46
40
|
const manifest = await this.manifest();
|
|
47
|
-
const known = Object.values(manifest.routes).some((entry) => entry
|
|
48
|
-
Object.values(entry.variants ?? {}).some((variant) => variant.payloadRef === payloadRef));
|
|
41
|
+
const known = Object.values(manifest.routes).some((entry) => routeContainsRef(entry, payloadRef));
|
|
49
42
|
if (!known) {
|
|
50
43
|
throw new PageNotFoundError(payloadRef);
|
|
51
44
|
}
|
|
@@ -56,6 +49,20 @@ export class S3ContentProvider {
|
|
|
56
49
|
throw new PageNotFoundError(payloadRef, error);
|
|
57
50
|
}
|
|
58
51
|
}
|
|
52
|
+
/** @internal */
|
|
53
|
+
async [selectVerifiedPage](request) {
|
|
54
|
+
const selection = await this.#selectPage(request, request.releaseId);
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
page: selection.page,
|
|
57
|
+
receipt: createVerifiedPageReceipt({
|
|
58
|
+
releaseId: request.releaseId,
|
|
59
|
+
route: selection.route,
|
|
60
|
+
contentRef: selection.contentRef,
|
|
61
|
+
contentSha256: selection.contentSha256,
|
|
62
|
+
variantKey: selection.variantKey,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
59
66
|
async getFragment(id) {
|
|
60
67
|
const manifest = await this.manifest();
|
|
61
68
|
const entry = manifest.fragments[id];
|
|
@@ -92,6 +99,149 @@ export class S3ContentProvider {
|
|
|
92
99
|
async getJSON(key) {
|
|
93
100
|
return await getS3JSON(joinKey(this.config.keyPrefix, key), this.config, this.#client);
|
|
94
101
|
}
|
|
102
|
+
async #selectPage(request, releaseId) {
|
|
103
|
+
if (request.variantKeyPresent && !request.contentRefPresent) {
|
|
104
|
+
throw pageIntegrityError('variant header requires a content ref');
|
|
105
|
+
}
|
|
106
|
+
const manifest = await this.manifest();
|
|
107
|
+
const normalized = normalizeRoute(request.route);
|
|
108
|
+
const routeMatches = Object.values(manifest.routes).filter((candidate) => normalizeRoute(candidate.path) === normalized);
|
|
109
|
+
if (routeMatches.length === 0) {
|
|
110
|
+
if (request.contentRefPresent) {
|
|
111
|
+
const elsewhere = Object.values(manifest.routes)
|
|
112
|
+
.some((candidate) => routeContainsRef(candidate, request.contentRef));
|
|
113
|
+
const reason = elsewhere ? 'cross-route content ref' : 'undeclared route content ref';
|
|
114
|
+
throw pageIntegrityError(`${reason}: ${request.contentRef}`);
|
|
115
|
+
}
|
|
116
|
+
throw new PageNotFoundError(request.route);
|
|
117
|
+
}
|
|
118
|
+
if (routeMatches.length !== 1) {
|
|
119
|
+
throw pageIntegrityError(`route ${normalized} is ambiguous`);
|
|
120
|
+
}
|
|
121
|
+
const entry = routeMatches[0];
|
|
122
|
+
const route = normalizeRoute(entry.path);
|
|
123
|
+
const tuples = [
|
|
124
|
+
{
|
|
125
|
+
contentRef: entry.payloadRef,
|
|
126
|
+
digest: entry.digest,
|
|
127
|
+
variantKey: '',
|
|
128
|
+
isVariant: false,
|
|
129
|
+
},
|
|
130
|
+
...Object.values(entry.variants ?? {}).map(pageTupleForVariant),
|
|
131
|
+
];
|
|
132
|
+
let tuple;
|
|
133
|
+
if (!request.contentRefPresent) {
|
|
134
|
+
tuple = tuples[0];
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const matches = tuples.filter((candidate) => {
|
|
138
|
+
if (candidate.contentRef !== request.contentRef)
|
|
139
|
+
return false;
|
|
140
|
+
if (!request.variantKeyPresent)
|
|
141
|
+
return true;
|
|
142
|
+
return candidate.isVariant && candidate.variantKey === request.variantKey;
|
|
143
|
+
});
|
|
144
|
+
if (matches.length === 0) {
|
|
145
|
+
const elsewhere = Object.values(manifest.routes)
|
|
146
|
+
.filter((candidate) => candidate !== entry)
|
|
147
|
+
.some((candidate) => routeContainsRef(candidate, request.contentRef));
|
|
148
|
+
const reason = elsewhere ? 'cross-route content ref' : 'undeclared route content ref';
|
|
149
|
+
throw pageIntegrityError(`${reason}: ${request.contentRef}`);
|
|
150
|
+
}
|
|
151
|
+
if (matches.length !== 1) {
|
|
152
|
+
throw pageIntegrityError(`ambiguous route content ref: ${request.contentRef}`);
|
|
153
|
+
}
|
|
154
|
+
tuple = matches[0];
|
|
155
|
+
}
|
|
156
|
+
const digestMatch = /^sha256:([0-9a-f]{64})$/.exec(tuple.digest);
|
|
157
|
+
if (!digestMatch) {
|
|
158
|
+
throw pageIntegrityError(`invalid manifest digest for ${tuple.contentRef}`);
|
|
159
|
+
}
|
|
160
|
+
const contentSha256 = digestMatch[1];
|
|
161
|
+
if (releaseId !== undefined) {
|
|
162
|
+
validateVerifiedPageReceipt({
|
|
163
|
+
releaseId,
|
|
164
|
+
route,
|
|
165
|
+
contentRef: tuple.contentRef,
|
|
166
|
+
contentSha256,
|
|
167
|
+
variantKey: tuple.variantKey,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const key = joinManifestContentKey(this.config.keyPrefix, tuple.contentRef);
|
|
171
|
+
let response;
|
|
172
|
+
try {
|
|
173
|
+
response = await getS3Raw(key, this.config, this.#client);
|
|
174
|
+
}
|
|
175
|
+
catch (cause) {
|
|
176
|
+
throw pageIntegrityError(`fetch failed for ${tuple.contentRef}`, cause);
|
|
177
|
+
}
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
const detail = await safeResponseText(response);
|
|
180
|
+
throw pageIntegrityError(`fetch failed for ${tuple.contentRef} with status ${response.status}${detail ? `: ${detail}` : ''}`);
|
|
181
|
+
}
|
|
182
|
+
let bytes;
|
|
183
|
+
try {
|
|
184
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
185
|
+
}
|
|
186
|
+
catch (cause) {
|
|
187
|
+
throw pageIntegrityError(`response body failed for ${tuple.contentRef}`, cause);
|
|
188
|
+
}
|
|
189
|
+
const actualDigest = crypto.createHash('sha256').update(bytes).digest('hex');
|
|
190
|
+
if (actualDigest !== contentSha256) {
|
|
191
|
+
throw pageIntegrityError(`digest mismatch for ${tuple.contentRef}: got ${actualDigest}, want ${contentSha256}`);
|
|
192
|
+
}
|
|
193
|
+
let source = '';
|
|
194
|
+
try {
|
|
195
|
+
source = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
196
|
+
}
|
|
197
|
+
catch (cause) {
|
|
198
|
+
throw pageIntegrityError(`invalid UTF-8 for ${tuple.contentRef}`, cause);
|
|
199
|
+
}
|
|
200
|
+
let page;
|
|
201
|
+
try {
|
|
202
|
+
page = JSON.parse(source);
|
|
203
|
+
}
|
|
204
|
+
catch (cause) {
|
|
205
|
+
throw pageIntegrityError(`invalid JSON for ${tuple.contentRef}`, cause);
|
|
206
|
+
}
|
|
207
|
+
return Object.freeze({
|
|
208
|
+
page,
|
|
209
|
+
route,
|
|
210
|
+
contentRef: tuple.contentRef,
|
|
211
|
+
contentSha256,
|
|
212
|
+
variantKey: tuple.variantKey,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function pageTupleForVariant(variant) {
|
|
217
|
+
if (typeof variant.variantKey !== 'string' || variant.variantKey === '') {
|
|
218
|
+
throw pageIntegrityError('variant manifest tuple has an empty variant key');
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
contentRef: variant.payloadRef,
|
|
222
|
+
digest: variant.digest,
|
|
223
|
+
variantKey: variant.variantKey,
|
|
224
|
+
isVariant: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function routeContainsRef(entry, contentRef) {
|
|
228
|
+
return entry.payloadRef === contentRef
|
|
229
|
+
|| Object.values(entry.variants ?? {})
|
|
230
|
+
.some((variant) => variant.payloadRef === contentRef);
|
|
231
|
+
}
|
|
232
|
+
function pageIntegrityError(message, cause) {
|
|
233
|
+
const error = new Error(`ACI S3 page integrity error: ${message}`);
|
|
234
|
+
if (cause !== undefined)
|
|
235
|
+
error.cause = cause;
|
|
236
|
+
return error;
|
|
237
|
+
}
|
|
238
|
+
async function safeResponseText(response) {
|
|
239
|
+
try {
|
|
240
|
+
return await response.text();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
95
245
|
}
|
|
96
246
|
// ---------------------------------------------------------------------------
|
|
97
247
|
// S3 fetch with AWS SigV4 signing
|
|
@@ -278,3 +428,7 @@ function joinKey(...parts) {
|
|
|
278
428
|
.filter(Boolean)
|
|
279
429
|
.join('/');
|
|
280
430
|
}
|
|
431
|
+
function joinManifestContentKey(prefix, contentRef) {
|
|
432
|
+
const normalizedPrefix = joinKey(prefix);
|
|
433
|
+
return normalizedPrefix ? `${normalizedPrefix}/${contentRef}` : contentRef;
|
|
434
|
+
}
|
|
@@ -5,7 +5,6 @@ export interface BlockRefMeta {
|
|
|
5
5
|
multiple: boolean;
|
|
6
6
|
}
|
|
7
7
|
declare const blockRefBase: z.ZodObject<{
|
|
8
|
-
$type: z.ZodLiteral<"component">;
|
|
9
8
|
id: z.ZodString;
|
|
10
9
|
component: z.ZodString;
|
|
11
10
|
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -13,7 +12,6 @@ declare const blockRefBase: z.ZodObject<{
|
|
|
13
12
|
type InferContractProps<T> = T extends ComponentContract<string, infer Schema> ? Schema extends z.ZodType<infer P> ? P : Record<string, unknown> : Record<string, unknown>;
|
|
14
13
|
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
15
14
|
export type BlockRefEntry<T extends ComponentContract> = {
|
|
16
|
-
$type: 'component';
|
|
17
15
|
id: string;
|
|
18
16
|
component: InferContractId<T>;
|
|
19
17
|
props: InferContractProps<T>;
|
package/dist/types/block-ref.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/gradial/bare-metal-go.git",
|
|
@@ -91,6 +91,11 @@
|
|
|
91
91
|
"types": "./dist/next/dev-refresh.d.ts",
|
|
92
92
|
"import": "./dist/next/dev-refresh.js"
|
|
93
93
|
},
|
|
94
|
+
"./next/error-seat": {
|
|
95
|
+
"types": "./dist/next/error-seat.d.ts",
|
|
96
|
+
"import": "./dist/next/error-seat.js",
|
|
97
|
+
"default": "./dist/next/error-seat.js"
|
|
98
|
+
},
|
|
94
99
|
"./next/server": {
|
|
95
100
|
"types": "./dist/next/server.d.ts",
|
|
96
101
|
"import": "./dist/next/server.js",
|
|
@@ -114,9 +119,10 @@
|
|
|
114
119
|
}
|
|
115
120
|
},
|
|
116
121
|
"scripts": {
|
|
117
|
-
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/build-collect.mjs",
|
|
118
|
-
"test": "node --import tsx --test \"src/**/*.test.ts\"",
|
|
122
|
+
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/assert-render-proof-public-surface.mjs && node scripts/build-collect.mjs",
|
|
123
|
+
"test": "node --import tsx --test \"src/**/*.test.ts\" \"src/**/*.test.tsx\"",
|
|
119
124
|
"build:collect": "node scripts/build-collect.mjs",
|
|
125
|
+
"generate-default-error-page": "node scripts/generate-default-error-page.mjs",
|
|
120
126
|
"build:cli": "cd ../.. && ./scripts/build-cli.sh",
|
|
121
127
|
"generate-starters": "cd ../.. && tsx scripts/generate-starters.ts",
|
|
122
128
|
"compile-registry": "tsx src/cli/compile-registry.mjs",
|
|
@@ -164,11 +170,11 @@
|
|
|
164
170
|
"zod": "^4.0.0"
|
|
165
171
|
},
|
|
166
172
|
"optionalDependencies": {
|
|
167
|
-
"@gradial/aci-cli-darwin-arm64": "0.1.
|
|
168
|
-
"@gradial/aci-cli-darwin-x64": "0.1.
|
|
169
|
-
"@gradial/aci-cli-linux-arm64": "0.1.
|
|
170
|
-
"@gradial/aci-cli-linux-x64": "0.1.
|
|
171
|
-
"@gradial/aci-cli-win32-arm64": "0.1.
|
|
172
|
-
"@gradial/aci-cli-win32-x64": "0.1.
|
|
173
|
+
"@gradial/aci-cli-darwin-arm64": "0.1.47",
|
|
174
|
+
"@gradial/aci-cli-darwin-x64": "0.1.47",
|
|
175
|
+
"@gradial/aci-cli-linux-arm64": "0.1.47",
|
|
176
|
+
"@gradial/aci-cli-linux-x64": "0.1.47",
|
|
177
|
+
"@gradial/aci-cli-win32-arm64": "0.1.47",
|
|
178
|
+
"@gradial/aci-cli-win32-x64": "0.1.47"
|
|
173
179
|
}
|
|
174
180
|
}
|
|
@@ -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';
|
|
@@ -20,15 +19,14 @@ try {
|
|
|
20
19
|
if (!globalThis.React) globalThis.React = React;
|
|
21
20
|
} catch { /* react not available — component files without JSX will still work */ }
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
22
|
+
const args = parseArgs(process.argv.slice(2));
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const result = await compileRegistry(args);
|
|
26
|
+
console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
29
|
+
process.exitCode = 1;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async function compileRegistry(options) {
|
|
@@ -432,10 +430,9 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
432
430
|
const defs = {
|
|
433
431
|
baseBlock: {
|
|
434
432
|
type: 'object',
|
|
435
|
-
required: ['
|
|
433
|
+
required: ['id', 'component', 'props'],
|
|
436
434
|
additionalProperties: true,
|
|
437
435
|
properties: {
|
|
438
|
-
$type: { const: 'component' },
|
|
439
436
|
id: { type: 'string', minLength: 1 },
|
|
440
437
|
component: { type: 'string' },
|
|
441
438
|
props: { type: 'object' },
|
|
@@ -744,14 +741,3 @@ function parseArgs(argv) {
|
|
|
744
741
|
}
|
|
745
742
|
return out;
|
|
746
743
|
}
|
|
747
|
-
|
|
748
|
-
function isMainModule() {
|
|
749
|
-
if (!process.argv[1]) return false;
|
|
750
|
-
try {
|
|
751
|
-
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
752
|
-
} catch {
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
export { compileRegistry, generatePageSchema };
|