@figma-vars/hooks 3.1.0 → 4.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.
package/README.md CHANGED
@@ -10,24 +10,25 @@ A fast, typed React 19.2.3 hooks library for the Figma Variables API: fetch, upd
10
10
 
11
11
  Built for the modern web, this library provides a suite of hooks to fetch, manage, and mutate your design tokens/variables, making it easy to sync them between Figma and your React applications, Storybooks, or design system dashboards.
12
12
 
13
- ![Status](https://img.shields.io/badge/status-stable-brightgreen)
14
- ![CI](https://github.com/marklearst/figma-vars-hooks/actions/workflows/ci.yml/badge.svg)
15
- [![codecov](https://codecov.io/gh/marklearst/figma-vars-hooks/branch/main/graph/badge.svg)](https://codecov.io/gh/marklearst/figma-vars-hooks)
16
- ![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
17
- ![License](https://img.shields.io/github/license/marklearst/figma-vars-hooks)
18
- ![GitHub last commit](https://img.shields.io/github/last-commit/marklearst/figma-vars-hooks)
19
- ![GitHub code size](https://img.shields.io/github/languages/code-size/marklearst/figma-vars-hooks)
20
-
21
- ## 📌 Why 3.0
22
-
23
- - **New DX Features**: SWR configuration support, error handling utilities, cache invalidation helpers
24
- - 🔧 **React 19.2 Ready**: Optimized hooks with proper cleanup and stable function references
25
- - 🛡️ **Better Error Handling**: `FigmaApiError` class with HTTP status codes for better error differentiation
26
- - **Type Safety**: Removed unsafe type assertions, improved type definitions throughout
27
- - 🚀 **Performance**: Hardened SWR usage (stable keys, `null` to disable, cleaner fallback handling)
13
+ | Package | Quality | Activity |
14
+ | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
15
+ | [![npm version](https://img.shields.io/npm/v/%40figma-vars%2Fhooks)](https://www.npmjs.com/package/@figma-vars/hooks) | ![CI](https://github.com/marklearst/figma-vars-hooks/actions/workflows/ci.yml/badge.svg) | ![GitHub last commit](https://img.shields.io/github/last-commit/marklearst/figma-vars-hooks) |
16
+ | ![npm downloads](https://img.shields.io/npm/dm/%40figma-vars%2Fhooks) | [![codecov](https://codecov.io/gh/marklearst/figma-vars-hooks/branch/main/graph/badge.svg)](https://codecov.io/gh/marklearst/figma-vars-hooks) | ![Status](https://img.shields.io/badge/status-stable-brightgreen) |
17
+ | ![bundle size](https://img.shields.io/bundlephobia/minzip/%40figma-vars%2Fhooks) | ![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen) | ![License](https://img.shields.io/github/license/marklearst/figma-vars-hooks) |
18
+ | ![node version](https://img.shields.io/node/v/%40figma-vars%2Fhooks) | ![TypeScript](https://img.shields.io/badge/TypeScript-100%25_Strict-blue?logo=typescript) | ![GitHub code size](https://img.shields.io/github/languages/code-size/marklearst/figma-vars-hooks) |
19
+
20
+ ## 📌 Why 4.0.0
21
+
22
+ - ✨ **New Utilities**: `withRetry()` for automatic retry with exponential backoff, `redactToken()` for safe logging
23
+ - 🔧 **Flexible API**: `baseUrl` option for fetcher/mutator, `caseInsensitive` option for filterVariables
24
+ - 🛡️ **Better Error Handling**: Improved parsing for non-JSON API responses (HTML, plain text)
25
+ - 🐛 **Critical Bug Fix**: SWR cache keys now correctly separate fallback and live data
26
+ - 📚 **Improved Docs**: Comprehensive mutation return type documentation with examples
28
27
  - 📦 **Modern Tooling**: Node 20+ toolchain, strict TypeScript, and ESM-first packaging with CJS interop
29
28
  - 🖥️ **CLI Export Tool**: Automate variable exports with `figma-vars-export` for CI/CD (Enterprise required)
30
29
 
30
+ > ⚠️ **Breaking Change**: `useFigmaToken` is now a named export. See [Migration Guide](#-migration-guide-3x--40).
31
+
31
32
  ## 🚀 Features at a Glance
32
33
 
33
34
  - **Modern React 19.2 hooks** for variables, collections, modes, and published variables
@@ -301,7 +302,7 @@ if (isLocalVariablesResponse(data)) {
301
302
 
302
303
  ### Error Utilities
303
304
 
304
- 3.0.0 introduces powerful error handling utilities for type-safe error checking:
305
+ v3 introduces powerful error handling utilities for type-safe error checking:
305
306
 
306
307
  ```tsx
307
308
  import { isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus } from '@figma-vars/hooks'
@@ -424,11 +425,13 @@ Customize SWR behavior globally through the provider:
424
425
 
425
426
  ### Utilities
426
427
 
427
- - **Filtering**: `filterVariables` (filter by type, name, etc.)
428
- - **Error Handling**: `isFigmaApiError`, `getErrorStatus`, `getErrorMessage`, `hasErrorStatus`
428
+ - **Filtering**: `filterVariables` (filter by type, name, with optional `caseInsensitive` matching)
429
+ - **Retry**: `withRetry` (automatic retry with exponential backoff for rate limits)
430
+ - **Security**: `redactToken` (safely redact tokens for logging/display)
431
+ - **Error Handling**: `isFigmaApiError`, `getErrorStatus`, `getErrorMessage`, `hasErrorStatus`, `isRateLimited`, `getRetryAfter`
429
432
  - **Type Guards**: `isLocalVariablesResponse`, `isPublishedVariablesResponse`, `validateFallbackData` (runtime validation)
430
433
  - **SWR Keys**: `getVariablesKey`, `getPublishedVariablesKey`, `getInvalidationKeys` (centralized cache key construction)
431
- - **Core helpers**: `fetcher`, `mutator`, constants for endpoints and headers
434
+ - **Core helpers**: `fetcher`, `mutator` (with `baseUrl` option), constants for endpoints and headers
432
435
 
433
436
  ### Types
434
437
 
@@ -454,13 +457,30 @@ Customize SWR behavior globally through the provider:
454
457
  - Never commit PATs or file keys to git, Storybook static builds, or client bundles.
455
458
  - Use environment variables (`process.env` / `import.meta.env`) and secret managers; keep them server-side where possible.
456
459
  - Prefer `fallbackFile` with `token={null}`/`fileKey={null}` for demos and public Storybooks.
457
- - Avoid logging tokens or keys; scrub them from error messages and analytics.
460
+ - Use `redactToken()` when logging tokens for debugging:
461
+
462
+ ```ts
463
+ import { redactToken } from '@figma-vars/hooks'
464
+
465
+ // Safe logging
466
+ console.log('Using token:', redactToken(token))
467
+ // Output: "Using token: figd_***...***cret"
468
+ ```
458
469
 
459
470
  ## 📈 Rate Limits
460
471
 
461
472
  - Figma enforces per-token limits. Rely on SWR/TanStack caching, avoid unnecessary refetches, and prefer fallback JSON for static sites.
462
473
  - Use `swrConfig` to customize `dedupingInterval` and `errorRetryCount` to optimize API usage.
463
- - Handle `429` rate limit errors with `isFigmaApiError` and implement exponential backoff if needed.
474
+ - Use `withRetry()` utility for automatic retry with exponential backoff on 429 errors:
475
+
476
+ ```ts
477
+ import { withRetry, fetcher } from '@figma-vars/hooks'
478
+
479
+ const fetchWithRetry = withRetry(() => fetcher('/v1/files/KEY/variables/local', token), {
480
+ maxRetries: 3,
481
+ onRetry: (attempt, delay) => console.log(`Retry ${attempt}...`),
482
+ })
483
+ ```
464
484
 
465
485
  ## 📚 Storybook & Next.js
466
486
 
@@ -511,11 +531,50 @@ export function Providers({ children }: { children: React.ReactNode }) {
511
531
  - `pnpm run build`, `pnpm test`, `pnpm run test:coverage`
512
532
  - `pnpm run check:publint`, `pnpm run check:attw`, `pnpm run check:size`
513
533
 
514
- ## 🧭 Release Checklist (for 3.0.0)
534
+ ## 🧭 Release Checklist (for 4.0.0)
515
535
 
516
536
  - Run `pnpm run check:release`
517
- - Tag `v3.0.0` (CI publishes to npm)
518
- - Update dist-tags on npm if needed (`latest` → 3.0.0)
537
+ - Run `pnpm version major` (creates `v4.0.0` tag)
538
+ - CI publishes to npm automatically
539
+ - Update dist-tags on npm if needed (`latest` → 4.0.0)
540
+
541
+ ## 🔄 Migration Guide (3.x → 4.0)
542
+
543
+ ### Breaking Change: `useFigmaToken` Export
544
+
545
+ ```tsx
546
+ // Before (3.x) - NO LONGER WORKS
547
+ import useFigmaToken from '@figma-vars/hooks'
548
+
549
+ // After (4.0) - USE THIS
550
+ import { useFigmaToken } from '@figma-vars/hooks'
551
+ ```
552
+
553
+ ### New Utilities (opt-in)
554
+
555
+ ```ts
556
+ import { withRetry, redactToken, filterVariables } from '@figma-vars/hooks'
557
+
558
+ // Automatic retry with exponential backoff
559
+ const fetchWithRetry = withRetry(() => myApiCall(), { maxRetries: 3 })
560
+
561
+ // Safe token logging
562
+ console.log('Token:', redactToken(token)) // "figd_***...***cret"
563
+
564
+ // Case-insensitive filtering
565
+ filterVariables(vars, { name: 'primary', caseInsensitive: true })
566
+ ```
567
+
568
+ ### Custom API Base URL
569
+
570
+ ```ts
571
+ import { fetcher, mutator } from '@figma-vars/hooks/core'
572
+
573
+ // Use mock server for testing
574
+ await fetcher('/v1/files/KEY/variables/local', token, {
575
+ baseUrl: 'http://localhost:3000',
576
+ })
577
+ ```
519
578
 
520
579
  ---
521
580
 
@@ -16,6 +16,11 @@ export interface FetcherOptions {
16
16
  * Optional fetch implementation override (useful for testing or custom fetch implementations).
17
17
  */
18
18
  fetch?: typeof fetch;
19
+ /**
20
+ * Optional base URL override. Defaults to 'https://api.figma.com'.
21
+ * Useful for testing with mocks or proxies.
22
+ */
23
+ baseUrl?: string;
19
24
  }
20
25
  /**
21
26
  * Low-level utility to fetch data from the Figma Variables REST API with authentication.
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/api/fetcher.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CA0FpB"}
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/api/fetcher.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CAyGpB"}
@@ -17,6 +17,11 @@ export interface MutatorOptions {
17
17
  * Optional fetch implementation override (useful for testing or custom fetch implementations).
18
18
  */
19
19
  fetch?: typeof fetch;
20
+ /**
21
+ * Optional base URL override. Defaults to 'https://api.figma.com'.
22
+ * Useful for testing with mocks or proxies.
23
+ */
24
+ baseUrl?: string;
20
25
  }
21
26
  /**
22
27
  * Low-level utility to send authenticated POST, PUT, or DELETE requests to the Figma Variables REST API.
@@ -1 +1 @@
1
- {"version":3,"file":"mutator.d.ts","sourceRoot":"","sources":["../../src/api/mutator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3E;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EACD,iBAAiB,GACjB;IAAE,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,GAC9C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CAyGpB"}
1
+ {"version":3,"file":"mutator.d.ts","sourceRoot":"","sources":["../../src/api/mutator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3E;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,OAAO,CAAC,SAAS,GAAG,OAAO,EAC/C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EACD,iBAAiB,GACjB;IAAE,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,GAC9C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CAwHpB"}
package/dist/core.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const _=require("./index-5ZyKWuYv.cjs");exports.CONTENT_TYPE_JSON=_.CONTENT_TYPE_JSON;exports.ERROR_MSG_BULK_UPDATE_FAILED=_.ERROR_MSG_BULK_UPDATE_FAILED;exports.ERROR_MSG_CREATE_VARIABLE_FAILED=_.ERROR_MSG_CREATE_VARIABLE_FAILED;exports.ERROR_MSG_DELETE_VARIABLE_FAILED=_.ERROR_MSG_DELETE_VARIABLE_FAILED;exports.ERROR_MSG_FETCH_FIGMA_DATA_FAILED=_.ERROR_MSG_FETCH_FIGMA_DATA_FAILED;exports.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED=_.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED;exports.ERROR_MSG_TOKEN_REQUIRED=_.ERROR_MSG_TOKEN_REQUIRED;exports.ERROR_MSG_UPDATE_VARIABLE_FAILED=_.ERROR_MSG_UPDATE_VARIABLE_FAILED;exports.FIGMA_API_BASE_URL=_.FIGMA_API_BASE_URL;exports.FIGMA_FILES_ENDPOINT=_.FIGMA_FILES_ENDPOINT;exports.FIGMA_FILE_VARIABLES_PATH=_.FIGMA_FILE_VARIABLES_PATH;exports.FIGMA_LOCAL_VARIABLES_ENDPOINT=_.FIGMA_LOCAL_VARIABLES_ENDPOINT;exports.FIGMA_PUBLISHED_VARIABLES_PATH=_.FIGMA_PUBLISHED_VARIABLES_PATH;exports.FIGMA_TOKEN_HEADER=_.FIGMA_TOKEN_HEADER;exports.fetcher=_.fetcher;exports.filterVariables=_.filterVariables;exports.mutator=_.mutator;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const _=require("./index-DFB8mxu_.cjs");exports.CONTENT_TYPE_JSON=_.CONTENT_TYPE_JSON;exports.ERROR_MSG_BULK_UPDATE_FAILED=_.ERROR_MSG_BULK_UPDATE_FAILED;exports.ERROR_MSG_CREATE_VARIABLE_FAILED=_.ERROR_MSG_CREATE_VARIABLE_FAILED;exports.ERROR_MSG_DELETE_VARIABLE_FAILED=_.ERROR_MSG_DELETE_VARIABLE_FAILED;exports.ERROR_MSG_FETCH_FIGMA_DATA_FAILED=_.ERROR_MSG_FETCH_FIGMA_DATA_FAILED;exports.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED=_.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED;exports.ERROR_MSG_TOKEN_REQUIRED=_.ERROR_MSG_TOKEN_REQUIRED;exports.ERROR_MSG_UPDATE_VARIABLE_FAILED=_.ERROR_MSG_UPDATE_VARIABLE_FAILED;exports.FIGMA_API_BASE_URL=_.FIGMA_API_BASE_URL;exports.FIGMA_FILES_ENDPOINT=_.FIGMA_FILES_ENDPOINT;exports.FIGMA_FILE_VARIABLES_PATH=_.FIGMA_FILE_VARIABLES_PATH;exports.FIGMA_LOCAL_VARIABLES_ENDPOINT=_.FIGMA_LOCAL_VARIABLES_ENDPOINT;exports.FIGMA_PUBLISHED_VARIABLES_PATH=_.FIGMA_PUBLISHED_VARIABLES_PATH;exports.FIGMA_TOKEN_HEADER=_.FIGMA_TOKEN_HEADER;exports.fetcher=_.fetcher;exports.filterVariables=_.filterVariables;exports.mutator=_.mutator;
package/dist/core.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as A, j as R, k as I, l as a, o as L, a as s, E as F, n as T, d as D, e as O, F as G, h as M, g as S, i as N, f as B, c as P, m as U } from "./index-ClHLYVvu.js";
1
+ import { C as A, j as R, k as I, l as a, o as L, a as s, E as F, n as T, d as D, e as O, F as G, h as M, g as S, i as N, f as B, c as P, m as U } from "./index-Dqg9kaMQ.js";
2
2
  export {
3
3
  A as CONTENT_TYPE_JSON,
4
4
  R as ERROR_MSG_BULK_UPDATE_FAILED,
@@ -6,21 +6,35 @@ import { BulkUpdatePayload } from '../types/mutations';
6
6
  * This hook is designed to perform a batch operation for creating, updating, and deleting variables, collections, and modes.
7
7
  * It provides an ergonomic API with `mutate` and loading/error state for easy integration.
8
8
  *
9
+ * ## Return Value
10
+ *
11
+ * The `mutate` function returns `Promise<TData | undefined>`:
12
+ * - On success: Returns the API response data
13
+ * - On error: Returns `undefined` (error stored in `error` state)
14
+ *
15
+ * Use `isSuccess`/`isError` flags or check the return value to handle results.
16
+ *
17
+ * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`),
18
+ * `data` (API response), and `error` (if failed).
19
+ *
9
20
  * @example
10
21
  * ```tsx
11
22
  * import { useBulkUpdateVariables } from '@figma-vars/hooks';
12
23
  *
13
24
  * function BulkUpdateButton() {
14
- * const { mutate, isLoading, error } = useBulkUpdateVariables();
25
+ * const { mutate, isLoading, isError, error } = useBulkUpdateVariables();
15
26
  *
16
- * const handleBulkUpdate = () => {
17
- * mutate({
27
+ * const handleBulkUpdate = async () => {
28
+ * const result = await mutate({
18
29
  * variables: [{ action: 'UPDATE', id: 'VariableId:123', name: 'new-name' }],
19
30
  * });
31
+ * if (result) {
32
+ * console.log('Bulk update successful');
33
+ * }
20
34
  * };
21
35
  *
22
36
  * if (isLoading) return <div>Updating...</div>;
23
- * if (error) return <div>Error: {error.message}</div>;
37
+ * if (isError) return <div>Error: {error?.message}</div>;
24
38
  * return <button onClick={handleBulkUpdate}>Bulk Update</button>;
25
39
  * }
26
40
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"useBulkUpdateVariables.d.ts","sourceRoot":"","sources":["../../src/hooks/useBulkUpdateVariables.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAQxD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,sBAAsB,4EAiBlC,CAAA"}
1
+ {"version":3,"file":"useBulkUpdateVariables.d.ts","sourceRoot":"","sources":["../../src/hooks/useBulkUpdateVariables.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAQxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,sBAAsB,4EAiBlC,CAAA"}
@@ -5,19 +5,37 @@ import { CreateVariablePayload } from '../types/mutations';
5
5
  * @remarks
6
6
  * The hook returns a `mutate` function to trigger the creation along with state flags and data.
7
7
  *
8
+ * ## Return Value
9
+ *
10
+ * The `mutate` function returns `Promise<TData | undefined>`:
11
+ * - On success: Returns the API response data
12
+ * - On error: Returns `undefined` (error stored in `error` state)
13
+ *
14
+ * Use `isSuccess`/`isError` flags or check the return value to handle results.
15
+ *
16
+ * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`),
17
+ * `data` (API response), and `error` (if failed).
18
+ *
8
19
  * @example
9
20
  * ```tsx
10
21
  * import { useCreateVariable } from '@figma-vars/hooks';
11
22
  *
12
23
  * function CreateVariableButton() {
13
- * const { mutate, isLoading, error } = useCreateVariable();
24
+ * const { mutate, isLoading, isError, error } = useCreateVariable();
14
25
  *
15
- * const handleCreate = () => {
16
- * mutate({ name: 'new-variable', variableCollectionId: 'VariableCollectionId:1:1', resolvedType: 'COLOR' });
26
+ * const handleCreate = async () => {
27
+ * const result = await mutate({
28
+ * name: 'new-variable',
29
+ * variableCollectionId: 'VariableCollectionId:1:1',
30
+ * resolvedType: 'COLOR'
31
+ * });
32
+ * if (result) {
33
+ * console.log('Created successfully:', result);
34
+ * }
17
35
  * };
18
36
  *
19
37
  * if (isLoading) return <div>Creating...</div>;
20
- * if (error) return <div>Error: {error.message}</div>;
38
+ * if (isError) return <div>Error: {error?.message}</div>;
21
39
  * return <button onClick={handleCreate}>Create Variable</button>;
22
40
  * }
23
41
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"useCreateVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useCreateVariable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAQ5D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,iBAAiB,gFAmB7B,CAAA"}
1
+ {"version":3,"file":"useCreateVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useCreateVariable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAQ5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,iBAAiB,gFAmB7B,CAAA"}
@@ -4,17 +4,33 @@
4
4
  * @remarks
5
5
  * This hook provides a `mutate` function to trigger the deletion and exposes loading and error states.
6
6
  *
7
+ * ## Return Value
8
+ *
9
+ * The `mutate` function returns `Promise<TData | undefined>`:
10
+ * - On success: Returns the API response data
11
+ * - On error: Returns `undefined` (error stored in `error` state)
12
+ *
13
+ * Use `isSuccess`/`isError` flags or check the return value to handle results.
14
+ *
15
+ * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`),
16
+ * `data` (API response), and `error` (if failed).
17
+ *
7
18
  * @example
8
19
  * ```tsx
9
20
  * import { useDeleteVariable } from '@figma-vars/hooks';
10
21
  *
11
22
  * function DeleteVariableButton({ id }: { id: string }) {
12
- * const { mutate, isLoading, error } = useDeleteVariable();
23
+ * const { mutate, isLoading, isError, error } = useDeleteVariable();
13
24
  *
14
- * const onDelete = () => mutate(id);
25
+ * const onDelete = async () => {
26
+ * const result = await mutate(id);
27
+ * if (result) {
28
+ * console.log('Deleted successfully');
29
+ * }
30
+ * };
15
31
  *
16
32
  * if (isLoading) return <div>Deleting...</div>;
17
- * if (error) return <div>Error: {error.message}</div>;
33
+ * if (isError) return <div>Error: {error?.message}</div>;
18
34
  * return <button onClick={onDelete}>Delete Variable</button>;
19
35
  * }
20
36
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"useDeleteVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useDeleteVariable.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,oDAmB7B,CAAA"}
1
+ {"version":3,"file":"useDeleteVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useDeleteVariable.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,iBAAiB,oDAmB7B,CAAA"}
@@ -5,17 +5,33 @@ import { UpdateVariablePayload } from '../types/mutations';
5
5
  * @remarks
6
6
  * The hook returns a `mutate` function to trigger the update with given payload and exposes state flags.
7
7
  *
8
+ * ## Return Value
9
+ *
10
+ * The `mutate` function returns `Promise<TData | undefined>`:
11
+ * - On success: Returns the API response data
12
+ * - On error: Returns `undefined` (error stored in `error` state)
13
+ *
14
+ * Use `isSuccess`/`isError` flags or check the return value to handle results.
15
+ *
16
+ * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`),
17
+ * `data` (API response), and `error` (if failed).
18
+ *
8
19
  * @example
9
20
  * ```tsx
10
21
  * import { useUpdateVariable } from '@figma-vars/hooks';
11
22
  *
12
23
  * function UpdateVariableButton({ id }: { id: string }) {
13
- * const { mutate, isLoading, error } = useUpdateVariable();
24
+ * const { mutate, isLoading, isError, error } = useUpdateVariable();
14
25
  *
15
- * const onUpdate = () => mutate({ variableId: id, payload: { name: 'new-name' } });
26
+ * const onUpdate = async () => {
27
+ * const result = await mutate({ variableId: id, payload: { name: 'new-name' } });
28
+ * if (result) {
29
+ * console.log('Updated successfully');
30
+ * }
31
+ * };
16
32
  *
17
33
  * if (isLoading) return <div>Updating...</div>;
18
- * if (error) return <div>Error: {error.message}</div>;
34
+ * if (isError) return <div>Error: {error?.message}</div>;
19
35
  * return <button onClick={onUpdate}>Update Variable</button>;
20
36
  * }
21
37
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"useUpdateVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useUpdateVariable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAQ5D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB;gBAOZ,MAAM;aACT,qBAAqB;EAyBnC,CAAA"}
1
+ {"version":3,"file":"useUpdateVariable.d.ts","sourceRoot":"","sources":["../../src/hooks/useUpdateVariable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAQ5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,iBAAiB;gBAOZ,MAAM;aACT,qBAAqB;EAyBnC,CAAA"}
@@ -0,0 +1 @@
1
+ "use strict";var y=Object.defineProperty;var N=(e,s,r)=>s in e?y(e,s,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[s]=r;var g=(e,s,r)=>N(e,typeof s!="symbol"?s+"":s,r);const L="https://api.figma.com",O=`${L}/v1/files`,v=e=>`/v1/files/${e}/variables/published`,P=e=>`/v1/files/${e}/variables`,w=e=>`${O}/${e}/variables/local`,D="application/json",S="X-FIGMA-TOKEN",m="A Figma API token is required.",B=`${m} and file key are required.`,U="Failed to perform bulk update.",b="Failed to create Figma variable.",C="Failed to delete Figma variable.",x="Failed to update Figma variable.",G="An error occurred while fetching data from the Figma API.";class h extends Error{constructor(r,o,l){super(r);g(this,"statusCode");g(this,"retryAfter");this.name="FigmaApiError",this.statusCode=o,this.retryAfter=l??void 0,Error.captureStackTrace&&Error.captureStackTrace(this,h)}}async function V(e,s,r){if(!s)throw new Error(m);const{signal:o,timeout:l,fetch:F=fetch,baseUrl:f=L}=r??{};let E,A;const i=o||(l?(A=new AbortController,E=setTimeout(()=>{A==null||A.abort()},l),A.signal):void 0);try{const c=e.startsWith("http://")||e.startsWith("https://")?e:`${f}${e.startsWith("/")?"":"/"}${e}`,a=await F(c,{method:"GET",headers:{[S]:s,"Content-Type":D},...i!==void 0&&{signal:i}});if(E!==void 0&&(clearTimeout(E),E=void 0),!a.ok){let R=G;const u=a.status;let I;if(u===429){const _=a.headers.get("Retry-After");if(_){const t=parseInt(_,10);Number.isNaN(t)||(I=t)}}try{const _=a.headers.get("content-type")??"";if(_.includes("application/json")){const t=await a.json();t!=null&&t.message?R=t.message:t!=null&&t.err&&(R=t.err)}else if(_.includes("text/plain")||_.includes("text/html")){const t=await a.text();t&&(R=t.length>200?`${t.slice(0,200)}...`:t)}}catch{}throw new h(R,u,I)}return a.json()}catch(c){throw E!==void 0&&(clearTimeout(E),E=void 0),c}}async function $(e,s,r,o,l){if(!s)throw new Error(m);const{signal:F,timeout:f,fetch:E=fetch,baseUrl:A=L}=l??{};let i,c;const a=F||(f?(c=new AbortController,i=setTimeout(()=>{c==null||c.abort()},f),c.signal):void 0);try{const I={method:{CREATE:"POST",UPDATE:"PUT",DELETE:"DELETE"}[r],headers:{"Content-Type":"application/json",[S]:s},...a!==void 0&&{signal:a}};o&&(I.body=JSON.stringify(o));const _=e.startsWith("http://")||e.startsWith("https://")?e:`${A}${e.startsWith("/")?"":"/"}${e}`,t=await E(_,I);if(i!==void 0&&(clearTimeout(i),i=void 0),!t.ok){const p=t.status;let T="An API error occurred",M;if(p===429){const d=t.headers.get("Retry-After");if(d){const n=parseInt(d,10);Number.isNaN(n)||(M=n)}}try{const d=t.headers.get("content-type")??"";if(d.includes("application/json")){const n=await t.json();T=n.err||n.message||T}else if(d.includes("text/plain")||d.includes("text/html")){const n=await t.text();n&&(T=n.length>200?`${n.slice(0,200)}...`:n)}}catch{}throw new h(T,p,M)}return t.status===204||!t.body?{}:t.json()}catch(R){throw i!==void 0&&(clearTimeout(i),i=void 0),R}}function H(e,s){return e.filter(r=>{let o=!0;return s.resolvedType&&(o=o&&r.resolvedType===s.resolvedType),s.name&&(s.caseInsensitive?o=o&&r.name.toLowerCase().includes(s.name.toLowerCase()):o=o&&r.name.includes(s.name)),o})}exports.CONTENT_TYPE_JSON=D;exports.ERROR_MSG_BULK_UPDATE_FAILED=U;exports.ERROR_MSG_CREATE_VARIABLE_FAILED=b;exports.ERROR_MSG_DELETE_VARIABLE_FAILED=C;exports.ERROR_MSG_FETCH_FIGMA_DATA_FAILED=G;exports.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED=B;exports.ERROR_MSG_TOKEN_REQUIRED=m;exports.ERROR_MSG_UPDATE_VARIABLE_FAILED=x;exports.FIGMA_API_BASE_URL=L;exports.FIGMA_FILES_ENDPOINT=O;exports.FIGMA_FILE_VARIABLES_PATH=P;exports.FIGMA_LOCAL_VARIABLES_ENDPOINT=w;exports.FIGMA_PUBLISHED_VARIABLES_PATH=v;exports.FIGMA_TOKEN_HEADER=S;exports.FigmaApiError=h;exports.fetcher=V;exports.filterVariables=H;exports.mutator=$;
@@ -0,0 +1,150 @@
1
+ var w = Object.defineProperty;
2
+ var S = (e, s, a) => s in e ? w(e, s, { enumerable: !0, configurable: !0, writable: !0, value: a }) : e[s] = a;
3
+ var g = (e, s, a) => S(e, typeof s != "symbol" ? s + "" : s, a);
4
+ const F = "https://api.figma.com", M = `${F}/v1/files`, D = (e) => `/v1/files/${e}/variables/published`, G = (e) => `/v1/files/${e}/variables`, P = (e) => `${M}/${e}/variables/local`, N = "application/json", v = "X-FIGMA-TOKEN", L = "A Figma API token is required.", C = `${L} and file key are required.`, $ = "Failed to perform bulk update.", U = "Failed to create Figma variable.", B = "Failed to delete Figma variable.", j = "Failed to update Figma variable.", O = "An error occurred while fetching data from the Figma API.";
5
+ class u extends Error {
6
+ constructor(a, o, f) {
7
+ super(a);
8
+ /** HTTP status code from the API response. */
9
+ g(this, "statusCode");
10
+ /**
11
+ * Retry-After header value in seconds (for 429 rate limit errors).
12
+ * Undefined if not a rate limit error or header not present.
13
+ */
14
+ g(this, "retryAfter");
15
+ this.name = "FigmaApiError", this.statusCode = o, this.retryAfter = f ?? void 0, Error.captureStackTrace && Error.captureStackTrace(this, u);
16
+ }
17
+ }
18
+ async function H(e, s, a) {
19
+ if (!s)
20
+ throw new Error(L);
21
+ const {
22
+ signal: o,
23
+ timeout: f,
24
+ fetch: T = fetch,
25
+ baseUrl: _ = F
26
+ } = a ?? {};
27
+ let c, E;
28
+ const r = o || (f ? (E = new AbortController(), c = setTimeout(() => {
29
+ E == null || E.abort();
30
+ }, f), E.signal) : void 0);
31
+ try {
32
+ const l = e.startsWith("http://") || e.startsWith("https://") ? e : `${_}${e.startsWith("/") ? "" : "/"}${e}`, i = await T(l, {
33
+ method: "GET",
34
+ headers: {
35
+ [v]: s,
36
+ "Content-Type": N
37
+ },
38
+ ...r !== void 0 && { signal: r }
39
+ });
40
+ if (c !== void 0 && (clearTimeout(c), c = void 0), !i.ok) {
41
+ let h = O;
42
+ const R = i.status;
43
+ let m;
44
+ if (R === 429) {
45
+ const d = i.headers.get("Retry-After");
46
+ if (d) {
47
+ const t = parseInt(d, 10);
48
+ Number.isNaN(t) || (m = t);
49
+ }
50
+ }
51
+ try {
52
+ const d = i.headers.get("content-type") ?? "";
53
+ if (d.includes("application/json")) {
54
+ const t = await i.json();
55
+ t != null && t.message ? h = t.message : t != null && t.err && (h = t.err);
56
+ } else if (d.includes("text/plain") || d.includes("text/html")) {
57
+ const t = await i.text();
58
+ t && (h = t.length > 200 ? `${t.slice(0, 200)}...` : t);
59
+ }
60
+ } catch {
61
+ }
62
+ throw new u(h, R, m);
63
+ }
64
+ return i.json();
65
+ } catch (l) {
66
+ throw c !== void 0 && (clearTimeout(c), c = void 0), l;
67
+ }
68
+ }
69
+ async function V(e, s, a, o, f) {
70
+ if (!s)
71
+ throw new Error(L);
72
+ const {
73
+ signal: T,
74
+ timeout: _,
75
+ fetch: c = fetch,
76
+ baseUrl: E = F
77
+ } = f ?? {};
78
+ let r, l;
79
+ const i = T || (_ ? (l = new AbortController(), r = setTimeout(() => {
80
+ l == null || l.abort();
81
+ }, _), l.signal) : void 0);
82
+ try {
83
+ const m = {
84
+ method: {
85
+ CREATE: "POST",
86
+ UPDATE: "PUT",
87
+ DELETE: "DELETE"
88
+ }[a],
89
+ headers: {
90
+ "Content-Type": "application/json",
91
+ [v]: s
92
+ },
93
+ ...i !== void 0 && { signal: i }
94
+ };
95
+ o && (m.body = JSON.stringify(o));
96
+ const d = e.startsWith("http://") || e.startsWith("https://") ? e : `${E}${e.startsWith("/") ? "" : "/"}${e}`, t = await c(d, m);
97
+ if (r !== void 0 && (clearTimeout(r), r = void 0), !t.ok) {
98
+ const I = t.status;
99
+ let p = "An API error occurred", y;
100
+ if (I === 429) {
101
+ const A = t.headers.get("Retry-After");
102
+ if (A) {
103
+ const n = parseInt(A, 10);
104
+ Number.isNaN(n) || (y = n);
105
+ }
106
+ }
107
+ try {
108
+ const A = t.headers.get("content-type") ?? "";
109
+ if (A.includes("application/json")) {
110
+ const n = await t.json();
111
+ p = n.err || n.message || p;
112
+ } else if (A.includes("text/plain") || A.includes("text/html")) {
113
+ const n = await t.text();
114
+ n && (p = n.length > 200 ? `${n.slice(0, 200)}...` : n);
115
+ }
116
+ } catch {
117
+ }
118
+ throw new u(p, I, y);
119
+ }
120
+ return t.status === 204 || !t.body ? {} : t.json();
121
+ } catch (h) {
122
+ throw r !== void 0 && (clearTimeout(r), r = void 0), h;
123
+ }
124
+ }
125
+ function k(e, s) {
126
+ return e.filter((a) => {
127
+ let o = !0;
128
+ return s.resolvedType && (o = o && a.resolvedType === s.resolvedType), s.name && (s.caseInsensitive ? o = o && a.name.toLowerCase().includes(s.name.toLowerCase()) : o = o && a.name.includes(s.name)), o;
129
+ });
130
+ }
131
+ export {
132
+ N as C,
133
+ L as E,
134
+ G as F,
135
+ C as a,
136
+ u as b,
137
+ k as c,
138
+ F as d,
139
+ M as e,
140
+ H as f,
141
+ D as g,
142
+ P as h,
143
+ v as i,
144
+ $ as j,
145
+ U as k,
146
+ B as l,
147
+ V as m,
148
+ j as n,
149
+ O as o
150
+ };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("react/jsx-runtime"),u=require("react"),m=require("swr"),i=require("./index-5ZyKWuYv.cjs"),k=u.createContext(void 0);function A(e){if(typeof e!="object"||e===null)return!1;const r=e;if(typeof r.meta!="object"||r.meta===null)return!1;const t=r.meta;return!(typeof t.variableCollections!="object"||t.variableCollections===null||typeof t.variables!="object"||t.variables===null)}function _(e){if(A(e))return e}const V=({children:e,token:r,fileKey:t,fallbackFile:o,swrConfig:s})=>{const a=u.useId(),l=u.useMemo(()=>`figma-vars-provider-${a}`,[a]),f=u.useMemo(()=>{if(o){if(typeof o=="object"){const n=_(o);if(n)return n;process.env.NODE_ENV!=="production"&&console.warn("[figma-vars-hooks] fallbackFile object does not match expected Figma Variables API response structure. Expected { meta: { variableCollections: {...}, variables: {...} } }");return}if(typeof o=="string")try{const n=JSON.parse(o),d=_(n);if(d)return d;process.env.NODE_ENV!=="production"&&console.warn("[figma-vars-hooks] Parsed fallbackFile JSON does not match expected Figma Variables API response structure. Expected { meta: { variableCollections: {...}, variables: {...} } }");return}catch(n){process.env.NODE_ENV!=="production"&&console.error(`[figma-vars-hooks] Failed to parse fallbackFile JSON: ${n instanceof Error?n.message:"Unknown error"}`);return}}},[o]),c=u.useMemo(()=>{const n={token:r,fileKey:t,providerId:l,...s!==void 0&&{swrConfig:s}};return o===void 0?n:{...n,fallbackFile:o,parsedFallbackFile:f}},[r,t,o,f,l,s]);return g.jsx(k.Provider,{value:c,children:e})},b=()=>{const e=u.useContext(k);if(e===void 0)throw new Error("useFigmaTokenContext must be used within a FigmaVarsProvider");return e};function M(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e;return!!(t&&r)&&t&&r?[`https://api.figma.com/v1/files/${r}/variables/local`,t]:s?[`fallback-${o??"default"}`,"fallback"]:null}function O(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e;return!!(t&&r)&&t&&r?[`https://api.figma.com/v1/files/${r}/variables/published`,t]:s?[`fallback-${o??"default"}`,"fallback"]:null}function I(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e,a=[];return t&&r&&(a.push([`https://api.figma.com/v1/files/${r}/variables/local`,t]),a.push([`https://api.figma.com/v1/files/${r}/variables/published`,t])),s&&o&&a.push([`fallback-${o}`,"fallback"]),a}const R=()=>{const{token:e,fileKey:r,fallbackFile:t,parsedFallbackFile:o,providerId:s,swrConfig:a}=b(),f=M({fileKey:r,token:e,providerId:s,hasFallback:!!(t||o)});return m(f,async(...n)=>{if(o)return o;if(t&&typeof t=="object")return t;const[d,E]=Array.isArray(n[0])?n[0]:[n[0],n[1]];if(!d||!E)throw new Error("Missing URL or token for live API request");return i.fetcher(d,E)},a)},S=()=>{const{data:e}=R(),r=u.useMemo(()=>e!=null&&e.meta?Object.values(e.meta.variableCollections):[],[e]),t=u.useMemo(()=>e!=null&&e.meta?e.meta.variableCollections:{},[e]);return{collections:r,collectionsById:t}},K=()=>{const{data:e}=R();return u.useMemo(()=>{const r=[],t={},o={};if(e!=null&&e.meta)for(const s of Object.values(e.meta.variableCollections)){r.push(...s.modes),t[s.id]=s.modes;for(const a of s.modes)o[a.modeId]=a}return{modes:r,modesByCollectionId:t,modesById:o}},[e])};function T(e,r){switch(r.type){case"loading":return{...e,status:"loading",error:null};case"success":return{...e,status:"success",data:r.payload};case"error":return{...e,status:"error",error:r.payload};default:return e}}const v=(e,r)=>{const{throwOnError:t=!1}={},o={status:"idle",data:null,error:null},[s,a]=u.useReducer(T,o),l=u.useRef(e),f=u.useRef({throwOnError:t}),c=u.useRef(!0),n=u.useRef(0);return u.useEffect(()=>{l.current=e,f.current={throwOnError:t}},[e,t]),u.useEffect(()=>(c.current=!0,()=>{c.current=!1}),[]),{mutate:u.useCallback(async E=>{if(!c.current)return;const y=++n.current;a({type:"loading"});try{const p=await l.current(E);return c.current&&y===n.current&&a({type:"success",payload:p}),p}catch(p){const h=p;if(c.current&&y===n.current&&a({type:"error",payload:h}),f.current.throwOnError)throw h;return}},[]),...s,isLoading:s.status==="loading",isSuccess:s.status==="success",isError:s.status==="error"}},C=()=>{const{token:e,fileKey:r}=b();return v(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"CREATE",{variables:[{action:"CREATE",...o}]})})},L=()=>{const{token:e,fileKey:r}=b();return v(async({variableId:o,payload:s})=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"UPDATE",{variables:[{action:"UPDATE",id:o,...s}]})})},P=()=>{const{token:e,fileKey:r}=b();return v(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"DELETE",{variables:[{action:"DELETE",id:o}]})})},D=()=>{const{token:e,fileKey:r}=b();return v(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"UPDATE",o)})},U=()=>{const{mutate:e}=m.useSWRConfig(),{token:r,fileKey:t,fallbackFile:o,providerId:s}=b(),a=!!o;return{invalidate:()=>{const c=I({fileKey:t,token:r,providerId:s,hasFallback:a});for(const n of c)e(n)},revalidate:()=>{const c=I({fileKey:t,token:r,providerId:s,hasFallback:a});for(const n of c)e(n,void 0,{revalidate:!0})}}},N=()=>{const{token:e,fileKey:r,fallbackFile:t,parsedFallbackFile:o,providerId:s,swrConfig:a}=b(),f=O({fileKey:r,token:e,providerId:s,hasFallback:!!(t||o)});return m(f,async(...n)=>{if(o)return o;if(t&&typeof t=="object")return t;const[d,E]=Array.isArray(n[0])?n[0]:[n[0],n[1]];if(!d||!E)throw new Error("Missing URL or token for live API request");return i.fetcher(d,E)},a)};function w(e){return e instanceof i.FigmaApiError}function F(e){return w(e)?e.statusCode:null}function j(e,r="An error occurred"){return e instanceof Error?e.message||r:typeof e=="string"?e:r}function B(e,r){return F(e)===r}exports.FigmaApiError=i.FigmaApiError;exports.filterVariables=i.filterVariables;exports.FigmaVarsProvider=V;exports.getErrorMessage=j;exports.getErrorStatus=F;exports.hasErrorStatus=B;exports.isFigmaApiError=w;exports.useBulkUpdateVariables=D;exports.useCreateVariable=C;exports.useDeleteVariable=P;exports.useInvalidateVariables=U;exports.usePublishedVariables=N;exports.useUpdateVariable=L;exports.useVariableCollections=S;exports.useVariableModes=K;exports.useVariables=R;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("react/jsx-runtime"),u=require("react"),v=require("swr"),i=require("./index-DFB8mxu_.cjs"),k=u.createContext(void 0);function A(e){if(typeof e!="object"||e===null)return!1;const r=e;if(typeof r.meta!="object"||r.meta===null)return!1;const t=r.meta;return!(typeof t.variableCollections!="object"||t.variableCollections===null||typeof t.variables!="object"||t.variables===null)}function _(e){if(A(e))return e}const V=({children:e,token:r,fileKey:t,fallbackFile:o,swrConfig:s})=>{const a=u.useId(),d=u.useMemo(()=>`figma-vars-provider-${a}`,[a]),l=u.useMemo(()=>{if(o){if(typeof o=="object"){const n=_(o);if(n)return n;process.env.NODE_ENV!=="production"&&console.warn("[figma-vars-hooks] fallbackFile object does not match expected Figma Variables API response structure. Expected { meta: { variableCollections: {...}, variables: {...} } }");return}if(typeof o=="string")try{const n=JSON.parse(o),f=_(n);if(f)return f;process.env.NODE_ENV!=="production"&&console.warn("[figma-vars-hooks] Parsed fallbackFile JSON does not match expected Figma Variables API response structure. Expected { meta: { variableCollections: {...}, variables: {...} } }");return}catch(n){process.env.NODE_ENV!=="production"&&console.error(`[figma-vars-hooks] Failed to parse fallbackFile JSON: ${n instanceof Error?n.message:"Unknown error"}`);return}}},[o]),c=u.useMemo(()=>{const n={token:r,fileKey:t,providerId:d,...s!==void 0&&{swrConfig:s}};return o===void 0?n:{...n,fallbackFile:o,parsedFallbackFile:l}},[r,t,o,l,d,s]);return g.jsx(k.Provider,{value:c,children:e})},b=()=>{const e=u.useContext(k);if(e===void 0)throw new Error("useFigmaTokenContext must be used within a FigmaVarsProvider");return e};function M(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e;return s?[`fallback-${o??"default"}`,"fallback"]:t&&r?[`https://api.figma.com/v1/files/${r}/variables/local`,t]:null}function O(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e;return s?[`fallback-${o??"default"}`,"fallback"]:t&&r?[`https://api.figma.com/v1/files/${r}/variables/published`,t]:null}function I(e){const{fileKey:r,token:t,providerId:o,hasFallback:s}=e,a=[];return t&&r&&(a.push([`https://api.figma.com/v1/files/${r}/variables/local`,t]),a.push([`https://api.figma.com/v1/files/${r}/variables/published`,t])),s&&o&&a.push([`fallback-${o}`,"fallback"]),a}const R=()=>{const{token:e,fileKey:r,fallbackFile:t,parsedFallbackFile:o,providerId:s,swrConfig:a}=b(),l=M({fileKey:r,token:e,providerId:s,hasFallback:!!(t||o)});return v(l,async(...n)=>{if(o)return o;if(t&&typeof t=="object")return t;const[f,E]=Array.isArray(n[0])?n[0]:[n[0],n[1]];if(!f||!E)throw new Error("Missing URL or token for live API request");return i.fetcher(f,E)},a)},S=()=>{const{data:e}=R(),r=u.useMemo(()=>e!=null&&e.meta?Object.values(e.meta.variableCollections):[],[e]),t=u.useMemo(()=>e!=null&&e.meta?e.meta.variableCollections:{},[e]);return{collections:r,collectionsById:t}},K=()=>{const{data:e}=R();return u.useMemo(()=>{const r=[],t={},o={};if(e!=null&&e.meta)for(const s of Object.values(e.meta.variableCollections)){r.push(...s.modes),t[s.id]=s.modes;for(const a of s.modes)o[a.modeId]=a}return{modes:r,modesByCollectionId:t,modesById:o}},[e])};function T(e,r){switch(r.type){case"loading":return{...e,status:"loading",error:null};case"success":return{...e,status:"success",data:r.payload};case"error":return{...e,status:"error",error:r.payload};default:return e}}const m=(e,r)=>{const{throwOnError:t=!1}={},o={status:"idle",data:null,error:null},[s,a]=u.useReducer(T,o),d=u.useRef(e),l=u.useRef({throwOnError:t}),c=u.useRef(!0),n=u.useRef(0);return u.useEffect(()=>{d.current=e,l.current={throwOnError:t}},[e,t]),u.useEffect(()=>(c.current=!0,()=>{c.current=!1}),[]),{mutate:u.useCallback(async E=>{if(!c.current)return;const y=++n.current;a({type:"loading"});try{const p=await d.current(E);return c.current&&y===n.current&&a({type:"success",payload:p}),p}catch(p){const h=p;if(c.current&&y===n.current&&a({type:"error",payload:h}),l.current.throwOnError)throw h;return}},[]),...s,isLoading:s.status==="loading",isSuccess:s.status==="success",isError:s.status==="error"}},C=()=>{const{token:e,fileKey:r}=b();return m(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"CREATE",{variables:[{action:"CREATE",...o}]})})},P=()=>{const{token:e,fileKey:r}=b();return m(async({variableId:o,payload:s})=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"UPDATE",{variables:[{action:"UPDATE",id:o,...s}]})})},D=()=>{const{token:e,fileKey:r}=b();return m(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"DELETE",{variables:[{action:"DELETE",id:o}]})})},L=()=>{const{token:e,fileKey:r}=b();return m(async o=>{if(!e)throw new Error(i.ERROR_MSG_TOKEN_REQUIRED);if(!r)throw new Error(i.ERROR_MSG_TOKEN_FILE_KEY_REQUIRED);return await i.mutator(i.FIGMA_FILE_VARIABLES_PATH(r),e,"UPDATE",o)})},U=()=>{const{mutate:e}=v.useSWRConfig(),{token:r,fileKey:t,fallbackFile:o,providerId:s}=b(),a=!!o;return{invalidate:()=>{const c=I({fileKey:t,token:r,providerId:s,hasFallback:a});for(const n of c)e(n)},revalidate:()=>{const c=I({fileKey:t,token:r,providerId:s,hasFallback:a});for(const n of c)e(n,void 0,{revalidate:!0})}}},N=()=>{const{token:e,fileKey:r,fallbackFile:t,parsedFallbackFile:o,providerId:s,swrConfig:a}=b(),l=O({fileKey:r,token:e,providerId:s,hasFallback:!!(t||o)});return v(l,async(...n)=>{if(o)return o;if(t&&typeof t=="object")return t;const[f,E]=Array.isArray(n[0])?n[0]:[n[0],n[1]];if(!f||!E)throw new Error("Missing URL or token for live API request");return i.fetcher(f,E)},a)};function w(e){return e instanceof i.FigmaApiError}function F(e){return w(e)?e.statusCode:null}function j(e,r="An error occurred"){return e instanceof Error?e.message||r:typeof e=="string"?e:r}function x(e,r){return F(e)===r}exports.FigmaApiError=i.FigmaApiError;exports.filterVariables=i.filterVariables;exports.FigmaVarsProvider=V;exports.getErrorMessage=j;exports.getErrorStatus=F;exports.hasErrorStatus=x;exports.isFigmaApiError=w;exports.useBulkUpdateVariables=L;exports.useCreateVariable=C;exports.useDeleteVariable=D;exports.useInvalidateVariables=U;exports.usePublishedVariables=N;exports.useUpdateVariable=P;exports.useVariableCollections=S;exports.useVariableModes=K;exports.useVariables=R;