@gradial/aci 0.1.48 → 0.1.51-ssr.2

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.
Files changed (109) hide show
  1. package/README.md +13 -20
  2. package/dist/astro/assets.d.ts +6 -6
  3. package/dist/astro/assets.js +6 -6
  4. package/dist/astro/index.d.ts +0 -2
  5. package/dist/astro/index.js +0 -4
  6. package/dist/cli/emit-error-pages.mjs +0 -1
  7. package/dist/cli/verify-next-cdn-routes.mjs +3 -4
  8. package/dist/collect/aci.js +1 -1
  9. package/dist/collect/aci.js.sha256 +1 -1
  10. package/dist/collect/webvitals.d.ts +0 -3
  11. package/dist/collect/webvitals.js +11 -21
  12. package/dist/content/cache.d.ts +1 -9
  13. package/dist/content/cache.js +2 -37
  14. package/dist/content/error-pages.d.ts +1 -2
  15. package/dist/content/index.d.ts +0 -1
  16. package/dist/content/index.js +0 -1
  17. package/dist/index.d.ts +0 -1
  18. package/dist/internal/domain/edge/identity.d.ts +14 -0
  19. package/dist/internal/domain/edge/identity.js +214 -0
  20. package/dist/internal/domain/edge/index.d.ts +4 -0
  21. package/dist/internal/domain/edge/index.js +6 -0
  22. package/dist/internal/domain/edge/personalization-core.d.ts +24 -0
  23. package/dist/{edge → internal/domain/edge}/personalization-core.js +165 -64
  24. package/dist/internal/domain/edge/record/codec.d.ts +71 -0
  25. package/dist/internal/domain/edge/record/codec.js +401 -0
  26. package/dist/internal/domain/edge/resolve.d.ts +2 -0
  27. package/dist/internal/domain/edge/resolve.js +76 -0
  28. package/dist/{next/personalization-host.d.ts → internal/domain/edge/types.d.ts} +110 -61
  29. package/dist/internal/domain/edge/types.js +7 -0
  30. package/dist/internal/platform/collectionstore/errors.d.ts +5 -0
  31. package/dist/internal/platform/collectionstore/errors.js +7 -0
  32. package/dist/next/config.d.ts +0 -8
  33. package/dist/next/config.js +3 -78
  34. package/dist/next/error-pages.d.ts +2 -3
  35. package/dist/next/error-pages.js +5 -5
  36. package/dist/next/error-seat.d.ts +2 -3
  37. package/dist/next/index.d.ts +2 -2
  38. package/dist/next/index.js +1 -2
  39. package/dist/next/page.d.ts +3 -13
  40. package/dist/next/page.js +39 -51
  41. package/dist/next/server.d.ts +12 -32
  42. package/dist/next/server.js +66 -295
  43. package/dist/providers/object.d.ts +24 -0
  44. package/dist/providers/object.js +105 -0
  45. package/dist/providers/vercel-object-reader.d.ts +20 -0
  46. package/dist/providers/vercel-object-reader.js +45 -0
  47. package/dist/testing/conformance/checks.d.ts +1 -1
  48. package/dist/testing/conformance/checks.js +14 -144
  49. package/dist/testing/conformance/index.d.ts +1 -1
  50. package/dist/testing/conformance/index.js +1 -1
  51. package/dist/testing/conformance/preset.js +1 -1
  52. package/dist/testing/conformance/types.d.ts +1 -1
  53. package/dist/types/config.d.ts +0 -4
  54. package/dist/types/index.d.ts +0 -2
  55. package/dist/types/index.js +0 -2
  56. package/dist/types/page.d.ts +0 -10
  57. package/package.json +11 -35
  58. package/src/cli/emit-error-pages.mjs +0 -1
  59. package/src/cli/verify-next-cdn-routes.mjs +8 -11
  60. package/src/types/config.ts +0 -5
  61. package/src/types/index.ts +0 -2
  62. package/src/types/page.ts +0 -11
  63. package/templates/astro/template/.aci.yaml.tmpl +0 -11
  64. package/templates/common/.agents/skills/aci/SKILL.md +8 -9
  65. package/templates/common/.agents/skills/authoring/SKILL.md +3 -4
  66. package/templates/common/.agents/skills/authoring/references/block-registry.md +13 -36
  67. package/templates/common/.agents/skills/authoring/references/contracts.md +10 -17
  68. package/templates/common/.agents/skills/authoring/references/repo-structure.md +2 -2
  69. package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +45 -95
  70. package/templates/common/.agents/skills/migration/references/canon-migration.md +17 -37
  71. package/templates/nextjs/template/.aci.yaml.tmpl +0 -11
  72. package/templates/nextjs/template/README.md.tmpl +0 -1
  73. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +1 -1
  74. package/dist/content/assets.d.ts +0 -18
  75. package/dist/content/assets.js +0 -66
  76. package/dist/edge/personalization-core.d.ts +0 -11
  77. package/dist/internal/render-proof.d.ts +0 -1
  78. package/dist/internal/render-proof.js +0 -197
  79. package/dist/next/asset-route.d.ts +0 -10
  80. package/dist/next/asset-route.js +0 -45
  81. package/dist/next/collect-route.d.ts +0 -19
  82. package/dist/next/collect-route.js +0 -422
  83. package/dist/next/edge-config.d.ts +0 -1
  84. package/dist/next/edge-config.js +0 -92
  85. package/dist/next/envelope-digest.d.ts +0 -21
  86. package/dist/next/envelope-digest.js +0 -58
  87. package/dist/next/middleware.d.ts +0 -40
  88. package/dist/next/middleware.js +0 -462
  89. package/dist/next/personalization-host.js +0 -257
  90. package/dist/next/vercel-collect-sdk-route.d.ts +0 -27
  91. package/dist/next/vercel-collect-sdk-route.js +0 -181
  92. package/dist/next/vercel-edge-projection-v1.d.ts +0 -24
  93. package/dist/next/vercel-edge-projection-v1.js +0 -880
  94. package/dist/next/vercel-personalization.d.ts +0 -4
  95. package/dist/next/vercel-personalization.js +0 -4
  96. package/dist/next/vercel-s3-personalization.d.ts +0 -79
  97. package/dist/next/vercel-s3-personalization.js +0 -570
  98. package/dist/providers/s3.d.ts +0 -47
  99. package/dist/providers/s3.js +0 -434
  100. package/dist/types/render-mode.d.ts +0 -2
  101. package/dist/types/render-mode.js +0 -1
  102. package/dist/types/renderer.d.ts +0 -71
  103. package/dist/types/renderer.js +0 -1
  104. package/src/types/render-mode.ts +0 -10
  105. package/src/types/renderer.ts +0 -83
  106. package/templates/nextjs/starter-content/src/middleware.ts +0 -41
  107. package/templates/nextjs/template/src/app/gradial/assets/[...path]/route.ts +0 -1
  108. package/templates/nextjs/template/src/middleware.ts +0 -41
  109. package/templates/nextjs/template/src/middleware.ts.tmpl +0 -40
