{"_id":"@banana.inc/cacheman-s3","_rev":"4-d1956105318999e6645b0b3806cd6608","name":"@banana.inc/cacheman-s3","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@banana.inc/cacheman-s3","version":"1.0.0","keywords":["cache","s3","aws","caching","store","ttl","cacheman","amazon","cloud","node","javascript","typescript","type-safe"],"author":{"name":"Long","email":"dragon.sunshine@gmail.com"},"license":"MIT","_id":"@banana.inc/cacheman-s3@1.0.0","maintainers":[{"name":"dragonxsx","email":"dragon.sunshine@gmail.com"}],"homepage":"https://github.com/dragonxsx/cacheman-s3#readme","bugs":{"url":"https://github.com/dragonxsx/cacheman-s3/issues"},"nyc":{"all":true,"exclude":["**/*.d.ts","test/","dist/"],"reporter":["text","lcov"],"extension":[".ts"]},"dist":{"shasum":"47b8c433dec72f011b11c14e14cc94900799a95d","tarball":"https://registry.npmjs.org/@banana.inc/cacheman-s3/-/cacheman-s3-1.0.0.tgz","fileCount":12,"integrity":"sha512-QZ5KolWTHN20lHl4eqsl0AT0NpjC7SCwT9B8TVqJ/2zw4Qjbv/V/ScdQw1wbwSAT/J8olSGJyslZ8htDa5/uuw==","signatures":[{"sig":"MEYCIQCCqj6WMwqLYgmH7G58XleSWYfMclYnbNMMBPTQNrnnwQIhAMGbn5NEjEpjKRYJIAZTRSsxSImjKyIbuBTVF0hJqSRP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":78023},"main":"dist/index.js","mocha":{"spec":["test/**/*.test.ts"],"require":["ts-node/register"],"timeout":10000,"extensions":["ts"]},"types":"dist/index.d.ts","engines":{"node":">= 14.0.0"},"gitHead":"e75923562f553bb4f52a88abbe987712d544c859","scripts":{"dev":"ts-node src/index.ts","lint":"eslint src/ test/ --ext .ts","test":"npm run test:unit && npm run test:integration","build":"tsc","clean":"rm -rf dist","prepack":"npm run build","coverage":"nyc npm run test:unit","lint:fix":"eslint src/ test/ --ext .ts --fix","prebuild":"npm run clean","test:unit":"mocha test/index.test.ts --require ts-node/register --timeout 10000","typecheck":"tsc --noEmit","build:watch":"tsc --watch","prepublishOnly":"npm run build","coverage:report":"nyc report --reporter=html","localstack:logs":"docker compose -f docker-compose.localstack.yml logs","localstack:stop":"docker compose -f docker-compose.localstack.yml down","localstack:setup":"./scripts/setup-localstack.sh","localstack:start":"docker compose -f docker-compose.localstack.yml up -d","test:integration":"npm run localstack:start && LOCALSTACK_ENDPOINT=http://localhost:4566 S3_TEST_BUCKET=test-bucket AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_REGION=us-east-1 mocha test/integration.test.ts --require ts-node/register --timeout 30000 && npm run localstack:stop","test:integration:ci":"mocha test/integration.test.ts --require ts-node/register --timeout 30000"},"_npmUser":{"name":"dragonxsx","actor":{"name":"dragonxsx","type":"user","email":"dragon.sunshine@gmail.com"},"email":"dragon.sunshine@gmail.com"},"repository":{"url":"git+https://github.com/dragonxsx/cacheman-s3.git","type":"git"},"_npmVersion":"11.4.1","description":"AWS S3 cache engine for cacheman","directories":{},"_nodeVersion":"22.16.0","dependencies":{"sanitize-filename":"^1.6.3","@aws-sdk/client-s3":"^3.100.0"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"^15.1.0","mocha":"^10.0.0","sinon":"^17.0.0","eslint":"^8.0.0","ts-node":"^10.9.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/mocha":"^10.0.0","@types/sinon":"^17.0.0","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cacheman-s3_1.0.0_1752014442360_0.8537151875478302","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@banana.inc/cacheman-s3","version":"1.0.1","keywords":["cache","s3","aws","caching","store","ttl","cacheman","amazon","cloud","node","javascript","typescript","type-safe"],"author":{"name":"Long Nguyen","email":"dragon.sunshine@gmail.com"},"license":"MIT","_id":"@banana.inc/cacheman-s3@1.0.1","maintainers":[{"name":"dragonxsx","email":"dragon.sunshine@gmail.com"}],"homepage":"https://github.com/dragonxsx/cacheman-s3#readme","bugs":{"url":"https://github.com/dragonxsx/cacheman-s3/issues"},"nyc":{"all":true,"exclude":["**/*.d.ts","test/","dist/"],"reporter":["text","lcov"],"extension":[".ts"]},"dist":{"shasum":"cd15e2d64b73701f01cbde8091aeefc1ad2fc033","tarball":"https://registry.npmjs.org/@banana.inc/cacheman-s3/-/cacheman-s3-1.0.1.tgz","fileCount":11,"integrity":"sha512-/+OcTvj+yekzZ0wh5JHa8wrEpVSOOsxyuegEJBxJl5D6eBD0L9FcIuNnH+D7tx9686jCdWE7BFS/wPExBAg6Sw==","signatures":[{"sig":"MEYCIQDaUXB3GsejmPyw9nHEc6DWoxX0WPMXzPvJ48aztOKecgIhALtZ2yPfq2MtsOdIEM4ZCFMfnQ4mY/7nidIzAveYHdTL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71753},"main":"dist/index.js","mocha":{"spec":["test/**/*.test.ts"],"require":["ts-node/register"],"timeout":10000,"extensions":["ts"]},"types":"dist/index.d.ts","engines":{"node":">= 14.0.0"},"gitHead":"6c7015f8da65f8cccf2190f27c629818ea7c06b0","scripts":{"dev":"ts-node src/index.ts","lint":"eslint src/ test/ --ext .ts","test":"npm run test:unit && npm run test:integration","build":"tsc","clean":"rm -rf dist","prepack":"npm run build","coverage":"nyc npm run test:unit","lint:fix":"eslint src/ test/ --ext .ts --fix","prebuild":"npm run clean","test:unit":"mocha test/index.test.ts --require ts-node/register --timeout 10000","typecheck":"tsc --noEmit","build:watch":"tsc --watch","prepublishOnly":"npm run build","coverage:report":"nyc report --reporter=html","localstack:logs":"docker compose -f docker-compose.localstack.yml logs","localstack:stop":"docker compose -f docker-compose.localstack.yml down","localstack:setup":"./scripts/setup-localstack.sh","localstack:start":"docker compose -f docker-compose.localstack.yml up -d","test:integration":"npm run localstack:start && LOCALSTACK_ENDPOINT=http://localhost:4566 S3_TEST_BUCKET=test-bucket AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_REGION=us-east-1 mocha test/integration.test.ts --require ts-node/register --timeout 30000 && npm run localstack:stop","test:integration:ci":"mocha test/integration.test.ts --require ts-node/register --timeout 30000"},"_npmUser":{"name":"dragonxsx","actor":{"name":"dragonxsx","type":"user","email":"dragon.sunshine@gmail.com"},"email":"dragon.sunshine@gmail.com"},"repository":{"url":"git+https://github.com/dragonxsx/cacheman-s3.git","type":"git"},"_npmVersion":"10.8.2","description":"AWS S3 cache engine for cacheman","directories":{},"_nodeVersion":"18.20.8","dependencies":{"sanitize-filename":"^1.6.3","@aws-sdk/client-s3":"^3.100.0"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"^15.1.0","mocha":"^10.0.0","sinon":"^17.0.0","eslint":"^8.0.0","ts-node":"^10.9.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/mocha":"^10.0.0","@types/sinon":"^17.0.0","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cacheman-s3_1.0.1_1752016420809_0.41122994409637204","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@banana.inc/cacheman-s3","version":"1.1.0","keywords":["cache","s3","aws","caching","store","ttl","cacheman","amazon","cloud","node","javascript","typescript","type-safe"],"author":{"name":"Long Nguyen","email":"dragon.sunshine@gmail.com"},"license":"MIT","_id":"@banana.inc/cacheman-s3@1.1.0","maintainers":[{"name":"dragonxsx","email":"dragon.sunshine@gmail.com"}],"homepage":"https://github.com/dragonxsx/cacheman-s3#readme","bugs":{"url":"https://github.com/dragonxsx/cacheman-s3/issues"},"nyc":{"all":true,"exclude":["**/*.d.ts","test/","dist/"],"reporter":["text","lcov"],"extension":[".ts"]},"dist":{"shasum":"b99e035ab29838db14061e36de4aa581dcf3dc94","tarball":"https://registry.npmjs.org/@banana.inc/cacheman-s3/-/cacheman-s3-1.1.0.tgz","fileCount":11,"integrity":"sha512-lRaud9c5sjZOnp7KMZlgBBDGQIYPisrxL7F01XWh4Ibm/z1pM8Z3ngqSIAkueRCwiKYKMmq5nZ8TKDyoZfQMaA==","signatures":[{"sig":"MEQCICHUgp5Cn8N3CjMx+LJ5KGZrIK5mKwbT7Db5/7ml5q1vAiBdxY+VtZQzGcw9sHzcwXu0Acan9vWkPg741kMGlxFIIg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":72972},"main":"dist/index.js","mocha":{"spec":["test/**/*.test.ts"],"require":["ts-node/register"],"timeout":10000,"extensions":["ts"]},"types":"dist/index.d.ts","engines":{"node":">= 14.0.0"},"gitHead":"b41e9f199a6061edc6e9f8d8b953a97e24e59351","scripts":{"dev":"ts-node src/index.ts","lint":"eslint src/ test/ --ext .ts","test":"npm run test:unit && npm run test:integration","build":"tsc","clean":"rm -rf dist","prepack":"npm run build","coverage":"nyc npm run test:unit","lint:fix":"eslint src/ test/ --ext .ts --fix","prebuild":"npm run clean","test:unit":"mocha test/index.test.ts --require ts-node/register --timeout 10000","typecheck":"tsc --noEmit","build:watch":"tsc --watch","prepublishOnly":"npm run build","coverage:report":"nyc report --reporter=html","localstack:logs":"docker compose -f docker-compose.localstack.yml logs","localstack:stop":"docker compose -f docker-compose.localstack.yml down","localstack:setup":"./scripts/setup-localstack.sh","localstack:start":"docker compose -f docker-compose.localstack.yml up -d","test:integration":"npm run localstack:start && LOCALSTACK_ENDPOINT=http://localhost:4566 S3_TEST_BUCKET=test-bucket AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_REGION=us-east-1 mocha test/integration.test.ts --require ts-node/register --timeout 30000 && npm run localstack:stop","test:integration:ci":"mocha test/integration.test.ts --require ts-node/register --timeout 30000"},"_npmUser":{"name":"dragonxsx","actor":{"name":"dragonxsx","type":"user","email":"dragon.sunshine@gmail.com"},"email":"dragon.sunshine@gmail.com"},"repository":{"url":"git+https://github.com/dragonxsx/cacheman-s3.git","type":"git"},"_npmVersion":"10.8.2","description":"AWS S3 cache engine for cacheman","directories":{},"_nodeVersion":"18.20.8","dependencies":{"sanitize-filename":"^1.6.3","@aws-sdk/client-s3":"^3.100.0"},"_hasShrinkwrap":false,"devDependencies":{"nyc":"^15.1.0","mocha":"^10.0.0","sinon":"^17.0.0","eslint":"^8.0.0","ts-node":"^10.9.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/mocha":"^10.0.0","@types/sinon":"^17.0.0","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cacheman-s3_1.1.0_1752106565510_0.6024682068405847","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@banana.inc/cacheman-s3","version":"1.1.1","description":"AWS S3 cache engine for cacheman","author":{"name":"Long Nguyen","email":"dragon.sunshine@gmail.com"},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch","clean":"rm -rf dist","prebuild":"npm run clean","prepack":"npm run build","test":"npm run test:unit && npm run test:integration","test:unit":"mocha test/index.test.ts --require ts-node/register --timeout 10000","test:integration":"npm run localstack:start && LOCALSTACK_ENDPOINT=http://localhost:4566 S3_TEST_BUCKET=test-bucket AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_REGION=us-east-1 mocha test/integration.test.ts --require ts-node/register --timeout 30000 && npm run localstack:stop","test:integration:ci":"mocha test/integration.test.ts --require ts-node/register --timeout 30000","localstack:start":"docker compose -f docker-compose.localstack.yml up -d","localstack:stop":"docker compose -f docker-compose.localstack.yml down","localstack:logs":"docker compose -f docker-compose.localstack.yml logs","localstack:setup":"./scripts/setup-localstack.sh","lint":"eslint src/ test/ --ext .ts","lint:fix":"eslint src/ test/ --ext .ts --fix","typecheck":"tsc --noEmit","coverage":"nyc npm run test:unit","coverage:report":"nyc report --reporter=html","dev":"ts-node src/index.ts","prepublishOnly":"npm run build"},"repository":{"type":"git","url":"git+https://github.com/dragonxsx/cacheman-s3.git"},"bugs":{"url":"https://github.com/dragonxsx/cacheman-s3/issues"},"homepage":"https://github.com/dragonxsx/cacheman-s3#readme","keywords":["cache","s3","aws","caching","store","ttl","cacheman","amazon","cloud","node","javascript","typescript","type-safe"],"license":"MIT","engines":{"node":">= 14.0.0"},"dependencies":{"@aws-sdk/client-s3":"^3.100.0","sanitize-filename":"^1.6.3"},"devDependencies":{"@types/node":"^20.0.0","@types/mocha":"^10.0.0","@types/sinon":"^17.0.0","@typescript-eslint/eslint-plugin":"^6.0.0","@typescript-eslint/parser":"^6.0.0","eslint":"^8.0.0","mocha":"^10.0.0","nyc":"^15.1.0","sinon":"^17.0.0","ts-node":"^10.9.0","typescript":"^5.0.0"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts","test/","dist/"],"reporter":["text","lcov"],"all":true},"mocha":{"require":["ts-node/register"],"extensions":["ts"],"spec":["test/**/*.test.ts"],"timeout":10000},"_id":"@banana.inc/cacheman-s3@1.1.1","gitHead":"f67817964d1aa7215a7bda4a0c229f9fe77b8601","_nodeVersion":"18.20.8","_npmVersion":"10.8.2","dist":{"integrity":"sha512-l2jRSx+GBlRCQ33MKxQIH/mSHWjG0ozeDCFgBP534AV74sEMuEpiqg9u6PYncgtQNP9cvbKPgHgPX1dw4e6K+Q==","shasum":"738c35c1031ce79dc6828164538726c4ac0959c2","tarball":"https://registry.npmjs.org/@banana.inc/cacheman-s3/-/cacheman-s3-1.1.1.tgz","fileCount":11,"unpackedSize":74193,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGqCO9gDfUZb/m6+sDCghxNZBEuL+pvsY+mbh7F2mb2aAiAexpEOs7a+XJGM97rkhFV9GjV4h2JLi8ykfNHMr17Kqw=="}]},"_npmUser":{"name":"dragonxsx","email":"dragon.sunshine@gmail.com","actor":{"name":"dragonxsx","email":"dragon.sunshine@gmail.com","type":"user"}},"directories":{},"maintainers":[{"name":"dragonxsx","email":"dragon.sunshine@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cacheman-s3_1.1.1_1752108215437_0.18987884125083054"},"_hasShrinkwrap":false}},"time":{"created":"2025-07-08T22:40:42.271Z","modified":"2025-07-10T00:43:36.118Z","1.0.0":"2025-07-08T22:40:42.560Z","1.0.1":"2025-07-08T23:13:41.012Z","1.1.0":"2025-07-10T00:16:05.682Z","1.1.1":"2025-07-10T00:43:35.603Z"},"bugs":{"url":"https://github.com/dragonxsx/cacheman-s3/issues"},"author":{"name":"Long Nguyen","email":"dragon.sunshine@gmail.com"},"license":"MIT","homepage":"https://github.com/dragonxsx/cacheman-s3#readme","keywords":["cache","s3","aws","caching","store","ttl","cacheman","amazon","cloud","node","javascript","typescript","type-safe"],"repository":{"type":"git","url":"git+https://github.com/dragonxsx/cacheman-s3.git"},"description":"AWS S3 cache engine for cacheman","maintainers":[{"name":"dragonxsx","email":"dragon.sunshine@gmail.com"}],"readme":"# @banana.inc/cacheman-s3\n\n[![Build Status](https://github.com/dragonxsx/cacheman-s3/workflows/CI/badge.svg)](https://github.com/dragonxsx/cacheman-s3/actions)\n[![NPM version](https://badge.fury.io/js/@banana.inc%2Fcacheman-s3.svg)](http://badge.fury.io/js/@banana.inc%2Fcacheman-s3)\n[![Coverage Status](https://codecov.io/gh/dragonxsx/cacheman-s3/branch/main/graph/badge.svg)](https://codecov.io/gh/dragonxsx/cacheman-s3)\n[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)\n\nTypeScript-first AWS S3 caching library for Node.JS and cache engine for [cacheman](https://github.com/cayasso/cacheman).\n\n## Features\n\n- 🚀 **High Performance**: Optimized for S3 operations with modern AWS SDK v3\n- 🔒 **Type-Safe**: Full TypeScript support with comprehensive type definitions\n- 🛡️ **Secure**: Supports IAM roles, encryption, and custom endpoints  \n- ⏰ **TTL Support**: Automatic expiration with lazy cleanup\n- 🔍 **Scanning**: Basic scan operations for cache inspection\n- 📊 **Monitoring**: Built-in health checks\n- 🌐 **AWS Integration**: Full AWS SDK v3 compatibility with LocalStack support\n- 📞 **Callback API**: Traditional Node.js callback patterns with TypeScript typing\n- 🎯 **Generic Support**: Type-safe caching for any data structure\n- 📁 **Hierarchical Keys**: Native support for slash-separated cache keys creating S3 object paths\n\n## Installation\n\n```bash\nnpm install @banana.inc/cacheman-s3\n```\n\n## Quick Start\n\n### TypeScript\n\n```typescript\nimport { S3Store } from '@banana.inc/cacheman-s3';\n\ninterface User {\n  id: number;\n  name: string;\n  email: string;\n  preferences: {\n    theme: 'light' | 'dark';\n    notifications: boolean;\n  };\n}\n\nconst cache = new S3Store<User>({\n  bucket: 'my-cache-bucket',\n  region: 'us-east-1'\n});\n\n// Set a typed value\ncache.set('user:123', {\n  id: 123,\n  name: 'John Doe',\n  email: 'john@example.com',\n  preferences: {\n    theme: 'dark',\n    notifications: true\n  }\n}, 3600, (error) => {\n  if (error) throw error;\n  console.log('User cached for 1 hour');\n  \n  // Get the typed value\n  cache.get('user:123', (error, user) => {\n    if (error) throw error;\n    if (user) {\n      // TypeScript knows user is of type User | null\n      console.log(`Welcome ${user.name}!`);\n      console.log(`Theme: ${user.preferences.theme}`);\n    }\n  });\n});\n```\n\n### JavaScript\n\n```javascript\nconst { S3Store } = require('@banana.inc/cacheman-s3');\n\nconst cache = new S3Store({\n  bucket: 'my-cache-bucket',\n  region: 'us-east-1'\n});\n\ncache.set('user:123', { name: 'John', age: 30 }, 3600, function(err) {\n  if (err) throw err;\n  \n  cache.get('user:123', function(err, user) {\n    if (err) throw err;\n    console.log('User:', user); // { name: 'John', age: 30 }\n  });\n});\n```\n\n## Usage with Cacheman\n\n### TypeScript\n\n```typescript\nimport Cacheman from 'cacheman';\nimport { S3Store } from '@banana.inc/cacheman-s3';\n\ninterface CacheData {\n  id: string;\n  data: any;\n  timestamp: number;\n}\n\nconst cache = new Cacheman<CacheData>('users', {\n  engine: S3Store,\n  bucket: 'my-cache-bucket',\n  region: 'us-east-1',\n  ttl: 3600 // 1 hour default TTL\n});\n\n// Type-safe operations\ncache.set('profile:123', {\n  id: 'profile:123',\n  data: { name: 'John', role: 'admin' },\n  timestamp: Date.now()\n}, (error) => {\n  if (error) throw error;\n  \n  cache.get('profile:123', (error, data) => {\n    if (error) throw error;\n    if (data) {\n      console.log(`Profile loaded: ${data.data.name}`);\n    }\n  });\n});\n```\n\n## Configuration\n\n### Basic Configuration\n\n```typescript\nimport { S3Store, S3StoreOptions } from '@banana.inc/cacheman-s3';\n\nconst options: S3StoreOptions = {\n  // Required\n  bucket: 'my-cache-bucket',\n  \n  // AWS Configuration\n  region: 'us-east-1',           // Default: 'us-east-1'\n  accessKeyId: 'AKIA...',        // Use IAM roles when possible\n  secretAccessKey: 'xxx',\n  sessionToken: 'xxx',           // For temporary credentials\n  \n  // Cache Configuration\n  prefix: 'cache:',              // Default: 'cacheman:'\n  defaultTtl: 3600,              // Default TTL in seconds\n  \n  // S3 Specific\n  storageClass: 'STANDARD',      // S3 storage class\n  serverSideEncryption: 'AES256', // Encryption at rest\n  \n  // Performance\n  maxRetries: 3,                 // AWS SDK retries\n  httpTimeout: 30000             // Request timeout (ms)\n};\n\nconst cache = new S3Store(options);\n```\n\n### Advanced Configuration with Types\n\n```typescript\ninterface CacheConfig extends S3StoreOptions {\n  customOption?: string;\n}\n\nconst createCache = <T>(config: CacheConfig): S3Store<T> => {\n  return new S3Store<T>({\n    bucket: config.bucket,\n    region: config.region || 'us-east-1',\n    prefix: config.prefix || 'app:',\n    defaultTtl: config.defaultTtl || 3600,\n    storageClass: 'INTELLIGENT_TIERING',\n    serverSideEncryption: 'AES256'\n  });\n};\n\n// Type-safe cache creation\nconst userCache = createCache<User>({\n  bucket: 'user-cache-bucket',\n  prefix: 'users:'\n});\n```\n\n## API Reference\n\n### Constructor\n\n```typescript\nnew S3Store<T>(options: S3StoreOptions): S3Store<T>\n```\n\nCreates a new type-safe S3Store instance.\n\n### Methods\n\n#### cache.set()\n\n```typescript\nset(key: string, value: T, ttl?: number, callback?: SetCallback<T>): void\nset(key: string, value: T, callback?: SetCallback<T>): void\n```\n\nStore a typed value in the cache.\n\n```typescript\ninterface Product {\n  id: string;\n  name: string;\n  price: number;\n}\n\nconst productCache = new S3Store<Product>({ bucket: 'products' });\n\nproductCache.set('product:123', {\n  id: '123',\n  name: 'Laptop',\n  price: 999.99\n}, 7200, (error) => {\n  if (error) throw error;\n  console.log('Product cached for 2 hours');\n});\n```\n\n#### cache.get()\n\n```typescript\nget(key: string, callback: GetCallback<T>): void\n```\n\nRetrieve a typed value from the cache.\n\n```typescript\nproductCache.get('product:123', (error, product) => {\n  if (error) throw error;\n  if (product) {\n    // TypeScript knows product is Product | null\n    console.log(`${product.name}: $${product.price}`);\n  }\n});\n```\n\n#### cache.del()\n\n```typescript\ndel(key: string, callback?: DeleteCallback): void\n```\n\nDelete a value from the cache.\n\n```typescript\nproductCache.del('product:123', (error) => {\n  if (error) throw error;\n  console.log('Product removed from cache');\n});\n```\n\n#### cache.clear()\n\n```typescript\nclear(callback?: ClearCallback): void\n```\n\nClear all cached values with the configured prefix.\n\n```typescript\nproductCache.clear((error) => {\n  if (error) throw error;\n  console.log('All products cleared from cache');\n});\n```\n\n#### cache.scan()\n\n```typescript\nscan(pattern?: string, limit?: number, callback?: ScanCallback<T>): void\nscan(pattern?: string, callback?: ScanCallback<T>): void\nscan(callback: ScanCallback<T>): void\n```\n\nScan cache entries with optional prefix matching.\n\n```typescript\nproductCache.scan('product', 100, (error, result) => {\n  if (error) throw error;\n  \n  console.log(`Found ${result.entries.length} products`);\n  result.entries.forEach(({ key, data }) => {\n    // data is typed as Product\n    console.log(`${key}: ${data.name} - $${data.price}`);\n  });\n});\n```\n\n#### cache.healthCheck()\n\n```typescript\nhealthCheck(callback?: HealthCallback): void\n```\n\nPerform a health check on the S3 connection.\n\n```typescript\ncache.healthCheck((error, status) => {\n  if (error) throw error;\n  \n  console.log('Health Status:', status);\n  // {\n  //   status: 'healthy',\n  //   bucket: 'my-cache-bucket',\n  //   region: 'us-east-1',\n  //   sdkVersion: 'v3'\n  // }\n});\n```\n\n## Type Definitions\n\n### Core Interfaces\n\n```typescript\n// Store options\ninterface S3StoreOptions {\n  bucket: string;\n  region?: string;\n  accessKeyId?: string;\n  secretAccessKey?: string;\n  sessionToken?: string;\n  prefix?: string;\n  defaultTtl?: number;\n  storageClass?: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'STANDARD_IA' | 'ONEZONE_IA' | 'INTELLIGENT_TIERING' | 'GLACIER' | 'DEEP_ARCHIVE';\n  serverSideEncryption?: 'AES256' | 'aws:kms';\n  maxRetries?: number;\n  httpTimeout?: number;\n}\n\n// Scan result\ninterface ScanResult<T> {\n  cursor: number | string;\n  entries: Array<{\n    key: string;\n    data: T;\n  }>;\n}\n\n// Health status\ninterface HealthStatus {\n  status: 'healthy' | 'unhealthy';\n  bucket: string;\n  region: string;\n  sdkVersion: string;\n  error?: string;\n}\n\n// Callback types\ntype GetCallback<T> = (error: Error | null, result?: T | null) => void;\ntype SetCallback<T> = (error: Error | null, result?: T) => void;\ntype DeleteCallback = (error: Error | null) => void;\ntype ClearCallback = (error: Error | null) => void;\ntype ScanCallback<T> = (error: Error | null, result?: ScanResult<T>) => void;\ntype HealthCallback = (error: Error | null, result?: HealthStatus) => void;\n```\n\n### Error Types\n\n```typescript\n// Base error class\nclass S3StoreError extends Error {\n  code: string;\n  statusCode?: number;\n  originalError?: Error;\n}\n\n// Specific error types\nclass ConfigurationError extends S3StoreError {}\nclass S3OperationError extends S3StoreError {}\nclass SerializationError extends S3StoreError {}\nclass TTLError extends S3StoreError {}\n```\n\n## Advanced Usage\n\n### Hierarchical Cache Keys\n\nS3Store supports hierarchical cache keys using forward slashes, which are preserved as S3 object paths:\n\n```typescript\nconst cache = new S3Store<any>({\n  bucket: 'my-cache-bucket',\n  prefix: 'app:'\n});\n\n// These create nested S3 object paths\ncache.set('users/123/profile', { name: 'John' }, (error) => {\n  // Creates S3 object: app:users/123/profile\n});\n\ncache.set('products/electronics/laptops/456', { name: 'MacBook' }, (error) => {\n  // Creates S3 object: app:products/electronics/laptops/456\n});\n\ncache.set('api/v1/cache/session/abc123', { userId: 789 }, (error) => {\n  // Creates S3 object: app:api/v1/cache/session/abc123\n});\n\n// Retrieve using the same hierarchical key\ncache.get('users/123/profile', (error, profile) => {\n  if (profile) {\n    console.log('User profile:', profile);\n  }\n});\n```\n\nThis allows for:\n- **Organized Data**: Logical grouping of related cache entries\n- **S3 Console Navigation**: Browse cache structure in AWS S3 console\n- **Prefix-based Operations**: Efficient scanning and clearing of key groups\n- **Natural Hierarchies**: Mirror your application's data structure\n\n### Generic Type Constraints\n\n```typescript\n// Define strict interfaces\ninterface BaseEntity {\n  id: string;\n  createdAt: string;\n  updatedAt: string;\n}\n\ninterface User extends BaseEntity {\n  name: string;\n  email: string;\n  role: 'admin' | 'user' | 'guest';\n}\n\ninterface Product extends BaseEntity {\n  name: string;\n  price: number;\n  category: string;\n  inStock: boolean;\n}\n\n// Create type-safe caches\nconst userCache = new S3Store<User>({\n  bucket: 'user-cache',\n  prefix: 'users:'\n});\n\nconst productCache = new S3Store<Product>({\n  bucket: 'product-cache',\n  prefix: 'products:'\n});\n\n// Type-safe operations\nuserCache.set('user:123', {\n  id: '123',\n  name: 'John Doe',\n  email: 'john@example.com',\n  role: 'admin', // TypeScript ensures valid role\n  createdAt: new Date().toISOString(),\n  updatedAt: new Date().toISOString()\n}, (error) => {\n  // Handle result\n});\n```\n\n### Utility Functions\n\n```typescript\nimport { S3Store, isValidTTL, isDefined } from '@banana.inc/cacheman-s3';\n\n// Type-safe cache wrapper\nclass TypedCache<T extends { id: string }> {\n  private cache: S3Store<T>;\n\n  constructor(options: S3StoreOptions) {\n    this.cache = new S3Store<T>(options);\n  }\n\n  async setEntity(entity: T, ttl: number = 3600): Promise<void> {\n    return new Promise((resolve, reject) => {\n      this.cache.set(entity.id, entity, ttl, (error) => {\n        if (error) reject(error);\n        else resolve();\n      });\n    });\n  }\n\n  async getEntity(id: string): Promise<T | null> {\n    return new Promise((resolve, reject) => {\n      this.cache.get(id, (error, entity) => {\n        if (error) reject(error);\n        else resolve(entity || null);\n      });\n    });\n  }\n}\n\n// Usage\nconst userCache = new TypedCache<User>({\n  bucket: 'users',\n  prefix: 'user:'\n});\n\n// Async/await usage\ntry {\n  await userCache.setEntity({\n    id: '123',\n    name: 'John',\n    email: 'john@example.com',\n    role: 'admin',\n    createdAt: new Date().toISOString(),\n    updatedAt: new Date().toISOString()\n  });\n\n  const user = await userCache.getEntity('123');\n  if (user) {\n    console.log(`User: ${user.name}`);\n  }\n} catch (error) {\n  console.error('Cache operation failed:', error);\n}\n```\n\n## AWS IAM Permissions\n\nMinimum required IAM permissions:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"s3:GetObject\",\n        \"s3:PutObject\",\n        \"s3:DeleteObject\",\n        \"s3:ListBucket\"\n      ],\n      \"Resource\": [\n        \"arn:aws:s3:::my-cache-bucket\",\n        \"arn:aws:s3:::my-cache-bucket/*\"\n      ]\n    }\n  ]\n}\n```\n\n## Development\n\n### Setup\n\n```bash\n# Install dependencies\nnpm install\n\n# Build TypeScript\nnpm run build\n\n# Run tests\nnpm test\n\n# Run unit tests only\nnpm run test:unit\n\n# Run integration tests (requires AWS credentials)\nnpm run test:integration\n\n# Type checking\nnpm run typecheck\n\n# Linting\nnpm run lint\n\n# Coverage\nnpm run coverage\n```\n\n### LocalStack Integration Testing\n\nThis package supports testing with [LocalStack](https://localstack.cloud/), which provides a local AWS cloud stack for development and testing.\n\n#### Prerequisites\n\n- Docker installed and running\n- Docker Compose (optional, for easier management)\n\n#### Quick Start with LocalStack\n\n```bash\n# Option 1: Using npm scripts (recommended)\nnpm run test:integration\n\n# Option 2: Manual setup\nnpm run localstack:start\nnpm run localstack:setup  # Creates S3 bucket\nnpm run test:integration\nnpm run localstack:stop\n```\n\n#### Docker Compose Method\n\n```bash\n# Start LocalStack using Docker Compose\nnpm run localstack:start\n\n# Run tests\nnpm run test:integration\n\n# Stop LocalStack\nnpm run localstack:stop\n```\n\n#### Manual Docker Method\n\n```bash\n# Start LocalStack container\ndocker run --rm -d -p 4566:4566 --name localstack-s3-test localstack/localstack:3.0\n\n# Wait for LocalStack to be ready\ncurl --retry 10 --retry-delay 1 --retry-connrefused http://localhost:4566/health\n\n# Create S3 bucket for testing\naws --endpoint-url=http://localhost:4566 s3 mb s3://test-bucket\n\n# Run integration tests\nLOCALSTACK_ENDPOINT=http://localhost:4566 \\\nS3_TEST_BUCKET=test-bucket \\\nAWS_ACCESS_KEY_ID=test \\\nAWS_SECRET_ACCESS_KEY=test \\\nAWS_REGION=us-east-1 \\\nnpm run test:integration\n\n# Cleanup\ndocker stop localstack-s3-test\n```\n\n#### Using LocalStack in Your Code\n\n```typescript\nimport { S3Store } from '@banana.inc/cacheman-s3';\n\n// Configure S3Store for LocalStack\nconst cache = new S3Store({\n  bucket: 'test-bucket',\n  region: 'us-east-1',\n  endpoint: 'http://localhost:4566',      // LocalStack endpoint\n  forcePathStyle: true,                   // Required for LocalStack\n  accessKeyId: 'test',                    // Any value works\n  secretAccessKey: 'test'                 // Any value works\n});\n\n// Use normally\ncache.set('key', { data: 'value' }, (error) => {\n  if (error) throw error;\n  console.log('Cached successfully with LocalStack!');\n});\n```\n\n#### LocalStack Configuration\n\nThe package automatically detects LocalStack when the `endpoint` option is provided:\n\n```typescript\nconst localstackOptions = {\n  bucket: 'my-bucket',\n  endpoint: 'http://localhost:4566',\n  forcePathStyle: true,  // Automatically set to true for LocalStack\n  accessKeyId: 'test',\n  secretAccessKey: 'test',\n  region: 'us-east-1'\n};\n\nconst cache = new S3Store(localstackOptions);\n```\n\n#### Benefits of LocalStack Testing\n\n- **No AWS Costs**: Test locally without incurring S3 charges\n- **Fast Feedback**: No network latency to AWS\n- **Isolation**: Tests don't affect production resources\n- **CI/CD Friendly**: Easy to integrate in GitHub Actions\n- **Offline Development**: Work without internet connection\n\n### TypeScript Compilation\n\n```bash\n# Watch mode for development\nnpm run build:watch\n\n# Clean build\nnpm run clean && npm run build\n```\n\n## License\n\nMIT\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n### Development Guidelines\n\n- Write TypeScript-first code with proper type definitions\n- Ensure test coverage for new features\n- Follow the existing code style (enforced by ESLint)\n- Update documentation for API changes\n- Add type definitions for all public APIs\n\n## Support\n\n- 📖 [Documentation](https://github.com/dragonxsx/cacheman-s3#readme)\n- 🐛 [Issues](https://github.com/dragonxsx/cacheman-s3/issues)\n- 💬 [Discussions](https://github.com/dragonxsx/cacheman-s3/discussions)\n- 📘 [TypeScript Documentation](https://www.typescriptlang.org/docs/)\n\n## Related Projects\n\n- [cacheman](https://github.com/cayasso/cacheman) - Caching library for Node.js\n- [AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3) - AWS SDK for JavaScript","readmeFilename":"README.md"}