Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- **Node Fastify package entry (`tricache/fastify`)** — first-class `createFastifyPlugin` / `fastifyCachePlugin` / `fastifyCache` re-export of the existing `src/http/fastify` plugin. `tricache/http` Fastify exports remain for back-compat.

## [0.8.0] — 2026-09-16

### Added
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ tricache/
│ ├── nestjs/ # NestJS CacheModule & interceptors
│ ├── prisma/ # Prisma client caching extension
│ ├── drizzle/ # Drizzle ORM query caching helper
│ └── http/ # HTTP reverse-proxy / fetch caching
│ ├── http/ # HTTP reverse-proxy / fetch caching
│ ├── fastify/ # First-class Fastify plugin entry (re-exports http)
├── tests/ # Vitest unit & integration test suites
├── bench/ # Microbenchmark suites
├── bin/ # CLI binaries
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Docs](https://img.shields.io/badge/docs-VitePress-blue.svg)](https://kareem411.github.io/TriCache/)
[![npm version](https://img.shields.io/npm/v/tricache.svg)](https://www.npmjs.com/package/tricache)
[![npm downloads](https://img.shields.io/npm/dm/tricache.svg)](https://www.npmjs.com/package/tricache)
[![Tests](https://img.shields.io/badge/tests-816%20passing-brightgreen)](tests)
[![Tests](https://img.shields.io/badge/tests-823%20passing-brightgreen)](tests)
[![Code Quality](https://img.shields.io/badge/oxlint-0%20warnings-brightgreen)](src)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Node.js ≥ 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
Expand Down Expand Up @@ -122,6 +122,7 @@ const cache = CacheService.preset('enterprise-hardened', { redisHost: 'redis.int
| **Prisma ORM** | `tricache/prisma` | `$extends` client extension with query hashing and auto-mutation tag eviction. |
| **Drizzle ORM** | `tricache/drizzle` | `withCache(query, opts)` query wrapper with SQL+parameters hashing and background SWR. |
| **Express & Fastify** | `tricache/http` | Route caching middleware with deterministic query sorting, weak ETag, and `304 Not Modified`. |
| **Fastify (Node)** | `tricache/fastify` | First-class `createFastifyPlugin` / `fastifyCachePlugin` / `fastifyCache` (same plugin as `tricache/http`). |
| **Hono & Edge Isolates** | `tricache/edge` | Zero-Node-dependency implementation for Cloudflare Workers, Fastly Compute, Hono, and Vercel Edge. |
| **SSE Dashboard** | `tricache/dashboard` | Zero-dependency Server-Sent Events real-time admin dashboard. |
| **Live CLI Top** | `npx tricache top` | Real-time terminal ASCII monitor over Unix sockets and Windows named pipes. |
Expand Down
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ export default defineConfig({
{ text: 'NestJS Dynamic Module', link: '/integrations/nestjs' },
{ text: 'Prisma ORM Extension', link: '/integrations/prisma' },
{ text: 'Drizzle ORM Wrapper', link: '/integrations/drizzle' },
{ text: 'Express & Hono Middleware', link: '/integrations/http' },
{ text: 'Express & Fastify Middleware', link: '/integrations/http' },
{ text: 'Fastify Plugin', link: '/integrations/fastify' },
{ text: 'Edge Isolates (Workers)', link: '/integrations/edge' },
{ text: 'Visual Dashboard & CLI', link: '/integrations/dashboard' },
],
Expand Down
12 changes: 7 additions & 5 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ The TriCache engine honors the following environment variables across all enviro

---

## 9. HTTP & Framework Middlewares (`tricache/http` & `tricache/edge`)
## 9. HTTP & Framework Middlewares (`tricache/http`, `tricache/fastify` & `tricache/edge`)

### `createExpressMiddleware(cache, options?)`
Creates an Express/Connect route middleware with deterministic query sorting, weak ETag calculation, and RFC 7232 `304 Not Modified` short-circuiting.
Expand All @@ -477,13 +477,15 @@ app.get('/api/users', createExpressMiddleware(cache, {
}), handler);
```

### `createFastifyPlugin(cache, options?)`
Creates an encapsulation-safe Fastify plugin (`[Symbol.for('skip-override')] = true`) intercepting requests early in `onRequest` and caching responses in `onSend`.
### `createFastifyPlugin(options?)` (`tricache/fastify`)
Creates an encapsulation-safe Fastify plugin (`[Symbol.for('skip-override')] = true`) intercepting requests early in `onRequest` and caching responses in `onSend`. Prefer the dedicated entry; `tricache/http` still re-exports the same functions.

```typescript
import { createFastifyPlugin } from 'tricache/http';
import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/fastify';

await fastify.register(createFastifyPlugin(cache, { ttlSeconds: 120 }));
await fastify.register(createFastifyPlugin({ cache, ttl: 120, tags: ['api'] }));
// or: await fastify.register(fastifyCachePlugin, { cache, ttl: 120 });
// or route-level: { preHandler: fastifyCache({ cache, ttl: 120, tags: ['catalog'] }) }
```

### `createHonoEdgeMiddleware(edgeCache, options?)`
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- **Node Fastify package entry (`tricache/fastify`)** — first-class `createFastifyPlugin` / `fastifyCachePlugin` / `fastifyCache` re-export of the existing `src/http/fastify` plugin. `tricache/http` Fastify exports remain for back-compat.

## [0.8.0] — 2026-09-16

### Added
Expand Down
76 changes: 76 additions & 0 deletions docs/integrations/fastify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Fastify Plugin

> Package entry: `tricache/fastify`

First-class **Node.js** Fastify plugin backed by `CacheService`. This is the dedicated export requested for Fastify apps — the same implementation as [`tricache/http`](/integrations/http), not a second stack.

```typescript
import Fastify from 'fastify';
import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/fastify';

const app = Fastify();

await app.register(createFastifyPlugin({
ttl: 300,
tags: ['api'],
}));
```

`fastifyCachePlugin` is the zero-arg alias (`createFastifyPlugin()`). Pass options at register time:

```typescript
import { CacheService } from 'tricache';
import { fastifyCachePlugin } from 'tricache/fastify';

const cache = CacheService.create();

await app.register(fastifyCachePlugin, {
cache,
ttl: 300,
swr: 60,
tags: ['api'],
headerWhitelist: ['accept-language'],
});
```

Route-level `preHandler` (ttl/tags without a global plugin):

```typescript
import { fastifyCache } from 'tricache/fastify';

app.get('/api/catalog', {
preHandler: fastifyCache({ cache, ttl: 300, tags: ['catalog'] }),
}, async () => {
return await fetchCatalog();
});
```

`import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/http'` remains supported for back-compat.

---

## Behavior

* **Safe methods only**: `GET` and `HEAD` are cached; other methods pass through.
* **Weak ETags**: SHA-1 weak validators (`ETag: W/"…"`).
* **304 Not Modified**: matching `If-None-Match` short-circuits with an empty body.
* **Status gate**: non-2xx responses are never kept (4xx/5xx cannot poison a key).
* **Bypass**: `Cache-Control: no-cache` / `no-store` and a custom `skipCache` predicate skip the cache.
* **ttl / tags**: already covered by plugin options and `fastifyCache({ ttl, tags })` `preHandler` opts.

Route-level `config.cache` and `x-cache: HIT|MISS|STALE` response headers are **not** in this entry. Those can land as a follow-up on the same plugin — this package does not introduce a second Fastify implementation.

---

## Options

| Option | Type | Default | Description |
|---|---|---|---|
| `cache` | `CacheService` | singleton | TriCache instance. If omitted, lazily resolves `CacheService.create()` |
| `ttl` | `number` | `300` | Time-to-live in seconds |
| `swr` | `number` | `undefined` | Stale-While-Revalidate window in seconds |
| `etag` | `boolean` | `true` | Generate and evaluate weak ETags (`W/"…"`) |
| `keyGenerator` | `(req) => string` | method + URL + sorted query | Custom cache key from the Fastify request |
| `headerWhitelist` | `string[]` | `[]` | Request headers incorporated into the cache key |
| `skipCache` | `(req) => boolean` | `undefined` | Predicate returning true to bypass cache |
| `tags` | `string[] \| ((req) => string[])` | `[]` | Semantic tags for targeted `cache.invalidateTag()` |
12 changes: 6 additions & 6 deletions docs/integrations/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

TriCache provides enterprise-grade HTTP route caching middleware with weak ETag calculation, deterministic query sorting, and RFC 7232 `304 Not Modified` short-circuiting for Express, Fastify, Connect, and Node.js HTTP servers.

For **Node Fastify** as a first-class subpath (`import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/fastify'`), see [Fastify Plugin](/integrations/fastify). The Fastify helpers below are the same implementation and remain exported from `tricache/http` for back-compat.

### Ready-to-run Express demo

A self-contained microservice lives at [`examples/express-api`](https://github.com/Kareem411/TriCache/tree/main/examples/express-api). It exercises weak ETags, `If-None-Match` → `304`, deterministic query sorting, `headerWhitelist: ['accept-language']`, and `skipCache` for `Authorization`.
Expand Down Expand Up @@ -52,12 +54,12 @@ app.get(

## 2. Fastify Plugin (`createFastifyPlugin`)

TriCache wraps Fastify middleware with `[Symbol.for('skip-override')] = true`, eliminating route encapsulation barriers.
TriCache wraps Fastify middleware with `[Symbol.for('skip-override')] = true`, eliminating route encapsulation barriers. Prefer `import { … } from 'tricache/fastify'`; `tricache/http` re-exports the same functions.

### Global Plugin Registration
```typescript
import Fastify from 'fastify';
import { createFastifyPlugin } from 'tricache/http';
import { createFastifyPlugin } from 'tricache/fastify';
import { CacheService } from 'tricache';

const fastify = Fastify();
Expand All @@ -73,12 +75,10 @@ await fastify.register(createFastifyPlugin({

### Route-Level `preHandler` Hook
```typescript
import { createFastifyPlugin } from 'tricache/http';

const plugin = createFastifyPlugin({ cache, ttl: 300 });
import { fastifyCache } from 'tricache/fastify';

fastify.get('/api/catalog', {
preHandler: plugin.preHandler,
preHandler: fastifyCache({ cache, ttl: 300, tags: ['catalog'] }),
}, async (request, reply) => {
return await fetchCatalog();
});
Expand Down
3 changes: 2 additions & 1 deletion docs/integrations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Explore the dedicated guides for your application stack:
| **[NestJS Module](/integrations/nestjs)** | `tricache/nestjs` | Dynamic `TriCacheModule` (`register`/`registerAsync`), `@Cacheable` and `@CacheEvict` decorators. |
| **[Prisma ORM Extension](/integrations/prisma)** | `tricache/prisma` | `$extends(withTriCache())`, automatic mutation invalidation, deterministic query key hashing. |
| **[Drizzle ORM Wrapper](/integrations/drizzle)** | `tricache/drizzle` | `withCache(query)`, SQL + parameterized argument hashing, custom TTL and tag assignment. |
| **[Express & Hono Middleware](/integrations/http)** | `tricache/http` | Route caching middleware, weak ETag calculation, RFC 7232 `304 Not Modified` short-circuiting. |
| **[Express & Fastify Middleware](/integrations/http)** | `tricache/http` | Route caching middleware, weak ETag calculation, RFC 7232 `304 Not Modified` short-circuiting. |
| **[Fastify Plugin](/integrations/fastify)** | `tricache/fastify` | First-class Fastify plugin / `preHandler` on `CacheService` (same API as `tricache/http`). |
| **[Edge Isolates (Workers)](/integrations/edge)** | `tricache/edge` | Universal zero-Node runtime for Cloudflare Workers, Fastly Compute, Web Crypto, WASM Bloom. |
| **[Visual Dashboard & CLI](/integrations/dashboard)** | `tricache/dashboard` | Real-time SSE Web UI, Next.js route handlers, standalone management server, CLI. |
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
"import": "./dist/http/index.js",
"require": "./dist/http/index.cjs"
},
"./fastify": {
"types": "./dist/fastify/index.d.ts",
"import": "./dist/fastify/index.js",
"require": "./dist/fastify/index.cjs"
},
"./dashboard": {
"types": "./dist/dashboard/index.d.ts",
"import": "./dist/dashboard/index.js",
Expand All @@ -93,7 +98,7 @@
"LICENSE"
],
"scripts": {
"build": "tsup src/index.ts src/cli.ts src/serialize-worker.ts src/next/index.ts src/nestjs/index.ts src/prisma/index.ts src/drizzle/index.ts src/http/index.ts src/dashboard/index.ts src/edge/index.ts --format esm,cjs --dts --clean",
"build": "tsup src/index.ts src/cli.ts src/serialize-worker.ts src/next/index.ts src/nestjs/index.ts src/prisma/index.ts src/drizzle/index.ts src/http/index.ts src/fastify/index.ts src/dashboard/index.ts src/edge/index.ts --format esm,cjs --dts --clean",
"postbuild": "node --input-type=module -e \"import{readdirSync,rmSync,statSync}from'fs';import{join}from'path';function clean(d){for(const f of readdirSync(d)){const p=join(d,f);if(statSync(p).isDirectory())clean(p);else if(p.endsWith('.d.cts'))rmSync(p,{force:true});}}clean('dist');\"",
"dev": "tsup src/index.ts src/serialize-worker.ts --format esm,cjs --dts --watch",
"typecheck": "tsc --noEmit",
Expand Down
22 changes: 22 additions & 0 deletions src/fastify/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* tricache/fastify — first-class Node.js Fastify plugin entry.
*
* Re-exports the existing Fastify plugin from `src/http/fastify.ts` so apps can:
*
* import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/fastify';
*
* `import { … } from 'tricache/http'` remains supported for back-compat.
*
* Plugin `options` and route `preHandler` already accept `ttl` / `tags` (and
* `swr`, `etag`, `skipCache`, `keyGenerator`, `headerWhitelist`). Route-level
* `config.cache` and `x-cache: HIT|MISS|STALE` headers are intentionally left
* for a follow-up so this entry does not fork a second Fastify stack.
*/

export {
createFastifyPlugin,
fastifyCachePlugin,
fastifyCache,
type FastifyCacheOptions,
type CachedFastifyResponse,
} from '../http/fastify.js';
2 changes: 1 addition & 1 deletion src/http/fastify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface CachedFastifyResponse {
*
* @example
* import Fastify from 'fastify';
* import { fastifyCachePlugin } from 'tricache/http';
* import { fastifyCachePlugin } from 'tricache/fastify';
*
* const app = Fastify();
* await app.register(fastifyCachePlugin, {
Expand Down
3 changes: 3 additions & 0 deletions src/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
* import { fastifyCachePlugin } from 'tricache/http';
* await fastify.register(fastifyCachePlugin, { cache, ttl: 300 });
*
* First-class Fastify entry (same plugin, dedicated subpath):
* import { createFastifyPlugin, fastifyCachePlugin, fastifyCache } from 'tricache/fastify';
*
* For Edge runtimes (Cloudflare Workers, Vercel Edge, Deno) and Hono, use:
* import { honoEdgeCache } from 'tricache/edge';
*/
Expand Down
Loading
Loading