RepoPilot

pmndrs/zustand

🐻 Bear necessities for state management in React

Healthy

Healthy across the board

HealthyDependency

Permissive license, no critical CVEs, actively maintained β€” safe to depend on.

HealthyFork & modify

Has a license, tests, and CI β€” clean foundation to fork and modify.

HealthyLearn from

Documented and popular β€” useful reference codebase to read through.

HealthyDeploy as-is

No critical CVEs, sane security posture β€” runnable as-is.

  • βœ“Last commit 5d ago
  • βœ“36+ active contributors
  • βœ“Distributed ownership (top contributor 24% of recent commits)
  • βœ“MIT licensed
  • βœ“CI configured
  • βœ“Tests present

Computed from maintenance signals β€” commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against dependency CVEs from deps.dev and OpenSSF Scorecard

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Want this for your own repo?

Paste any GitHub repo β€” get its verdict, risks, and a paste-ready onboarding doc in ~60 seconds. Free, no sign-up.

Embed the "Healthy" badge

Paste into your README β€” live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/pmndrs/zustand)](https://repopilot.app/r/pmndrs/zustand)

Paste at the top of your README.md β€” renders inline like a shields.io badge.

β–ΈPreview social card

This card auto-renders when someone shares https://repopilot.app/r/pmndrs/zustand on X, Slack, or LinkedIn.

Ask AI about pmndrs/zustand

Grounded in the actual source code. Pick a starter question or write your own.

Or write your own question β†’

Onboarding doc

Onboarding: pmndrs/zustand

Generated by RepoPilot Β· 2026-06-27 Β· Source

🎯Verdict

GO β€” Healthy across the board

  • Last commit 5d ago
  • 36+ active contributors
  • Distributed ownership (top contributor 24% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Computed from maintenance signals β€” commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against dependency CVEs from deps.dev and OpenSSF Scorecard</sub>

⚑TL;DR

Zustand is a lightweight state management library for React (264KB TypeScript) that replaces Redux/Context with a simpler hook-based API. It solves the complexity and boilerplate of traditional state managers by providing a single create() function that returns a hookβ€”no providers, no actions, no reducers required. The core strength is handling React's notorious pitfalls: zombie child problem, concurrent rendering, and context loss. Simple monorepo structure: src/ contains the core library (~300 lines of TypeScript per bundle size), docs/learn/ and docs/reference/ hold extensive guides (tutorials, TypeScript patterns, SSR, testing), .github/workflows/ orchestrate CI. The library itself is intentionally minimalβ€”state logic lives in user's stores, not framework code.

πŸ‘₯Who it's for

React developers (especially in the pmndrs ecosystem like three.js, jotai users) who want minimal-config state management without Redux boilerplate. Teams valuing small bundle size (dynamically tracked in CI) and TypeScript-first development (90%+ of codebase is TS).

🌱Maturity & risk

Highly mature and production-ready. The repo shows strong signals: 40K+ stars (per badge in README), extensive CI pipeline (.github/workflows/ has 8 test workflows including old TypeScript, multiple React versions, and compressed size tracking), comprehensive docs in docs/learn/ and docs/reference/, and active maintenance visible in dependabot.yml. This is a stable, widely-trusted library.

Very low risk. Single maintained by Poimandres org (not single person), no heavy external dependencies (React is peer-dep), and transparent bundle size monitoring. Breaking changes are rare; version 4.3.9 suggests mature versioning. The only minor consideration: small core team means features move at community-driven pace, not corporate velocity.

Active areas of work

Active maintenance: .github/workflows/ shows recent CI runs (test.yml, publish.yml), docs/learn/guides/ covers modern patterns (ssr-and-hydration, prevent-rerenders-with-use-shallow). The focus appears to be on TypeScript ergonomics and edge-case documentation rather than major feature additions.

πŸš€Get running

git clone https://github.com/pmndrs/zustand.git
cd zustand
npm install
npm run dev

Note: This repo is the library itself. To use Zustand, npm install zustand in a React project.

Daily commands:

npm run dev    # Start Vite dev server (likely on :5173)
npm run build  # Vite build β†’ dist/
npm run test   # Run test suite (inferred from .github/workflows/test.yml)

πŸ—ΊοΈMap of the codebase

  • src/index.ts β€” Main entry point exporting the core create() store factory and all public APIs.
  • src/vanilla/index.ts β€” Core vanilla (non-React) store implementation with state management logic independent of React.
  • src/react/index.ts β€” React bindings wrapping vanilla stores to provide hooks and component integration.
  • src/middleware/index.ts β€” Middleware system enabling plugins like persist, immer, devtools, and subscribe-with-selector.
  • package.json β€” Defines build targets, exports, TypeScript versions, and dependencies critical for bundling.
  • tsconfig.json β€” TypeScript configuration ensuring correct type inference and module resolution across all exports.
  • .github/workflows/test.yml β€” Primary CI workflow validating core functionality, build integrity, and backwards compatibility.

🧩Components & responsibilities

  • Vanilla Store (createStore) (TypeScript, Set data structure) β€” Manages internal state atom, subscriber registry, and dispatch mechanism; triggers notifications on mutation.
    • Failure mode: Synchronous state updates fail if mutations are async or if listeners throw; race conditions if store is shared across async boundaries.
  • React Hook (useStore) (React hooks (useState, useEffect), shallow comparison) β€” Wraps vanilla store with React lifecycle; subscribes on mount, unsubscribes on unmount, triggers re-renders on selector change.
    • Failure mode: Re-render loops if selector function reference changes; stale closures if dependencies not managed correctly.
  • Persist Middleware (localStorage/AsyncStorage, JSON serialization) β€” Serializes store state to storage backend (localStorage, AsyncStorage) on mutation; rehydrates on store creation.
    • Failure mode: Storage quota exceeded, serialization errors on complex objects (Dates, Maps, Sets), network failure in AsyncStorage.
  • Immer Middleware (Immer library) β€” Wraps state setter to allow draft-based mutations; converts drafts back to immutable snapshots.
    • Failure mode: Complex nested object mutations may produce unexpected results; immutable comparison breaks if drafts are mutated outside Immer context.
  • Redux DevTools Middleware (Redux DevTools protocol, JSON serialization) β€” Captures action history and state snapshots; enables time-travel debugging via browser extension.
    • Failure mode: DevTools not installed breaks integration; circular references in state cause serialization errors.
  • Subscribe-with-Selector Middleware (Selector functions, shallow equality) β€” Allows listeners to subscribe to specific state slices instead of whole state; calls listener only if slice changes.
    • Failure mode: Selector identity changes trigger spurious subscriptions; non-memoized selectors cause constant re-subscriptions.

πŸ”€Data flow

  • React Component β†’ useStore Hook β€” Component calls hook to retrieve current state slice and action dispatcher.
  • useStore Hook β†’ Vanilla Store β€” Hook queries store state and registers/unregisters listeners on mount/unmount.
  • Action (in store) β†’ Middleware chain β€” Mutation triggered by action flows through middleware (persist, immer, devtools) before state update.
  • Vanilla Store β†’ useStore Hook β€” Store notifies hook of state change; hook runs selector and compares shallow equality to decide re-render.
  • Persist Middleware β†’ LocalStorage β€” State snapshot serialized and written to storage backend after each mutation.

πŸ› οΈHow to make changes

Create a new store with middleware

  1. Import create and desired middleware from zustand (e.g., immer, persist, devtools). (src/index.ts)
  2. Define your state type and apply middleware as a higher-order function wrapping the store factory. (src/middleware/index.ts)
  3. Export the hook from your store file; ensure TypeScript generics match your state shape. (src/vanilla/index.ts)

Add a new middleware plugin

  1. Create a new file in src/middleware/ (e.g., src/middleware/myPlugin.ts). (src/middleware/index.ts)
  2. Implement the middleware type signature matching the pattern in immer.ts or devtools.ts. (src/middleware/immer.ts)
  3. Export from src/middleware/index.ts and add type definitions. (src/index.ts)
  4. Document usage with examples in docs/reference/middlewares/. (docs/reference/middlewares/immer.md)

Add a guide or tutorial to documentation

  1. Create a new .md file in docs/learn/guides/ following existing naming conventions. (docs/learn/guides/updating-state.md)
  2. Structure with headings, code examples, and cross-links to related guides and API docs. (docs/learn/guides/slices-pattern.md)
  3. Update docs/learn/index.md or docs/index.md to include the new guide in navigation. (docs/learn/index.md)

πŸ”§Why these technologies

  • TypeScript β€” Provides type-safe state definitions, middleware composition, and excellent IDE autocomplete for store selectors.
  • Vanilla JS core + React adapter β€” Decouples state logic from React, enabling framework-agnostic stores and reducing bundle size for non-React consumers.
  • Middleware pattern β€” Allows composable, plugin-based extensions (immer, persist, devtools) without mutating core store factory.
  • Shallow subscription model β€” Minimizes re-renders by allowing granular selector subscriptions instead of forcing full state updates.

βš–οΈTrade-offs already made

  • No centralized reducer pattern (unlike Redux).

    • Why: Reduces boilerplate and allows direct state mutations in actions, lowering learning curve.
    • Consequence: Requires discipline to avoid mutation bugs; Immer middleware recommended for safety.
  • Framework-agnostic vanilla core.

    • Why: Single store implementation works with React, Vue, vanilla JS, etc.
    • Consequence: React bindings are thin and non-opinionated; developers must handle subscriptions themselves in non-React environments.
  • No built-in time-travel debugging in core.

    • Why: Keeps bundle small; Redux DevTools middleware available as opt-in.
    • Consequence: Requires explicit middleware setup for advanced debugging workflows.

🚫Non-goals (don't propose these)

  • Real-time sync across distributed clients (no server integration).
  • GraphQL/REST query normalization (state shape is user-defined).
  • Automatic form state management (integrate manually or use companion libraries).
  • Async action handling (use async/await in actions or pair with libraries like TanStack Query).

πŸ“ŠCode metrics

  • Avg cyclomatic complexity: ~5 β€” Core store logic is straightforward (listener pattern); complexity rises with middleware composition and TypeScript generics for type inference.
  • Largest file: src/middleware/persist.ts (350 lines)
  • Estimated quality issues: ~3 β€” Minimal; main issues are missing error boundaries in listener callbacks, lack of timeout safeguards for long-running listeners, and insufficient validation of serializable state in persist middleware.

⚠️Anti-patterns to avoid

  • Mutable state mutations outside actions (High) β€” src/vanilla/index.ts: Direct mutations without middleware may cause missed listener notifications or stale state references.
  • Non-memoized selector functions in components (Medium) β€” src/react/index.ts: Inline selectors cause hook to re-subscribe on every render, bypassing shallow comparison optimization.
  • Storing non-serializable objects in persist middleware (Medium) β€” src/middleware/persist.ts: Dates, Functions, Map/Set objects fail to serialize/deserialize from localStorage; state hydration silently fails.
  • Circular references in Redux DevTools state (Low) β€” src/middleware/devtools.ts: Complex nested state with cycles cannot be serialized for time-travel; DevTools integration breaks silently.

πŸ”₯Performance hotspots

  • src/vanilla/index.ts - listener notification loop (Scalability) β€” All listeners called synchronously on each mutation; O(n) complexity with subscriber count.
  • src/react/index.ts - shallow comparison on every subscription change (Performance) β€” Selector function re-evaluated per listener callback; no memoization of previous selector results.
  • src/middleware/persist.ts - synchronous localStorage writes (I/O) β€” State serialization and storage I/O blocks action completion; no batching or debouncing.

πŸͺ€Traps & gotchas

  1. The library is intentionally minimalβ€”if you're looking for devtools, middleware, or async helpers, they're community plugins (zustand-devtools, etc.), not in core. 2. State mutations must be immutable; direct mutations are silently ignored (no error thrown). 3. React version matters: some features (like use-shallow) require React 18+; check .github/workflows/test-multiple-versions.yml for the full matrix. 4. TypeScript generics are tricky; beginner-typescript.md and advanced-typescript.md are required reading, not optional.

πŸ—οΈArchitecture

πŸ’‘Concepts to learn

  • Zombie Child Problem β€” Zustand explicitly solves this React Context bug (stale closures in nested children); understanding it explains why Zustand's hook-based approach is safer than Context
  • Flux Architecture (Simplified) β€” Zustand follows single-dispatch Flux principles (action β†’ mutation β†’ subscribers); docs/learn/guides/flux-inspired-practice.md codifies this; it's lighter than Redux but same mental model
  • Closure-based Store Creation β€” Core Zustand pattern: create((set) => ({ state, actions })) returns a closure; avoids class boilerplate and enables tree-shaking; fundamental to understanding why Zustand is so small
  • React Concurrent Rendering β€” README explicitly mentions this; Zustand handles useTransition and useDeferredValue correctly (unlike older state libs); crucial for modern React 18 apps
  • Selective Subscription / Memoization β€” Zustand selectors prevent unnecessary re-renders; docs/learn/guides/prevent-rerenders-with-use-shallow.md shows this pattern is non-obvious and critical for performance
  • Immutability & Shallow Merging β€” Set function auto-merges state shallowly; docs/learn/guides/immutable-state-and-merging.md is required reading; common gotcha if coming from MobX (mutative) or Recoil
  • Tree-shakable Bundle Size β€” Badge in README dynamically tracks compressed size; Zustand's minimalism is a feature, not accident; .github/workflows/compressed-size.yml enforces this
  • reduxjs/redux β€” Direct predecessor and alternative; Zustand's pitch is 'Redux without boilerplate and ceremony'
  • pmndrs/jotai β€” Sister library in same org; atom-based state (vs. store-based); users often choose between Zustand and Jotai
  • pmndrs/valtio β€” Alternative in same org; proxy-based mutative state (vs. Zustand's immutable hook API); different philosophy, same ecosystem
  • facebook/react β€” Peer dependency and primary target; Zustand is designed to fix React's state-management gaps (context loss, zombie children)
  • pmndrs/drei β€” Ecosystem companion; three.js + React users often pair Zustand + Drei for state + 3D scene management

πŸͺ„PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive integration tests for persist middleware with multiple storage backends

The docs/reference/integrations/persisting-store-data.md exists but there's no dedicated test suite visible for the persist middleware across different storage backends (localStorage, sessionStorage, custom storage). This is critical for a state management library where persistence is a core feature. New contributors could add tests covering edge cases like hydration failures, storage quota exceeded, and cross-tab synchronization.

  • [ ] Create tests/persist.test.ts with test cases for localStorage, sessionStorage, and custom storage backends
  • [ ] Add tests for hydration scenarios (async initialization, mismatched state shapes)
  • [ ] Add tests for error handling when storage is unavailable or quota is exceeded
  • [ ] Reference: docs/reference/integrations/persisting-store-data.md for expected behavior
  • [ ] Ensure tests run in both Node.js and browser environments via test-multiple-builds.yml

Add end-to-end tests for SSR and hydration patterns with Next.js

The docs/learn/guides/ssr-and-hydration.md and docs/learn/guides/nextjs.md guide exist, but there's no visible E2E test suite validating these critical patterns work correctly. This is high-value because SSR hydration mismatches are a common source of bugs in React apps. New contributors could create a test fixture that validates state hydration across server and client boundaries.

  • [ ] Create tests/e2e/ssr-hydration.test.ts or similar with fixtures for Next.js SSR scenarios
  • [ ] Add tests validating that store state matches between server-rendered and client-rendered output
  • [ ] Add tests for async store initialization during SSR (getServerSideProps patterns)
  • [ ] Validate that .github/workflows/test.yml runs these E2E tests (may need new CI step)
  • [ ] Reference implementation patterns from docs/learn/guides/ssr-and-hydration.md

Add TypeScript test suite for advanced generic patterns documented in advanced-typescript.md

docs/learn/guides/advanced-typescript.md exists as a guide, but there's a .github/workflows/test-old-typescript.yml which suggests TypeScript compatibility testing. However, there's likely no dedicated test file validating the advanced patterns (complex generics, discriminated unions, type inference) work correctly across TypeScript versions. This prevents regressions in the TypeScript DX.

  • [ ] Create tests/typescript/advanced-patterns.test.ts with compile-time type assertions using ts-expect-error and satisfies operator
  • [ ] Add test cases for: generic store factories, discriminated union states, strict type inference with middleware, complex selector patterns
  • [ ] Validate tests pass in test-old-typescript.yml workflow (test against multiple TS versions like 4.8, 5.0, 5.2)
  • [ ] Reference patterns from docs/learn/guides/advanced-typescript.md and docs/learn/guides/beginner-typescript.md
  • [ ] Consider using typescript-eslint or tsd for type testing

🌿Good first issues

  • Add missing TypeScript examples to docs/learn/guides/advanced-typescript.md for discriminated union state patterns (common pain point in typed stores): 90% TS codebase but advanced patterns aren't well-documented; low-risk doc-only change
  • Expand docs/learn/guides/testing.md with concrete examples for testing stores with async actions and error boundaries: Current testing guide is sparse; test.yml shows Zustand is tested extensively, but best practices aren't codified
  • Add a new guide docs/learn/guides/debugging-with-devtools.md linking to zustand-devtools ecosystem and showing middleware setup: Ecosystem is fragmented; new users struggle to find devtools; centralizing links is a docs-only, high-value win

⭐Top contributors

Click to expand

πŸ“Recent commits

Click to expand
  • a1f685c β€” docs: fix missing code highlights in tic-tac-toe tutorial (#3527) (Copilot)
  • 07cee61 β€” docs: fix missing highlight on setXIsNext line in tic-tac-toe tutorial (#3524) (Copilot)
  • 566b5bf β€” Fix dead link in README.md: update immer guide path (#3519) (Copilot)
  • 4e15c2e β€” docs(use-shallow): add troubleshooting entry for "Maximum update depth exceeded" (#3507) (hyun907)
  • 04a8487 β€” chore(workflows): remove resolutions (#3514) (dai-shi)
  • bfb2a9e β€” 5.0.14 (dai-shi)
  • 62b2aff β€” chore(deps): update dev dependencies (#3513) (dai-shi)
  • ad77bd3 β€” fix(devtools): improve type inference for Devtools initializer (#3511) (dbritto-dev)
  • 8476d2c β€” update pnpm etc (#3512) (dai-shi)
  • d690ec2 β€” docs(combine): add object constraints to T and U in signature (#3506) (hyun907)

πŸ”’Security observations

The Zustand repository demonstrates a reasonable security posture for a state management library and demo project. The primary concerns are dependency currency, lack of documented security policies, and absence of automated security scanning in CI/CD. The demo application itself has low XSS risk due to use of established libraries (Prism, React Three), but would benefit from explicit security configuration validation. No critical vulnerabilities, hardcoded secrets, or infrastructure misconfigurations were detected. The codebase appears well-maintained with active GitHub workflows, though security-specific workflows should be added.

  • Medium Β· Outdated Direct Dependencies β€” examples/demo/package.json - dependencies section. Several dependencies in the demo project are using older versions that may contain known vulnerabilities. Notably: vite@4.4.0 is significantly outdated (current is v5+), and three@0.154.0, @react-three/fiber@8.13.7, and @react-three/drei@9.78.2 are not on the latest versions. Fix: Update all dependencies to their latest stable versions. Run 'npm audit' to identify specific CVEs. Consider using dependabot or similar tools for automated updates.
  • Low Β· Missing package.json Security Fields β€” examples/demo/package.json. The demo package.json lacks security-related best practices such as 'engines' specification, 'private' flag (though set to true, which is good), and no explicit security policy file referenced. Fix: Add 'engines' field to specify minimum Node.js version. Consider adding a SECURITY.md file in the root documenting security vulnerability reporting procedures.
  • Low Β· Potential XSS in Documentation Examples β€” examples/demo/src/components/CodePreview.jsx (inferred from package.json). The use of 'prism-react-renderer' and 'prismjs' in the demo suggests code highlighting functionality. If user-provided code snippets are rendered without proper sanitization, XSS vulnerabilities could occur. Fix: Ensure all code rendering uses proper escaping. Validate that prism-react-renderer is configured with appropriate security settings. Use Content Security Policy (CSP) headers in production.
  • Low Β· No Security Audit Configuration β€” .github/workflows/ - missing security-specific workflows. The repository structure shows GitHub workflows but no evidence of automated security scanning (SAST, dependency auditing, DAST) beyond standard testing. Fix: Add GitHub security workflows: npm audit CI, Dependabot alerts, CodeQL analysis, and SNYK or similar vulnerability scanning in CI/CD pipeline.
  • Low Β· Development Build Tool Security β€” examples/demo/package.json and vite configuration (not shown). Vite is configured as a dev dependency but no documented security practices around the dev server or build output are evident. Three.js and React Three Fiber may load external assets. Fix: Document secure Vite configuration practices. Ensure subresource integrity (SRI) is used for any external CDN resources. Verify Three.js WebGL contexts are properly sandboxed.

LLM-derived; treat as a starting point, not a security audit.

πŸ€–Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale β€” STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI Β· unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/pmndrs/zustand shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything β€” but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

βœ…Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live pmndrs/zustand repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale β€” regenerate it at repopilot.app/r/pmndrs/zustand.

What it runs against: a local clone of pmndrs/zustand β€” the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in pmndrs/zustand | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≀ 35 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> β€” paste this script from inside your clone of <code>pmndrs/zustand</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of pmndrs/zustand. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/pmndrs/zustand.git
#   cd zustand
#
# Then paste this script. Every check is read-only β€” no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of pmndrs/zustand and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "pmndrs/zustand(\\.git)?\\b" \\
  && ok "origin remote is pmndrs/zustand" \\
  || miss "origin remote is not pmndrs/zustand (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift β€” was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "src/index.ts" \\
  && ok "src/index.ts" \\
  || miss "missing critical file: src/index.ts"
test -f "src/vanilla/index.ts" \\
  && ok "src/vanilla/index.ts" \\
  || miss "missing critical file: src/vanilla/index.ts"
test -f "src/react/index.ts" \\
  && ok "src/react/index.ts" \\
  || miss "missing critical file: src/react/index.ts"
test -f "src/middleware/index.ts" \\
  && ok "src/middleware/index.ts" \\
  || miss "missing critical file: src/middleware/index.ts"
test -f "package.json" \\
  && ok "package.json" \\
  || miss "missing critical file: package.json"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 35 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~5d)"
else
  miss "last commit was $days_since_last days ago β€” artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) β€” safe to trust"
else
  echo "artifact has $fail stale claim(s) β€” regenerate at https://repopilot.app/r/pmndrs/zustand"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

Generated by RepoPilot. Verdict based on maintenance signals β€” see the live page for receipts. Re-run on a new commit to refresh.

Featured in stacks

Curated, side-by-side comparisons that include this repo.

Embed this chat in your README β†’

Drop this iframe anywhere β€” the widget runs against the same live analysis cache as the main app.

<iframe
  src="https://repopilot.app/embed/pmndrs/zustand"
  width="100%" height="500"
  style="border:1px solid #d0d7de; border-radius:8px;"
  allow="microphone"
  loading="lazy"
></iframe>