{"_id":"@alphinex/api-laravel","_rev":"4-fb6503eb84c9b5bb8c627084633213ee","name":"@alphinex/api-laravel","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@alphinex/api-laravel","version":"1.0.0","license":"UNLICENSED","_id":"@alphinex/api-laravel@1.0.0","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"dist":{"shasum":"a1df7be0b2705cfdb3f7557c928eb1d04f15a0c5","tarball":"https://registry.npmjs.org/@alphinex/api-laravel/-/api-laravel-1.0.0.tgz","fileCount":5,"integrity":"sha512-2QlXMwQZd+x1Vvnba7jBYtkJVscedaD84C4bWwkmjS8FIw4UqCRoCyJVLQo88O9sxDUeRnTiPp6ao7Ipbq8eeQ==","signatures":[{"sig":"MEUCICioVPQDVxgarHFJs7WGMSZpT8WrELAdEN1kdi2kjV18AiEAgG7wEN3HB1gt2cDE6reZoSvkXQCjP2mMNQSZp2urWpU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27961},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"private":false,"scripts":{"dev":"vite build --watch","lint":"eslint .","test":"vitest run","build":"vite build","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit"},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"description":"Laravel Sanctum/Reverb/Horizon adapter implementing the @alphinex/api ApiClient contract.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","dependencies":{"@alphinex/api":"1.0.0","@alphinex/core":"1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","jsdom":"^25.0.1","react":"^19.0.0","eslint":"^9.17.0","vitest":"^2.1.8","react-dom":"^19.0.0","typescript":"^5.7.2","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@vitejs/plugin-react":"^4.3.4","@tanstack/react-query":"^5.62.7","@alphinex/build-config":"0.0.0","@testing-library/react":"^16.1.0","@alphinex/eslint-config":"0.0.0","@testing-library/jest-dom":"^6.6.3","@alphinex/typescript-config":"0.0.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","@tanstack/react-query":"^5.62.7"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false},"@tanstack/react-query":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/api-laravel_1.0.0_1785852479273_0.6215262135284232","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alphinex/api-laravel","version":"1.0.1","license":"UNLICENSED","_id":"@alphinex/api-laravel@1.0.1","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"dist":{"shasum":"cb15f1d6747b5fe091e09ba5a99fcb31ce35ffc3","tarball":"https://registry.npmjs.org/@alphinex/api-laravel/-/api-laravel-1.0.1.tgz","fileCount":5,"integrity":"sha512-wmQ8QPVj4aVS6NSJFIpAxtntSHIhfhrYhdkWANI45k4WZluclaqYHJMMU9XlZcffsflNas2DmGLcuUvNRAZmqg==","signatures":[{"sig":"MEUCIQDLJMCTXn/5JOt7i5YDeDPy0QbYqZFbMgFXvTDvBvIRKwIgCdR3bw3qk7ToeMDBqO762M5vm9utxrooE39kWjmM5qY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34815},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"private":false,"scripts":{"dev":"vite build --watch","lint":"eslint .","test":"vitest run","build":"vite build","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit"},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"description":"Laravel Sanctum/Reverb/Horizon adapter implementing the @alphinex/api ApiClient contract.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","dependencies":{"@alphinex/api":"1.1.0","@alphinex/core":"1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","jsdom":"^25.0.1","react":"^19.0.0","eslint":"^9.17.0","vitest":"^2.1.8","react-dom":"^19.0.0","typescript":"^5.7.2","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@vitejs/plugin-react":"^4.3.4","@tanstack/react-query":"^5.62.7","@alphinex/build-config":"0.0.0","@testing-library/react":"^16.1.0","@alphinex/eslint-config":"0.0.0","@testing-library/jest-dom":"^6.6.3","@alphinex/typescript-config":"0.0.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","@tanstack/react-query":"^5.62.7"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false},"@tanstack/react-query":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/api-laravel_1.0.1_1786525481990_0.01018134360961831","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@alphinex/api-laravel","version":"1.0.2","license":"UNLICENSED","_id":"@alphinex/api-laravel@1.0.2","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"dist":{"shasum":"0b1dab2e7d5a6fb6a26c9adf52438bbad6d2f2b1","tarball":"https://registry.npmjs.org/@alphinex/api-laravel/-/api-laravel-1.0.2.tgz","fileCount":5,"integrity":"sha512-9gKSCiwntinyxC4yjba019FmvbUCqv1X/iOwuZApIW8D00X3lA+ivFk9Hi96rlYFWDiqrHfA1Mf+ZHjM4tw5SQ==","signatures":[{"sig":"MEUCIAxYf70bj1XrZ7V4WklWDNj5TkWao6e9yyd0xoy/rJZHAiEAi29m8RJNqLGrRmaShsid5bz2sG4j7idMKc+xoTmcNyQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35709},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"private":false,"scripts":{"dev":"vite build --watch","lint":"eslint .","test":"vitest run","build":"vite build","clean":"rimraf dist .turbo","typecheck":"tsc --noEmit"},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"description":"Laravel Sanctum/Reverb/Horizon adapter implementing the @alphinex/api ApiClient contract.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","dependencies":{"@alphinex/api":"1.1.0","@alphinex/core":"1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","jsdom":"^25.0.1","react":"^19.0.0","eslint":"^9.17.0","vitest":"^2.1.8","react-dom":"^19.0.0","typescript":"^5.7.2","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@vitejs/plugin-react":"^4.3.4","@tanstack/react-query":"^5.62.7","@alphinex/build-config":"0.0.0","@testing-library/react":"^16.1.0","@alphinex/eslint-config":"1.0.0","@testing-library/jest-dom":"^6.6.3","@alphinex/typescript-config":"0.0.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","@tanstack/react-query":"^5.62.7"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false},"@tanstack/react-query":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/api-laravel_1.0.2_1786551564809_0.4678018844944831","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@alphinex/api-laravel","version":"1.1.0","private":false,"description":"Laravel Sanctum/Reverb/Horizon adapter implementing the @alphinex/api ApiClient contract.","license":"UNLICENSED","type":"module","sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"dependencies":{"@alphinex/core":"1.0.0","@alphinex/api":"1.1.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","@tanstack/react-query":"^5.62.7"},"peerDependenciesMeta":{"react":{"optional":false},"react-dom":{"optional":false},"@tanstack/react-query":{"optional":false}},"devDependencies":{"typescript":"^5.7.2","vite":"^6.0.5","vitest":"^2.1.8","react":"^19.0.0","react-dom":"^19.0.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","@testing-library/jest-dom":"^6.6.3","@vitejs/plugin-react":"^4.3.4","jsdom":"^25.0.1","eslint":"^9.17.0","@tanstack/react-query":"^5.62.7","@alphinex/eslint-config":"1.0.1","@alphinex/build-config":"0.0.0","@alphinex/typescript-config":"0.0.0"},"scripts":{"build":"vite build","dev":"vite build --watch","lint":"eslint .","typecheck":"tsc --noEmit","test":"vitest run","clean":"rimraf dist .turbo"},"_nodeVersion":"22.14.0","_id":"@alphinex/api-laravel@1.1.0","dist":{"integrity":"sha512-y+kuwqFNJCF3K5AU1A2u6suwxiIa15hQWGL+oYFkfeBdnExxJDeepeAiHxZ21lG5AXzmPtcs0YnYq3tNCnsSOA==","shasum":"18374eff3177d6c01b530768fc62aac82ec379fc","tarball":"https://registry.npmjs.org/@alphinex/api-laravel/-/api-laravel-1.1.0.tgz","fileCount":5,"unpackedSize":45181,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDI1yGV0V2o6qh39+ozBnY64raA14pdWk2F87l1jf3wLgIhAMRfmADeAOtH4K7nPEGJ+UnT1SXS4638NKDsMnCZ/Nx6"}]},"_npmUser":{"name":"anasaliqureshi","email":"anas@alphinex.com"},"directories":{},"maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/api-laravel_1.1.0_1786638715866_0.31691491420966456"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T14:07:59.068Z","modified":"2026-08-13T16:31:56.144Z","1.0.0":"2026-08-04T14:07:59.417Z","1.0.1":"2026-08-12T09:04:42.158Z","1.0.2":"2026-08-12T16:19:24.961Z","1.1.0":"2026-08-13T16:31:55.986Z"},"license":"UNLICENSED","description":"Laravel Sanctum/Reverb/Horizon adapter implementing the @alphinex/api ApiClient contract.","maintainers":[{"name":"anasaliqureshi","email":"anas@alphinex.com"}],"readme":"# @alphinex/api-laravel\n\nLaravel Sanctum/Reverb adapter that implements `@alphinex/api`'s `ApiClient` contract: CSRF-cookie\nor Bearer-token auth, Laravel's 422 validation shape mapped into `ApiError.fieldErrors`, a\nsession-expiry event you can wire to `@alphinex/auth`, and hooks for real-time updates over\nLaravel Echo/Reverb.\n\n## `createLaravelApiClient(options)`\n\nThe main entry point — wires the right interceptors onto `@alphinex/api`'s `createApiClient` for\nyou and returns both the resulting `client` and an `events` emitter:\n\n```ts\nimport { createLaravelApiClient } from \"@alphinex/api-laravel\";\n\nexport const { client, events } = createLaravelApiClient({\n  baseUrl: \"https://api.example.com\",\n});\n\nconst invoices = await client.get(\"/invoices\");\n```\n\n`mode` defaults to `\"cookie\"` (Sanctum SPA session auth) and attaches the `X-XSRF-TOKEN` header\nfrom the `XSRF-TOKEN` cookie automatically. For Bearer-token auth (mobile/3rd-party clients), pass\n`mode: \"bearer\"` with a `tokenStore` and a `refresh` function — concurrent 401s share a single\nin-flight refresh call, and each queued request replays with the new token:\n\n```ts\nimport { createLaravelApiClient, createInMemoryTokenStore } from \"@alphinex/api-laravel\";\n\nconst tokenStore = createInMemoryTokenStore();\n\nconst { client, events } = createLaravelApiClient({\n  baseUrl: \"https://api.example.com\",\n  mode: \"bearer\",\n  bearer: {\n    tokenStore,\n    refresh: async (fetchFn) => {\n      const response = await fetchFn(\"https://api.example.com/refresh\", { method: \"POST\" });\n      const { token } = await response.json();\n      return token;\n    },\n  },\n});\n```\n\nEvery request/validation-error/session-expiry interceptor described below is already included;\nuse `extraRequestInterceptors`/`extraResponseInterceptors` to append your own on top, and\n`fetchFn` to override the global `fetch` (mocking, tests). If you need fine-grained control\ninstead of the bundled defaults, call the individual pieces below directly against\n`@alphinex/api`'s `createApiClient`.\n\n## Composing with `createApiClient` yourself\n\n`createLaravelApiClient` is really just a preset over `@alphinex/api`'s `createApiClient` and the\npieces below — reach for the pieces directly if you need a different combination:\n\n```ts\nimport { createApiClient } from \"@alphinex/api\";\nimport {\n  createCsrfInterceptor,\n  createValidationErrorInterceptor,\n  createSessionEvents,\n  createSessionExpiryInterceptor,\n} from \"@alphinex/api-laravel\";\n\nconst events = createSessionEvents();\n\nconst client = createApiClient({\n  baseUrl: \"https://api.example.com\",\n  requestInterceptors: [createCsrfInterceptor()],\n  responseInterceptors: [\n    createValidationErrorInterceptor(),\n    createSessionExpiryInterceptor(events),\n  ],\n});\n```\n\n## CSRF (`createCsrfInterceptor`, `ensureCsrfCookie`)\n\n`createCsrfInterceptor(cookieName?, headerName?)` reads the `XSRF-TOKEN` cookie (defaults match\nSanctum) and attaches it as `X-XSRF-TOKEN` on outgoing requests — already included by\n`createLaravelApiClient` in `\"cookie\"` mode. Call `ensureCsrfCookie(baseUrl, fetchFn?, path?)` once\nper session, before the first mutating request, to have the browser fetch and store that cookie:\n\n```ts\nimport { ensureCsrfCookie } from \"@alphinex/api-laravel\";\n\nawait ensureCsrfCookie(\"https://api.example.com\"); // GET /sanctum/csrf-cookie\n```\n\n## Session events (`createSessionEvents`, `createSessionExpiryInterceptor`)\n\n`createSessionEvents()` returns an `EventEmitter<ApiLaravelEvents>` emitting a\n`\"session-expired\"` event (with `{ reason: \"cookie-401\" | \"bearer-refresh-failed\" }`) whenever the\nsession is no longer valid. Create one instance per client and pass it to both\n`createLaravelApiClient` (or `createSessionExpiryInterceptor` directly) and to `@alphinex/auth`'s\n`AuthProvider` so the app signs the user out and redirects automatically:\n\n```tsx\nimport { AuthProvider } from \"@alphinex/auth\";\nimport { createLaravelApiClient } from \"@alphinex/api-laravel\";\n\nconst { client, events } = createLaravelApiClient({ baseUrl: \"https://api.example.com\" });\n\n<AuthProvider\n  adapter={authAdapter}\n  subscribeSessionExpired={(handler) => events.on(\"session-expired\", () => handler())}\n>\n  <App />\n</AuthProvider>;\n```\n\n## Validation errors (`createValidationErrorInterceptor`, `mapLaravelValidationErrors`)\n\n`createValidationErrorInterceptor()` intercepts Laravel's 422 responses and throws an `ApiError`\nwith `fieldErrors` already mapped from `{ message, errors: { field: string[] } }` into\n`Record<string, string[]>`. `mapLaravelValidationErrors(body)` is the underlying mapping function,\nexported separately if you need it outside the interceptor pipeline (e.g. mapping a body you\nalready parsed):\n\n```ts\nimport { ApiError } from \"@alphinex/api\";\n\ntry {\n  await client.post(\"/invoices\", payload);\n} catch (error) {\n  if (error instanceof ApiError && error.fieldErrors) {\n    console.log(error.fieldErrors.amount?.[0]); // \"The amount field is required.\"\n  }\n}\n```\n\n## Bearer auth (`createBearerAuthInterceptor`, `createBearerRefreshInterceptor`, `createInMemoryTokenStore`)\n\nThe building blocks behind `mode: \"bearer\"` above, exported for custom wiring.\n`createInMemoryTokenStore(initialToken?)` implements the `TokenStore` contract\n(`getToken`/`setToken`) in memory — swap it for a persisted store (secure storage, etc.) in a real\napp. `createBearerAuthInterceptor(tokenStore)` attaches `Authorization: Bearer <token>` to every\nrequest. `createBearerRefreshInterceptor(options)` retries a 401 once, sharing a single in-flight\nrefresh across concurrent failed requests, and emits `\"session-expired\"` (reason\n`\"bearer-refresh-failed\"`) if the refresh call itself fails.\n\n## Laravel Echo (`useEchoChannel`, `useEchoQuerySync`)\n\n`useEchoChannel(echo, channelName, event, handler)` subscribes to a Reverb channel/event for the\nlifetime of the component and cleans up on unmount. `echo` is typed against the structural\n`EchoLike`/`EchoChannelLike` interfaces (this package has no hard dependency on `laravel-echo` —\nany real `Echo` instance satisfies them):\n\n```tsx\nimport { useEchoChannel } from \"@alphinex/api-laravel\";\nimport Echo from \"laravel-echo\";\n\nfunction InvoiceNotifications({ echo }: { echo: Echo }) {\n  useEchoChannel(echo, \"invoices\", \"InvoiceCreated\", (payload) => {\n    console.log(\"New invoice\", payload);\n  });\n  return null;\n}\n```\n\n`useEchoQuerySync(echo, channelName, event, queryClient, queryKey)` is the common case built on\ntop: invalidate a TanStack Query key whenever the event fires, keeping WebSocket concerns out of\ncomponent code:\n\n```tsx\nimport { useEchoQuerySync } from \"@alphinex/api-laravel\";\nimport { queryKeys } from \"@alphinex/api\";\nimport { useQueryClient } from \"@tanstack/react-query\";\n\nfunction InvoicesLive({ echo }: { echo: Echo }) {\n  const queryClient = useQueryClient();\n  useEchoQuerySync(\n    echo,\n    \"invoices\",\n    \"InvoiceCreated\",\n    queryClient,\n    queryKeys.resource(\"invoices\").lists()\n  );\n  return null;\n}\n```\n\nSee [documentation/ARCHITECTURE.md](../../documentation/ARCHITECTURE.md) for the full package contract, dependency rules, and roadmap placement.\n","readmeFilename":""}