jamdesk 1.0.15 → 1.0.16
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/__tests__/unit/login.test.js +63 -20
- package/dist/__tests__/unit/login.test.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +9 -2
- package/dist/commands/login.js.map +1 -1
- package/package.json +1 -1
- package/vendored/app/api/og/route.tsx +64 -28
- package/vendored/lib/seo.ts +38 -12
|
@@ -18,6 +18,26 @@ const { mockOutput } = vi.hoisted(() => ({
|
|
|
18
18
|
vi.mock('../../lib/output.js', () => ({ output: mockOutput }));
|
|
19
19
|
vi.mock('open', () => ({ default: vi.fn() }));
|
|
20
20
|
import { login } from '../../commands/login.js';
|
|
21
|
+
function sendOptions(port) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const req = http.request({
|
|
24
|
+
hostname: '127.0.0.1',
|
|
25
|
+
port,
|
|
26
|
+
path: '/callback',
|
|
27
|
+
method: 'OPTIONS',
|
|
28
|
+
headers: {
|
|
29
|
+
Origin: 'https://dashboard.jamdesk.com',
|
|
30
|
+
'Access-Control-Request-Method': 'POST',
|
|
31
|
+
'Access-Control-Request-Private-Network': 'true',
|
|
32
|
+
},
|
|
33
|
+
}, (res) => {
|
|
34
|
+
res.resume();
|
|
35
|
+
res.on('end', () => resolve({ statusCode: res.statusCode || 0, headers: res.headers }));
|
|
36
|
+
});
|
|
37
|
+
req.on('error', reject);
|
|
38
|
+
req.end();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
21
41
|
function sendCallback(port, body) {
|
|
22
42
|
return new Promise((resolve, reject) => {
|
|
23
43
|
const data = JSON.stringify(body);
|
|
@@ -26,7 +46,11 @@ function sendCallback(port, body) {
|
|
|
26
46
|
port,
|
|
27
47
|
path: '/callback',
|
|
28
48
|
method: 'POST',
|
|
29
|
-
headers: {
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
'Content-Length': Buffer.byteLength(data),
|
|
52
|
+
Origin: 'https://dashboard.jamdesk.com',
|
|
53
|
+
},
|
|
30
54
|
}, (res) => {
|
|
31
55
|
res.resume();
|
|
32
56
|
res.on('end', () => resolve({ statusCode: res.statusCode || 0 }));
|
|
@@ -38,15 +62,22 @@ function sendCallback(port, body) {
|
|
|
38
62
|
}
|
|
39
63
|
/** Start login, extract port and state from the logged URL. */
|
|
40
64
|
async function startLoginAndGetParams() {
|
|
41
|
-
|
|
65
|
+
vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
42
66
|
const loginPromise = login();
|
|
43
67
|
await new Promise((r) => setTimeout(r, 200));
|
|
68
|
+
const consoleSpy = vi.mocked(console.log);
|
|
44
69
|
const urlLog = consoleSpy.mock.calls.find((c) => typeof c[0] === 'string' && c[0].includes('cli-auth'));
|
|
45
70
|
expect(urlLog).toBeTruthy();
|
|
46
71
|
const url = new URL(urlLog[0].trim());
|
|
47
72
|
const port = parseInt(url.searchParams.get('port'), 10);
|
|
48
73
|
const state = url.searchParams.get('state');
|
|
49
|
-
return { loginPromise, port, state
|
|
74
|
+
return { loginPromise, port, state };
|
|
75
|
+
}
|
|
76
|
+
/** Send a valid callback to shut down the login server. */
|
|
77
|
+
function shutdownServer(port, state) {
|
|
78
|
+
return sendCallback(port, {
|
|
79
|
+
token: 't', refreshToken: 't', email: 'e@e.com', uid: 'u', state, firebaseApiKey: 'k',
|
|
80
|
+
});
|
|
50
81
|
}
|
|
51
82
|
describe('login', () => {
|
|
52
83
|
beforeEach(() => {
|
|
@@ -87,15 +118,35 @@ describe('login', () => {
|
|
|
87
118
|
});
|
|
88
119
|
expect(response.statusCode).toBe(403);
|
|
89
120
|
expect(mockStoreAuth).not.toHaveBeenCalled();
|
|
90
|
-
|
|
91
|
-
await
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
121
|
+
await shutdownServer(port, state);
|
|
122
|
+
await loginPromise;
|
|
123
|
+
}, 10000);
|
|
124
|
+
it('responds to CORS preflight with PNA header', async () => {
|
|
125
|
+
const { loginPromise, port, state } = await startLoginAndGetParams();
|
|
126
|
+
const res = await sendOptions(port);
|
|
127
|
+
expect(res.statusCode).toBe(204);
|
|
128
|
+
expect(res.headers['access-control-allow-private-network']).toBe('true');
|
|
129
|
+
expect(res.headers['access-control-allow-origin']).toBe('https://dashboard.jamdesk.com');
|
|
130
|
+
await shutdownServer(port, state);
|
|
131
|
+
await loginPromise;
|
|
132
|
+
}, 10000);
|
|
133
|
+
it('rejects POST without Origin header', async () => {
|
|
134
|
+
const { loginPromise, port, state } = await startLoginAndGetParams();
|
|
135
|
+
// POST without Origin (non-browser client) should be rejected
|
|
136
|
+
const res = await new Promise((resolve, reject) => {
|
|
137
|
+
const data = JSON.stringify({
|
|
138
|
+
refreshToken: 't', email: 'e@e.com', uid: 'u', state, firebaseApiKey: 'k',
|
|
139
|
+
});
|
|
140
|
+
const req = http.request({
|
|
141
|
+
hostname: '127.0.0.1', port, path: '/callback', method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
143
|
+
}, (r) => { r.resume(); r.on('end', () => resolve({ statusCode: r.statusCode || 0 })); });
|
|
144
|
+
req.on('error', reject);
|
|
145
|
+
req.write(data);
|
|
146
|
+
req.end();
|
|
98
147
|
});
|
|
148
|
+
expect(res.statusCode).toBe(403);
|
|
149
|
+
await shutdownServer(port, state);
|
|
99
150
|
await loginPromise;
|
|
100
151
|
}, 10000);
|
|
101
152
|
it('rejects callback missing firebaseApiKey', async () => {
|
|
@@ -110,15 +161,7 @@ describe('login', () => {
|
|
|
110
161
|
});
|
|
111
162
|
expect(response.statusCode).toBe(400);
|
|
112
163
|
expect(mockStoreAuth).not.toHaveBeenCalled();
|
|
113
|
-
|
|
114
|
-
await sendCallback(port, {
|
|
115
|
-
token: 'test',
|
|
116
|
-
refreshToken: 'test',
|
|
117
|
-
email: 'test@test.com',
|
|
118
|
-
uid: 'uid',
|
|
119
|
-
state,
|
|
120
|
-
firebaseApiKey: 'key',
|
|
121
|
-
});
|
|
164
|
+
await shutdownServer(port, state);
|
|
122
165
|
await loginPromise;
|
|
123
166
|
}, 10000);
|
|
124
167
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/login.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1C,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE;CACvB,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC,CAAC;AAEJ,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACvC,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;QACb,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;QACd,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;QACb,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;KACd;CACF,CAAC,CAAC,CAAC;AACJ,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AAC/D,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAE9C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAEhD,SAAS,YAAY,CAAC,IAAY,EAAE,IAA4B;IAC9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CACtB;YACE,QAAQ,EAAE,WAAW;YACrB,IAAI;YACJ,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"login.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/login.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAiJ3C"}
|
package/dist/commands/login.js
CHANGED
|
@@ -26,8 +26,15 @@ export async function login() {
|
|
|
26
26
|
}
|
|
27
27
|
}, TIMEOUT_MS);
|
|
28
28
|
const server = http.createServer(async (req, res) => {
|
|
29
|
-
// Validate Origin header (defense-in-depth alongside state parameter)
|
|
29
|
+
// Validate Origin header (defense-in-depth alongside state parameter).
|
|
30
|
+
// OPTIONS preflight may omit Origin in non-browser clients, so only
|
|
31
|
+
// require it on POST where browsers always send it cross-origin.
|
|
30
32
|
const origin = req.headers.origin;
|
|
33
|
+
if (req.method === 'POST' && origin !== ALLOWED_ORIGIN) {
|
|
34
|
+
res.writeHead(403);
|
|
35
|
+
res.end();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
if (origin && origin !== ALLOWED_ORIGIN) {
|
|
32
39
|
res.writeHead(403);
|
|
33
40
|
res.end();
|
|
@@ -37,6 +44,7 @@ export async function login() {
|
|
|
37
44
|
res.setHeader('Access-Control-Allow-Origin', ALLOWED_ORIGIN);
|
|
38
45
|
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
39
46
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
47
|
+
res.setHeader('Access-Control-Allow-Private-Network', 'true');
|
|
40
48
|
if (req.method === 'OPTIONS') {
|
|
41
49
|
res.writeHead(204);
|
|
42
50
|
res.end();
|
|
@@ -74,7 +82,6 @@ export async function login() {
|
|
|
74
82
|
res.end(JSON.stringify({ error: 'Missing required fields' }));
|
|
75
83
|
return;
|
|
76
84
|
}
|
|
77
|
-
// Store auth
|
|
78
85
|
await storeAuth({
|
|
79
86
|
refreshToken,
|
|
80
87
|
email,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,aAAa,GAAG,+BAA+B,CAAC;AACtD,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,YAAY;AACxC,MAAM,cAAc,GAAG,aAAa,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAErD,qEAAqE;IACrE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,QAAQ,CAAC,oCAAoC,EAAE,eAAe,CAAC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;QAEf,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAClD,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,aAAa,GAAG,+BAA+B,CAAC;AACtD,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,YAAY;AACxC,MAAM,cAAc,GAAG,aAAa,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAErD,qEAAqE;IACrE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,QAAQ,CAAC,oCAAoC,EAAE,eAAe,CAAC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;QAEf,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAClD,uEAAuE;YACvE,oEAAoE;YACpE,iEAAiE;YACjE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;YAClC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;gBACvD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YACD,IAAI,MAAM,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;gBACxC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,6BAA6B;YAC7B,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,cAAc,CAAC,CAAC;YAC7D,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,eAAe,CAAC,CAAC;YAC/D,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;YAC9D,GAAG,CAAC,SAAS,CAAC,sCAAsC,EAAE,MAAM,CAAC,CAAC;YAE9D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;gBACrD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,uCAAuC;YACvC,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;YAChC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;gBAC9B,QAAQ,IAAK,KAAgB,CAAC,MAAM,CAAC;gBACrC,IAAI,QAAQ,GAAG,aAAa,EAAE,CAAC;oBAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;oBACV,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;YAC/B,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC1D,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;gBAEhF,mCAAmC;gBACnC,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;oBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC,CAAC;oBAC9D,OAAO;gBACT,CAAC;gBAED,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;oBACvD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC,CAAC;oBAC9D,OAAO;gBACT,CAAC;gBAED,MAAM,SAAS,CAAC;oBACd,YAAY;oBACZ,KAAK;oBACL,GAAG;oBACH,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,SAAS;oBAC9C,cAAc;iBACf,CAAC,CAAC;gBAEH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBAE3C,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,OAAO,CAAC,gBAAgB,KAAK,EAAE,CAAC,CAAC;oBACxC,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,kBAAkB,GAAG,GAAG,EAAE;YAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,QAAQ,CAAC,0DAA0D,EAAE,YAAY,CAAC,CAAC,CAAC;gBAC/F,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,GAAG,aAAa,kBAAkB,IAAI,UAAU,KAAK,EAAE,CAAC;YAEzE,8DAA8D;YAC9D,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,OAAO,QAAQ,IAAI,CAAC,CAAC;YAEjC,oDAAoD;YACpD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAE3E,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC/C,CAAC,CAAC;QAEF,0DAA0D;QAC1D,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAE3C,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;QAE3C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;YAChD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,sDAAsD;gBACtD,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,MAAM,CAAC,IAAI,QAAQ,CAAC,oCAAoC,GAAG,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;gBAC1F,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -3,6 +3,31 @@ import { NextRequest } from 'next/server';
|
|
|
3
3
|
|
|
4
4
|
export const runtime = 'edge';
|
|
5
5
|
|
|
6
|
+
const ACCENT_COLOR = '#8B5CF6';
|
|
7
|
+
const MAX_DESCRIPTION_LENGTH = 120;
|
|
8
|
+
const LONG_TITLE_THRESHOLD = 40;
|
|
9
|
+
const FONT_STACK =
|
|
10
|
+
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
11
|
+
const ACCENT_GRADIENT = `linear-gradient(90deg, ${ACCENT_COLOR} 0%, #C4B5FD 50%, #A78BFA 100%)`;
|
|
12
|
+
|
|
13
|
+
function getThemeColors(isDark: boolean) {
|
|
14
|
+
return {
|
|
15
|
+
text: isDark ? '#ffffff' : '#1a1a2e',
|
|
16
|
+
muted: isDark ? '#a0a0b0' : '#666680',
|
|
17
|
+
background: isDark
|
|
18
|
+
? 'linear-gradient(135deg, #1a1a3e 0%, #0f0f23 100%)'
|
|
19
|
+
: 'linear-gradient(135deg, #f8f8fc 0%, #ffffff 100%)',
|
|
20
|
+
badgeBackground: isDark
|
|
21
|
+
? 'rgba(139, 92, 246, 0.15)'
|
|
22
|
+
: 'rgba(139, 92, 246, 0.1)',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function truncateText(text: string, maxLength: number): string {
|
|
27
|
+
if (text.length <= maxLength) return text;
|
|
28
|
+
return text.substring(0, maxLength) + '...';
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
/**
|
|
7
32
|
* OG Image Generator
|
|
8
33
|
*
|
|
@@ -26,18 +51,27 @@ export async function GET(request: NextRequest) {
|
|
|
26
51
|
const description = searchParams.get('description') || '';
|
|
27
52
|
const section = searchParams.get('section') || '';
|
|
28
53
|
const siteName = searchParams.get('siteName') || 'Documentation';
|
|
29
|
-
const
|
|
30
|
-
const
|
|
54
|
+
const logoUrl = searchParams.get('logo') || '';
|
|
55
|
+
const isDark = (searchParams.get('theme') || 'dark') === 'dark';
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
// Fetch logo and convert to base64 data URI for Satori compatibility
|
|
58
|
+
// (Satori doesn't support webp and can fail on remote URLs)
|
|
59
|
+
let logo = '';
|
|
60
|
+
if (logoUrl) {
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(logoUrl);
|
|
63
|
+
if (res.ok) {
|
|
64
|
+
const contentType = res.headers.get('content-type') || 'image/png';
|
|
65
|
+
const buf = await res.arrayBuffer();
|
|
66
|
+
logo = `data:${contentType};base64,${Buffer.from(buf).toString('base64')}`;
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Skip logo on fetch failure
|
|
70
|
+
}
|
|
71
|
+
}
|
|
33
72
|
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const textColor = isDark ? '#ffffff' : '#1a1a2e';
|
|
37
|
-
const mutedColor = isDark ? '#a0a0b0' : '#666680';
|
|
38
|
-
const accentColor = '#8B5CF6'; // Jamdesk purple
|
|
39
|
-
const gradientStart = isDark ? '#1a1a3e' : '#f8f8fc';
|
|
40
|
-
const gradientEnd = isDark ? '#0f0f23' : '#ffffff';
|
|
73
|
+
const colors = getThemeColors(isDark);
|
|
74
|
+
const titleFontSize = title.length > LONG_TITLE_THRESHOLD ? '52px' : '64px';
|
|
41
75
|
|
|
42
76
|
return new ImageResponse(
|
|
43
77
|
(
|
|
@@ -48,15 +82,16 @@ export async function GET(request: NextRequest) {
|
|
|
48
82
|
display: 'flex',
|
|
49
83
|
flexDirection: 'column',
|
|
50
84
|
padding: '60px',
|
|
51
|
-
background:
|
|
52
|
-
fontFamily:
|
|
85
|
+
background: colors.background,
|
|
86
|
+
fontFamily: FONT_STACK,
|
|
53
87
|
}}
|
|
54
88
|
>
|
|
55
|
-
{/*
|
|
89
|
+
{/* Logo and site name */}
|
|
56
90
|
<div
|
|
57
91
|
style={{
|
|
58
92
|
display: 'flex',
|
|
59
|
-
|
|
93
|
+
flexDirection: 'column',
|
|
94
|
+
alignItems: 'flex-start',
|
|
60
95
|
marginBottom: '40px',
|
|
61
96
|
}}
|
|
62
97
|
>
|
|
@@ -64,10 +99,10 @@ export async function GET(request: NextRequest) {
|
|
|
64
99
|
<img
|
|
65
100
|
src={logo}
|
|
66
101
|
alt=""
|
|
67
|
-
width={
|
|
68
|
-
height={
|
|
102
|
+
width={48}
|
|
103
|
+
height={48}
|
|
69
104
|
style={{
|
|
70
|
-
|
|
105
|
+
marginBottom: '16px',
|
|
71
106
|
borderRadius: '8px',
|
|
72
107
|
}}
|
|
73
108
|
/>
|
|
@@ -76,7 +111,7 @@ export async function GET(request: NextRequest) {
|
|
|
76
111
|
style={{
|
|
77
112
|
fontSize: '24px',
|
|
78
113
|
fontWeight: 600,
|
|
79
|
-
color:
|
|
114
|
+
color: colors.muted,
|
|
80
115
|
textTransform: 'uppercase',
|
|
81
116
|
letterSpacing: '0.05em',
|
|
82
117
|
}}
|
|
@@ -97,9 +132,9 @@ export async function GET(request: NextRequest) {
|
|
|
97
132
|
style={{
|
|
98
133
|
fontSize: '18px',
|
|
99
134
|
fontWeight: 500,
|
|
100
|
-
color:
|
|
135
|
+
color: ACCENT_COLOR,
|
|
101
136
|
padding: '8px 16px',
|
|
102
|
-
background:
|
|
137
|
+
background: colors.badgeBackground,
|
|
103
138
|
borderRadius: '20px',
|
|
104
139
|
}}
|
|
105
140
|
>
|
|
@@ -108,7 +143,7 @@ export async function GET(request: NextRequest) {
|
|
|
108
143
|
</div>
|
|
109
144
|
)}
|
|
110
145
|
|
|
111
|
-
{/*
|
|
146
|
+
{/* Title and description */}
|
|
112
147
|
<div
|
|
113
148
|
style={{
|
|
114
149
|
display: 'flex',
|
|
@@ -119,9 +154,9 @@ export async function GET(request: NextRequest) {
|
|
|
119
154
|
>
|
|
120
155
|
<h1
|
|
121
156
|
style={{
|
|
122
|
-
fontSize:
|
|
157
|
+
fontSize: titleFontSize,
|
|
123
158
|
fontWeight: 700,
|
|
124
|
-
color:
|
|
159
|
+
color: colors.text,
|
|
125
160
|
lineHeight: 1.2,
|
|
126
161
|
margin: 0,
|
|
127
162
|
maxWidth: '900px',
|
|
@@ -134,15 +169,13 @@ export async function GET(request: NextRequest) {
|
|
|
134
169
|
<p
|
|
135
170
|
style={{
|
|
136
171
|
fontSize: '28px',
|
|
137
|
-
color:
|
|
172
|
+
color: colors.muted,
|
|
138
173
|
marginTop: '24px',
|
|
139
174
|
lineHeight: 1.4,
|
|
140
175
|
maxWidth: '800px',
|
|
141
176
|
}}
|
|
142
177
|
>
|
|
143
|
-
{description
|
|
144
|
-
? description.substring(0, 120) + '...'
|
|
145
|
-
: description}
|
|
178
|
+
{truncateText(description, MAX_DESCRIPTION_LENGTH)}
|
|
146
179
|
</p>
|
|
147
180
|
)}
|
|
148
181
|
</div>
|
|
@@ -152,7 +185,7 @@ export async function GET(request: NextRequest) {
|
|
|
152
185
|
style={{
|
|
153
186
|
display: 'flex',
|
|
154
187
|
height: '6px',
|
|
155
|
-
background:
|
|
188
|
+
background: ACCENT_GRADIENT,
|
|
156
189
|
borderRadius: '3px',
|
|
157
190
|
marginTop: '40px',
|
|
158
191
|
}}
|
|
@@ -162,6 +195,9 @@ export async function GET(request: NextRequest) {
|
|
|
162
195
|
{
|
|
163
196
|
width: 1200,
|
|
164
197
|
height: 630,
|
|
198
|
+
headers: {
|
|
199
|
+
'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=604800',
|
|
200
|
+
},
|
|
165
201
|
}
|
|
166
202
|
);
|
|
167
203
|
}
|
package/vendored/lib/seo.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Metadata } from 'next';
|
|
11
|
-
import type { DocsConfig, Logo, LogoConfig, LanguageConfig, LanguageCode } from './docs-types';
|
|
11
|
+
import type { DocsConfig, Logo, LogoConfig, Favicon, FaviconConfig, LanguageConfig, LanguageCode } from './docs-types';
|
|
12
12
|
import { transformLanguagePath, extractLanguageFromPath, isValidLanguageCode } from './language-utils';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -23,6 +23,7 @@ function buildOgImageUrl(
|
|
|
23
23
|
description?: string;
|
|
24
24
|
section?: string;
|
|
25
25
|
logo?: Logo;
|
|
26
|
+
favicon?: Favicon;
|
|
26
27
|
theme?: 'light' | 'dark';
|
|
27
28
|
}
|
|
28
29
|
): string {
|
|
@@ -40,16 +41,31 @@ function buildOgImageUrl(
|
|
|
40
41
|
params.set('theme', options.theme);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
//
|
|
44
|
+
// For hostAtDocs sites (baseUrl ends with /docs), use the parent domain with
|
|
45
|
+
// /_jd/og path instead of cross-domain /api/og. LinkedIn's crawler fails to
|
|
46
|
+
// fetch OG images from different domains. The /_jd/og path is proxied through
|
|
47
|
+
// the customer's CF Worker/proxy to the ISR app's /api/og via Next.js rewrite.
|
|
48
|
+
const isHostAtDocs = baseUrl.endsWith('/docs');
|
|
44
49
|
const rootUrl = baseUrl.replace(/\/docs$/, '');
|
|
45
50
|
|
|
46
51
|
// Handle logo - prefer dark variant for OG images (dark background)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
// Satori (@vercel/og) can't render webp, so fall back to favicon if logo is webp
|
|
53
|
+
if (options?.logo || options?.favicon) {
|
|
54
|
+
let logoPath = '';
|
|
55
|
+
if (options?.logo) {
|
|
56
|
+
if (typeof options.logo === 'string') {
|
|
57
|
+
logoPath = options.logo;
|
|
58
|
+
} else {
|
|
59
|
+
logoPath = (options.logo as LogoConfig).dark || (options.logo as LogoConfig).light;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Fall back to favicon when logo is webp (Satori can't decode webp)
|
|
63
|
+
if ((!logoPath || logoPath.endsWith('.webp')) && options?.favicon) {
|
|
64
|
+
if (typeof options.favicon === 'string') {
|
|
65
|
+
logoPath = options.favicon;
|
|
66
|
+
} else {
|
|
67
|
+
logoPath = (options.favicon as FaviconConfig).dark || (options.favicon as FaviconConfig).light;
|
|
68
|
+
}
|
|
53
69
|
}
|
|
54
70
|
// Make logo URL absolute (use root URL since assets are at domain root)
|
|
55
71
|
if (logoPath && !logoPath.startsWith('http')) {
|
|
@@ -60,7 +76,10 @@ function buildOgImageUrl(
|
|
|
60
76
|
}
|
|
61
77
|
}
|
|
62
78
|
|
|
63
|
-
|
|
79
|
+
// hostAtDocs: use /_jd/og (proxied to ISR app's /api/og) for same-domain OG images
|
|
80
|
+
// non-hostAtDocs: use /api/og directly (already same-domain)
|
|
81
|
+
const ogPath = isHostAtDocs ? '/_jd/og' : '/api/og';
|
|
82
|
+
return `${rootUrl}${ogPath}?${params.toString()}`;
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
export interface PageFrontmatter {
|
|
@@ -137,7 +156,7 @@ export function buildSeoMetadata(
|
|
|
137
156
|
frontmatter: PageFrontmatter,
|
|
138
157
|
pagePath: string,
|
|
139
158
|
baseUrl: string,
|
|
140
|
-
languages?: LanguageConfig[]
|
|
159
|
+
languages?: LanguageConfig[],
|
|
141
160
|
): Partial<Metadata> {
|
|
142
161
|
const globalMeta = config.seo?.metatags || {};
|
|
143
162
|
const pageMeta = (frontmatter.seo as Record<string, string>) || {};
|
|
@@ -210,6 +229,7 @@ export function buildSeoMetadata(
|
|
|
210
229
|
description: frontmatter.description,
|
|
211
230
|
section: frontmatter.section,
|
|
212
231
|
logo: config.logo,
|
|
232
|
+
favicon: config.favicon,
|
|
213
233
|
theme: 'dark', // Dark theme looks better for OG images
|
|
214
234
|
}
|
|
215
235
|
);
|
|
@@ -220,13 +240,19 @@ export function buildSeoMetadata(
|
|
|
220
240
|
url: metatags['og:url'] || pageUrl,
|
|
221
241
|
siteName: metatags['og:site_name'] || config.name,
|
|
222
242
|
type: (metatags['og:type'] as 'website' | 'article') || 'website',
|
|
223
|
-
images: [
|
|
243
|
+
images: [{
|
|
244
|
+
url: ogImageUrl,
|
|
245
|
+
width: 1200,
|
|
246
|
+
height: 630,
|
|
247
|
+
type: 'image/png',
|
|
248
|
+
}],
|
|
224
249
|
};
|
|
225
250
|
}
|
|
226
251
|
|
|
227
252
|
// 9. Twitter Cards
|
|
228
253
|
// Auto-generate Twitter card with OG image
|
|
229
|
-
const
|
|
254
|
+
const ogImages = (metadata.openGraph as { images?: Array<{ url: string }> })?.images;
|
|
255
|
+
const twitterImageUrl = metatags['twitter:image'] || ogImages?.[0]?.url;
|
|
230
256
|
metadata.twitter = {
|
|
231
257
|
card: (metatags['twitter:card'] as 'summary' | 'summary_large_image') || 'summary_large_image',
|
|
232
258
|
site: metatags['twitter:site'],
|