diff --git a/.changeset/adapter-specific-cache-config.md b/.changeset/adapter-specific-cache-config.md deleted file mode 100644 index 54ce2c8..0000000 --- a/.changeset/adapter-specific-cache-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Give the IndexedDB and Redis cache stores adapter-specific config: IndexedDB gains `version`, `onQuotaExceeded`, and proactive `maxEntries` eviction; Redis gains a real `clear()` (opt-in via `scanKeys`, namespace-safe) and a custom `serializer`. Both gain a shared `onStoreError` diagnostic hook (error + op/key only, never cached data). diff --git a/.changeset/additional-hardening.md b/.changeset/additional-hardening.md deleted file mode 100644 index fe30bad..0000000 --- a/.changeset/additional-hardening.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add a grab-bag of caching hardening features: - -- Negative caching (`cache.cacheableStatuses`/`negativeTtl`) for error statuses like 404. -- ETag/`If-None-Match` conditional revalidation — automatic once a response captures an `ETag`. -- `client.cache.getStats()` — aggregate hit/miss/hitRate metrics. -- A circuit breaker for a failing persistent (L2) store (`cache.circuitBreaker`), observable via `cache.onStoreError`. -- At-rest encryption for IndexedDB (`createIndexedDbStore({ encrypt })`) — bring your own cipher, no default key management. -- Schema-version cache busting: a cached entry is invalidated automatically once the runtime OpenAPI schema hash changes (e.g. after a deploy). -- `client.cache.preview(pattern)` — dry-run a glob invalidation before running it for real. - -All are opt-in / additive; default behavior is unchanged. diff --git a/.changeset/cross-instance-invalidation.md b/.changeset/cross-instance-invalidation.md deleted file mode 100644 index 8d7e7f6..0000000 --- a/.changeset/cross-instance-invalidation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add cross-instance cache invalidation via Redis pub/sub: `createRedisStore(client, { crossInstance: true })` broadcasts tag/key/clear invalidations to sibling server instances (opt-in, off by default). Fixes stale cache reads when a mutation lands on one instance behind a load balancer but the stale GET was cached on another. diff --git a/.changeset/custom-cache-key-composition.md b/.changeset/custom-cache-key-composition.md deleted file mode 100644 index 94f458d..0000000 --- a/.changeset/custom-cache-key-composition.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add `cache.cacheKeyParts` — extra cache-key dimensions folded into the built-in tenant/auth-fingerprint scoping, settable per auto-method descriptor (computed from call args) or statically at the module/global/per-call layer. Safer than `keyResolver` for the common "add one more thing to the scope" case (e.g. an admin viewing "as" a target user, or a multi-workspace dashboard) since it can only add dimensions, never drop the existing tenant/auth isolation. A descriptor's `cacheKeyParts(args)` throwing fails closed (disables caching for that call). Also fixes a pre-existing gap where `cache.keyResolver` was declared on `CacheConfig` but never actually wired into the request pipeline. diff --git a/.changeset/filesystem-cache-store.md b/.changeset/filesystem-cache-store.md deleted file mode 100644 index 6289ce9..0000000 --- a/.changeset/filesystem-cache-store.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add `createFileSystemStore` — a Node-only, disk-backed `PersistentCacheStore` (the `.next/cache` / nginx `proxy_cache` shape) for long-running server processes that want to trade RAM for disk and survive restarts. Cache keys are SHA-256 hashed into fixed-length filenames (no path traversal), writes are atomic (write-then-rename), and it accepts the same `onStoreError`/`maxSizeBytes`/`encrypt` options as the other adapters. Not for serverless/edge; does not solve cross-instance staleness (use Redis `crossInstance` for that). diff --git a/.changeset/manual-types-docs.md b/.changeset/manual-types-docs.md deleted file mode 100644 index 9a0d5e8..0000000 --- a/.changeset/manual-types-docs.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": patch ---- - -Document and type-test full type-safety without codegen: `createTypedClient()` already accepts a hand-written `Ops` interface with the exact shape codegen emits (`{ params, query, body, response }` per operation) — see `docs/manual-types.md`. diff --git a/.changeset/memory-aware-caching.md b/.changeset/memory-aware-caching.md deleted file mode 100644 index 28318a0..0000000 --- a/.changeset/memory-aware-caching.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add memory-aware caching for constrained hosts: `cache.maxSizeBytes` (approximate byte cap alongside the existing entry-count `maxSize`), `cache.mode` (`'layered'` default, `'l1-only'`, `'l2-only'` with a tiny bounded shadow L1), and a Node-only opt-in `cache.memoryPressure` guard that proactively evicts before RSS crosses a threshold. diff --git a/.changeset/refresh-token-dx.md b/.changeset/refresh-token-dx.md deleted file mode 100644 index 1498711..0000000 --- a/.changeset/refresh-token-dx.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add a callback alternative to endpoint-based OAuth2 refresh (`refresh: (refreshToken) => Promise`, for refresh logic that isn't "just POST a URL") and pluggable `tokenStorage` (mirrors `PersistentCacheStore`'s adapter pattern; ships `createMemoryTokenStorage`/`createLocalStorageTokenStorage`). Both are mutually exclusive with the existing `refreshEndpoint`/manual-getters shape, enforced at the type level and defense-in-depth at construction time. diff --git a/.changeset/server-only-boundary.md b/.changeset/server-only-boundary.md deleted file mode 100644 index 42b7c5a..0000000 --- a/.changeset/server-only-boundary.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add a dev-time server-only guard: `createRpcHandler` now throws immediately if constructed in a browser context (`enforceServerOnly`, on by default), and `defineModule({ serverOnly: true, ... })` does the same for your own server-only modules. Both are defense-in-depth on top of the existing `scripts/check-browser-bundle.mjs` CI check, which remains authoritative. diff --git a/.changeset/tag-based-cache-invalidation.md b/.changeset/tag-based-cache-invalidation.md deleted file mode 100644 index c81dc61..0000000 --- a/.changeset/tag-based-cache-invalidation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@developerehsan/api-client": minor ---- - -Add tag-based cache invalidation: `cacheTags`/`invalidatesTags` on auto-method descriptors and `cache.tags`/`cache.invalidatesTags` on per-call config, plus `client.cache.invalidateTags(tags)`. A mutation's `invalidatesTags` now clears every cached copy of a tagged resource in one process, regardless of which auth scope cached it — fixing stale GETs after a mutation and same-VM cross-user staleness. diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 64fb949..c50eb6b 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -9,9 +9,9 @@ "lint": "eslint" }, "dependencies": { - "@developerehsan/api-client": "^1.0.0", - "@developerehsan/api-client-cli": "^1.0.0", - "@developerehsan/api-client-query": "^0.2.2", + "@developerehsan/api-client": "^1.1.0", + "@developerehsan/api-client-cli": "^1.0.1", + "@developerehsan/api-client-query": "^0.2.3", "@tanstack/react-query": "^5.101.4", "axios": "^1.18.1", "next": "16.2.12", diff --git a/examples/react-vite/package.json b/examples/react-vite/package.json index 31e2009..0e8c6d2 100644 --- a/examples/react-vite/package.json +++ b/examples/react-vite/package.json @@ -11,16 +11,16 @@ "typecheck:js": "tsc -p tsconfig.jscheck.json" }, "dependencies": { - "@developerehsan/api-client": "^1.0.0", - "@developerehsan/api-client-query": "^0.2.2", + "@developerehsan/api-client": "^1.1.0", + "@developerehsan/api-client-query": "^0.2.3", "@tanstack/react-query": "^5.101.4", "react": "^19.2.8", "react-dom": "^19.2.8" }, "devDependencies": { "@babel/core": "^7.29.7", - "@developerehsan/api-client-cli": "^1.0.0", - "@developerehsan/api-client-vite": "^1.0.0", + "@developerehsan/api-client-cli": "^1.0.1", + "@developerehsan/api-client-vite": "^1.0.1", "@rolldown/plugin-babel": "^0.2.3", "@types/babel__core": "^7.20.5", "@types/node": "^24.13.2", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index effc225..b34a3ee 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @developerehsan/api-client-cli +## 1.0.1 + +### Patch Changes + +- Updated dependencies [[`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346)]: + - @developerehsan/api-client@1.1.0 + ## 1.0.0 ### Major Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 8e76f79..70d14aa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@developerehsan/api-client-cli", - "version": "1.0.0", + "version": "1.0.1", "description": "OpenAPI codegen CLI for @developerehsan/api-client — generate typed TypeScript types and module descriptors, validate specs, and diff generated output.", "license": "MIT", "author": "EHSAN ", @@ -35,7 +35,9 @@ "bin": { "developerEhsan-api-client": "./dist/cli.js" }, - "files": ["dist"], + "files": [ + "dist" + ], "scripts": { "build": "tsup", "test": "vitest run --passWithNoTests", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 5f95898..1138213 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,41 @@ # @developerehsan/api-client +## 1.1.0 + +### Minor Changes + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Give the IndexedDB and Redis cache stores adapter-specific config: IndexedDB gains `version`, `onQuotaExceeded`, and proactive `maxEntries` eviction; Redis gains a real `clear()` (opt-in via `scanKeys`, namespace-safe) and a custom `serializer`. Both gain a shared `onStoreError` diagnostic hook (error + op/key only, never cached data). + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add a grab-bag of caching hardening features: + + - Negative caching (`cache.cacheableStatuses`/`negativeTtl`) for error statuses like 404. + - ETag/`If-None-Match` conditional revalidation — automatic once a response captures an `ETag`. + - `client.cache.getStats()` — aggregate hit/miss/hitRate metrics. + - A circuit breaker for a failing persistent (L2) store (`cache.circuitBreaker`), observable via `cache.onStoreError`. + - At-rest encryption for IndexedDB (`createIndexedDbStore({ encrypt })`) — bring your own cipher, no default key management. + - Schema-version cache busting: a cached entry is invalidated automatically once the runtime OpenAPI schema hash changes (e.g. after a deploy). + - `client.cache.preview(pattern)` — dry-run a glob invalidation before running it for real. + + All are opt-in / additive; default behavior is unchanged. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add cross-instance cache invalidation via Redis pub/sub: `createRedisStore(client, { crossInstance: true })` broadcasts tag/key/clear invalidations to sibling server instances (opt-in, off by default). Fixes stale cache reads when a mutation lands on one instance behind a load balancer but the stale GET was cached on another. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add `cache.cacheKeyParts` — extra cache-key dimensions folded into the built-in tenant/auth-fingerprint scoping, settable per auto-method descriptor (computed from call args) or statically at the module/global/per-call layer. Safer than `keyResolver` for the common "add one more thing to the scope" case (e.g. an admin viewing "as" a target user, or a multi-workspace dashboard) since it can only add dimensions, never drop the existing tenant/auth isolation. A descriptor's `cacheKeyParts(args)` throwing fails closed (disables caching for that call). Also fixes a pre-existing gap where `cache.keyResolver` was declared on `CacheConfig` but never actually wired into the request pipeline. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add `createFileSystemStore` — a Node-only, disk-backed `PersistentCacheStore` (the `.next/cache` / nginx `proxy_cache` shape) for long-running server processes that want to trade RAM for disk and survive restarts. Cache keys are SHA-256 hashed into fixed-length filenames (no path traversal), writes are atomic (write-then-rename), and it accepts the same `onStoreError`/`maxSizeBytes`/`encrypt` options as the other adapters. Not for serverless/edge; does not solve cross-instance staleness (use Redis `crossInstance` for that). + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add memory-aware caching for constrained hosts: `cache.maxSizeBytes` (approximate byte cap alongside the existing entry-count `maxSize`), `cache.mode` (`'layered'` default, `'l1-only'`, `'l2-only'` with a tiny bounded shadow L1), and a Node-only opt-in `cache.memoryPressure` guard that proactively evicts before RSS crosses a threshold. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add a callback alternative to endpoint-based OAuth2 refresh (`refresh: (refreshToken) => Promise`, for refresh logic that isn't "just POST a URL") and pluggable `tokenStorage` (mirrors `PersistentCacheStore`'s adapter pattern; ships `createMemoryTokenStorage`/`createLocalStorageTokenStorage`). Both are mutually exclusive with the existing `refreshEndpoint`/manual-getters shape, enforced at the type level and defense-in-depth at construction time. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add a dev-time server-only guard: `createRpcHandler` now throws immediately if constructed in a browser context (`enforceServerOnly`, on by default), and `defineModule({ serverOnly: true, ... })` does the same for your own server-only modules. Both are defense-in-depth on top of the existing `scripts/check-browser-bundle.mjs` CI check, which remains authoritative. + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Add tag-based cache invalidation: `cacheTags`/`invalidatesTags` on auto-method descriptors and `cache.tags`/`cache.invalidatesTags` on per-call config, plus `client.cache.invalidateTags(tags)`. A mutation's `invalidatesTags` now clears every cached copy of a tagged resource in one process, regardless of which auth scope cached it — fixing stale GETs after a mutation and same-VM cross-user staleness. + +### Patch Changes + +- [#27](https://github.com/developerEhsan/api-client/pull/27) [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346) Thanks [@developerEhsan](https://github.com/developerEhsan)! - Document and type-test full type-safety without codegen: `createTypedClient()` already accepts a hand-written `Ops` interface with the exact shape codegen emits (`{ params, query, body, response }` per operation) — see `docs/manual-types.md`. + ## 1.0.0 ### Major Changes diff --git a/packages/core/package.json b/packages/core/package.json index 9b3d349..6ed3f0d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@developerehsan/api-client", - "version": "1.0.0", + "version": "1.1.0", "description": "Typed, modular, universal TypeScript API client factory with caching, deduplication, retries, auth, OpenAPI codegen, response validation, SSR RPC bridge, and TanStack Query support.", "license": "MIT", "author": "EHSAN ", @@ -50,7 +50,9 @@ "access": "public", "provenance": true }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/tanstack-query/CHANGELOG.md b/packages/tanstack-query/CHANGELOG.md index 7b60a43..728ace4 100644 --- a/packages/tanstack-query/CHANGELOG.md +++ b/packages/tanstack-query/CHANGELOG.md @@ -1,5 +1,12 @@ # @developerehsan/api-client-query +## 0.2.3 + +### Patch Changes + +- Updated dependencies [[`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346)]: + - @developerehsan/api-client@1.1.0 + ## 0.2.2 ### Patch Changes diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index 8876392..d5d22c6 100644 --- a/packages/tanstack-query/package.json +++ b/packages/tanstack-query/package.json @@ -1,6 +1,6 @@ { "name": "@developerehsan/api-client-query", - "version": "0.2.2", + "version": "0.2.3", "description": "TanStack Query v5 integration for @developerehsan/api-client (React, Vue, Solid)", "license": "MIT", "author": "EHSAN ", @@ -37,7 +37,9 @@ "access": "public", "provenance": true }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { "./react": { "types": "./dist/react.d.ts", diff --git a/packages/vite-plugin/CHANGELOG.md b/packages/vite-plugin/CHANGELOG.md index beef26a..bd52a49 100644 --- a/packages/vite-plugin/CHANGELOG.md +++ b/packages/vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @developerehsan/api-client-vite +## 1.0.1 + +### Patch Changes + +- Updated dependencies [[`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346), [`ed369db`](https://github.com/developerEhsan/api-client/commit/ed369db8f57d8460021a75b3e32ddbb67d086346)]: + - @developerehsan/api-client@1.1.0 + ## 1.0.0 ### Major Changes diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index 86106a7..f6ed2eb 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@developerehsan/api-client-vite", - "version": "1.0.0", + "version": "1.0.1", "description": "Vite plugin that auto-generates @developerehsan/api-client types on dev-server start and build (also covers TanStack Start).", "license": "MIT", "author": "EHSAN ", @@ -31,7 +31,9 @@ "access": "public", "provenance": true }, - "files": ["dist"], + "files": [ + "dist" + ], "exports": { ".": { "types": "./dist/index.d.ts",