@open-mercato/cache 0.5.1-develop.2744.9c8be0dd93 → 0.5.1-develop.2756.cce1739df3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- [build:cache] found 9 entry points
1
+ [build:cache] found 10 entry points
2
2
  [build:cache] built successfully
package/ENV.md CHANGED
@@ -51,13 +51,13 @@ REDIS_URL=redis://localhost:6379
51
51
 
52
52
  **Type:** String (file path)
53
53
 
54
- **Default:** `.cache.db`
54
+ **Default:** `./.mercato/cache/cache.db`
55
55
 
56
56
  **Required:** Only for `sqlite` strategy
57
57
 
58
58
  **Example:**
59
59
  ```bash
60
- CACHE_SQLITE_PATH=./data/.cache.db
60
+ CACHE_SQLITE_PATH=./.mercato/cache/cache.db
61
61
  ```
62
62
 
63
63
  ### `CACHE_JSON_FILE_PATH`
@@ -66,13 +66,13 @@ CACHE_SQLITE_PATH=./data/.cache.db
66
66
 
67
67
  **Type:** String (file path)
68
68
 
69
- **Default:** `.cache.json`
69
+ **Default:** `./.mercato/cache/cache.json`
70
70
 
71
71
  **Required:** Only for `jsonfile` strategy
72
72
 
73
73
  **Example:**
74
74
  ```bash
75
- CACHE_JSON_FILE_PATH=./data/.cache.json
75
+ CACHE_JSON_FILE_PATH=./.mercato/cache/cache.json
76
76
  ```
77
77
 
78
78
  ## Example Configurations
@@ -102,7 +102,7 @@ Persistent, file-based, good for single instance deployments.
102
102
 
103
103
  ```bash
104
104
  CACHE_STRATEGY=sqlite
105
- CACHE_SQLITE_PATH=./data/.cache.db
105
+ CACHE_SQLITE_PATH=./.mercato/cache/cache.db
106
106
  CACHE_TTL=3600000
107
107
  ```
108
108
 
@@ -112,7 +112,7 @@ Persistent, human-readable, slow, good for debugging.
112
112
 
113
113
  ```bash
114
114
  CACHE_STRATEGY=jsonfile
115
- CACHE_JSON_FILE_PATH=./data/.cache.json
115
+ CACHE_JSON_FILE_PATH=./.mercato/cache/cache.json
116
116
  CACHE_TTL=600000
117
117
  ```
118
118
 
@@ -170,4 +170,3 @@ yarn add better-sqlite3
170
170
  ### Memory and JSON File Strategies
171
171
 
172
172
  No additional dependencies required.
173
-
package/README.md CHANGED
@@ -33,8 +33,8 @@ import { createCacheService } from '@open-mercato/cache'
33
33
  const cache = createCacheService({
34
34
  strategy: process.env.CACHE_STRATEGY || 'memory',
35
35
  redisUrl: process.env.CACHE_REDIS_URL,
36
- sqlitePath: './data/cache.db',
37
- jsonFilePath: './data/cache.json',
36
+ sqlitePath: './.mercato/cache/cache.db',
37
+ jsonFilePath: './.mercato/cache/cache.json',
38
38
  defaultTtl: 5 * 60_000,
39
39
  })
40
40
 
@@ -72,8 +72,8 @@ Switch the strategy via:
72
72
  CACHE_STRATEGY=memory|redis|sqlite|jsonfile
73
73
  CACHE_TTL=300000
74
74
  CACHE_REDIS_URL=redis://localhost:6379
75
- CACHE_SQLITE_PATH=.cache/cache.db
76
- CACHE_JSON_FILE_PATH=.cache/cache.json
75
+ CACHE_SQLITE_PATH=./.mercato/cache/cache.db
76
+ CACHE_JSON_FILE_PATH=./.mercato/cache/cache.json
77
77
  ```
78
78
 
79
79
  ### Programmatic
@@ -81,7 +81,7 @@ CACHE_JSON_FILE_PATH=.cache/cache.json
81
81
  ```ts
82
82
  const sqliteCache = createCacheService({
83
83
  strategy: 'sqlite',
84
- sqlitePath: './data/cache.db',
84
+ sqlitePath: './.mercato/cache/cache.db',
85
85
  defaultTtl: 30 * 60_000,
86
86
  })
