@servicetitan/hammer-token 2.5.0 → 3.0.0

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