{"_id":"@alikhalilll/nuxt-api-provider","_rev":"12-4ea3cf24af096242230eb0235907bfa1","name":"@alikhalilll/nuxt-api-provider","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@alikhalilll/nuxt-api-provider","version":"1.0.0","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.0.0","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"dist":{"shasum":"707171667231843909e5f403058b7a9d0f8ef388","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.0.0.tgz","fileCount":20,"integrity":"sha512-3HYEEh2ekV74dMmx+S63rnCawF+ocD/iWdkaX/OJUG3DL4vzJU1Kifc4jnnlIPYiqveTugzDvHMiM9DRCaXRQQ==","signatures":[{"sig":"MEUCIQC5+VaQ+p/n/D8iTnr2da/jdz9wT9UdwJ16262c9vI+ZAIgek4HTawTvkIJYhWEijQWcVPNpKP8TJEZRNhsrV4iavs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":92115},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.0.0.tgz","types":"./dist/module.d.ts","exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/5eb128a992c9b10a4ada1613d3a7dd27/alikhalilll-nuxt-api-provider-1.0.0.tgz","_integrity":"sha512-3HYEEh2ekV74dMmx+S63rnCawF+ocD/iWdkaX/OJUG3DL4vzJU1Kifc4jnnlIPYiqveTugzDvHMiM9DRCaXRQQ==","_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.0.0_1776387513082_0.7188047620095044","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@alikhalilll/nuxt-api-provider","version":"1.0.2","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.0.2","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"f49a0c0f33fc87bedcdd4b9543613148a579d0f8","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.0.2.tgz","fileCount":21,"integrity":"sha512-OHFT5XJfpwZKPig7+xqBAExf0Ae0CUIkdficFzoY3YJzhDL4SOvi+ne9fZEcbM5tI7hogdCjsao8NTn3nteoEw==","signatures":[{"sig":"MEUCIQDuQrelfAtG/uUCdtBLfed8AEoNlAIu6Kmc7X4Ajvr5XAIgYtBWCnrtorUXKeGg7K0njulpNB1axIZjMZAjrUx3jPs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93540},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.0.2.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/d12a73fb8cfb60356be4837d55ac5a44/alikhalilll-nuxt-api-provider-1.0.2.tgz","_integrity":"sha512-OHFT5XJfpwZKPig7+xqBAExf0Ae0CUIkdficFzoY3YJzhDL4SOvi+ne9fZEcbM5tI7hogdCjsao8NTn3nteoEw==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.0.2_1776389360730_0.6341011500686093","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@alikhalilll/nuxt-api-provider","version":"1.0.3","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.0.3","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"51ac36aa28da980746939f3cd277c9ce5e79250a","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.0.3.tgz","fileCount":21,"integrity":"sha512-InS9qDcFCFab8hQWPFj1V5XVAOFdZ2irc7Vu3BD7udiHpE2yEwwMJPV3UHYfbY7tZontH9eIvEA8Big9AT/3Vg==","signatures":[{"sig":"MEQCIAWvGsUUbQRnyjDfSFMIPMKcJcgOwylz3FdSxnVwHvaTAiAweVTvPBI4mz0ruIE0GfjJq3xJR2Xtu5+pTfqBePCTuw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93540},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.0.3.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/467a99d923066ce226eb67d8a1e2305b/alikhalilll-nuxt-api-provider-1.0.3.tgz","_integrity":"sha512-InS9qDcFCFab8hQWPFj1V5XVAOFdZ2irc7Vu3BD7udiHpE2yEwwMJPV3UHYfbY7tZontH9eIvEA8Big9AT/3Vg==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.0.3_1776389666215_0.15649759390819185","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@alikhalilll/nuxt-api-provider","version":"1.0.4","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.0.4","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"bdc279a6db2cc3b5b8fd7657ec9a0ad3e1206c09","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.0.4.tgz","fileCount":21,"integrity":"sha512-tL+eMG+4Laa1JFmS58SMqKGtxssHMG7jVmUef2tEgsjKcCd9G9ST7BjeelVbHNnyLDda5CRAJZphl03EA7dijg==","signatures":[{"sig":"MEYCIQC8MwH5H8gw+QCWHrVBxmlBTOia8BOzQAnVeZa9ZzVMEgIhAKXra56XF6gLOgmuQNsjXj4tiL4HjP3BCVydnAadbWTM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93540},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.0.4.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/b7a048421dc4133679b1cd606599d339/alikhalilll-nuxt-api-provider-1.0.4.tgz","_integrity":"sha512-tL+eMG+4Laa1JFmS58SMqKGtxssHMG7jVmUef2tEgsjKcCd9G9ST7BjeelVbHNnyLDda5CRAJZphl03EA7dijg==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.0.4_1776390671603_0.4809458902127761","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@alikhalilll/nuxt-api-provider","version":"1.0.5","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.0.5","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"2ac7876d139ee5978196edb0b3a161d57b62584a","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.0.5.tgz","fileCount":21,"integrity":"sha512-ZEurDl7MHixwZVbZ57/BWtTq/MmxXKtzlfPBnxUDWbmVcOxvmDD88ncbXKoEzYZjUB9IIBL/RZYjqSYUSBCVMw==","signatures":[{"sig":"MEUCIFCxu6e3i14b5NGYZFAtVQ6HGaR/9JCVDQIrkspGtIHEAiEAqguL2jm8NGfV33aveYPLm3ryjd6+F2OlYUigU+uvbAc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96049},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.0.5.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/f78241fcb3e2056840626179310e7b77/alikhalilll-nuxt-api-provider-1.0.5.tgz","_integrity":"sha512-ZEurDl7MHixwZVbZ57/BWtTq/MmxXKtzlfPBnxUDWbmVcOxvmDD88ncbXKoEzYZjUB9IIBL/RZYjqSYUSBCVMw==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.0.5_1776442530778_0.2460781583379983","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@alikhalilll/nuxt-api-provider","version":"1.1.0","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.1.0","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"5c435324e265f39495e9743a97b9941a35be4642","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.1.0.tgz","fileCount":21,"integrity":"sha512-bxLIv7X09EeFeaO7CFuf5LnS2RsDjVZLrD2HlSw1hAdDIw3QG7e4rmae6Z4wRfOuJlXYPQhl+05oLQ9T/DGPRw==","signatures":[{"sig":"MEUCIQDJuVuNe0xOmFpEI6JtYcL+bAb4LAx4uKJpmVEWRtuCigIgRUloVNY4HVqBl31OMhPHaAHncDiZ74KlAe3ES8p0kQE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96049},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.1.0.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/17b21eafc6d8a63b0bf88257912a8fc1/alikhalilll-nuxt-api-provider-1.1.0.tgz","_integrity":"sha512-bxLIv7X09EeFeaO7CFuf5LnS2RsDjVZLrD2HlSw1hAdDIw3QG7e4rmae6Z4wRfOuJlXYPQhl+05oLQ9T/DGPRw==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.1.0_1776476889123_0.766271967843617","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@alikhalilll/nuxt-api-provider","version":"1.1.1","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.1.1","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://github.com/alikhalilll/ali-nuxt-toolkit#readme","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"d918fca70f79b0815a1e1e205d5ecfeccff33425","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.1.1.tgz","fileCount":21,"integrity":"sha512-pC4JEFIX5O6BFkGsQnQABClvcAnFx8OQqZz3AJMdpl8ca52/LRPItet+sNmtAx/tbRIuTbApeZ7Jcs/1klvEnw==","signatures":[{"sig":"MEYCIQDKiZ5l9wW9sW8jejspkuLYoC2C8nVzA58oaufymy4eDQIhAIpBQDDByRjWjvE6b/5ecXoGHqLzW/hlsKCOoswpM7/Z","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96739},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.1.1.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/7665cdf9b684a49cd6f7ad482ca2c829/alikhalilll-nuxt-api-provider-1.1.1.tgz","_integrity":"sha512-pC4JEFIX5O6BFkGsQnQABClvcAnFx8OQqZz3AJMdpl8ca52/LRPItet+sNmtAx/tbRIuTbApeZ7Jcs/1klvEnw==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.1.1_1776525110800_0.7198068428925675","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@alikhalilll/nuxt-api-provider","version":"1.1.2","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.1.2","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://alikhalilll.github.io/ali-nuxt-toolkit/api-provider","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"58f19590ec670e077951480e819344f7e1466905","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.1.2.tgz","fileCount":21,"integrity":"sha512-44C0KSn8SXKIuFNLVGWpAqXcIQz4dlqXsqB6TKxKfh8krpcczgU4bS9tBFs+jYsH0CMOSpRjNJQleObWFFa/Bg==","signatures":[{"sig":"MEUCIQDCxAkDNfscuUcRe8WpeAWraZSWJaZLDt4TI7ljkYTMVQIgCvcjwPAOVp+6msypLRLXMRrzU2A2NofL997YzaINYTY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":99435},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.1.2.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/4b30f5a5180a746556ee48e233646696/alikhalilll-nuxt-api-provider-1.1.2.tgz","_integrity":"sha512-44C0KSn8SXKIuFNLVGWpAqXcIQz4dlqXsqB6TKxKfh8krpcczgU4bS9tBFs+jYsH0CMOSpRjNJQleObWFFa/Bg==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.1.2_1776531449531_0.8190117418930478","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@alikhalilll/nuxt-api-provider","version":"1.2.0","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.2.0","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://alikhalilll.github.io/ali-nuxt-toolkit/api-provider","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"1a2195f888e82da9c32ad91a6e63cd75cf23d064","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.2.0.tgz","fileCount":21,"integrity":"sha512-kUinpe8DEE9HkZly6DymSAh3PgbnaX2XGEKXeiKNwaJTaTIsvmZVQzKOFCW+w2EN7+M0KhEbVC9gcxyU4PsVZA==","signatures":[{"sig":"MEUCIQD5lnTs04Ynmz8DSlVkXOrG7P9chGAYFpIW25Hfknfe0gIgU5eHd4nFWM/mOsZUnYAh1qtPBLBtKF79I0mvVyyEz28=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112535},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.2.0.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"pnpm clean && tsup","clean":"rimraf dist","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/7v/1sk02ysd1xq6wmwprzz6h2cr0000gn/T/d018a9293eecd110bbe2b3b65b1197c9/alikhalilll-nuxt-api-provider-1.2.0.tgz","_integrity":"sha512-kUinpe8DEE9HkZly6DymSAh3PgbnaX2XGEKXeiKNwaJTaTIsvmZVQzKOFCW+w2EN7+M0KhEbVC9gcxyU4PsVZA==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"10.9.3","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","rimraf":"^6.0.1","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.2.0_1777661508062_0.05754584796348694","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@alikhalilll/nuxt-api-provider","version":"1.3.0","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"author":{"name":"alikhalilll"},"license":"MIT","_id":"@alikhalilll/nuxt-api-provider@1.3.0","maintainers":[{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"}],"homepage":"https://alikhalilll.github.io/ali-nuxt-toolkit/api-provider","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"dist":{"shasum":"390ce6577bbc8fe9fcec8a985c174ed95eaae42c","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-1.3.0.tgz","fileCount":19,"integrity":"sha512-XtXNcyTuMa6AmSKHbINrt01GJesta3xZxdDMBzVu5Jn0ksjR+N25CDHbefGqddrm/Cz3hi6iLGJ9Uk+NjwMFnQ==","signatures":[{"sig":"MEYCIQC6bkwNlIWP6fNuQ4DoIyaoMkgwHJA+n7v43KzAoGFsEAIhAKkpGBJu8Oto8Z4l7CDyC+vzpoRBug2KGruaeP9WUZvW","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":115146},"type":"module","_from":"file:alikhalilll-nuxt-api-provider-1.3.0.tgz","types":"./dist/module.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"scripts":{"build":"tsdown","clean":"rimraf dist","validate":"pnpm build && pnpm validate-dist && pnpm validate-consumer","typecheck":"tsc --noEmit -p tsconfig.json","validate-dist":"tsx ../../scripts/validate/dist-validate.ts --pkg api-provider","validate-consumer":"tsx ../../scripts/validate/consumer-validate.ts --pkg api-provider"},"_npmUser":{"name":"alikhalilll","email":"alikhalilll.dev@gmail.com"},"_resolved":"/private/var/folders/4j/cgljlpzd0n550x2mkwwsrj6c0000gn/T/6babb262b3a78b7b2a7ee7a7852010eb/alikhalilll-nuxt-api-provider-1.3.0.tgz","_integrity":"sha512-XtXNcyTuMa6AmSKHbINrt01GJesta3xZxdDMBzVu5Jn0ksjR+N25CDHbefGqddrm/Cz3hi6iLGJ9Uk+NjwMFnQ==","repository":{"url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","type":"git","directory":"packages/api-provider"},"_npmVersion":"11.12.1","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"unrun":"0.3.0","rimraf":"^6.0.1","tsdown":"0.22.0","@nuxt/kit":"^4.0.0","typescript":"^5.9.2","@types/node":"^22.13.0","@nuxt/schema":"^4.0.0"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nuxt-api-provider_1.3.0_1779969344970_0.5729318107337622","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@alikhalilll/nuxt-api-provider","version":"2.0.0","description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","license":"MIT","author":{"name":"alikhalilll"},"repository":{"type":"git","url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","directory":"packages/api-provider"},"homepage":"https://alikhalilll.github.io/ali-nuxt-toolkit/api-provider","bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"type":"module","sideEffects":false,"publishConfig":{"access":"public"},"keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"exports":{".":{"types":"./dist/module.d.ts","import":"./dist/module.mjs"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.mjs"},"./types":{"types":"./dist/types/index.d.ts","import":"./dist/types/index.mjs"},"./package.json":"./package.json"},"types":"./dist/module.d.ts","typesVersions":{"*":{".":["./dist/module.d.ts"],"core":["./dist/core/index.d.ts"],"types":["./dist/types/index.d.ts"]}},"engines":{"node":">=18"},"peerDependencies":{"@nuxt/kit":"^3.0.0 || ^4.0.0"},"peerDependenciesMeta":{"@nuxt/kit":{"optional":true}},"devDependencies":{"@nuxt/kit":"^4.0.0","@nuxt/schema":"^4.0.0","@types/node":"^22.13.0","rimraf":"^6.0.1","tsdown":"0.22.0","typescript":"^5.9.2","unrun":"0.3.0"},"scripts":{"clean":"rimraf dist","build":"tsdown","typecheck":"tsc --noEmit -p tsconfig.json","validate-dist":"tsx ../../scripts/validate/dist-validate.ts --pkg api-provider","validate-consumer":"tsx ../../scripts/validate/consumer-validate.ts --pkg api-provider","validate":"pnpm build && pnpm validate-dist && pnpm validate-consumer"},"_id":"@alikhalilll/nuxt-api-provider@2.0.0","_integrity":"sha512-voz70y6LVMfo9f04ewTMlJ66IRgf7FlmbU0flDIiEwrmrk6brUXJhX3cLArjXiPvTED6dSJQo0Pc0G0aAFThCA==","_resolved":"/private/var/folders/4j/cgljlpzd0n550x2mkwwsrj6c0000gn/T/444c30d1d2dd414986d597232da3e9d0/alikhalilll-nuxt-api-provider-2.0.0.tgz","_from":"file:alikhalilll-nuxt-api-provider-2.0.0.tgz","_nodeVersion":"26.0.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-voz70y6LVMfo9f04ewTMlJ66IRgf7FlmbU0flDIiEwrmrk6brUXJhX3cLArjXiPvTED6dSJQo0Pc0G0aAFThCA==","shasum":"06fa672a92f336b52b939e325e514dbe7f21bde9","tarball":"https://registry.npmjs.org/@alikhalilll/nuxt-api-provider/-/nuxt-api-provider-2.0.0.tgz","fileCount":19,"unpackedSize":174812,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH4dyHrDEXhcEMdInJls40f1kviCLo1nfRzPOUJtmoGXAiAmxaKd0fSEQagec1pSjvB2Mc4iMrkn/lmYYdiKEXJ+1A=="}]},"_npmUser":{"name":"alikhalilll_dev","email":"alikhalilll.dev@gmail.com"},"directories":{},"maintainers":[{"name":"alikhalilll_dev","email":"alikhalilll.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nuxt-api-provider_2.0.0_1782831067407_0.9170394693015402"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-17T00:58:32.993Z","modified":"2026-06-30T14:51:07.677Z","1.0.0":"2026-04-17T00:58:33.226Z","1.0.2":"2026-04-17T01:29:20.876Z","1.0.3":"2026-04-17T01:34:26.361Z","1.0.4":"2026-04-17T01:51:11.760Z","1.0.5":"2026-04-17T16:15:30.934Z","1.1.0":"2026-04-18T01:48:09.263Z","1.1.1":"2026-04-18T15:11:50.933Z","1.1.2":"2026-04-18T16:57:29.648Z","1.2.0":"2026-05-01T18:51:48.221Z","1.3.0":"2026-05-28T11:55:45.121Z","2.0.0":"2026-06-30T14:51:07.556Z"},"bugs":{"url":"https://github.com/alikhalilll/ali-nuxt-toolkit/issues"},"author":{"name":"alikhalilll"},"license":"MIT","homepage":"https://alikhalilll.github.io/ali-nuxt-toolkit/api-provider","keywords":["nuxt","nuxt-module","fetch","http-client","api","interceptors","retry"],"repository":{"type":"git","url":"git+https://github.com/alikhalilll/ali-nuxt-toolkit.git","directory":"packages/api-provider"},"description":"Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a framework-agnostic core.","maintainers":[{"name":"alikhalilll_dev","email":"alikhalilll.dev@gmail.com"}],"readme":"# @alikhalilll/nuxt-api-provider\n\nA strongly-typed `fetch` client for Nuxt 3 / 4 with interceptors, retry/backoff, and a framework-agnostic core you can use anywhere.\n\n- **Typed client type** — `ApiProviderClient` is exported so you can annotate your own wrappers and composables.\n- **Caching, on by default** — TanStack-Query-style: `staleTime` 30s, `gcTime` 5min, stale-while-revalidate, request deduplication. GET/HEAD only — mutations never cached. Per-call overrides and a `client.cache` API for manual control.\n- **Interceptor chain** — register multiple request/response/error interceptors via `.useRequest`, `.useResponse`, `.useError`.\n- **Retry + backoff** — per-client defaults with per-call overrides. Configurable status codes and exponential delay.\n- **Smart body encoding** — plain objects → JSON; `FormData` / `URLSearchParams` / `Blob` / `ArrayBuffer` / `string` pass through with correct Content-Type.\n- **Timeouts + abort** — per-call timeout plus `AbortSignal` support via `AbortSignal.any` with a polyfill fallback.\n- **Upload + download progress** — a single `onRequestProgress` hook. The client transparently switches to `XMLHttpRequest` only when you pass it, since `fetch` has no upload-progress support.\n- **`ApiError` class** — structured errors with `.status`, `.details`, `.payload`, `.response`, plus an `isApiError(e)` / `ApiError.is(e)` guard that works across bundles and realms.\n- **Framework-agnostic core** — `import { createApiClient } from '@alikhalilll/nuxt-api-provider/core'` to use outside Nuxt.\n\n---\n\n## Table of contents\n\n1. [Install](#install)\n2. [Register the module](#register-the-module)\n3. [Usage](#usage)\n   - [Typed client + composable](#typed-client--composable)\n   - [SSR (server-side rendering)](#ssr-server-side-rendering)\n   - [`useAsyncData`](#useasyncdata)\n   - [GET](#get)\n   - [Query parameters](#query-parameters)\n   - [POST with JSON](#post-with-json)\n   - [PATCH / PUT / DELETE](#patch--put--delete)\n   - [Multipart / FormData upload](#multipart--formdata-upload)\n   - [`application/x-www-form-urlencoded`](#applicationx-www-form-urlencoded)\n   - [Custom headers](#custom-headers)\n   - [Per-call timeout](#per-call-timeout)\n   - [External `AbortSignal`](#external-abortsignal)\n   - [Retry + backoff](#retry--backoff)\n   - [Upload / download progress](#upload--download-progress)\n   - [Error handling with `ApiError`](#error-handling-with-apierror)\n   - [Paginated list](#paginated-list)\n   - [Cancel previous request (debounced search)](#cancel-previous-request-debounced-search)\n   - [Passing metadata to interceptors](#passing-metadata-to-interceptors)\n4. [Caching](#caching)\n   - [Defaults](#defaults)\n   - [Per-call overrides](#per-call-overrides)\n   - [Forcing a refetch](#forcing-a-refetch)\n   - [Custom cache keys](#custom-cache-keys)\n   - [Manual invalidation after mutations](#manual-invalidation-after-mutations)\n   - [Disabling the cache](#disabling-the-cache)\n   - [SSR → CSR hydration](#ssr--csr-hydration)\n   - [`CacheOptions` reference](#cacheoptions-reference)\n   - [`CacheConfig` reference](#cacheconfig-reference)\n5. [Interceptors](#interceptors)\n   - [Lifecycle](#lifecycle)\n   - [Type signatures](#type-signatures)\n   - [Authentication header](#authentication-header)\n   - [Response transform / unwrap](#response-transform--unwrap)\n   - [Error → toast + redirect](#error--toast--redirect)\n   - [Runtime registration](#runtime-registration)\n6. [Framework-agnostic core](#framework-agnostic-core)\n   - [Node / Bun / CLI](#node--bun--cli)\n   - [Inside Nitro server routes](#inside-nitro-server-routes)\n   - [`ApiClientConfig` reference](#apiclientconfig-reference)\n   - [Initial interceptors at construction](#initial-interceptors-at-construction)\n   - [Custom fetch (testing / instrumentation)](#custom-fetch-testing--instrumentation)\n   - [`/core` helpers](#core-helpers)\n7. [Module options reference](#module-options-reference)\n8. [`RequestOptions` reference](#requestoptions-reference)\n9. [`RetryOptions` reference](#retryoptions-reference)\n10. [Exported types](#exported-types)\n\n---\n\n## Install\n\n```bash\npnpm add @alikhalilll/nuxt-api-provider\n```\n\n## Register the module\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@alikhalilll/nuxt-api-provider'],\n  apiProvider: {\n    baseURL: 'https://api.example.com',\n    provideName: '$apiProvider', // leading \"$\" optional — gets stripped\n    defaultTimeoutMs: 20_000,\n    server: true, // set false to skip SSR (client-only)\n    retry: { attempts: 2, delayMs: 500, backoff: 2 },\n    // Optional paths to modules with default-exported interceptors.\n    onRequestPath: '~/api/on-request',\n    onSuccessPath: '~/api/on-success',\n    onErrorPath: '~/api/on-error',\n  },\n});\n```\n\n## Usage\n\n### Typed client + composable\n\n`$apiProvider` is augmented onto `NuxtApp` and `ComponentCustomProperties` as an `ApiProviderClient`. Wrap it in a composable to stop destructuring it everywhere:\n\n```ts\n// composables/useApi.ts\nimport type { ApiProviderClient } from '@alikhalilll/nuxt-api-provider/types';\n\nexport const useApi = (): ApiProviderClient => useNuxtApp().$apiProvider;\n```\n\n```vue\n<script setup lang=\"ts\">\ninterface User {\n  id: string;\n  email: string;\n}\n\nconst api = useApi();\nconst user = await api<User>('/me');\n</script>\n```\n\n### SSR (server-side rendering)\n\nWith `server: true` (the default), `$apiProvider` is available during SSR, so top-level `await` inside a page's `<script setup>` runs on the server and the HTML arrives fully populated.\n\n```vue\n<!-- pages/posts/[id].vue -->\n<script setup lang=\"ts\">\ninterface Post {\n  id: number;\n  title: string;\n  body: string;\n}\n\nconst route = useRoute();\nconst api = useApi();\n\n// Resolves on the server during SSR, then hydrates on the client.\nconst post = await api<Post>(`/posts/${route.params.id}`);\n</script>\n\n<template>\n  <article>\n    <h1>{{ post?.title }}</h1>\n    <p>{{ post?.body }}</p>\n  </article>\n</template>\n```\n\nIf you set `server: false` in `nuxt.config.ts`, `$apiProvider` won't exist during SSR and this pattern will throw — you'd need to move the call into `onMounted` or `useAsyncData(..., { server: false })`.\n\n### `useAsyncData`\n\n`useAsyncData` is the idiomatic way to fetch in Nuxt: it deduplicates per-key, populates the SSR payload, and exposes `pending` / `error` / `refresh`. Capture the client **synchronously** at the top of `<script setup>` — `useNuxtApp()` only works in the synchronous prefix of a setup or a Nuxt-managed callback, so reaching for it after an `await` throws _\"A composable that requires access to the Nuxt instance was called outside of …\"_.\n\n```vue\n<script setup lang=\"ts\">\ninterface Post {\n  id: number;\n  title: string;\n}\n\nconst { $apiProvider } = useNuxtApp(); // capture ONCE, before any await\n\nconst { data, pending, error, refresh } = await useAsyncData('posts', () =>\n  $apiProvider<Post[]>('/posts')\n);\n</script>\n\n<template>\n  <div v-if=\"pending\">Loading…</div>\n  <div v-else-if=\"error\">Failed: {{ error.message }}</div>\n  <ul v-else>\n    <li v-for=\"post in data\" :key=\"post.id\">{{ post.title }}</li>\n  </ul>\n  <button @click=\"refresh()\">Reload</button>\n</template>\n```\n\nKeep the handler thin — if you factor the call into a service, pass the client in rather than calling `useNuxtApp()` inside the service:\n\n```ts\n// services/posts.ts\nimport type { ApiProviderClient } from '@alikhalilll/nuxt-api-provider/types';\n\nexport const getPost = (api: ApiProviderClient, id: string) =>\n  api<{ id: number; title: string }>(`/posts/${id}`);\n```\n\n```vue\n<script setup lang=\"ts\">\nimport { getPost } from '~/services/posts';\n\nconst { $apiProvider } = useNuxtApp();\nconst { data } = await useAsyncData('post-1', () => getPost($apiProvider, '1'));\n</script>\n```\n\nThe same function now works from a Nitro route (pass a `createApiClient` instance instead) — see [Framework-agnostic core](#framework-agnostic-core).\n\n### GET\n\n```ts\ninterface Post {\n  id: number;\n  title: string;\n}\nconst post = await api<Post>('/posts/1');\n```\n\n`Promise<T | undefined>` — you get `undefined` on 204/205 (no body).\n\n### Query parameters\n\nQueries are the third argument. `null`/`undefined`/empty-string are skipped; arrays are repeated as `?tag=a&tag=b`.\n\n```ts\nconst posts = await api<Post[]>('/posts', null, {\n  userId: 1,\n  tag: ['news', 'featured'],\n  q: '', // skipped\n  draft: undefined, // skipped\n});\n```\n\n### POST with JSON\n\nPlain objects (and arrays) are JSON-encoded; `Content-Type: application/json` is set automatically.\n\n```ts\nconst created = await api<Post>('/posts', {\n  method: 'POST',\n  body: { userId: 42, title: 'Hello', body: 'World' },\n});\n```\n\n### PATCH / PUT / DELETE\n\n```ts\nawait api<Post>('/posts/1', { method: 'PATCH', body: { title: 'Updated' } });\nawait api<Post>('/posts/1', { method: 'PUT', body: fullReplacement });\nawait api('/posts/1', { method: 'DELETE' });\n```\n\n### Multipart / FormData upload\n\n`FormData` is passed through and the `Content-Type` header is dropped so the browser sets the correct boundary.\n\n```ts\nconst form = new FormData();\nform.append('file', fileInput.files[0]);\nform.append('caption', 'my file');\n\nawait api<{ url: string }>('/uploads', { method: 'POST', body: form });\n```\n\n### `application/x-www-form-urlencoded`\n\nPass a `URLSearchParams` instance — Content-Type is set for you if missing.\n\n```ts\nconst body = new URLSearchParams({ grant_type: 'refresh_token', token: rt });\nawait api<{ access_token: string }>('/oauth/token', { method: 'POST', body });\n```\n\n### Custom headers\n\n```ts\nawait api('/posts/1', {\n  headers: { 'X-Trace-Id': crypto.randomUUID() },\n});\n```\n\n### Per-call timeout\n\n```ts\n// This call aborts after 3s regardless of the client-level default.\nawait api('/slow', { timeoutMs: 3_000 });\n```\n\n### External `AbortSignal`\n\nYour signal is combined with the internal timeout signal via `AbortSignal.any` (with a polyfill fallback).\n\n```ts\nconst ctrl = new AbortController();\nconst promise = api('/stream', { signal: ctrl.signal });\n\n// ... later\nctrl.abort();\n```\n\n### Retry + backoff\n\nPer-client defaults from `nuxt.config.ts` can be overridden per call:\n\n```ts\n// Retry up to 3 more times on 503, doubling the delay each time.\nawait api('/flaky', {\n  retry: { attempts: 3, delayMs: 500, backoff: 2, statusCodes: [503] },\n});\n\n// Disable retries for this call specifically.\nawait api('/critical', { retry: { attempts: 0 } });\n```\n\nDelay for attempt `n` (0-indexed) is `delayMs * backoff^n`. Default retryable status codes: `[408, 429, 500, 502, 503, 504]`.\n\n### Upload / download progress\n\nPass `onRequestProgress` to observe both phases of a request. The callback receives `{ phase, loaded, total, ratio }` — `phase` is `'upload'` while the body is being sent and `'download'` while the response is being received. `total` and `ratio` are `null` when the length isn't known (chunked / streaming responses).\n\n```vue\n<script setup lang=\"ts\">\nconst uploaded = ref(0);\nconst uploadRatio = ref(0);\nconst downloaded = ref(0);\n\nconst form = new FormData();\nform.append('file', fileInput.value!.files![0]);\n\nawait useApi()('/uploads', {\n  method: 'POST',\n  body: form,\n  retry: { attempts: 0 }, // don't silently re-upload on failure\n  timeoutMs: 60_000,\n  onRequestProgress: ({ phase, loaded, total, ratio }) => {\n    if (phase === 'upload') {\n      uploaded.value = loaded;\n      if (ratio !== null) uploadRatio.value = ratio;\n    } else {\n      downloaded.value = loaded;\n    }\n  },\n});\n</script>\n\n<template>\n  <progress :value=\"uploadRatio\" max=\"1\" />\n  <span>{{ uploaded }} bytes</span>\n</template>\n```\n\n#### How it works\n\n- `fetch` cannot report upload progress in any browser today, so when `onRequestProgress` is set the client transparently swaps its transport to `XMLHttpRequest`. Everything else — interceptors, retry, timeout, headers, body encoding, `AbortSignal`, `ApiError` — continues to work identically.\n- The fast path (no progress callback) stays on native `fetch`; there's no perf cost for requests that don't need progress.\n- Browser-only: progress callbacks have no meaning on the server and the XHR transport isn't available in Nitro. The module will throw if you try to use it there.\n\n### Error handling with `ApiError`\n\nEvery failure throws an `ApiError`. It's the same class for HTTP errors and network errors (`status === 0` means the request never reached a response).\n\nThe class implements the generic `IError<TErrors, TOtherKeys>` interface — a reusable contract for typed error shapes:\n\n```ts\nimport type { IError } from '@alikhalilll/nuxt-api-provider/types';\n\ntype LoginError = IError<'email' | 'password', 'hint'>;\n```\n\nDiscriminate caught errors with `isApiError(e)` (or the equivalent `ApiError.is(e)`) instead of `instanceof`. `instanceof` breaks when the package is duplicated in a bundle, when errors cross realm boundaries (iframes, workers), or when the class is downleveled to ES5 — `isApiError` uses a `Symbol.for(...)` brand and is robust to all three.\n\n```ts\nimport { isApiError } from '@alikhalilll/nuxt-api-provider/types';\n\ntry {\n  await api('/users', { method: 'POST', body: { email: 'bad' } });\n} catch (e) {\n  if (isApiError(e)) {\n    console.log(e.status); // 422\n    console.log(e.message); // 'Validation failed'\n    console.log(e.details.errors); // { email: 'Required' }\n    console.log(e.payload); // raw server payload (unknown)\n    console.log(e.response); // the Response object, if any\n  }\n}\n```\n\nThe `normalizeErrorPayload` helper digs into `errors`, `detail`, `details`, and `data.errors`, flattening arrays and nested objects into `details.errors` for you.\n\n### Paginated list\n\n```ts\nasync function listPostsPage(page: number) {\n  return api<Post[]>('/posts', null, { _page: page, _limit: 10 }) ?? [];\n}\n\nconst all: Post[] = [];\nfor (let p = 1; ; p++) {\n  const chunk = await listPostsPage(p);\n  if (chunk.length === 0) break;\n  all.push(...chunk);\n}\n```\n\n### Cancel previous request (debounced search)\n\n```ts\nlet current: AbortController | null = null;\n\nasync function search(q: string) {\n  current?.abort();\n  current = new AbortController();\n  try {\n    return await api<Result[]>('/search', { signal: current.signal }, { q });\n  } catch (e) {\n    if (isApiError(e) && e.message.includes('abort')) return [];\n    throw e;\n  }\n}\n```\n\n### Passing metadata to interceptors\n\n`options.meta` is copied onto `context.meta` and is readable in every interceptor.\n\n```ts\nawait api<Post>('/analytics', {\n  method: 'POST',\n  body: event,\n  meta: { feature: 'onboarding', silent: true },\n});\n\napi.useError((err, ctx) => {\n  if (ctx.meta.silent) return; // don't toast silent calls\n  toast.error(err.message);\n});\n```\n\n## Caching\n\nThe client ships with a request cache modelled on [TanStack Query](https://tanstack.com/query/latest) and **enabled by default**. The mental model is `staleTime` + `gcTime`:\n\n- An entry is **fresh** for `staleTime` after it's stored — reads inside this window return cached data with **no network call**.\n- After `staleTime`, the entry is **stale** but still cached for `gcTime`. Reads return cached data immediately **and** fire a background refetch (stale-while-revalidate).\n- After `gcTime` of inactivity, the entry is garbage-collected.\n\nIn-flight identical calls are **deduplicated** — fire 10 `GET /me` at once and only one network request goes out. Only `GET` and `HEAD` are cached; every mutation passes through untouched.\n\n### Defaults\n\n| Setting            | Default          | Meaning                                                 |\n| ------------------ | ---------------- | ------------------------------------------------------- |\n| `enabled`          | `true`           | Cache is on out of the box.                             |\n| `staleTime`        | `30_000` (30s)   | Fresh window — pure cache hit, zero network.            |\n| `gcTime`           | `300_000` (5min) | Stale window — cache hit + background refetch.          |\n| `swr`              | `true`           | When stale, refresh in the background.                  |\n| `cacheableMethods` | `['GET','HEAD']` | Mutations are never cached.                             |\n| `dedupe`           | `true`           | Identical concurrent calls share one in-flight promise. |\n| `maxEntries`       | `500`            | LRU eviction safety net.                                |\n\nTune them globally in `nuxt.config.ts`:\n\n```ts\napiProvider: {\n  baseURL: '...',\n  cache: {\n    staleTime: 60_000,      // 1 minute fresh window\n    gcTime: 10 * 60_000,    // 10 minute survival\n    swr: true,\n    hydrate: true,          // forward SSR cache to the client (see below)\n  },\n}\n```\n\n### Per-call overrides\n\n```ts\n// Long-fresh: this list barely changes — keep it for 5 minutes.\nconst countries = await api<Country[]>('/countries', { cache: { staleTime: 5 * 60_000 } });\n\n// Disable the cache for this one call.\nconst live = await api<Quote>('/stocks/AAPL', { cache: false });\n\n// Cache a POST (you're sure it's idempotent — e.g. a search endpoint).\nconst results = await api<Hit[]>('/search', {\n  method: 'POST',\n  body: { q: 'nuxt' },\n  cache: { cacheableMethods: ['POST'] },\n});\n```\n\n### Forcing a refetch\n\n```ts\n// Bypass the fresh window for this call. The response replaces the cached entry.\nconst fresh = await api<User>('/me', { cache: { refetch: true } });\n```\n\n### Custom cache keys\n\nBy default the key is derived from `method + URL + sorted query string + body fingerprint`. Supply your own when the auto-derived shape is too narrow (e.g. different query strings should share a cache) or too broad (e.g. you want a per-locale cache that the URL doesn't reflect):\n\n```ts\n// Locale-scoped cache for the same endpoint.\nconst intro = await api<Intro>('/intro', {\n  cache: { key: ['intro', locale.value] },\n});\n```\n\n### Manual invalidation after mutations\n\nThe cache exposes a small imperative API. The pattern is the same as TanStack Query — mutate, then invalidate:\n\n```ts\n// Drop every entry whose key starts with the posts namespace.\nawait api<Post>('/posts', { method: 'POST', body: draft });\napi.cache.invalidate((key) => key.startsWith('GET:') && key.includes('/posts'));\n\n// Or do it from a response interceptor for any mutation that touches /posts.\napi.useResponse((ctx) => {\n  const method = (ctx.request.options.method || 'GET').toUpperCase();\n  if (method === 'GET' || method === 'HEAD') return;\n  if (ctx.request.endpoint.startsWith('/posts')) {\n    api.cache.invalidate((key) => key.includes('/posts'));\n  }\n});\n\n// Nuke everything.\napi.cache.clear();\n```\n\nAvailable methods:\n\n| Method                    | Purpose                                                              |\n| ------------------------- | -------------------------------------------------------------------- |\n| `cache.invalidate(pred)`  | Drop every entry matching the predicate. Returns the number removed. |\n| `cache.delete(key)`       | Drop a single entry by its cache key.                                |\n| `cache.clear()`           | Drop every stored entry. In-flight requests keep running.            |\n| `cache.serialize()`       | Dump the store as a `SerializedCache` snapshot.                      |\n| `cache.hydrate(snapshot)` | Restore entries from a snapshot. Expired entries are skipped.        |\n| `cache.entriesIter()`     | Iterate `[key, entry]` pairs, ordered by recency (LRU at the front). |\n\n### Disabling the cache\n\n```ts\n// Globally\napiProvider: {\n  cache: {\n    enabled: false;\n  }\n}\n\n// Per call\nawait api('/something', { cache: false });\n```\n\n### SSR → CSR hydration\n\nBy default the SSR cache and the CSR cache are independent. The Nuxt module can wire them together via the Nuxt payload when you opt in:\n\n```ts\napiProvider: {\n  cache: { hydrate: true },\n}\n```\n\nWith `hydrate: true`:\n\n- On the server, after rendering completes, `client.cache.serialize()` is stored in `useState('apiProvider:<name>:cache', ...)`.\n- On the client, the state is read back during plugin init and applied via `client.cache.hydrate(snapshot)`.\n\nNet effect: an SSR fetch for `/posts/1` populates the client cache too, so the first client navigation hits the cache instead of refetching. Combine with `staleTime` to control how long the hydrated entry stays fresh.\n\n> Heads up: hydrating the cache adds the cached payload to the SSR response. If your responses are large or contain anything user-specific you don't want serialised into HTML, leave hydration off and let `useAsyncData` handle the SSR hand-off per key instead.\n\n### `CacheOptions` reference\n\nPer-call options on `RequestOptions.cache`. May also be `false` to disable for that call.\n\n| Field              | Type                           | Default        | Purpose                                                 |\n| ------------------ | ------------------------------ | -------------- | ------------------------------------------------------- |\n| `enabled`          | `boolean`                      | client default | Per-call enable/disable.                                |\n| `staleTime`        | `number` (ms)                  | client default | Fresh window override.                                  |\n| `gcTime`           | `number` (ms)                  | client default | GC horizon override.                                    |\n| `swr`              | `boolean`                      | client default | Background refetch on stale.                            |\n| `cacheableMethods` | `readonly string[]`            | client default | Methods eligible for caching.                           |\n| `dedupe`           | `boolean`                      | client default | Share in-flight identical calls.                        |\n| `maxEntries`       | `number`                       | client default | LRU bound (rarely useful per-call).                     |\n| `key`              | `string \\| readonly unknown[]` | auto-derived   | Explicit cache key. Arrays are deeply hashed.           |\n| `refetch`          | `boolean`                      | `false`        | Force the network, replace the cached entry on success. |\n\n### `CacheConfig` reference\n\nClient-level config under `ApiClientConfig.cache` (or `apiProvider.cache` in `nuxt.config.ts`). Same fields as `CacheOptions` minus `key` and `refetch`, which are per-call only. The Nuxt module accepts one extra field — `hydrate: boolean` — handled by the module itself, not the core client.\n\n## Interceptors\n\nThere are three kinds of interceptors. Register them via module options (file paths with a default export) or at runtime on the client. Every registration returns an unregister function.\n\n### Lifecycle\n\n```\nclient(endpoint)\n  ├─ runRequestInterceptors(ctx)        ← can mutate ctx or return a new one (chained)\n  ├─ build URL · encode body · fetch\n  ├─ if !response.ok → throw ApiError\n  ├─ retry loop on retryable status / network error\n  ├─ runResponseInterceptors(resCtx)    ← can mutate resCtx.data or return a new resCtx (chained)\n  └─ on thrown ApiError → runErrorInterceptors(err, ctx)   ← side-effect only\n```\n\n`skipInterceptors: true` on a per-call basis bypasses request, response, **and** error interceptors for that one call.\n\n### Type signatures\n\n```ts\n// All three live on `@alikhalilll/nuxt-api-provider/types`.\n\ninterface RequestContext {\n  endpoint: string;\n  baseURL: string;\n  headers: Record<string, string>;\n  queries: Record<string, unknown>;\n  options: RequestOptions; // per-call options (headers cleared — they live on ctx.headers)\n  meta: Record<string, unknown>;\n}\n\ninterface ResponseContext<T = unknown> {\n  request: RequestContext;\n  response: Response;\n  data: T | undefined; // already JSON-parsed; mutate or replace\n}\n\ntype RequestInterceptor = (\n  ctx: RequestContext\n) => void | RequestContext | Promise<void | RequestContext>;\n\ntype ResponseInterceptor = <T = unknown>(\n  ctx: ResponseContext<T>\n) => void | ResponseContext<T> | Promise<void | ResponseContext<T>>;\n\ntype ErrorInterceptor = (err: ApiError, ctx: RequestContext) => void | Promise<void>;\n```\n\nRequest and response interceptors **chain** — the value returned by interceptor _N_ is what interceptor _N+1_ receives. Returning nothing keeps the previous context. Error interceptors are side-effect only; they can't suppress or rewrite the thrown `ApiError`.\n\n### Authentication header\n\n```ts\n// ~/api/on-request.ts\nimport type { RequestInterceptor } from '@alikhalilll/nuxt-api-provider/types';\n\nconst onRequest: RequestInterceptor = (ctx) => {\n  const token = useCookie('token').value;\n  if (token) ctx.headers.Authorization = `Bearer ${token}`;\n};\nexport default onRequest;\n```\n\n```ts\n// nuxt.config.ts\napiProvider: {\n  baseURL: '...',\n  onRequestPath: '~/api/on-request',\n}\n```\n\n### Response transform / unwrap\n\nResponse interceptors are chained the same way as request interceptors. Each one gets the `ResponseContext` from the previous step and may either **mutate it in place** or **return a new context**. Whatever the last interceptor leaves in `ctx.data` is what the caller `await`s.\n\nCommon use case — strip a `{ data: T }` envelope so consumers see `T` directly:\n\n```ts\n// ~/api/on-success.ts\nimport type { ResponseInterceptor } from '@alikhalilll/nuxt-api-provider/types';\n\nconst unwrapEnvelope: ResponseInterceptor = (ctx) => {\n  const payload = ctx.data as { data?: unknown } | undefined;\n  if (payload && typeof payload === 'object' && 'data' in payload) {\n    ctx.data = payload.data as typeof ctx.data;\n  }\n};\nexport default unwrapEnvelope;\n```\n\nOr return a brand-new context (handy when you'd rather not mutate):\n\n```ts\nconst onSuccess: ResponseInterceptor = (ctx) => ({\n  ...ctx,\n  data: normalize(ctx.data),\n});\n```\n\nPure side-effect interceptors return nothing — useful for tracing, analytics, latency logs:\n\n```ts\napi.useResponse((ctx) => {\n  console.debug(ctx.response.status, ctx.response.headers.get('server-timing'));\n});\n```\n\nThe `ctx.request` field gives you the full `RequestContext` that produced this response (endpoint, baseURL, headers, queries, options, meta) so you can branch on `ctx.request.meta.feature`, the URL, or any header you set in the request interceptor.\n\n### Error → toast + redirect\n\n```ts\n// ~/api/on-error.ts\nimport type { ErrorInterceptor } from '@alikhalilll/nuxt-api-provider/types';\n\nconst onError: ErrorInterceptor = (err, ctx) => {\n  if (err.status === 401) return navigateTo('/login');\n  if (ctx.meta.silent) return;\n  useToast().error(err.message);\n};\nexport default onError;\n```\n\n### Runtime registration\n\nUseful when your interceptor depends on a composable (i18n, toast, router) that isn't available at module-setup time:\n\n```ts\n// plugins/01-api-interceptors.client.ts\nexport default defineNuxtPlugin(() => {\n  const { $apiProvider } = useNuxtApp();\n  const { locale } = useI18n();\n\n  const unregister = $apiProvider.useRequest((ctx) => {\n    ctx.headers['X-Locale'] = locale.value;\n  });\n\n  // Call `unregister()` to remove this interceptor later.\n});\n```\n\n## Framework-agnostic core\n\nEverything the Nuxt plugin wraps is available as a plain function you can use in Node, Bun, Deno, a CLI, or a test.\n\n### Node / Bun / CLI\n\n```ts\nimport { createApiClient, isApiError } from '@alikhalilll/nuxt-api-provider/core';\n\nconst client = createApiClient({\n  baseURL: 'https://api.github.com',\n  headers: { Accept: 'application/vnd.github+json' },\n  retry: { attempts: 2 },\n});\n\nclient.useRequest((ctx) => {\n  ctx.headers['User-Agent'] = 'my-cli/1.0';\n  if (process.env.GITHUB_TOKEN) {\n    ctx.headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;\n  }\n});\n\ntry {\n  const repo = await client<{ stargazers_count: number }>('/repos/nuxt/nuxt');\n  console.log(repo?.stargazers_count);\n} catch (e) {\n  if (isApiError(e)) process.exitCode = 1;\n  throw e;\n}\n```\n\n### Inside Nitro server routes\n\n```ts\n// server/api/proxy.ts\nimport { createApiClient } from '@alikhalilll/nuxt-api-provider/core';\n\nconst upstream = createApiClient({\n  baseURL: 'https://internal.example.com',\n  headers: { 'X-Internal-Key': process.env.INTERNAL_KEY! },\n});\n\nexport default defineEventHandler(async (event) => {\n  const query = getQuery(event);\n  return upstream('/widgets', null, query);\n});\n```\n\n### `ApiClientConfig` reference\n\n| Field          | Type                              | Default            | Purpose                                                                |\n| -------------- | --------------------------------- | ------------------ | ---------------------------------------------------------------------- |\n| `baseURL`      | `string`                          | `''`               | Prepended to every relative endpoint.                                  |\n| `timeoutMs`    | `number`                          | `20000`            | Default timeout (overridable per call).                                |\n| `retry`        | `Partial<RetryOptions>`           | `{}`               | Default retry policy.                                                  |\n| `headers`      | `HeadersInit`                     | —                  | Default headers merged into every request.                             |\n| `fetch`        | `typeof fetch`                    | `globalThis.fetch` | Inject a custom fetch (test doubles, polyfills, instrumented fetches). |\n| `interceptors` | `{ request?, response?, error? }` | `{}`               | Initial interceptor arrays — equivalent to calling `.useX()` later.    |\n| `cache`        | `CacheConfig`                     | defaults           | Request-cache configuration. See [Caching](#caching).                  |\n\n### Initial interceptors at construction\n\n```ts\nconst client = createApiClient({\n  baseURL: 'https://api.example.com',\n  interceptors: {\n    request: [\n      (ctx) => {\n        ctx.headers['X-Trace'] = crypto.randomUUID();\n      },\n    ],\n    response: [\n      (ctx) => {\n        ctx.data = unwrap(ctx.data);\n      },\n    ],\n    error: [\n      (err) => {\n        logger.error(err);\n      },\n    ],\n  },\n});\n```\n\n### Custom fetch (testing / instrumentation)\n\n> **You almost never need this.** Modern Nuxt, browsers, Node 18+, Bun, and Deno all ship a global `fetch`, so leaving `fetch` unset is the right default. It's an escape hatch for environments or use cases the platform `fetch` can't cover.\n\nWhen you'd actually inject one:\n\n- **Unit tests** that stub the network without an MSW / `nock` layer — return canned `Response` objects.\n- **Older Node** (≤ 17) or sandboxes without a global `fetch` — pass `undici` / `node-fetch`.\n- **CLI or Nitro edge cases** that need a custom dispatcher (HTTP proxy, mTLS, custom DNS, cookie jar).\n- **Transport-layer tracing / metrics** that need to live below the interceptor chain.\n\n```ts\nconst fakeFetch: typeof fetch = async () =>\n  new Response(JSON.stringify({ ok: true }), {\n    status: 200,\n    headers: { 'content-type': 'application/json' },\n  });\n\nconst client = createApiClient({ fetch: fakeFetch });\n```\n\nNotes:\n\n- Must match the platform `fetch` signature `(input, init) => Promise<Response>`.\n- Bypassed for any call that sets `onRequestProgress` — the client switches to `XMLHttpRequest` for that single request (the only way to observe upload progress), then the rest of the pipeline (interceptors, retry, error mapping) continues unchanged.\n- There's **no per-call override** — `fetch` lives on `ApiClientConfig` only. Use a separate `createApiClient` instance if you need different transports for different call sites.\n\n### `/core` helpers\n\nThe `/core` entry exports the building blocks the client itself uses. They're stable and useful in tests, custom transports, and adapters.\n\n| Export                    | Signature / purpose                                                                                           |\n| ------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| `createApiClient`         | `(config?: ApiClientConfig) => ApiProviderClient`. The factory.                                               |\n| `joinUrl`                 | `(endpoint, baseURL) => string`. Absolute URLs pass through; collapses duplicate slashes.                     |\n| `buildQueryString`        | `(params) => string`. `null`/`undefined`/empty + whitespace strings skipped; arrays repeated.                 |\n| `normalizeHeaders`        | `(HeadersInit) => Record<string,string>`. Accepts `Headers`, arrays, plain objects.                           |\n| `dropContentType`         | `(headers) => headers`. Case-insensitive Content-Type strip — used internally for `FormData`.                 |\n| `encodeBody`              | `(headers, body) => { headers, body }`. JSON / FormData / URLSearchParams / passthrough pipeline.             |\n| `shouldOmitBody`          | `(method?) => boolean`. `true` for `GET` and `HEAD`.                                                          |\n| `safeParseJson`           | `(Response) => Promise<T \\| undefined>`. `204`/`205` → `undefined`; non-JSON bodies fall through as **text**. |\n| `combineSignals`          | `(internal, external?) => AbortSignal`. `AbortSignal.any` with a polyfill fallback.                           |\n| `DEFAULT_RETRY`           | The default `RetryOptions` constant.                                                                          |\n| `resolveRetry`            | `(clientDefaults, perCall) => RetryOptions`. Layered merge.                                                   |\n| `shouldRetryStatus`       | `(status, options) => boolean`.                                                                               |\n| `computeDelay`            | `(attempt, options) => number`. `delayMs * backoff^n`.                                                        |\n| `sleep`                   | `(ms, signal?) => Promise<void>`. Abortable.                                                                  |\n| `createXhrFetch`          | `(onProgress) => fetch`. The XHR-backed fetch used for upload progress. Browser-only.                         |\n| `normalizeErrorPayload`   | `(input, fallback) => { message, details }`. Flattens common server error shapes into `ApiErrorDetails`.      |\n| `ApiError` / `isApiError` | The error class and its brand-checked guard (works across realms).                                            |\n| `ApiCache`                | Framework-agnostic request cache. Construct directly or read via `client.cache`. See [Caching](#caching).     |\n| `buildCacheKey`           | `(input) => string`. Deterministic key from `{ method, url, body, override }`.                                |\n| `DEFAULT_CACHE_CONFIG`    | The default `CacheConfig` constant.                                                                           |\n| `resolveCacheConfig`      | `(input?) => Required<CacheConfig>`. Merge a user config over defaults.                                       |\n| `isFresh` / `isExpired`   | Pure entry-lifecycle helpers — `(entry, now?) => boolean`.                                                    |\n\n## Module options reference\n\n| Option             | Type                                  | Default          | Purpose                                                                                              |\n| ------------------ | ------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------- |\n| `baseURL`          | `string`                              | `''`             | Prepended to every relative endpoint.                                                                |\n| `provideName`      | `string`                              | `'$apiProvider'` | Injected under `$<name>`. Leading `$` is stripped.                                                   |\n| `defaultTimeoutMs` | `number`                              | `20000`          | Client-wide request timeout.                                                                         |\n| `server`           | `boolean`                             | `true`           | Register the plugin on the server. Set `false` for client-only.                                      |\n| `retry`            | `Partial<RetryOptions>`               | `{}`             | Default retry policy, overridable per call.                                                          |\n| `cache`            | `CacheConfig & { hydrate?: boolean }` | defaults         | Cache config — see [Caching](#caching). `hydrate: true` forwards the SSR cache via the Nuxt payload. |\n| `onRequestPath`    | `string` (optional)                   | —                | Path to a module with a default-exported `RequestInterceptor`.                                       |\n| `onSuccessPath`    | `string` (optional)                   | —                | Path to a module with a default-exported `ResponseInterceptor`.                                      |\n| `onErrorPath`      | `string` (optional)                   | —                | Path to a module with a default-exported `ErrorInterceptor`.                                         |\n\n## `RequestOptions` reference\n\n`RequestOptions` extends the standard `RequestInit` (so `method`, `credentials`, `cache`, `mode`, `redirect`, `referrer`, `keepalive`, `integrity`, etc. all pass through) and adds:\n\n| Field               | Type                                                                                         | Default        | Purpose                                                                                                |\n| ------------------- | -------------------------------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------ |\n| `body`              | object · array · `FormData` · `URLSearchParams` · `Blob` · `ArrayBuffer` · `string` · `null` | —              | Plain objects / arrays are JSON-encoded; everything else passes through with the right `Content-Type`. |\n| `timeoutMs`         | `number`                                                                                     | client default | Per-call timeout. Aborts via an internal `AbortController`.                                            |\n| `signal`            | `AbortSignal`                                                                                | —              | External abort signal. Combined with the timeout signal via `AbortSignal.any`.                         |\n| `retry`             | `Partial<RetryOptions>`                                                                      | client default | Per-call retry override. `{ attempts: 0 }` disables retries.                                           |\n| `skipInterceptors`  | `boolean`                                                                                    | `false`        | Bypass request, response, **and** error interceptors for this call.                                    |\n| `meta`              | `Record<string, unknown>`                                                                    | `{}`           | Arbitrary data forwarded to interceptors via `ctx.meta`.                                               |\n| `onRequestProgress` | `(p: RequestProgress) => void`                                                               | —              | Upload + download progress. Switches transport to `XMLHttpRequest`.                                    |\n| `cache`             | `CacheOptions \\| false`                                                                      | client default | Per-call cache override. `false` disables caching for this call. See [Caching](#caching).              |\n\n## `RetryOptions` reference\n\n| Field                 | Type       | Default                          | Purpose                                               |\n| --------------------- | ---------- | -------------------------------- | ----------------------------------------------------- |\n| `attempts`            | `number`   | `0`                              | Retry attempts **in addition** to the initial call.   |\n| `delayMs`             | `number`   | `300`                            | Base delay between retries, in ms.                    |\n| `backoff`             | `number`   | `2`                              | Exponent applied per attempt (`delayMs * backoff^n`). |\n| `statusCodes`         | `number[]` | `[408, 429, 500, 502, 503, 504]` | Response status codes that trigger a retry.           |\n| `retryOnNetworkError` | `boolean`  | `true`                           | Retry on aborted / network failures (no `Response`).  |\n\n## Exported types\n\n```ts\nimport type {\n  ApiProviderClient,\n  ApiProviderModuleOptions,\n  ApiClientConfig,\n  RequestOptions,\n  RequestContext,\n  ResponseContext,\n  RequestInterceptor,\n  ResponseInterceptor,\n  ErrorInterceptor,\n  RetryOptions,\n  RequestProgress,\n  ProgressPhase,\n  ApiError,\n  ApiErrorDetails,\n  IError,\n  isApiError,\n  // Cache surface\n  CacheConfig,\n  CacheOptions,\n  CacheEntry,\n  CachePredicate,\n  SerializedCache,\n  ApiCache,\n} from '@alikhalilll/nuxt-api-provider/types';\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}