@@ -1,47 +0,0 @@
1
- import { type CompiledManifest, type ContentProvider, type RouteEntry } from '../content/provider.js';
2
- export interface S3ContentProviderConfig {
3
- bucket?: string;
4
- keyPrefix?: string;
5
- region?: string;
6
- accessKeyId?: string;
7
- secretAccessKey?: string;
8
- sessionToken?: string;
9
- roleArn?: string;
10
- endpoint?: string;
11
- }
12
- type ResolvedS3Config = {
13
- bucket: string;
14
- keyPrefix: string;
15
- region: string;
16
- accessKeyId: string;
17
- secretAccessKey: string;
18
- sessionToken: string;
19
- roleArn: string;
20
- endpoint: string;
21
- };
22
- export interface S3CommandClient {
23
- send(command: {
24
- input: unknown;
25
- }): Promise<any>;
26
- }
27
- export declare class S3ContentProvider implements ContentProvider {
28
- #private;
29
- readonly config: ResolvedS3Config;
30
- constructor(config?: S3ContentProviderConfig, client?: S3CommandClient);
31
- listRoutes(): Promise<RouteEntry[]>;
32
- getSiteConfig<T = unknown>(): Promise<T>;
33
- getPage<T = unknown>(route: string): Promise<T>;
34
- /**
35
- * getPageByRef serves an exact payload the manifest declares. The stricter
36
- * route-bound selection used by server rendering stays package-internal.
37
- */
38
- getPageByRef<T = unknown>(payloadRef: string): Promise<T>;
39
- getFragment<T = unknown>(id: string): Promise<T>;
40
- getLayout<T = unknown>(name: string): Promise<T>;
41
- manifest(): Promise<CompiledManifest>;
42
- fetchRaw(key: string): Promise<Response>;
43
- private getJSON;
44
- }
45
- export declare function getS3JSON<T>(key: string, config: ResolvedS3Config, client?: S3CommandClient): Promise<T>;
46
- export declare function getS3Raw(key: string, config: ResolvedS3Config, client?: S3CommandClient): Promise<Response>;
47
- export {};
@@ -1,434 +0,0 @@
1
- import crypto from 'node:crypto';
2
- import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
3
- import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
4
- import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
5
- import { normalizeRoute } from '../content/routes.js';
6
- import { createVerifiedPageReceipt, selectVerifiedPage, validateVerifiedPageReceipt, } from '../internal/render-proof.js';
7
- // ---------------------------------------------------------------------------
8
- // S3 content provider — reads compiled content files from S3
9
- // ---------------------------------------------------------------------------
10
- export class S3ContentProvider {
11
- config;
12
- #manifest;
13
- #client;
14
- constructor(config = {}, client) {
15
- this.config = resolveS3Config(config);
16
- this.#client = client || oidcS3Client(this.config);
17
- }
18
- async listRoutes() {
19
- const manifest = await this.manifest();
20
- return Object.values(manifest.routes).sort((a, b) => a.path.localeCompare(b.path));
21
- }
22
- async getSiteConfig() {
23
- const manifest = await this.manifest();
24
- return await this.getJSON(manifest.siteConfigRef);
25
- }
26
- async getPage(route) {
27
- return (await this.#selectPage({
28
- route,
29
- contentRef: '',
30
- contentRefPresent: false,
31
- variantKey: '',
32
- variantKeyPresent: false,
33
- })).page;
34
- }
35
- /**
36
- * getPageByRef serves an exact payload the manifest declares. The stricter
37
- * route-bound selection used by server rendering stays package-internal.
38
- */
39
- async getPageByRef(payloadRef) {
40
- const manifest = await this.manifest();
41
- const known = Object.values(manifest.routes).some((entry) => routeContainsRef(entry, payloadRef));
42
- if (!known) {
43
- throw new PageNotFoundError(payloadRef);
44
- }
45
- try {
46
- return await this.getJSON(payloadRef);
47
- }
48
- catch (error) {
49
- throw new PageNotFoundError(payloadRef, error);
50
- }
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
- }
66
- async getFragment(id) {
67
- const manifest = await this.manifest();
68
- const entry = manifest.fragments[id];
69
- if (!entry) {
70
- throw new FragmentNotFoundError(id);
71
- }
72
- try {
73
- return await this.getJSON(entry.payloadRef);
74
- }
75
- catch (error) {
76
- throw new FragmentNotFoundError(id, error);
77
- }
78
- }
79
- async getLayout(name) {
80
- const manifest = await this.manifest();
81
- const layouts = manifest.layouts;
82
- if (!layouts || !layouts[name]) {
83
- throw new LayoutNotFoundError(name);
84
- }
85
- try {
86
- return await this.getJSON(`layouts/${name}.json`);
87
- }
88
- catch (error) {
89
- throw new LayoutNotFoundError(name, error);
90
- }
91
- }
92
- async manifest() {
93
- this.#manifest ??= this.getJSON('manifest.json');
94
- return await this.#manifest;
95
- }
96
- async fetchRaw(key) {
97
- return await getS3Raw(key, this.config, this.#client);
98
- }
99
- async getJSON(key) {
100
- return await getS3JSON(joinKey(this.config.keyPrefix, key), this.config, this.#client);
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
- }
245
- }
246
- // ---------------------------------------------------------------------------
247
- // S3 fetch with AWS SigV4 signing
248
- // ---------------------------------------------------------------------------
249
- export async function getS3JSON(key, config, client) {
250
- const startTime = Date.now();
251
- const res = await getS3Raw(key, config, client);
252
- const duration = Date.now() - startTime;
253
- console.log(`[ACI S3] GET ${key} - ${res.status} in ${duration}ms`);
254
- if (!res.ok) {
255
- throw new Error(`S3 GET ${key} failed with status ${res.status}: ${await res.text()}`);
256
- }
257
- return (await res.json());
258
- }
259
- export async function getS3Raw(key, config, client) {
260
- if (client)
261
- return await getS3RawWithClient(key, config, client);
262
- const url = s3ObjectURL(config, key);
263
- const signedHeaders = signedS3Headers('GET', url, config);
264
- return await fetch(url, {
265
- method: 'GET',
266
- headers: signedHeaders,
267
- cache: 'no-store'
268
- });
269
- }
270
- // ---------------------------------------------------------------------------
271
- // Internal helpers
272
- // ---------------------------------------------------------------------------
273
- function resolveS3Config(config) {
274
- const resolved = {
275
- bucket: configuredValue(config.bucket, process.env.ACI_S3_BUCKET),
276
- keyPrefix: configuredValue(config.keyPrefix, process.env.ACI_S3_KEY_PREFIX),
277
- region: configuredValue(config.region, process.env.AWS_REGION) || 'us-east-1',
278
- accessKeyId: configuredValue(config.accessKeyId, process.env.AWS_ACCESS_KEY_ID),
279
- secretAccessKey: configuredValue(config.secretAccessKey, process.env.AWS_SECRET_ACCESS_KEY),
280
- sessionToken: configuredValue(config.sessionToken, process.env.AWS_SESSION_TOKEN),
281
- roleArn: configuredValue(config.roleArn, process.env.AWS_ROLE_ARN),
282
- endpoint: trimSlash(configuredValue(config.endpoint, process.env.ACI_S3_ENDPOINT))
283
- };
284
- if (!resolved.bucket)
285
- throw new Error('S3 config missing bucket');
286
- if (!resolved.roleArn) {
287
- if (resolved.accessKeyId && !resolved.secretAccessKey) {
288
- throw new Error('S3 config missing secretAccessKey');
289
- }
290
- if (!resolved.accessKeyId && resolved.secretAccessKey) {
291
- throw new Error('S3 config missing accessKeyId');
292
- }
293
- if (!resolved.accessKeyId) {
294
- throw new Error('S3 config missing credential mode: set AWS_ROLE_ARN or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY');
295
- }
296
- }
297
- return resolved;
298
- }
299
- function oidcS3Client(config) {
300
- if (!config.roleArn)
301
- return undefined;
302
- return new S3Client({
303
- region: config.region,
304
- ...(config.endpoint ? {
305
- endpoint: config.endpoint,
306
- forcePathStyle: true,
307
- } : {}),
308
- credentials: awsCredentialsProvider({
309
- roleArn: config.roleArn,
310
- clientConfig: { region: config.region },
311
- }),
312
- });
313
- }
314
- async function getS3RawWithClient(key, config, client) {
315
- try {
316
- const output = await client.send(new GetObjectCommand({
317
- Bucket: config.bucket,
318
- Key: key,
319
- }));
320
- if (!output.Body?.transformToWebStream) {
321
- throw new Error(`S3 GET ${key} returned an unsupported object body`);
322
- }
323
- const headers = new Headers();
324
- if (output.ContentType)
325
- headers.set('content-type', output.ContentType);
326
- if (output.ContentLength !== undefined) {
327
- headers.set('content-length', String(output.ContentLength));
328
- }
329
- if (output.ETag)
330
- headers.set('etag', output.ETag);
331
- if (output.LastModified) {
332
- headers.set('last-modified', output.LastModified.toUTCString());
333
- }
334
- return new Response(output.Body.transformToWebStream(), {
335
- status: 200,
336
- headers,
337
- });
338
- }
339
- catch (error) {
340
- const status = s3ErrorStatus(error);
341
- if (status) {
342
- return new Response(error instanceof Error ? error.message : String(error), { status });
343
- }
344
- throw new Error(`S3 GET ${key} failed`, { cause: error });
345
- }
346
- }
347
- function s3ErrorStatus(error) {
348
- if (!error || typeof error !== 'object')
349
- return undefined;
350
- const status = error
351
- .$metadata?.httpStatusCode;
352
- return typeof status === 'number' && status >= 400 && status <= 599
353
- ? status
354
- : undefined;
355
- }
356
- function configuredValue(configured, environment) {
357
- return (configured === undefined ? environment : configured)?.trim() || '';
358
- }
359
- function s3ObjectURL(config, key) {
360
- const encodedKey = encodeKey(key);
361
- if (config.endpoint) {
362
- return new URL(`${config.endpoint}/${encodeURIComponent(config.bucket)}${encodedKey}`);
363
- }
364
- return new URL(`https://${config.bucket}.s3.${config.region}.amazonaws.com${encodedKey}`);
365
- }
366
- function signedS3Headers(method, url, config) {
367
- const now = new Date();
368
- const amzDate = amzTimestamp(now);
369
- const dateStamp = amzDate.slice(0, 8);
370
- const payloadHash = sha256Hex('');
371
- const headers = new Headers({
372
- host: url.host,
373
- 'x-amz-content-sha256': payloadHash,
374
- 'x-amz-date': amzDate
375
- });
376
- if (config.sessionToken)
377
- headers.set('x-amz-security-token', config.sessionToken);
378
- const signedHeaderNames = Array.from(headers.keys()).sort();
379
- const canonicalHeaders = signedHeaderNames.map((k) => `${k}:${headers.get(k)?.trim()}\n`).join('');
380
- const canonicalRequest = [
381
- method,
382
- url.pathname,
383
- url.searchParams.toString(),
384
- canonicalHeaders,
385
- signedHeaderNames.join(';'),
386
- payloadHash
387
- ].join('\n');
388
- const credentialScope = `${dateStamp}/${config.region}/s3/aws4_request`;
389
- const stringToSign = [
390
- 'AWS4-HMAC-SHA256',
391
- amzDate,
392
- credentialScope,
393
- sha256Hex(canonicalRequest)
394
- ].join('\n');
395
- const signingKey = awsSigningKey(config.secretAccessKey, dateStamp, config.region, 's3');
396
- const signature = hmacHex(signingKey, stringToSign);
397
- headers.set('authorization', `AWS4-HMAC-SHA256 Credential=${config.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaderNames.join(';')}, Signature=${signature}`);
398
- return headers;
399
- }
400
- function awsSigningKey(secret, dateStamp, region, service) {
401
- const kDate = hmac(Buffer.from(`AWS4${secret}`, 'utf8'), dateStamp);
402
- const kRegion = hmac(kDate, region);
403
- const kService = hmac(kRegion, service);
404
- return hmac(kService, 'aws4_request');
405
- }
406
- function amzTimestamp(date) {
407
- return date.toISOString().replace(/[:-]|\.\d{3}/g, '');
408
- }
409
- function hmac(key, value) {
410
- return crypto.createHmac('sha256', key).update(value, 'utf8').digest();
411
- }
412
- function hmacHex(key, value) {
413
- return crypto.createHmac('sha256', key).update(value, 'utf8').digest('hex');
414
- }
415
- function sha256Hex(value) {
416
- return crypto.createHash('sha256').update(value, 'utf8').digest('hex');
417
- }
418
- function trimSlash(value) {
419
- return value.replace(/\/+$/, '');
420
- }
421
- function encodeKey(key) {
422
- return `/${key.split('/').map(encodeURIComponent).join('/')}`;
423
- }
424
- function joinKey(...parts) {
425
- return parts
426
- .flatMap((part) => part.split('/'))
427
- .map((part) => part.trim())
428
- .filter(Boolean)
429
- .join('/');
430
- }
431
- function joinManifestContentKey(prefix, contentRef) {
432
- const normalizedPrefix = joinKey(prefix);
433
- return normalizedPrefix ? `${normalizedPrefix}/${contentRef}` : contentRef;
434
- }
@@ -1,2 +0,0 @@
1
- export type IslandMode = 'ssr' | 'client';
2
- export type VaryDimension = 'geo:country' | 'geo:region' | 'geo:city' | `cookie:${string}` | `header:${string}` | 'locale' | `query:${string}`;
@@ -1 +0,0 @@
1
- export {};
@@ -1,71 +0,0 @@
1
- import type { PageDocument } from './page.js';
2
- import type { VaryDimension } from './render-mode.js';
3
- export interface RenderCapabilities {
4
- staticRender: boolean;
5
- ssr: boolean;
6
- ssrIslands: boolean;
7
- clientIslands: boolean;
8
- fragmentRender: boolean;
9
- }
10
- export interface Renderer {
11
- renderPage(request: RenderPageRequest): Promise<RenderPageResult>;
12
- renderFragment?(request: RenderFragmentRequest): Promise<RenderResult>;
13
- renderIsland?(request: RenderIslandRequest): Promise<RenderResult>;
14
- }
15
- export interface RenderPageRequest {
16
- page: PageDocument;
17
- requestContext?: RequestContext;
18
- release: ReleaseContext;
19
- }
20
- export interface RequestContext {
21
- url: string;
22
- method: string;
23
- headers: Record<string, string>;
24
- cookies: Record<string, string>;
25
- geo?: GeoContext;
26
- }
27
- export interface GeoContext {
28
- country?: string;
29
- region?: string;
30
- city?: string;
31
- }
32
- export interface ReleaseContext {
33
- releaseId: string;
34
- codeDigest: string;
35
- contentSnapshotId: string;
36
- assetUrl(logicalPath: string): string;
37
- }
38
- export interface RenderFragmentRequest {
39
- fragmentId: string;
40
- content: unknown;
41
- release: ReleaseContext;
42
- }
43
- export interface RenderIslandRequest {
44
- islandId: string;
45
- component: string;
46
- props: Record<string, unknown>;
47
- requestContext: RequestContext;
48
- release: ReleaseContext;
49
- }
50
- export interface RenderResult {
51
- html: string;
52
- status?: number;
53
- headers?: Record<string, string>;
54
- cachePolicy?: CachePolicy;
55
- }
56
- export interface RenderPageResult extends RenderResult {
57
- islands?: IslandDescriptor[];
58
- }
59
- export interface IslandDescriptor {
60
- islandId: string;
61
- component: string;
62
- props: Record<string, unknown>;
63
- mode: 'ssr' | 'client';
64
- varyDimensions?: VaryDimension[];
65
- }
66
- export interface CachePolicy {
67
- scope: 'public' | 'private' | 'no-store';
68
- ttl?: number;
69
- staleWhileRevalidate?: number;
70
- tags?: string[];
71
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,10 +0,0 @@
1
- export type IslandMode = 'ssr' | 'client';
2
-
3
- export type VaryDimension =
4
- | 'geo:country'
5
- | 'geo:region'
6
- | 'geo:city'
7
- | `cookie:${string}`
8
- | `header:${string}`
9
- | 'locale'
10
- | `query:${string}`;
@@ -1,83 +0,0 @@
1
- import type { PageDocument } from './page.js';
2
- import type { VaryDimension } from './render-mode.js';
3
-
4
- export interface RenderCapabilities {
5
- staticRender: boolean;
6
- ssr: boolean;
7
- ssrIslands: boolean;
8
- clientIslands: boolean;
9
- fragmentRender: boolean;
10
- }
11
-
12
- export interface Renderer {
13
- renderPage(request: RenderPageRequest): Promise<RenderPageResult>;
14
- renderFragment?(request: RenderFragmentRequest): Promise<RenderResult>;
15
- renderIsland?(request: RenderIslandRequest): Promise<RenderResult>;
16
- }
17
-
18
- export interface RenderPageRequest {
19
- page: PageDocument;
20
- requestContext?: RequestContext;
21
- release: ReleaseContext;
22
- }
23
-
24
- export interface RequestContext {
25
- url: string;
26
- method: string;
27
- headers: Record<string, string>;
28
- cookies: Record<string, string>;
29
- geo?: GeoContext;
30
- }
31
-
32
- export interface GeoContext {
33
- country?: string;
34
- region?: string;
35
- city?: string;
36
- }
37
-
38
- export interface ReleaseContext {
39
- releaseId: string;
40
- codeDigest: string;
41
- contentSnapshotId: string;
42
- assetUrl(logicalPath: string): string;
43
- }
44
-
45
- export interface RenderFragmentRequest {
46
- fragmentId: string;
47
- content: unknown;
48
- release: ReleaseContext;
49
- }
50
-
51
- export interface RenderIslandRequest {
52
- islandId: string;
53
- component: string;
54
- props: Record<string, unknown>;
55
- requestContext: RequestContext;
56
- release: ReleaseContext;
57
- }
58
-
59
- export interface RenderResult {
60
- html: string;
61
- status?: number;
62
- headers?: Record<string, string>;
63
- cachePolicy?: CachePolicy;
64
- }
65
-
66
- export interface RenderPageResult extends RenderResult {
67
- islands?: IslandDescriptor[];
68
- }
69
-
70
- export interface IslandDescriptor {
71
- islandId: string;
72
- component: string;
73
- props: Record<string, unknown>;
74
- mode: 'ssr' | 'client';
75
- varyDimensions?: VaryDimension[];
76
- }
77
-
78
- export interface CachePolicy {
79
- scope: 'public' | 'private' | 'no-store';
80
- ttl?: number;
81
- staleWhileRevalidate?: number;
82
- tags?: string[];
83
- }
@@ -1,41 +0,0 @@
1
- import { createGradialMiddleware } from '@gradial/aci/next/middleware';
2
- import { createVercelPersonalizationFromEnv } from '@gradial/aci/next/vercel-personalization';
3
-
4
- export default createGradialMiddleware({
5
- personalization: createVercelPersonalizationFromEnv(),
6
- });
7
-
8
- export const config = {
9
- runtime: 'nodejs',
10
- matcher: [
11
- '/((?!_next(?:/|$)|api(?:/|$)|collect(?:/|$)|gradial/assets(?:/|$)|favicon\\.ico$).*)',
12
- {
13
- source: '/:path*',
14
- has: [{ type: 'header', key: 'x-gradial-framework-origin-auth' }],
15
- },
16
- {
17
- source: '/:path*',
18
- has: [{ type: 'header', key: 'x-baremetal-site-id' }],
19
- },
20
- {
21
- source: '/:path*',
22
- has: [{ type: 'header', key: 'x-baremetal-release-id' }],
23
- },
24
- {
25
- source: '/:path*',
26
- has: [{ type: 'header', key: 'x-aci-content-ref' }],
27
- },
28
- {
29
- source: '/:path*',
30
- has: [{ type: 'header', key: 'x-baremetal-content-base-url' }],
31
- },
32
- {
33
- source: '/:path*',
34
- has: [{ type: 'header', key: 'x-aci-content-path' }],
35
- },
36
- {
37
- source: '/:path*',
38
- has: [{ type: 'query', key: '__aci_content_path' }],
39
- },
40
- ],
41
- };