@servicetitan/hammer-token 2.5.2 ā 3.0.1
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/CHANGELOG.md +52 -2
- package/README.md +332 -0
- package/build/web/core/component-variables.scss +1088 -131
- package/build/web/core/component.d.ts +558 -0
- package/build/web/core/component.js +6685 -249
- package/build/web/core/component.scss +557 -69
- package/build/web/core/css-utils/a2-border.css +23 -51
- package/build/web/core/css-utils/a2-color.css +221 -233
- package/build/web/core/css-utils/a2-font.css +1 -29
- package/build/web/core/css-utils/a2-spacing.css +238 -483
- package/build/web/core/css-utils/a2-utils.css +496 -781
- package/build/web/core/css-utils/border.css +23 -51
- package/build/web/core/css-utils/color.css +221 -233
- package/build/web/core/css-utils/font.css +1 -29
- package/build/web/core/css-utils/spacing.css +238 -483
- package/build/web/core/css-utils/utils.css +496 -781
- package/build/web/core/index.d.ts +6 -0
- package/build/web/core/index.js +1 -1
- package/build/web/core/primitive-variables.scss +148 -65
- package/build/web/core/primitive.d.ts +209 -0
- package/build/web/core/primitive.js +779 -61
- package/build/web/core/primitive.scss +207 -124
- package/build/web/core/semantic-variables.scss +363 -245
- package/build/web/core/semantic.d.ts +221 -0
- package/build/web/core/semantic.js +1592 -347
- package/build/web/core/semantic.scss +219 -140
- package/build/web/index.d.ts +3 -4
- package/build/web/types.d.ts +17 -0
- package/config.js +121 -496
- package/eslint.config.mjs +11 -1
- package/package.json +15 -5
- package/src/global/primitive/breakpoint.tokens.json +54 -0
- package/src/global/primitive/color.tokens.json +1092 -0
- package/src/global/primitive/duration.tokens.json +44 -0
- package/src/global/primitive/font.tokens.json +151 -0
- package/src/global/primitive/radius.tokens.json +94 -0
- package/src/global/primitive/size.tokens.json +174 -0
- package/src/global/primitive/transition.tokens.json +32 -0
- package/src/theme/core/background.tokens.json +1312 -0
- package/src/theme/core/border.tokens.json +192 -0
- package/src/theme/core/chart.tokens.json +982 -0
- package/src/theme/core/component/ai-mark.tokens.json +20 -0
- package/src/theme/core/component/alert.tokens.json +261 -0
- package/src/theme/core/component/announcement.tokens.json +460 -0
- package/src/theme/core/component/avatar.tokens.json +137 -0
- package/src/theme/core/component/badge.tokens.json +42 -0
- package/src/theme/core/component/breadcrumb.tokens.json +42 -0
- package/src/theme/core/component/button-toggle.tokens.json +428 -0
- package/src/theme/core/component/button.tokens.json +941 -0
- package/src/theme/core/component/calendar.tokens.json +391 -0
- package/src/theme/core/component/card.tokens.json +107 -0
- package/src/theme/core/component/checkbox.tokens.json +631 -0
- package/src/theme/core/component/chip.tokens.json +169 -0
- package/src/theme/core/component/combobox.tokens.json +269 -0
- package/src/theme/core/component/details.tokens.json +152 -0
- package/src/theme/core/component/dialog.tokens.json +87 -0
- package/src/theme/core/component/divider.tokens.json +23 -0
- package/src/theme/core/component/dnd.tokens.json +208 -0
- package/src/theme/core/component/drawer.tokens.json +61 -0
- package/src/theme/core/component/drilldown.tokens.json +61 -0
- package/src/theme/core/component/edit-card.tokens.json +381 -0
- package/src/theme/core/component/field-label.tokens.json +42 -0
- package/src/theme/core/component/field-message.tokens.json +65 -0
- package/src/theme/core/component/icon.tokens.json +42 -0
- package/src/theme/core/component/link.tokens.json +108 -0
- package/src/theme/core/component/list-view.tokens.json +82 -0
- package/src/theme/core/component/listbox.tokens.json +283 -0
- package/src/theme/core/component/menu.tokens.json +230 -0
- package/src/theme/core/component/overflow.tokens.json +84 -0
- package/src/theme/core/component/page.tokens.json +377 -0
- package/src/theme/core/component/pagination.tokens.json +63 -0
- package/src/theme/core/component/popover.tokens.json +122 -0
- package/src/theme/core/component/progress-bar.tokens.json +133 -0
- package/src/theme/core/component/radio.tokens.json +631 -0
- package/src/theme/core/component/segmented-control.tokens.json +175 -0
- package/src/theme/core/component/select-card.tokens.json +943 -0
- package/src/theme/core/component/side-nav.tokens.json +349 -0
- package/src/theme/core/component/skeleton.tokens.json +42 -0
- package/src/theme/core/component/spinner.tokens.json +96 -0
- package/src/theme/core/component/status-icon.tokens.json +164 -0
- package/src/theme/core/component/stepper.tokens.json +484 -0
- package/src/theme/core/component/switch.tokens.json +285 -0
- package/src/theme/core/component/tab.tokens.json +192 -0
- package/src/theme/core/component/text-field.tokens.json +160 -0
- package/src/theme/core/component/text.tokens.json +59 -0
- package/src/theme/core/component/toast.tokens.json +343 -0
- package/src/theme/core/component/toolbar.tokens.json +114 -0
- package/src/theme/core/component/tooltip.tokens.json +61 -0
- package/src/theme/core/focus.tokens.json +56 -0
- package/src/theme/core/foreground.tokens.json +416 -0
- package/src/theme/core/gradient.tokens.json +41 -0
- package/src/theme/core/opacity.tokens.json +25 -0
- package/src/theme/core/shadow.tokens.json +81 -0
- package/src/theme/core/status.tokens.json +74 -0
- package/src/theme/core/typography.tokens.json +163 -0
- package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
- package/src/utils/__tests__/sd-build-configs.test.js +306 -0
- package/src/utils/__tests__/sd-formats.test.js +942 -0
- package/src/utils/__tests__/sd-transforms.test.js +336 -0
- package/src/utils/__tests__/token-helpers.test.js +1160 -0
- package/src/utils/copy-css-utils-cli.js +13 -1
- package/src/utils/css-utils-format-utils.js +105 -176
- package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
- package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
- package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
- package/src/utils/figma/auth.js +355 -0
- package/src/utils/figma/constants.js +22 -0
- package/src/utils/figma/errors.js +80 -0
- package/src/utils/figma/figma-api.js +1069 -0
- package/src/utils/figma/get-token.js +348 -0
- package/src/utils/figma/sync-components.js +909 -0
- package/src/utils/figma/sync-main.js +692 -0
- package/src/utils/figma/sync-orchestration.js +683 -0
- package/src/utils/figma/sync-primitives.js +230 -0
- package/src/utils/figma/sync-semantic.js +1056 -0
- package/src/utils/figma/token-conversion.js +340 -0
- package/src/utils/figma/token-parsing.js +186 -0
- package/src/utils/figma/token-resolution.js +569 -0
- package/src/utils/figma/utils.js +199 -0
- package/src/utils/sd-build-configs.js +305 -0
- package/src/utils/sd-formats.js +948 -0
- package/src/utils/sd-transforms.js +165 -0
- package/src/utils/token-helpers.js +848 -0
- package/tsconfig.json +18 -0
- package/vitest.config.js +17 -0
- package/.turbo/turbo-build.log +0 -37
- package/build/web/core/raw.js +0 -234
- package/src/global/primitive/breakpoint.js +0 -19
- package/src/global/primitive/color.js +0 -231
- package/src/global/primitive/duration.js +0 -16
- package/src/global/primitive/font.js +0 -60
- package/src/global/primitive/radius.js +0 -31
- package/src/global/primitive/size.js +0 -55
- package/src/global/primitive/transition.js +0 -16
- package/src/theme/core/background.js +0 -170
- package/src/theme/core/border.js +0 -103
- package/src/theme/core/charts.js +0 -464
- package/src/theme/core/component/button.js +0 -708
- package/src/theme/core/component/checkbox.js +0 -405
- package/src/theme/core/focus.js +0 -35
- package/src/theme/core/foreground.js +0 -148
- package/src/theme/core/overlay.js +0 -137
- package/src/theme/core/shadow.js +0 -29
- package/src/theme/core/status.js +0 -49
- package/src/theme/core/typography.js +0 -82
- package/type/types.ts +0 -344
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper script to obtain Figma OAuth2 refresh token
|
|
5
|
+
*
|
|
6
|
+
* This script guides you through the OAuth2 authorization flow to get a refresh token.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const http = require("http");
|
|
10
|
+
const { URL } = require("url");
|
|
11
|
+
|
|
12
|
+
// Load .env.local file if it exists
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const ENV_FILE = path.join(__dirname, "../../../../../.env.local");
|
|
16
|
+
|
|
17
|
+
function loadEnvFile() {
|
|
18
|
+
if (fs.existsSync(ENV_FILE)) {
|
|
19
|
+
try {
|
|
20
|
+
const envContent = fs.readFileSync(ENV_FILE, "utf8");
|
|
21
|
+
const lines = envContent.split("\n");
|
|
22
|
+
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
30
|
+
if (match) {
|
|
31
|
+
const key = match[1].trim();
|
|
32
|
+
const value = match[2].trim().replace(/^["']|["']$/g, "");
|
|
33
|
+
if (!process.env[key]) {
|
|
34
|
+
process.env[key] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch (_error) {
|
|
39
|
+
// Silently fail
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
loadEnvFile();
|
|
45
|
+
|
|
46
|
+
const CLIENT_ID = process.env.FIGMA_CLIENT_ID;
|
|
47
|
+
const CLIENT_SECRET = process.env.FIGMA_CLIENT_SECRET;
|
|
48
|
+
const REDIRECT_URI = "http://localhost:3000/figma-oauth-callback";
|
|
49
|
+
const PORT = 3000;
|
|
50
|
+
const FIGMA_OAUTH_BASE = "https://api.figma.com/v1";
|
|
51
|
+
|
|
52
|
+
if (!CLIENT_ID || !CLIENT_SECRET) {
|
|
53
|
+
console.error(
|
|
54
|
+
"ā Error: FIGMA_CLIENT_ID and FIGMA_CLIENT_SECRET must be set in .env.local file",
|
|
55
|
+
);
|
|
56
|
+
console.error("\nPlease add to your .env.local file:");
|
|
57
|
+
console.error(" FIGMA_CLIENT_ID=your-client-id");
|
|
58
|
+
console.error(" FIGMA_CLIENT_SECRET=your-client-secret");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Required scopes for variables API
|
|
63
|
+
// Note: current_user:read is required for /me endpoint used in token validation
|
|
64
|
+
const SCOPES =
|
|
65
|
+
"file_variables:read file_variables:write file_content:read current_user:read";
|
|
66
|
+
|
|
67
|
+
// Generate a random state for security
|
|
68
|
+
const state =
|
|
69
|
+
Math.random().toString(36).substring(2, 15) +
|
|
70
|
+
Math.random().toString(36).substring(2, 15);
|
|
71
|
+
|
|
72
|
+
// Build authorization URL
|
|
73
|
+
const authUrl = new URL("https://www.figma.com/oauth");
|
|
74
|
+
authUrl.searchParams.set("client_id", CLIENT_ID);
|
|
75
|
+
authUrl.searchParams.set("redirect_uri", REDIRECT_URI);
|
|
76
|
+
authUrl.searchParams.set("scope", SCOPES);
|
|
77
|
+
authUrl.searchParams.set("state", state);
|
|
78
|
+
authUrl.searchParams.set("response_type", "code");
|
|
79
|
+
|
|
80
|
+
console.log("š Figma OAuth2 Token Helper\n");
|
|
81
|
+
console.log(
|
|
82
|
+
"This script will help you obtain a refresh token for Figma OAuth2.\n",
|
|
83
|
+
);
|
|
84
|
+
console.log("š Prerequisites:");
|
|
85
|
+
console.log(
|
|
86
|
+
" 1. Make sure your Figma OAuth app has this redirect URI configured:",
|
|
87
|
+
);
|
|
88
|
+
console.log(` ${REDIRECT_URI}\n`);
|
|
89
|
+
console.log(" 2. Your app should have these scopes enabled:");
|
|
90
|
+
console.log(` - ${SCOPES.replace(/ /g, "\n - ")}\n`);
|
|
91
|
+
|
|
92
|
+
// Create HTTP server to handle OAuth callback
|
|
93
|
+
const server = http.createServer((req, res) => {
|
|
94
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
95
|
+
|
|
96
|
+
if (url.pathname !== "/figma-oauth-callback") {
|
|
97
|
+
res.writeHead(404, { "Content-Type": "text/html" });
|
|
98
|
+
res.end("<h1>404 Not Found</h1>");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const code = url.searchParams.get("code");
|
|
103
|
+
const returnedState = url.searchParams.get("state");
|
|
104
|
+
const error = url.searchParams.get("error");
|
|
105
|
+
|
|
106
|
+
// Handle errors
|
|
107
|
+
if (error) {
|
|
108
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
109
|
+
res.end(`
|
|
110
|
+
<html>
|
|
111
|
+
<head><title>OAuth Error</title></head>
|
|
112
|
+
<body>
|
|
113
|
+
<h1>ā OAuth Error</h1>
|
|
114
|
+
<p>Error: ${error}</p>
|
|
115
|
+
<p>Error description: ${url.searchParams.get("error_description") || "Unknown error"}</p>
|
|
116
|
+
<p>You can close this window.</p>
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
`);
|
|
120
|
+
server.close();
|
|
121
|
+
console.error(`\nā OAuth error: ${error}`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Verify state
|
|
127
|
+
if (returnedState !== state) {
|
|
128
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
129
|
+
res.end(`
|
|
130
|
+
<html>
|
|
131
|
+
<head><title>Security Error</title></head>
|
|
132
|
+
<body>
|
|
133
|
+
<h1>ā Security Error</h1>
|
|
134
|
+
<p>State mismatch. Please try again.</p>
|
|
135
|
+
<p>You can close this window.</p>
|
|
136
|
+
</body>
|
|
137
|
+
</html>
|
|
138
|
+
`);
|
|
139
|
+
server.close();
|
|
140
|
+
console.error("\nā Security error: State mismatch");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Exchange authorization code for tokens
|
|
146
|
+
if (!code) {
|
|
147
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
148
|
+
res.end(`
|
|
149
|
+
<html>
|
|
150
|
+
<head><title>Error</title></head>
|
|
151
|
+
<body>
|
|
152
|
+
<h1>ā Error</h1>
|
|
153
|
+
<p>No authorization code received.</p>
|
|
154
|
+
<p>You can close this window.</p>
|
|
155
|
+
</body>
|
|
156
|
+
</html>
|
|
157
|
+
`);
|
|
158
|
+
server.close();
|
|
159
|
+
console.error("\nā No authorization code received");
|
|
160
|
+
process.exit(1);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Show success page
|
|
165
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
166
|
+
res.end(`
|
|
167
|
+
<html>
|
|
168
|
+
<head><title>Success</title></head>
|
|
169
|
+
<body>
|
|
170
|
+
<h1>ā
Authorization Successful!</h1>
|
|
171
|
+
<p>Exchanging authorization code for tokens...</p>
|
|
172
|
+
<p>Please wait and check the terminal.</p>
|
|
173
|
+
</body>
|
|
174
|
+
</html>
|
|
175
|
+
`);
|
|
176
|
+
|
|
177
|
+
// Exchange code for tokens
|
|
178
|
+
console.log("\nš Exchanging authorization code for tokens...");
|
|
179
|
+
exchangeCodeForTokens(code)
|
|
180
|
+
.then((tokens) => {
|
|
181
|
+
console.log("\nā
Success! Here are your tokens:\n");
|
|
182
|
+
console.log("š Add this to your .env.local file:\n");
|
|
183
|
+
console.log(`FIGMA_REFRESH_TOKEN=${tokens.refresh_token}\n`);
|
|
184
|
+
console.log(
|
|
185
|
+
"š” Note: The access token expires in 1 hour, but the refresh token",
|
|
186
|
+
);
|
|
187
|
+
console.log(
|
|
188
|
+
" is long-lived and can be used to get new access tokens.\n",
|
|
189
|
+
);
|
|
190
|
+
console.log("š Full token response:");
|
|
191
|
+
console.log(
|
|
192
|
+
` Access Token: ${tokens.access_token.substring(0, 20)}...`,
|
|
193
|
+
);
|
|
194
|
+
console.log(` Refresh Token: ${tokens.refresh_token}`);
|
|
195
|
+
console.log(` Expires In: ${tokens.expires_in || "3600"} seconds\n`);
|
|
196
|
+
server.close();
|
|
197
|
+
process.exit(0);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
console.error("\nā Failed to exchange code for tokens:", error.message);
|
|
201
|
+
if (error.stack && process.env.DEBUG) {
|
|
202
|
+
console.error("\nStack trace:");
|
|
203
|
+
console.error(error.stack);
|
|
204
|
+
}
|
|
205
|
+
server.close();
|
|
206
|
+
process.exit(1);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
async function exchangeCodeForTokens(code) {
|
|
211
|
+
console.log(` Code received: ${code.substring(0, 10)}...`);
|
|
212
|
+
console.log(` Exchanging at: ${FIGMA_OAUTH_BASE}/oauth/token`);
|
|
213
|
+
|
|
214
|
+
// Figma requires HTTP Basic Authentication with Base64 encoded client_id:client_secret
|
|
215
|
+
const credentials = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString(
|
|
216
|
+
"base64",
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// Figma requires form-encoded body, not JSON
|
|
220
|
+
const formData = new URLSearchParams({
|
|
221
|
+
redirect_uri: REDIRECT_URI,
|
|
222
|
+
code: code,
|
|
223
|
+
grant_type: "authorization_code",
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
console.log(
|
|
227
|
+
` Using Basic Auth with client_id: ${CLIENT_ID.substring(0, 10)}...`,
|
|
228
|
+
);
|
|
229
|
+
console.log(
|
|
230
|
+
` Form data: redirect_uri=${REDIRECT_URI}, grant_type=authorization_code`,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const response = await fetch(`${FIGMA_OAUTH_BASE}/oauth/token`, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
237
|
+
Authorization: `Basic ${credentials}`,
|
|
238
|
+
},
|
|
239
|
+
body: formData.toString(),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const responseText = await response.text();
|
|
243
|
+
console.log(` Response status: ${response.status}`);
|
|
244
|
+
console.log(
|
|
245
|
+
` Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (!response.ok) {
|
|
249
|
+
let errorMessage = `Token exchange failed (${response.status}): ${responseText}`;
|
|
250
|
+
|
|
251
|
+
// Always log the full error response for debugging
|
|
252
|
+
console.error(` Full error response: ${responseText}`);
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const errorJson = JSON.parse(responseText);
|
|
256
|
+
if (errorJson.error) {
|
|
257
|
+
errorMessage = `Token exchange failed: ${errorJson.error}`;
|
|
258
|
+
if (errorJson.error_description) {
|
|
259
|
+
errorMessage += ` - ${errorJson.error_description}`;
|
|
260
|
+
}
|
|
261
|
+
console.error(` Parsed error: ${errorJson.error}`);
|
|
262
|
+
console.error(
|
|
263
|
+
` Error description: ${errorJson.error_description || "none"}`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
// Not JSON, use raw text
|
|
268
|
+
if (responseText.length < 500) {
|
|
269
|
+
console.error(` Error response (raw): ${responseText}`);
|
|
270
|
+
} else {
|
|
271
|
+
console.error(
|
|
272
|
+
` Error response (first 500 chars): ${responseText.substring(0, 500)}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
throw new Error(errorMessage);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let data;
|
|
281
|
+
try {
|
|
282
|
+
data = JSON.parse(responseText);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
throw new Error(`Failed to parse token response: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!data.refresh_token) {
|
|
288
|
+
console.error(" Token response:", JSON.stringify(data, null, 2));
|
|
289
|
+
throw new Error("Token exchange response missing refresh_token");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
access_token: data.access_token,
|
|
294
|
+
refresh_token: data.refresh_token,
|
|
295
|
+
expires_in: data.expires_in,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Start server
|
|
300
|
+
server.listen(PORT, () => {
|
|
301
|
+
console.log(`š Local server started on port ${PORT}`);
|
|
302
|
+
console.log(`\nš Opening browser to authorize...\n`);
|
|
303
|
+
console.log(
|
|
304
|
+
` If the browser doesn't open automatically, visit this URL:\n`,
|
|
305
|
+
);
|
|
306
|
+
console.log(` ${authUrl.toString()}\n`);
|
|
307
|
+
|
|
308
|
+
// Try to open browser (optional, may fail)
|
|
309
|
+
try {
|
|
310
|
+
const { exec } = require("child_process");
|
|
311
|
+
const platform = process.platform;
|
|
312
|
+
let command;
|
|
313
|
+
|
|
314
|
+
if (platform === "darwin") {
|
|
315
|
+
command = "open";
|
|
316
|
+
} else if (platform === "win32") {
|
|
317
|
+
command = "start";
|
|
318
|
+
} else {
|
|
319
|
+
command = "xdg-open";
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
exec(`${command} "${authUrl.toString()}"`, (error) => {
|
|
323
|
+
if (error && process.env.DEBUG) {
|
|
324
|
+
console.warn(` ā ļø Could not open browser: ${error.message}`);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
} catch (_error) {
|
|
328
|
+
// Ignore
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.log("ā³ Waiting for authorization...");
|
|
332
|
+
console.log(
|
|
333
|
+
" (The server will close automatically after receiving the token)\n",
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Handle server errors
|
|
338
|
+
server.on("error", (error) => {
|
|
339
|
+
if (error.code === "EADDRINUSE") {
|
|
340
|
+
console.error(`\nā Error: Port ${PORT} is already in use.`);
|
|
341
|
+
console.error(
|
|
342
|
+
" Please close any other applications using this port and try again.\n",
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
console.error(`\nā Server error: ${error.message}\n`);
|
|
346
|
+
}
|
|
347
|
+
process.exit(1);
|
|
348
|
+
});
|