87
87
  ```
@@ -0,0 +1,7 @@
1
+ const DEFAULT_SQLITE_CACHE_PATH = ".mercato/cache/cache.db";
2
+ const DEFAULT_JSON_FILE_CACHE_PATH = ".mercato/cache/cache.json";
3
+ export {
4
+ DEFAULT_JSON_FILE_CACHE_PATH,
5
+ DEFAULT_SQLITE_CACHE_PATH
6
+ };
7
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/defaults.ts"],
4
+ "sourcesContent": ["export const DEFAULT_SQLITE_CACHE_PATH = '.mercato/cache/cache.db'\nexport const DEFAULT_JSON_FILE_CACHE_PATH = '.mercato/cache/cache.json'\n"],
5
+ "mappings": "AAAO,MAAM,4BAA4B;AAClC,MAAM,+BAA+B;",
6
+ "names": []
7
+ }
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./types.js";
2
2
  export * from "./service.js";
3
+ export * from "./defaults.js";
3
4
  import { createMemoryStrategy } from "./strategies/memory.js";
4
5
  import { createRedisStrategy } from "./strategies/redis.js";
5
6
  import { createSqliteStrategy } from "./strategies/sqlite.js";
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export * from './types'\nexport * from './service'\nexport { createMemoryStrategy } from './strategies/memory'\nexport { createRedisStrategy } from './strategies/redis'\nexport { createSqliteStrategy } from './strategies/sqlite'\nexport { createJsonFileStrategy } from './strategies/jsonfile'\nexport { runWithCacheTenant, getCurrentCacheTenant } from './tenantContext'\n"],
5
- "mappings": "AAAA,cAAc;AACd,cAAc;AACd,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB,6BAA6B;",
4
+ "sourcesContent": ["export * from './types'\nexport * from './service'\nexport * from './defaults'\nexport { createMemoryStrategy } from './strategies/memory'\nexport { createRedisStrategy } from './strategies/redis'\nexport { createSqliteStrategy } from './strategies/sqlite'\nexport { createJsonFileStrategy } from './strategies/jsonfile'\nexport { runWithCacheTenant, getCurrentCacheTenant } from './tenantContext'\n"],
5
+ "mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB,6BAA6B;",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,9 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { DEFAULT_JSON_FILE_CACHE_PATH } from "../defaults.js";
3
4
  function createJsonFileStrategy(filePath, options) {
4
5
  const defaultTtl = options?.defaultTtl;
5
- const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || ".cache.json";
6
+ const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || DEFAULT_JSON_FILE_CACHE_PATH;
6
7
  const dir = path.dirname(cacheFile);
7
8
  function ensureDir() {
8
9
  if (!fs.existsSync(dir)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/strategies/jsonfile.ts"],
4
- "sourcesContent": ["import type { CacheStrategy, CacheEntry, CacheGetOptions, CacheSetOptions, CacheValue } from '../types'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * JSON file cache strategy with tag support\n * Persistent across process restarts, stored in JSON files\n * Simple and requires no external dependencies, but not suitable for high-performance scenarios\n */\nexport function createJsonFileStrategy(filePath?: string, options?: { defaultTtl?: number }): CacheStrategy {\n const defaultTtl = options?.defaultTtl\n const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || '.cache.json'\n const dir = path.dirname(cacheFile)\n\n type StorageData = {\n entries: Record<string, CacheEntry>\n tagIndex: Record<string, string[]> // tag -> array of keys\n }\n\n function ensureDir(): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n }\n\n function readData(): StorageData {\n ensureDir()\n if (!fs.existsSync(cacheFile)) {\n return { entries: {}, tagIndex: {} }\n }\n try {\n const content = fs.readFileSync(cacheFile, 'utf8')\n return JSON.parse(content) as StorageData\n } catch {\n return { entries: {}, tagIndex: {} }\n }\n }\n\n function writeData(data: StorageData): void {\n ensureDir()\n fs.writeFileSync(cacheFile, JSON.stringify(data, null, 2), 'utf8')\n }\n\n function isExpired(entry: CacheEntry): boolean {\n if (entry.expiresAt === null) return false\n return Date.now() > entry.expiresAt\n }\n\n function matchPattern(key: string, pattern: string): boolean {\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(key)\n }\n\n function addToTagIndex(data: StorageData, key: string, tags: string[]): void {\n for (const tag of tags) {\n if (!data.tagIndex[tag]) {\n data.tagIndex[tag] = []\n }\n if (!data.tagIndex[tag].includes(key)) {\n data.tagIndex[tag].push(key)\n }\n }\n }\n\n function removeFromTagIndex(data: StorageData, key: string, tags: string[]): void {\n for (const tag of tags) {\n if (data.tagIndex[tag]) {\n data.tagIndex[tag] = data.tagIndex[tag].filter((k) => k !== key)\n if (data.tagIndex[tag].length === 0) {\n delete data.tagIndex[tag]\n }\n }\n }\n }\n\n const get = async (key: string, options?: CacheGetOptions): Promise<CacheValue | null> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return null\n\n if (isExpired(entry)) {\n if (options?.returnExpired) {\n return entry.value\n }\n // Clean up expired entry\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n return null\n }\n\n return entry.value\n }\n\n const set = async (key: string, value: CacheValue, options?: CacheSetOptions): Promise<void> => {\n const data = readData()\n\n // Remove old entry from tag index if it exists\n const oldEntry = data.entries[key]\n if (oldEntry) {\n removeFromTagIndex(data, key, oldEntry.tags)\n }\n\n const ttl = options?.ttl ?? defaultTtl\n const tags = options?.tags || []\n const expiresAt = ttl ? Date.now() + ttl : null\n\n const entry: CacheEntry = {\n key,\n value,\n tags,\n expiresAt,\n createdAt: Date.now(),\n }\n\n data.entries[key] = entry\n addToTagIndex(data, key, tags)\n\n writeData(data)\n }\n\n const has = async (key: string): Promise<boolean> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return false\n\n if (isExpired(entry)) {\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n return false\n }\n\n return true\n }\n\n const deleteKey = async (key: string): Promise<boolean> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return false\n\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n\n return true\n }\n\n const deleteByTags = async (tags: string[]): Promise<number> => {\n const data = readData()\n const keysToDelete = new Set<string>()\n\n // Collect all keys that have any of the specified tags\n for (const tag of tags) {\n const keys = data.tagIndex[tag] || []\n for (const key of keys) {\n keysToDelete.add(key)\n }\n }\n\n // Delete all collected keys\n for (const key of keysToDelete) {\n const entry = data.entries[key]\n if (entry) {\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n }\n }\n\n writeData(data)\n return keysToDelete.size\n }\n\n const clear = async (): Promise<number> => {\n const data = readData()\n const size = Object.keys(data.entries).length\n\n writeData({ entries: {}, tagIndex: {} })\n\n return size\n }\n\n const keys = async (pattern?: string): Promise<string[]> => {\n const data = readData()\n const allKeys = Object.keys(data.entries)\n\n if (!pattern) return allKeys\n\n return allKeys.filter((key) => matchPattern(key, pattern))\n }\n\n const stats = async (): Promise<{ size: number; expired: number }> => {\n const data = readData()\n const allEntries = Object.values(data.entries)\n\n let expired = 0\n for (const entry of allEntries) {\n if (isExpired(entry)) {\n expired++\n }\n }\n\n return { size: allEntries.length, expired }\n }\n\n const cleanup = async (): Promise<number> => {\n const data = readData()\n let removed = 0\n\n const keysToRemove: string[] = []\n for (const [key, entry] of Object.entries(data.entries)) {\n if (isExpired(entry)) {\n keysToRemove.push(key)\n }\n }\n\n for (const key of keysToRemove) {\n const entry = data.entries[key]\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n removed++\n }\n\n if (removed > 0) {\n writeData(data)\n }\n\n return removed\n }\n\n return {\n get,\n set,\n has,\n delete: deleteKey,\n deleteByTags,\n clear,\n keys,\n stats,\n cleanup,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CacheStrategy, CacheEntry, CacheGetOptions, CacheSetOptions, CacheValue } from '../types'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { DEFAULT_JSON_FILE_CACHE_PATH } from '../defaults'\n\n/**\n * JSON file cache strategy with tag support\n * Persistent across process restarts, stored in JSON files\n * Simple and requires no external dependencies, but not suitable for high-performance scenarios\n */\nexport function createJsonFileStrategy(filePath?: string, options?: { defaultTtl?: number }): CacheStrategy {\n const defaultTtl = options?.defaultTtl\n const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || DEFAULT_JSON_FILE_CACHE_PATH\n const dir = path.dirname(cacheFile)\n\n type StorageData = {\n entries: Record<string, CacheEntry>\n tagIndex: Record<string, string[]> // tag -> array of keys\n }\n\n function ensureDir(): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n }\n\n function readData(): StorageData {\n ensureDir()\n if (!fs.existsSync(cacheFile)) {\n return { entries: {}, tagIndex: {} }\n }\n try {\n const content = fs.readFileSync(cacheFile, 'utf8')\n return JSON.parse(content) as StorageData\n } catch {\n return { entries: {}, tagIndex: {} }\n }\n }\n\n function writeData(data: StorageData): void {\n ensureDir()\n fs.writeFileSync(cacheFile, JSON.stringify(data, null, 2), 'utf8')\n }\n\n function isExpired(entry: CacheEntry): boolean {\n if (entry.expiresAt === null) return false\n return Date.now() > entry.expiresAt\n }\n\n function matchPattern(key: string, pattern: string): boolean {\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(key)\n }\n\n function addToTagIndex(data: StorageData, key: string, tags: string[]): void {\n for (const tag of tags) {\n if (!data.tagIndex[tag]) {\n data.tagIndex[tag] = []\n }\n if (!data.tagIndex[tag].includes(key)) {\n data.tagIndex[tag].push(key)\n }\n }\n }\n\n function removeFromTagIndex(data: StorageData, key: string, tags: string[]): void {\n for (const tag of tags) {\n if (data.tagIndex[tag]) {\n data.tagIndex[tag] = data.tagIndex[tag].filter((k) => k !== key)\n if (data.tagIndex[tag].length === 0) {\n delete data.tagIndex[tag]\n }\n }\n }\n }\n\n const get = async (key: string, options?: CacheGetOptions): Promise<CacheValue | null> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return null\n\n if (isExpired(entry)) {\n if (options?.returnExpired) {\n return entry.value\n }\n // Clean up expired entry\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n return null\n }\n\n return entry.value\n }\n\n const set = async (key: string, value: CacheValue, options?: CacheSetOptions): Promise<void> => {\n const data = readData()\n\n // Remove old entry from tag index if it exists\n const oldEntry = data.entries[key]\n if (oldEntry) {\n removeFromTagIndex(data, key, oldEntry.tags)\n }\n\n const ttl = options?.ttl ?? defaultTtl\n const tags = options?.tags || []\n const expiresAt = ttl ? Date.now() + ttl : null\n\n const entry: CacheEntry = {\n key,\n value,\n tags,\n expiresAt,\n createdAt: Date.now(),\n }\n\n data.entries[key] = entry\n addToTagIndex(data, key, tags)\n\n writeData(data)\n }\n\n const has = async (key: string): Promise<boolean> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return false\n\n if (isExpired(entry)) {\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n return false\n }\n\n return true\n }\n\n const deleteKey = async (key: string): Promise<boolean> => {\n const data = readData()\n const entry = data.entries[key]\n\n if (!entry) return false\n\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n writeData(data)\n\n return true\n }\n\n const deleteByTags = async (tags: string[]): Promise<number> => {\n const data = readData()\n const keysToDelete = new Set<string>()\n\n // Collect all keys that have any of the specified tags\n for (const tag of tags) {\n const keys = data.tagIndex[tag] || []\n for (const key of keys) {\n keysToDelete.add(key)\n }\n }\n\n // Delete all collected keys\n for (const key of keysToDelete) {\n const entry = data.entries[key]\n if (entry) {\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n }\n }\n\n writeData(data)\n return keysToDelete.size\n }\n\n const clear = async (): Promise<number> => {\n const data = readData()\n const size = Object.keys(data.entries).length\n\n writeData({ entries: {}, tagIndex: {} })\n\n return size\n }\n\n const keys = async (pattern?: string): Promise<string[]> => {\n const data = readData()\n const allKeys = Object.keys(data.entries)\n\n if (!pattern) return allKeys\n\n return allKeys.filter((key) => matchPattern(key, pattern))\n }\n\n const stats = async (): Promise<{ size: number; expired: number }> => {\n const data = readData()\n const allEntries = Object.values(data.entries)\n\n let expired = 0\n for (const entry of allEntries) {\n if (isExpired(entry)) {\n expired++\n }\n }\n\n return { size: allEntries.length, expired }\n }\n\n const cleanup = async (): Promise<number> => {\n const data = readData()\n let removed = 0\n\n const keysToRemove: string[] = []\n for (const [key, entry] of Object.entries(data.entries)) {\n if (isExpired(entry)) {\n keysToRemove.push(key)\n }\n }\n\n for (const key of keysToRemove) {\n const entry = data.entries[key]\n removeFromTagIndex(data, key, entry.tags)\n delete data.entries[key]\n removed++\n }\n\n if (removed > 0) {\n writeData(data)\n }\n\n return removed\n }\n\n return {\n get,\n set,\n has,\n delete: deleteKey,\n deleteByTags,\n clear,\n keys,\n stats,\n cleanup,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["options", "keys"]
7
7
  }
@@ -2,11 +2,12 @@ import fs from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import path from "node:path";
4
4
  import { CacheDependencyUnavailableError } from "../errors.js";
5
+ import { DEFAULT_SQLITE_CACHE_PATH } from "../defaults.js";
5
6
  const sqliteRequire = createRequire(path.join(process.cwd(), "package.json"));
6
7
  function createSqliteStrategy(dbPath, options) {
7
8
  let db = null;
8
9
  const defaultTtl = options?.defaultTtl;
9
- const filePath = dbPath || process.env.CACHE_SQLITE_PATH || ".cache.db";
10
+ const filePath = dbPath || process.env.CACHE_SQLITE_PATH || DEFAULT_SQLITE_CACHE_PATH;
10
11
  async function getDb() {
11
12
  if (db) return db;
12
13
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/strategies/sqlite.ts"],
4
- "sourcesContent": ["import type { CacheStrategy, CacheGetOptions, CacheSetOptions, CacheValue } from '../types'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { CacheDependencyUnavailableError } from '../errors'\n\ntype SqliteStatement<TResult = unknown> = {\n get(...args: unknown[]): TResult | undefined\n all(...args: unknown[]): TResult[]\n run(...args: unknown[]): { changes: number }\n}\n\ntype SqliteTransaction<TResult = unknown> = () => TResult\n\ntype SqliteDatabase = {\n prepare<TResult = unknown>(sql: string): SqliteStatement<TResult>\n exec(sql: string): unknown\n transaction<TResult>(fn: () => TResult): SqliteTransaction<TResult>\n close(): void\n}\n\ntype SqliteConstructor = new (file: string) => SqliteDatabase\ntype SqliteModule = SqliteConstructor | { default: SqliteConstructor }\n\nconst sqliteRequire = createRequire(path.join(process.cwd(), 'package.json'))\n\n/**\n * SQLite cache strategy with tag support\n * Persistent across process restarts, stored in a SQLite database file\n * \n * Uses two tables:\n * - cache_entries: stores cache data\n * - cache_tags: stores tag associations (many-to-many)\n */\nexport function createSqliteStrategy(dbPath?: string, options?: { defaultTtl?: number }): CacheStrategy {\n let db: SqliteDatabase | null = null\n const defaultTtl = options?.defaultTtl\n const filePath = dbPath || process.env.CACHE_SQLITE_PATH || '.cache.db'\n\n async function getDb(): Promise<SqliteDatabase> {\n if (db) return db\n\n try {\n const imported = sqliteRequire('better-sqlite3') as SqliteModule\n const Database = typeof imported === 'function' ? imported : imported.default\n \n // Ensure directory exists\n const dir = path.dirname(filePath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n db = new Database(filePath)\n\n // Create tables\n db.exec(`\n CREATE TABLE IF NOT EXISTS cache_entries (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at INTEGER,\n created_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS cache_tags (\n key TEXT NOT NULL,\n tag TEXT NOT NULL,\n PRIMARY KEY (key, tag),\n FOREIGN KEY (key) REFERENCES cache_entries(key) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_cache_tags_tag ON cache_tags(tag);\n CREATE INDEX IF NOT EXISTS idx_cache_entries_expires_at ON cache_entries(expires_at);\n `)\n\n return db\n } catch (error) {\n throw new CacheDependencyUnavailableError('sqlite', 'better-sqlite3', error)\n }\n }\n\n function isExpired(expiresAt: number | null): boolean {\n if (expiresAt === null) return false\n return Date.now() > expiresAt\n }\n\n function matchPattern(key: string, pattern: string): boolean {\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(key)\n }\n\n type EntryRow = { value: string; expires_at: number | null }\n type ExpiresRow = { expires_at: number | null }\n type CountRow = { count: number }\n type KeyRow = { key: string }\n\n const get = async (key: string, options?: CacheGetOptions): Promise<CacheValue | null> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT value, expires_at FROM cache_entries WHERE key = ?')\n const row = stmt.get(key) as EntryRow | undefined\n\n if (!row) return null\n\n try {\n const value = JSON.parse(row.value) as CacheValue\n const expiresAt = row.expires_at\n\n if (isExpired(expiresAt)) {\n if (options?.returnExpired) {\n return value\n }\n // Clean up expired entry\n await deleteKey(key)\n return null\n }\n\n return value\n } catch {\n // Invalid JSON, remove it\n await deleteKey(key)\n return null\n }\n }\n\n const set = async (key: string, value: CacheValue, options?: CacheSetOptions): Promise<void> => {\n const database = await getDb()\n const ttl = options?.ttl ?? defaultTtl\n const tags = options?.tags || []\n const expiresAt = ttl ? Date.now() + ttl : null\n const createdAt = Date.now()\n\n const serialized = JSON.stringify(value)\n\n database.transaction(() => {\n // Delete old tags\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(key)\n\n // Insert or replace cache entry\n database.prepare(`\n INSERT OR REPLACE INTO cache_entries (key, value, expires_at, created_at)\n VALUES (?, ?, ?, ?)\n `).run(key, serialized, expiresAt, createdAt)\n\n // Insert new tags\n if (tags.length > 0) {\n const insertTag = database.prepare('INSERT INTO cache_tags (key, tag) VALUES (?, ?)')\n for (const tag of tags) {\n insertTag.run(key, tag)\n }\n }\n })()\n }\n\n const has = async (key: string): Promise<boolean> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT expires_at FROM cache_entries WHERE key = ?')\n const row = stmt.get(key) as ExpiresRow | undefined\n\n if (!row) return false\n\n if (isExpired(row.expires_at)) {\n await deleteKey(key)\n return false\n }\n\n return true\n }\n\n const deleteKey = async (key: string): Promise<boolean> => {\n const database = await getDb()\n \n const result = database.transaction(() => {\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(key)\n const info = database.prepare('DELETE FROM cache_entries WHERE key = ?').run(key)\n return info.changes > 0\n })()\n\n return result\n }\n\n const deleteByTags = async (tags: string[]): Promise<number> => {\n const database = await getDb()\n \n // Get all unique keys that have any of the specified tags\n const placeholders = tags.map(() => '?').join(',')\n const stmt = database.prepare(`\n SELECT DISTINCT key FROM cache_tags WHERE tag IN (${placeholders})\n `)\n const rows = stmt.all(...tags) as KeyRow[]\n\n let deleted = 0\n for (const row of rows) {\n const success = await deleteKey(row.key)\n if (success) deleted++\n }\n\n return deleted\n }\n\n const clear = async (): Promise<number> => {\n const database = await getDb()\n \n const result = database.transaction(() => {\n const countStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries')\n const count = (countStmt.get() as CountRow).count\n\n database.prepare('DELETE FROM cache_tags').run()\n database.prepare('DELETE FROM cache_entries').run()\n\n return count\n })()\n\n return result\n }\n\n const keys = async (pattern?: string): Promise<string[]> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT key FROM cache_entries')\n const rows = stmt.all() as KeyRow[]\n \n const allKeys = rows.map((row) => row.key)\n \n if (!pattern) return allKeys\n \n return allKeys.filter((key: string) => matchPattern(key, pattern))\n }\n\n const stats = async (): Promise<{ size: number; expired: number }> => {\n const database = await getDb()\n \n const sizeStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries')\n const size = (sizeStmt.get() as CountRow).count\n\n const now = Date.now()\n const expiredStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?')\n const expired = (expiredStmt.get(now) as CountRow).count\n\n return { size, expired }\n }\n\n const cleanup = async (): Promise<number> => {\n const database = await getDb()\n const now = Date.now()\n \n const result = database.transaction(() => {\n // Get keys to delete\n const stmt = database.prepare('SELECT key FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?')\n const rows = stmt.all(now) as KeyRow[]\n\n // Delete tags for expired keys\n for (const row of rows) {\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(row.key)\n }\n\n // Delete expired entries\n const info = database.prepare('DELETE FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?').run(now)\n return info.changes\n })()\n\n return result\n }\n\n const close = async (): Promise<void> => {\n if (db) {\n db.close()\n db = null\n }\n }\n\n return {\n get,\n set,\n has,\n delete: deleteKey,\n deleteByTags,\n clear,\n keys,\n stats,\n cleanup,\n close,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CacheStrategy, CacheGetOptions, CacheSetOptions, CacheValue } from '../types'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { CacheDependencyUnavailableError } from '../errors'\nimport { DEFAULT_SQLITE_CACHE_PATH } from '../defaults'\n\ntype SqliteStatement<TResult = unknown> = {\n get(...args: unknown[]): TResult | undefined\n all(...args: unknown[]): TResult[]\n run(...args: unknown[]): { changes: number }\n}\n\ntype SqliteTransaction<TResult = unknown> = () => TResult\n\ntype SqliteDatabase = {\n prepare<TResult = unknown>(sql: string): SqliteStatement<TResult>\n exec(sql: string): unknown\n transaction<TResult>(fn: () => TResult): SqliteTransaction<TResult>\n close(): void\n}\n\ntype SqliteConstructor = new (file: string) => SqliteDatabase\ntype SqliteModule = SqliteConstructor | { default: SqliteConstructor }\n\nconst sqliteRequire = createRequire(path.join(process.cwd(), 'package.json'))\n\n/**\n * SQLite cache strategy with tag support\n * Persistent across process restarts, stored in a SQLite database file\n * \n * Uses two tables:\n * - cache_entries: stores cache data\n * - cache_tags: stores tag associations (many-to-many)\n */\nexport function createSqliteStrategy(dbPath?: string, options?: { defaultTtl?: number }): CacheStrategy {\n let db: SqliteDatabase | null = null\n const defaultTtl = options?.defaultTtl\n const filePath = dbPath || process.env.CACHE_SQLITE_PATH || DEFAULT_SQLITE_CACHE_PATH\n\n async function getDb(): Promise<SqliteDatabase> {\n if (db) return db\n\n try {\n const imported = sqliteRequire('better-sqlite3') as SqliteModule\n const Database = typeof imported === 'function' ? imported : imported.default\n \n // Ensure directory exists\n const dir = path.dirname(filePath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n db = new Database(filePath)\n\n // Create tables\n db.exec(`\n CREATE TABLE IF NOT EXISTS cache_entries (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at INTEGER,\n created_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS cache_tags (\n key TEXT NOT NULL,\n tag TEXT NOT NULL,\n PRIMARY KEY (key, tag),\n FOREIGN KEY (key) REFERENCES cache_entries(key) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_cache_tags_tag ON cache_tags(tag);\n CREATE INDEX IF NOT EXISTS idx_cache_entries_expires_at ON cache_entries(expires_at);\n `)\n\n return db\n } catch (error) {\n throw new CacheDependencyUnavailableError('sqlite', 'better-sqlite3', error)\n }\n }\n\n function isExpired(expiresAt: number | null): boolean {\n if (expiresAt === null) return false\n return Date.now() > expiresAt\n }\n\n function matchPattern(key: string, pattern: string): boolean {\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(key)\n }\n\n type EntryRow = { value: string; expires_at: number | null }\n type ExpiresRow = { expires_at: number | null }\n type CountRow = { count: number }\n type KeyRow = { key: string }\n\n const get = async (key: string, options?: CacheGetOptions): Promise<CacheValue | null> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT value, expires_at FROM cache_entries WHERE key = ?')\n const row = stmt.get(key) as EntryRow | undefined\n\n if (!row) return null\n\n try {\n const value = JSON.parse(row.value) as CacheValue\n const expiresAt = row.expires_at\n\n if (isExpired(expiresAt)) {\n if (options?.returnExpired) {\n return value\n }\n // Clean up expired entry\n await deleteKey(key)\n return null\n }\n\n return value\n } catch {\n // Invalid JSON, remove it\n await deleteKey(key)\n return null\n }\n }\n\n const set = async (key: string, value: CacheValue, options?: CacheSetOptions): Promise<void> => {\n const database = await getDb()\n const ttl = options?.ttl ?? defaultTtl\n const tags = options?.tags || []\n const expiresAt = ttl ? Date.now() + ttl : null\n const createdAt = Date.now()\n\n const serialized = JSON.stringify(value)\n\n database.transaction(() => {\n // Delete old tags\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(key)\n\n // Insert or replace cache entry\n database.prepare(`\n INSERT OR REPLACE INTO cache_entries (key, value, expires_at, created_at)\n VALUES (?, ?, ?, ?)\n `).run(key, serialized, expiresAt, createdAt)\n\n // Insert new tags\n if (tags.length > 0) {\n const insertTag = database.prepare('INSERT INTO cache_tags (key, tag) VALUES (?, ?)')\n for (const tag of tags) {\n insertTag.run(key, tag)\n }\n }\n })()\n }\n\n const has = async (key: string): Promise<boolean> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT expires_at FROM cache_entries WHERE key = ?')\n const row = stmt.get(key) as ExpiresRow | undefined\n\n if (!row) return false\n\n if (isExpired(row.expires_at)) {\n await deleteKey(key)\n return false\n }\n\n return true\n }\n\n const deleteKey = async (key: string): Promise<boolean> => {\n const database = await getDb()\n \n const result = database.transaction(() => {\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(key)\n const info = database.prepare('DELETE FROM cache_entries WHERE key = ?').run(key)\n return info.changes > 0\n })()\n\n return result\n }\n\n const deleteByTags = async (tags: string[]): Promise<number> => {\n const database = await getDb()\n \n // Get all unique keys that have any of the specified tags\n const placeholders = tags.map(() => '?').join(',')\n const stmt = database.prepare(`\n SELECT DISTINCT key FROM cache_tags WHERE tag IN (${placeholders})\n `)\n const rows = stmt.all(...tags) as KeyRow[]\n\n let deleted = 0\n for (const row of rows) {\n const success = await deleteKey(row.key)\n if (success) deleted++\n }\n\n return deleted\n }\n\n const clear = async (): Promise<number> => {\n const database = await getDb()\n \n const result = database.transaction(() => {\n const countStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries')\n const count = (countStmt.get() as CountRow).count\n\n database.prepare('DELETE FROM cache_tags').run()\n database.prepare('DELETE FROM cache_entries').run()\n\n return count\n })()\n\n return result\n }\n\n const keys = async (pattern?: string): Promise<string[]> => {\n const database = await getDb()\n \n const stmt = database.prepare('SELECT key FROM cache_entries')\n const rows = stmt.all() as KeyRow[]\n \n const allKeys = rows.map((row) => row.key)\n \n if (!pattern) return allKeys\n \n return allKeys.filter((key: string) => matchPattern(key, pattern))\n }\n\n const stats = async (): Promise<{ size: number; expired: number }> => {\n const database = await getDb()\n \n const sizeStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries')\n const size = (sizeStmt.get() as CountRow).count\n\n const now = Date.now()\n const expiredStmt = database.prepare('SELECT COUNT(*) as count FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?')\n const expired = (expiredStmt.get(now) as CountRow).count\n\n return { size, expired }\n }\n\n const cleanup = async (): Promise<number> => {\n const database = await getDb()\n const now = Date.now()\n \n const result = database.transaction(() => {\n // Get keys to delete\n const stmt = database.prepare('SELECT key FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?')\n const rows = stmt.all(now) as KeyRow[]\n\n // Delete tags for expired keys\n for (const row of rows) {\n database.prepare('DELETE FROM cache_tags WHERE key = ?').run(row.key)\n }\n\n // Delete expired entries\n const info = database.prepare('DELETE FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ?').run(now)\n return info.changes\n })()\n\n return result\n }\n\n const close = async (): Promise<void> => {\n if (db) {\n db.close()\n db = null\n }\n }\n\n return {\n get,\n set,\n has,\n delete: deleteKey,\n deleteByTags,\n clear,\n keys,\n stats,\n cleanup,\n close,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["options"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cache",
3
- "version": "0.5.1-develop.2744.9c8be0dd93",
3
+ "version": "0.5.1-develop.2756.cce1739df3",
4
4
  "description": "Multi-strategy cache service with tag-based invalidation support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,14 @@
1
1
  import { createCacheService, CacheService } from '../service'
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import { DEFAULT_JSON_FILE_CACHE_PATH, DEFAULT_SQLITE_CACHE_PATH } from '../defaults'
2
5
 
3
6
  describe('Cache Service', () => {
7
+ afterEach(() => {
8
+ delete process.env.CACHE_SQLITE_PATH
9
+ fs.rmSync(path.resolve('.mercato/cache'), { recursive: true, force: true })
10
+ })
11
+
4
12
  describe('Strategy selection', () => {
5
13
  it('should default to memory strategy', async () => {
6
14
  const cache = createCacheService()
@@ -41,6 +49,11 @@ describe('Cache Service', () => {
41
49
  await new Promise((resolve) => setTimeout(resolve, 150))
42
50
  expect(await cache.get('test')).toBeNull()
43
51
  })
52
+
53
+ it('should keep file-backed cache defaults under .mercato', async () => {
54
+ expect(DEFAULT_SQLITE_CACHE_PATH).toBe('.mercato/cache/cache.db')
55
+ expect(DEFAULT_JSON_FILE_CACHE_PATH).toBe('.mercato/cache/cache.json')
56
+ })
44
57
  })
45
58
 
46
59
  describe('CacheService class', () => {
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_SQLITE_CACHE_PATH = '.mercato/cache/cache.db'
2
+ export const DEFAULT_JSON_FILE_CACHE_PATH = '.mercato/cache/cache.json'
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './types'
2
2
  export * from './service'
3
+ export * from './defaults'
3
4
  export { createMemoryStrategy } from './strategies/memory'
4
5
  export { createRedisStrategy } from './strategies/redis'
5
6
  export { createSqliteStrategy } from './strategies/sqlite'
@@ -1,6 +1,7 @@
1
1
  import type { CacheStrategy, CacheEntry, CacheGetOptions, CacheSetOptions, CacheValue } from '../types'
2
2
  import fs from 'node:fs'
3
3
  import path from 'node:path'
4
+ import { DEFAULT_JSON_FILE_CACHE_PATH } from '../defaults'
4
5
 
5
6
  /**
6
7
  * JSON file cache strategy with tag support
@@ -9,7 +10,7 @@ import path from 'node:path'
9
10
  */
10
11
  export function createJsonFileStrategy(filePath?: string, options?: { defaultTtl?: number }): CacheStrategy {
11
12
  const defaultTtl = options?.defaultTtl
12
- const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || '.cache.json'
13
+ const cacheFile = filePath || process.env.CACHE_JSON_FILE_PATH || DEFAULT_JSON_FILE_CACHE_PATH
13
14
  const dir = path.dirname(cacheFile)
14
15
 
15
16
  type StorageData = {
@@ -3,6 +3,7 @@ import fs from 'node:fs'
3
3
  import { createRequire } from 'node:module'
4
4
  import path from 'node:path'
5
5
  import { CacheDependencyUnavailableError } from '../errors'
6
+ import { DEFAULT_SQLITE_CACHE_PATH } from '../defaults'
6
7
 
7
8
  type SqliteStatement<TResult = unknown> = {
8
9
  get(...args: unknown[]): TResult | undefined
@@ -35,7 +36,7 @@ const sqliteRequire = createRequire(path.join(process.cwd(), 'package.json'))
35
36
  export function createSqliteStrategy(dbPath?: string, options?: { defaultTtl?: number }): CacheStrategy {
36
37
  let db: SqliteDatabase | null = null
37
38
  const defaultTtl = options?.defaultTtl
38
- const filePath = dbPath || process.env.CACHE_SQLITE_PATH || '.cache.db'
39
+ const filePath = dbPath || process.env.CACHE_SQLITE_PATH || DEFAULT_SQLITE_CACHE_PATH
39
40
 
40
41
  async function getDb(): Promise<SqliteDatabase> {
41
42
  if (db) return db