@gradial/aci 0.1.13 → 0.1.15

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.
@@ -1,8 +1,7 @@
1
- import { type NextRequest } from 'next/server.js';
1
+ import { NextResponse, type NextRequest } from 'next/server.js';
2
2
  export interface GradialMiddlewareConfig {
3
3
  siteId: string;
4
4
  edgeConfig?: string;
5
- previewSignKey?: string;
6
5
  deploymentId?: string;
7
6
  }
8
- export declare function createGradialMiddleware(config: GradialMiddlewareConfig): (request: NextRequest) => Promise<Response>;
7
+ export declare function createGradialMiddleware(config: GradialMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
@@ -7,7 +7,15 @@ const DISPATCH_HEADER = 'x-gradial-dispatch';
7
7
  export function createGradialMiddleware(config) {
8
8
  return async function gradialMiddleware(request) {
9
9
  const url = request.nextUrl;
10
+ console.log('[gradial/mw] === middleware start ===');
11
+ console.log('[gradial/mw] path:', url.pathname, 'search:', url.search.slice(0, 100));
12
+ console.log('[gradial/mw] siteId:', config.siteId);
13
+ console.log('[gradial/mw] EDGE_CONFIG:', process.env.EDGE_CONFIG ? 'set' : 'NOT SET');
14
+ console.log('[gradial/mw] GRADIAL_PREVIEW_SIGN_KEY:', process.env.GRADIAL_PREVIEW_SIGN_KEY ? `set (${process.env.GRADIAL_PREVIEW_SIGN_KEY.length} chars, prefix: ${process.env.GRADIAL_PREVIEW_SIGN_KEY.slice(0, 6)})` : 'NOT SET');
15
+ console.log('[gradial/mw] VERCEL_DEPLOYMENT_ID:', process.env.VERCEL_DEPLOYMENT_ID || 'NOT SET');
16
+ // ?leave_preview=1 → clear cookies, redirect to clean URL
10
17
  if (url.searchParams.get('leave_preview') === '1') {
18
+ console.log('[gradial/mw] leave_preview=1, clearing cookies and redirecting');
11
19
  const cleanUrl = new URL(url.pathname, url.origin);
12
20
  const response = NextResponse.redirect(cleanUrl);
13
21
  response.cookies.delete('aci_preview');
@@ -15,36 +23,45 @@ export function createGradialMiddleware(config) {
15
23
  return response;
16
24
  }
17
25
  const requestHeaders = gradialHeaders(request.headers);
18
- const preview = await releaseFromPreviewToken(request, config);
19
- if (preview.invalid) {
20
- return new Response('invalid preview token', { status: 403 });
21
- }
22
- const releaseId = preview.releaseId || await activeReleaseFromEdgeConfig(config);
26
+ // Try preview token first; if valid, use that release.
27
+ // If no token or invalid, fall through to active release from edge config.
28
+ const previewReleaseId = await releaseIdFromPreviewToken(request, config);
29
+ console.log('[gradial/mw] previewReleaseId from token:', previewReleaseId || '(none)');
30
+ const releaseId = previewReleaseId || await activeReleaseFromEdgeConfig(config);
31
+ console.log('[gradial/mw] final releaseId:', releaseId || '(none)');
23
32
  if (!releaseId) {
33
+ console.log('[gradial/mw] no releaseId, passing through with no headers');
24
34
  return NextResponse.next({ request: { headers: requestHeaders } });
25
35
  }
26
36
  requestHeaders.set(RELEASE_HEADER, releaseId);
27
37
  requestHeaders.set(ROUTE_HEADER, url.pathname);
28
38
  requestHeaders.set(DISPATCH_HEADER, 'ssr-page');
29
- if (preview.releaseId) {
39
+ if (previewReleaseId) {
30
40
  requestHeaders.set('x-gradial-preview', '1');
41
+ console.log('[gradial/mw] set x-gradial-preview=1 and x-gradial-release-id:', releaseId);
42
+ }
43
+ else {
44
+ console.log('[gradial/mw] set x-gradial-release-id (active):', releaseId);
31
45
  }
32
46
  const response = NextResponse.next({ request: { headers: requestHeaders } });
47
+ // Persist preview token as cookie so navigation stays in preview
33
48
  const queryToken = url.searchParams.get('preview_token');
34
- if (queryToken && preview.releaseId) {
49
+ if (queryToken && previewReleaseId) {
50
+ console.log('[gradial/mw] setting preview cookies for release:', previewReleaseId);
35
51
  response.cookies.set('aci_preview', queryToken, {
36
52
  httpOnly: true,
37
53
  sameSite: 'lax',
38
54
  maxAge: 60 * 60,
39
55
  path: '/',
40
56
  });
41
- response.cookies.set('aci_preview_release', preview.releaseId, {
57
+ response.cookies.set('aci_preview_release', previewReleaseId, {
42
58
  httpOnly: false,
43
59
  sameSite: 'lax',
44
60
  maxAge: 60 * 60,
45
61
  path: '/',
46
62
  });
47
63
  }
64
+ console.log('[gradial/mw] === middleware end ===');
48
65
  return response;
49
66
  };
50
67
  }
@@ -57,15 +74,19 @@ function gradialHeaders(headers) {
57
74
  }
58
75
  async function activeReleaseFromEdgeConfig(config) {
59
76
  const edgeConfig = config.edgeConfig || process.env.EDGE_CONFIG || '';
60
- if (!edgeConfig || !config.siteId)
77
+ if (!edgeConfig || !config.siteId) {
78
+ console.log('[gradial/mw] activeRelease: no edgeConfig or siteId, skipping');
61
79
  return '';
80
+ }
62
81
  const active = await activeReleasePointer(edgeConfig, config.siteId);
82
+ console.log('[gradial/mw] activeRelease pointer:', JSON.stringify(active));
63
83
  if (!active.releaseId)
64
84
  return '';
65
85
  const deploymentId = config.deploymentId || process.env.VERCEL_DEPLOYMENT_ID || '';
66
86
  if (!deploymentId)
67
87
  return active.releaseId;
68
88
  const compatibility = await getEdgeConfigJSON(edgeConfig, deploymentCompatibilityKey(config.siteId, deploymentId));
89
+ console.log('[gradial/mw] compatibility:', compatibility ? JSON.stringify(compatibility) : '(none)');
69
90
  if (!compatibility)
70
91
  return active.releaseId;
71
92
  return clampRelease(active.releaseId, active.sequence || 0, compatibility);
@@ -125,23 +146,34 @@ function clampRelease(activeReleaseId, activeSequence, compatibility) {
125
146
  function edgeKeySegment(value) {
126
147
  return value.replace(/[^A-Za-z0-9_-]/g, '_');
127
148
  }
128
- async function releaseFromPreviewToken(request, config) {
149
+ async function releaseIdFromPreviewToken(request, config) {
129
150
  const token = previewTokenFromRequest(request);
151
+ console.log('[gradial/mw] previewToken: token found:', !!token, 'len:', token.length);
130
152
  if (!token)
131
- return { releaseId: '', invalid: false };
132
- const signKey = config.previewSignKey || process.env.ACI_PREVIEW_SIGN_KEY || process.env.GRADIAL_PREVIEW_SIGN_KEY || '';
153
+ return '';
154
+ const signKey = process.env.GRADIAL_PREVIEW_SIGN_KEY || '';
133
155
  if (!signKey) {
134
- console.warn('[gradial] preview sign key not set (ACI_PREVIEW_SIGN_KEY or GRADIAL_PREVIEW_SIGN_KEY) preview token ignored');
135
- return { releaseId: '', invalid: false };
156
+ console.warn('[gradial/mw] GRADIAL_PREVIEW_SIGN_KEY not set preview tokens will be ignored');
157
+ return '';
136
158
  }
159
+ console.log('[gradial/mw] signKey present, len:', signKey.length, 'prefix:', signKey.slice(0, 6));
160
+ console.log('[gradial/mw] calling validatePreviewToken...');
137
161
  const claims = await validatePreviewToken(token, signKey);
138
- if (!claims || claims.scope !== 'preview' || !claims.releaseId)
139
- return { releaseId: '', invalid: true };
140
- if (claims.siteId && config.siteId && claims.siteId !== config.siteId)
141
- return { releaseId: '', invalid: true };
142
- if (!claims.expiresAt || Date.parse(claims.expiresAt) <= Date.now())
143
- return { releaseId: '', invalid: true };
144
- return { releaseId: claims.releaseId, invalid: false };
162
+ console.log('[gradial/mw] validatePreviewToken result:', claims ? JSON.stringify(claims) : 'null');
163
+ if (!claims || claims.scope !== 'preview' || !claims.releaseId) {
164
+ console.log('[gradial/mw] claims invalid: scope=', claims?.scope, 'releaseId=', claims?.releaseId);
165
+ return '';
166
+ }
167
+ if (claims.siteId && config.siteId && claims.siteId !== config.siteId) {
168
+ console.log('[gradial/mw] siteId mismatch: token=', claims.siteId, 'config=', config.siteId);
169
+ return '';
170
+ }
171
+ if (!claims.expiresAt || Date.parse(claims.expiresAt) <= Date.now()) {
172
+ console.log('[gradial/mw] token expired or no expiresAt:', claims.expiresAt);
173
+ return '';
174
+ }
175
+ console.log('[gradial/mw] preview token valid! releaseId:', claims.releaseId);
176
+ return claims.releaseId;
145
177
  }
146
178
  function previewTokenFromRequest(request) {
147
179
  const queryToken = request.nextUrl.searchParams.get('preview_token');
@@ -1,18 +1,32 @@
1
1
  export async function validatePreviewToken(token, signKey) {
2
2
  try {
3
+ console.log('[gradial/preview] validatePreviewToken start, token len:', token.length, 'key len:', signKey.length);
3
4
  const parts = token.split('.');
4
- if (parts.length !== 3)
5
+ console.log('[gradial/preview] parts:', parts.length);
6
+ if (parts.length !== 3) {
7
+ console.log('[gradial/preview] invalid token structure, parts:', parts.length);
5
8
  return null;
9
+ }
6
10
  const signingInput = `${parts[0]}.${parts[1]}`;
7
11
  const signature = base64URLToBytes(parts[2]);
12
+ console.log('[gradial/preview] signature bytes:', signature.length);
8
13
  const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(signKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
9
14
  const expected = new Uint8Array(await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(signingInput)));
10
- if (!constantTimeEqual(signature, expected))
15
+ console.log('[gradial/preview] expected sig bytes:', expected.length);
16
+ console.log('[gradial/preview] sig match:', constantTimeEqual(signature, expected));
17
+ if (!constantTimeEqual(signature, expected)) {
18
+ console.log('[gradial/preview] signature mismatch!');
19
+ console.log('[gradial/preview] got sig:', Array.from(signature.slice(0, 8)).join(','));
20
+ console.log('[gradial/preview] exp sig:', Array.from(expected.slice(0, 8)).join(','));
11
21
  return null;
22
+ }
12
23
  const payload = new TextDecoder().decode(base64URLToBytes(parts[1]));
13
- return JSON.parse(payload);
24
+ const claims = JSON.parse(payload);
25
+ console.log('[gradial/preview] valid token, claims:', JSON.stringify(claims));
26
+ return claims;
14
27
  }
15
- catch {
28
+ catch (err) {
29
+ console.log('[gradial/preview] validation error:', String(err));
16
30
  return null;
17
31
  }
18
32
  }
@@ -74,11 +74,16 @@ export async function getPageRuntimeRenderInput() {
74
74
  export async function isPreviewMode() {
75
75
  try {
76
76
  const current = await headers();
77
- if (current.get(PREVIEW_HEADER) === '1') {
78
- return current.get(RELEASE_HEADER) || '';
77
+ const previewFlag = current.get(PREVIEW_HEADER);
78
+ const releaseId = current.get(RELEASE_HEADER);
79
+ console.log('[gradial/server] isPreviewMode: x-gradial-preview=', previewFlag, 'x-gradial-release-id=', releaseId || '(none)');
80
+ if (previewFlag === '1') {
81
+ return releaseId || '';
79
82
  }
80
83
  }
81
- catch { }
84
+ catch (err) {
85
+ console.log('[gradial/server] isPreviewMode error:', String(err));
86
+ }
82
87
  return '';
83
88
  }
84
89
  export async function routeFromNextParams(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradial/aci",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",