{"_id":"@aimform/state","_rev":"3-c5a1e33ef2184652e52745fa94794490","name":"@aimform/state","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@aimform/state","version":"0.1.0","keywords":["aimform","zustand","state","immer","async"],"author":{"name":"Universal Reason LLC"},"license":"MIT","_id":"@aimform/state@0.1.0","maintainers":[{"name":"adamhalasz","email":"mail@adamhalasz.com"}],"homepage":"https://aimform.com","bugs":{"url":"https://github.com/aimform/state/issues"},"dist":{"shasum":"50c8e17a1f3cfcc77be42abe6df91a3be6625b88","tarball":"https://registry.npmjs.org/@aimform/state/-/state-0.1.0.tgz","fileCount":5,"integrity":"sha512-X4X26xReYkpTQm8rV/Po0183fRki3acWl7TcNBJmfRu8zcIWZTVMQ61nYhuPjFDjaj1EmsC3uFcjWtBKOjUuAw==","signatures":[{"sig":"MEQCIC+UapUayowQVSsSrmt5F7Si5iNylu3I44wNwELKMf4IAiBZby9OWG8ohwSk2JhRywpOCMj2n4Ck6dARxe3YJC69Tg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":5536},"type":"module","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"}},"gitHead":"4e8e4f1dc041a7e6eb769a306f05b75093bc1b43","scripts":{"test":"vitest run","typecheck":"tsc --noEmit"},"_npmUser":{"name":"adamhalasz","email":"mail@adamhalasz.com"},"repository":{"url":"git+https://github.com/aimform/state.git","type":"git"},"_npmVersion":"11.8.0","description":"Aimform State — Zustand utilities: withLoading, async wrappers, and store patterns for the Aimform platform.","directories":{},"sideEffects":false,"_nodeVersion":"25.6.0","dependencies":{"immer":"^10.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/state_0.1.0_1785342962127_0.4245394804892202","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aimform/state","version":"0.2.0","keywords":["aimform","zustand","state","immer","async","store"],"author":{"name":"Universal Reason LLC"},"license":"MIT","_id":"@aimform/state@0.2.0","maintainers":[{"name":"adamhalasz","email":"mail@adamhalasz.com"}],"homepage":"https://aimform.com","bugs":{"url":"https://github.com/aimform/state/issues"},"dist":{"shasum":"352fe8fcaad47670008d829361dbdaf1b8a0d092","tarball":"https://registry.npmjs.org/@aimform/state/-/state-0.2.0.tgz","fileCount":6,"integrity":"sha512-fG2UgPtna+jbcx3aGzQAN0Li81Qb+ifFTzHIWtn74rZitzNcpy3gB0c738qPh8YIxLP6oh0Pmi+LCHfAypAC/Q==","signatures":[{"sig":"MEUCIQCgG3nZgqYq2Vl8oK9dfVGEOGbyiokrMbsKnS9tI0dj3AIgRbLE53sstrpURO2qjbyBB80ytnhJYUKJZKDSlQPiobo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10599},"type":"module","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"}},"gitHead":"e602254a10efeed4cf32ff2c7228344f3832b8ed","scripts":{"test":"vitest run","typecheck":"tsc --noEmit"},"_npmUser":{"name":"adamhalasz","email":"mail@adamhalasz.com"},"repository":{"url":"git+https://github.com/aimform/state.git","type":"git"},"_npmVersion":"11.8.0","description":"Aimform State — complete Zustand + Immer abstraction. Create stores, async actions, and hooks without importing zustand or immer directly.","directories":{},"sideEffects":false,"_nodeVersion":"25.6.0","dependencies":{"immer":"^10.0.0","zustand":"^5.0.0"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.0.0","typescript":"^5.7.0","@types/react":"^19.0.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/state_0.2.0_1785343596098_0.7322357848475234","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aimform/state","version":"0.2.1","description":"Aimform State — complete Zustand + Immer abstraction. Create stores, async actions, and hooks without importing zustand or immer directly.","type":"module","license":"MIT","sideEffects":false,"exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"}},"scripts":{"typecheck":"tsc --noEmit","test":"vitest run"},"dependencies":{"immer":"^10.0.0","zustand":"^5.0.0"},"devDependencies":{"@types/react":"^19.0.0","typescript":"^5.7.0","vitest":"^3.0.0","react":"^19.0.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"repository":{"type":"git","url":"git+https://github.com/aimform/state.git"},"keywords":["aimform","zustand","state","immer","async","store"],"author":{"name":"Universal Reason LLC"},"homepage":"https://aimform.com","gitHead":"c1856e6853227511fd64330dd54b97d608bbd260","_id":"@aimform/state@0.2.1","bugs":{"url":"https://github.com/aimform/state/issues"},"_nodeVersion":"25.6.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-GjsGhm2xYz3Su3xHbbdKb1jfauwbMZ1YFAOs5Sdag79748CWXplUZS0WTsuDdEkcQPyw7jTtNavr10/2ePBOHw==","shasum":"1d25151e8c5842a2bf32fe26866b488afd98cc42","tarball":"https://registry.npmjs.org/@aimform/state/-/state-0.2.1.tgz","fileCount":6,"unpackedSize":11092,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICKfyhJMmC67lx+M7xsgR6rtZrw4cejxSCFzvdntPGT4AiB/mMaj/+x8s4D9iVLTaLhig9pksTJdzyRK8PekNmaXbw=="}]},"_npmUser":{"name":"adamhalasz","email":"mail@adamhalasz.com"},"directories":{},"maintainers":[{"name":"adamhalasz","email":"mail@adamhalasz.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/state_0.2.1_1785344011732_0.87628104870208"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-29T16:36:01.908Z","modified":"2026-07-29T16:53:32.053Z","0.1.0":"2026-07-29T16:36:02.270Z","0.2.0":"2026-07-29T16:46:36.249Z","0.2.1":"2026-07-29T16:53:31.881Z"},"bugs":{"url":"https://github.com/aimform/state/issues"},"author":{"name":"Universal Reason LLC"},"license":"MIT","homepage":"https://aimform.com","keywords":["aimform","zustand","state","immer","async","store"],"repository":{"type":"git","url":"git+https://github.com/aimform/state.git"},"description":"Aimform State — complete Zustand + Immer abstraction. Create stores, async actions, and hooks without importing zustand or immer directly.","maintainers":[{"name":"adamhalasz","email":"mail@adamhalasz.com"}],"readme":"# @aimform/state\n\n> Complete Zustand + Immer abstraction. Create stores, async actions, and hooks without importing `zustand` or `immer` directly.\n\n```sh\nnpm install @aimform/state\n```\n\nNo other state dependencies needed — `zustand` and `immer` are bundled.\n\n## Usage\n\n### `createStore(initialData, actionsFactory)`\n\nCreates a Zustand store with Immer-powered immutable updates and built-in `loading`/`errors` state.\n\n```ts\nimport { createStore, withLoading } from \"@aimform/state\";\n\nconst useStore = createStore({\n  items: [] as Item[],\n  filter: \"\" as string,\n}, (set, get) => ({\n  fetchItems: withLoading(\"fetchItems\", async ({ setKey }, orgId: string) => {\n    const items = await api.fetchItems(orgId);\n    setKey(\"items\", items);\n  }),\n\n  addItem: withLoading(\"addItem\", async ({ setKey }, item: Item) => {\n    const created = await api.createItem(item);\n    setKey(\"items\", [created, ...get().items]);\n  }),\n\n  setFilter: (filter: string) => set({ filter }),\n}));\n```\n\n### `withLoading(key, fn)`\n\nWraps an async action. Returns a function that receives the store context and the action's own arguments.\n\n| Field | Description |\n|-------|-------------|\n| `key` | Unique action key stored in `loading` and `errors` maps |\n| `fn` | `(ctx, ...args) => Promise<T>` — async function with `ctx.setKey` and `ctx.setError` |\n\n**State contract:** Every store using `createStore` automatically gets:\n- `loading: Record<string, boolean>` — `true` while action runs, `false` after\n- `errors: Record<string, string | null>` — error message on failure, `null` otherwise\n- Both managed entirely by `withLoading` — you never set them manually\n\n### `LoadingContext`\n\n```ts\ninterface LoadingContext {\n  setKey: (path: string | string[], value: unknown) => void;  // deep set via immer\n  setError: (msg: string | null) => void;                      // set error for this action\n}\n```\n\n### Hooks\n\nWrite hooks manually to expose `{ data, isLoading, error, run }`:\n\n```ts\nexport function useItems() {\n  const data = useStore((s) => s.items);\n  const isLoading = useStore((s) => s.loading[\"fetchItems\"] ?? false);\n  const error = useStore((s) => s.errors[\"fetchItems\"] ?? null);\n  return { data, isLoading, error, run: (orgId: string) => useStore.getState().fetchItems(orgId) };\n}\n```\n\n## Full example — store + hooks\n\n```ts\n// store.ts\nimport { createStore, withLoading } from \"@aimform/state\";\nimport * as api from \"./api\";\n\nexport const useSpacesStore = createStore({\n  spaces: [] as Space[],\n}, (set, get) => ({\n  fetchSpaces: withLoading(\"fetchSpaces\", async ({ setKey }, orgId: string) => {\n    setKey(\"spaces\", await api.listSpaces(orgId));\n  }),\n  createSpace: withLoading(\"createSpace\", async ({ setKey }, name: string) => {\n    const space = await api.createSpace(name);\n    setKey(\"spaces\", [space, ...get().spaces]);\n  }),\n}));\n\n// hooks.ts\nexport function useSpaces() {\n  const data = useSpacesStore((s) => s.spaces);\n  const isLoading = useSpacesStore((s) => s.loading[\"fetchSpaces\"] ?? false);\n  return { data, isLoading, run: (orgId: string) => useSpacesStore.getState().fetchSpaces(orgId) };\n}\n```\n\n## Why not use zustand directly?\n\n- No manual `loading`/`errors` boilerplate — `withLoading` handles it\n- Immer baked in — `setKey(\"items\", [...get().items, newItem])` works without spread operators\n- Consistent action pattern across every store in your project\n- One dependency (`@aimform/state`) instead of three (`zustand`, `immer`, `@aimform/state`)\n\n## License\n\nMIT © Universal Reason LLC\n","readmeFilename":"README.md"}