@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.
Files changed (146) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/README.md +332 -0
  3. package/build/web/core/component-variables.scss +1088 -131
  4. package/build/web/core/component.d.ts +558 -0
  5. package/build/web/core/component.js +6685 -249
  6. package/build/web/core/component.scss +557 -69
  7. package/build/web/core/css-utils/a2-border.css +23 -51
  8. package/build/web/core/css-utils/a2-color.css +221 -233
  9. package/build/web/core/css-utils/a2-font.css +1 -29
  10. package/build/web/core/css-utils/a2-spacing.css +238 -483
  11. package/build/web/core/css-utils/a2-utils.css +496 -781
  12. package/build/web/core/css-utils/border.css +23 -51
  13. package/build/web/core/css-utils/color.css +221 -233
  14. package/build/web/core/css-utils/font.css +1 -29
  15. package/build/web/core/css-utils/spacing.css +238 -483
  16. package/build/web/core/css-utils/utils.css +496 -781
  17. package/build/web/core/index.d.ts +6 -0
  18. package/build/web/core/index.js +1 -1
  19. package/build/web/core/primitive-variables.scss +148 -65
  20. package/build/web/core/primitive.d.ts +209 -0
  21. package/build/web/core/primitive.js +779 -61
  22. package/build/web/core/primitive.scss +207 -124
  23. package/build/web/core/semantic-variables.scss +363 -245
  24. package/build/web/core/semantic.d.ts +221 -0
  25. package/build/web/core/semantic.js +1592 -347
  26. package/build/web/core/semantic.scss +219 -140
  27. package/build/web/index.d.ts +3 -4
  28. package/build/web/types.d.ts +17 -0
  29. package/config.js +121 -496
  30. package/eslint.config.mjs +11 -1
  31. package/package.json +15 -5
  32. package/src/global/primitive/breakpoint.tokens.json +54 -0
  33. package/src/global/primitive/color.tokens.json +1092 -0
  34. package/src/global/primitive/duration.tokens.json +44 -0
  35. package/src/global/primitive/font.tokens.json +151 -0
  36. package/src/global/primitive/radius.tokens.json +94 -0
  37. package/src/global/primitive/size.tokens.json +174 -0
  38. package/src/global/primitive/transition.tokens.json +32 -0
  39. package/src/theme/core/background.tokens.json +1312 -0
  40. package/src/theme/core/border.tokens.json +192 -0
  41. package/src/theme/core/chart.tokens.json +982 -0
  42. package/src/theme/core/component/ai-mark.tokens.json +20 -0
  43. package/src/theme/core/component/alert.tokens.json +261 -0
  44. package/src/theme/core/component/announcement.tokens.json +460 -0
  45. package/src/theme/core/component/avatar.tokens.json +137 -0
  46. package/src/theme/core/component/badge.tokens.json +42 -0
  47. package/src/theme/core/component/breadcrumb.tokens.json +42 -0
  48. package/src/theme/core/component/button-toggle.tokens.json +428 -0
  49. package/src/theme/core/component/button.tokens.json +941 -0
  50. package/src/theme/core/component/calendar.tokens.json +391 -0
  51. package/src/theme/core/component/card.tokens.json +107 -0
  52. package/src/theme/core/component/checkbox.tokens.json +631 -0
  53. package/src/theme/core/component/chip.tokens.json +169 -0
  54. package/src/theme/core/component/combobox.tokens.json +269 -0
  55. package/src/theme/core/component/details.tokens.json +152 -0
  56. package/src/theme/core/component/dialog.tokens.json +87 -0
  57. package/src/theme/core/component/divider.tokens.json +23 -0
  58. package/src/theme/core/component/dnd.tokens.json +208 -0
  59. package/src/theme/core/component/drawer.tokens.json +61 -0
  60. package/src/theme/core/component/drilldown.tokens.json +61 -0
  61. package/src/theme/core/component/edit-card.tokens.json +381 -0
  62. package/src/theme/core/component/field-label.tokens.json +42 -0
  63. package/src/theme/core/component/field-message.tokens.json +65 -0
  64. package/src/theme/core/component/icon.tokens.json +42 -0
  65. package/src/theme/core/component/link.tokens.json +108 -0
  66. package/src/theme/core/component/list-view.tokens.json +82 -0
  67. package/src/theme/core/component/listbox.tokens.json +283 -0
  68. package/src/theme/core/component/menu.tokens.json +230 -0
  69. package/src/theme/core/component/overflow.tokens.json +84 -0
  70. package/src/theme/core/component/page.tokens.json +377 -0
  71. package/src/theme/core/component/pagination.tokens.json +63 -0
  72. package/src/theme/core/component/popover.tokens.json +122 -0
  73. package/src/theme/core/component/progress-bar.tokens.json +133 -0
  74. package/src/theme/core/component/radio.tokens.json +631 -0
  75. package/src/theme/core/component/segmented-control.tokens.json +175 -0
  76. package/src/theme/core/component/select-card.tokens.json +943 -0
  77. package/src/theme/core/component/side-nav.tokens.json +349 -0
  78. package/src/theme/core/component/skeleton.tokens.json +42 -0
  79. package/src/theme/core/component/spinner.tokens.json +96 -0
  80. package/src/theme/core/component/status-icon.tokens.json +164 -0
  81. package/src/theme/core/component/stepper.tokens.json +484 -0
  82. package/src/theme/core/component/switch.tokens.json +285 -0
  83. package/src/theme/core/component/tab.tokens.json +192 -0
  84. package/src/theme/core/component/text-field.tokens.json +160 -0
  85. package/src/theme/core/component/text.tokens.json +59 -0
  86. package/src/theme/core/component/toast.tokens.json +343 -0
  87. package/src/theme/core/component/toolbar.tokens.json +114 -0
  88. package/src/theme/core/component/tooltip.tokens.json +61 -0
  89. package/src/theme/core/focus.tokens.json +56 -0
  90. package/src/theme/core/foreground.tokens.json +416 -0
  91. package/src/theme/core/gradient.tokens.json +41 -0
  92. package/src/theme/core/opacity.tokens.json +25 -0
  93. package/src/theme/core/shadow.tokens.json +81 -0
  94. package/src/theme/core/status.tokens.json +74 -0
  95. package/src/theme/core/typography.tokens.json +163 -0
  96. package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
  97. package/src/utils/__tests__/sd-build-configs.test.js +306 -0
  98. package/src/utils/__tests__/sd-formats.test.js +942 -0
  99. package/src/utils/__tests__/sd-transforms.test.js +336 -0
  100. package/src/utils/__tests__/token-helpers.test.js +1160 -0
  101. package/src/utils/copy-css-utils-cli.js +13 -1
  102. package/src/utils/css-utils-format-utils.js +105 -176
  103. package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
  104. package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
  105. package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
  106. package/src/utils/figma/auth.js +355 -0
  107. package/src/utils/figma/constants.js +22 -0
  108. package/src/utils/figma/errors.js +80 -0
  109. package/src/utils/figma/figma-api.js +1069 -0
  110. package/src/utils/figma/get-token.js +348 -0
  111. package/src/utils/figma/sync-components.js +909 -0
  112. package/src/utils/figma/sync-main.js +692 -0
  113. package/src/utils/figma/sync-orchestration.js +683 -0
  114. package/src/utils/figma/sync-primitives.js +230 -0
  115. package/src/utils/figma/sync-semantic.js +1056 -0
  116. package/src/utils/figma/token-conversion.js +340 -0
  117. package/src/utils/figma/token-parsing.js +186 -0
  118. package/src/utils/figma/token-resolution.js +569 -0
  119. package/src/utils/figma/utils.js +199 -0
  120. package/src/utils/sd-build-configs.js +305 -0
  121. package/src/utils/sd-formats.js +948 -0
  122. package/src/utils/sd-transforms.js +165 -0
  123. package/src/utils/token-helpers.js +848 -0
  124. package/tsconfig.json +18 -0
  125. package/vitest.config.js +17 -0
  126. package/.turbo/turbo-build.log +0 -37
  127. package/build/web/core/raw.js +0 -234
  128. package/src/global/primitive/breakpoint.js +0 -19
  129. package/src/global/primitive/color.js +0 -231
  130. package/src/global/primitive/duration.js +0 -16
  131. package/src/global/primitive/font.js +0 -60
  132. package/src/global/primitive/radius.js +0 -31
  133. package/src/global/primitive/size.js +0 -55
  134. package/src/global/primitive/transition.js +0 -16
  135. package/src/theme/core/background.js +0 -170
  136. package/src/theme/core/border.js +0 -103
  137. package/src/theme/core/charts.js +0 -464
  138. package/src/theme/core/component/button.js +0 -708
  139. package/src/theme/core/component/checkbox.js +0 -405
  140. package/src/theme/core/focus.js +0 -35
  141. package/src/theme/core/foreground.js +0 -148
  142. package/src/theme/core/overlay.js +0 -137
  143. package/src/theme/core/shadow.js +0 -29
  144. package/src/theme/core/status.js +0 -49
  145. package/src/theme/core/typography.js +0 -82
  146. 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
+ });