Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

## 4.1.1 (2026-02-17)

### 🐛 Fixed

- **Root export parity**: Re-exported `withRetry`, `redactToken`, rate-limit helpers, and runtime validation guards from the package root so documented imports from `@figma-vars/hooks` work as expected.
- **Fallback key edge case**: Updated variables hooks and invalidation logic to only treat fallback mode as active when `parsedFallbackFile` is valid, preventing invalid fallback strings from forcing fallback SWR keys.
- **Test coverage**: Added regression coverage for invalid fallback + live credentials and expanded index export checks for newly documented utilities.

## 4.0.0 (2025-12-29)

### ⚠️ BREAKING CHANGES
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Built for the modern web, this library provides a suite of hooks to fetch, manag

> ⚠️ **Breaking Change**: `useFigmaToken` is now a named export. See [Migration Guide](#-migration-guide-3x--40).

> ✅ **4.1.1 patch**: Fixed root utility exports and fallback key handling when fallback JSON is invalid.

## 🚀 Features at a Glance

- **Modern React 19.2 hooks** for variables, collections, modes, and published variables
Expand Down Expand Up @@ -302,7 +304,7 @@ if (isLocalVariablesResponse(data)) {

### Error Utilities

v3 introduces powerful error handling utilities for type-safe error checking:
The library includes powerful error handling utilities for type-safe error checking:

```tsx
import { isFigmaApiError, getErrorStatus, getErrorMessage, hasErrorStatus } from '@figma-vars/hooks'
Expand Down Expand Up @@ -531,12 +533,21 @@ export function Providers({ children }: { children: React.ReactNode }) {
- `pnpm run build`, `pnpm test`, `pnpm run test:coverage`
- `pnpm run check:publint`, `pnpm run check:attw`, `pnpm run check:size`

## 🧭 Release Checklist (for 4.0.0)
## 🧭 Release Checklist (4.x)

- Run `pnpm run check:release`
- Run `pnpm version major` (creates `v4.0.0` tag)
- Bump the version with `pnpm version patch|minor|major` (creates matching git tag, e.g. `v4.1.1`)
- Push commit + tags (`git push && git push --tags`) so release workflows can run
- CI publishes to npm automatically
- Update dist-tags on npm if needed (`latest` → 4.0.0)
- Update dist-tags on npm if needed (`latest` → current stable)

**Backfill tags if missing:**

```bash
git tag -a v4.1.0 <commit-sha-for-4.1.0> -m "v4.1.0"
git tag -a v4.1.1 <commit-sha-for-4.1.1> -m "v4.1.1"
Comment on lines +547 to +548
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README shows instructions for backfilling tags for v4.1.0, but there is no 4.1.0 release documented in the CHANGELOG. The version jumps directly from 4.0.0 to 4.1.1. If v4.1.0 was actually released but not documented, the CHANGELOG should be updated to include it. If v4.1.0 was never released, the README examples should use v4.1.1 instead of v4.1.0 to avoid confusion.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

git push --tags
```

## 🔄 Migration Guide (3.x → 4.0)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@figma-vars/hooks",
"version": "4.1.0",
"version": "4.1.1",
"description": "Typed React hooks for managing Figma Variables, modes, tokens, and bindings via API.",
"author": "Mark Learst",
"license": "MIT",
Expand Down
32 changes: 32 additions & 0 deletions tests/hooks/usePublishedVariables.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -514,4 +514,36 @@ describe('usePublishedVariables', () => {

spy.mockRestore()
})

it('should use live key when fallbackFile exists but parsedFallbackFile is invalid', () => {
const spy = vi
.spyOn(useFigmaTokenContextModule, 'useFigmaTokenContext')
.mockReturnValue({
token: 'test-token',
fileKey: 'test-key',
fallbackFile: '{invalid-json}',
parsedFallbackFile: undefined,
providerId: 'test-provider',
} as ReturnType<typeof useFigmaTokenContextModule.useFigmaTokenContext>)

mockedUseSWR.mockReturnValue({
data: undefined,
error: undefined,
isLoading: false,
isValidating: false,
})

renderHook(() => usePublishedVariables())

expect(mockedUseSWR).toHaveBeenCalledWith(
[
'https://api.figma.com/v1/files/test-key/variables/published',
'test-token',
],
expect.any(Function),
undefined
)

spy.mockRestore()
})
})
29 changes: 29 additions & 0 deletions tests/hooks/useVariables.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -495,4 +495,33 @@ describe('useVariables', () => {

spy.mockRestore()
})

it('should use live key when fallbackFile exists but parsedFallbackFile is invalid', () => {
const spy = vi
.spyOn(useFigmaTokenContextModule, 'useFigmaTokenContext')
.mockReturnValue({
token: 'test-token',
fileKey: 'test-key',
fallbackFile: '{invalid-json}',
parsedFallbackFile: undefined,
providerId: 'test-provider',
} as ReturnType<typeof useFigmaTokenContextModule.useFigmaTokenContext>)

mockedUseSWR.mockReturnValue({
data: undefined,
error: undefined,
isLoading: false,
isValidating: false,
})

renderHook(() => useVariables())

expect(mockedUseSWR).toHaveBeenCalledWith(
['https://api.figma.com/v1/files/test-key/variables/local', 'test-token'],
expect.any(Function),
undefined
)

spy.mockRestore()
})
Comment on lines +498 to +526
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test expects legacy fallback behavior where a fallbackFile object would be used even when parsedFallbackFile is undefined. However, the PR removes this legacy support - the hook now only treats fallback mode as active when parsedFallbackFile is present (line 24 in useVariables.ts). With parsedFallbackFile set to undefined in this test, hasFallback will be false, and the code will attempt to use live credentials instead of the fallback. This test should either be updated to reflect the new behavior (testing that live credentials are used when parsedFallbackFile is undefined) or removed if the legacy behavior is no longer supported.

Suggested change
it('should use live key when fallbackFile exists but parsedFallbackFile is invalid', () => {
const spy = vi
.spyOn(useFigmaTokenContextModule, 'useFigmaTokenContext')
.mockReturnValue({
token: 'test-token',
fileKey: 'test-key',
fallbackFile: '{invalid-json}',
parsedFallbackFile: undefined,
providerId: 'test-provider',
} as ReturnType<typeof useFigmaTokenContextModule.useFigmaTokenContext>)
mockedUseSWR.mockReturnValue({
data: undefined,
error: undefined,
isLoading: false,
isValidating: false,
})
renderHook(() => useVariables())
expect(mockedUseSWR).toHaveBeenCalledWith(
['https://api.figma.com/v1/files/test-key/variables/local', 'test-token'],
expect.any(Function),
undefined
)
spy.mockRestore()
})

Copilot uses AI. Check for mistakes.
})