{"version":3,"file":"cacheDisk.cjs","names":["computeKeyId","configPackageJson"],"sources":["../../../src/utils/cacheDisk.ts"],"sourcesContent":["import {\n  mkdir,\n  readFile,\n  rename,\n  rm,\n  stat,\n  unlink,\n  writeFile,\n} from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { deserialize, serialize } from 'node:v8';\nimport { gunzipSync, gzipSync } from 'node:zlib';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport configPackageJson from '@intlayer/types/package.json' with {\n  type: 'json',\n};\nimport { type CacheKey, clearAllCache, computeKeyId } from './cacheMemory';\n\n/** ------------------------- Persistence layer ------------------------- **/\n\n/** Cache envelope structure stored on disk */\ntype CacheEnvelope = {\n  /** Version of the config package (for cache invalidation) */\n  version: string;\n  /** Timestamp when the cache entry was created (in milliseconds) */\n  timestamp: number;\n  /** Data payload (the actual cached value) */\n  data: unknown;\n};\n\ntype LocalCacheOptions = {\n  /** Preferred new option name */\n  persistent?: boolean;\n  /** Time-to-live in ms; if expired, disk entry is ignored. */\n  ttlMs?: number;\n  /** Max age in ms based on stored creation timestamp; invalidates on exceed. */\n  maxTimeMs?: number;\n  /** Optional namespace to separate different logical caches. */\n  namespace?: string;\n  /** Gzip values on disk (on by default for blobs > 1KB). */\n  compress?: boolean;\n};\n\nconst DEFAULTS: Required<Pick<LocalCacheOptions, 'compress'>> = {\n  compress: true,\n};\n\nconst ensureDir = async (dir: string) => {\n  await mkdir(dir, { recursive: true });\n};\n\nconst atomicWriteFile = async (\n  file: string,\n  data: Buffer,\n  tempDir?: string\n) => {\n  if (tempDir) {\n    try {\n      await ensureDir(tempDir);\n    } catch {}\n  }\n\n  const tempFileName = `${basename(file)}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n  const tmp = tempDir\n    ? join(tempDir, tempFileName)\n    : `${file}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;\n  try {\n    await writeFile(tmp, data);\n    await rename(tmp, file);\n  } catch (error) {\n    try {\n      await rm(tmp, { force: true });\n    } catch {\n      // Ignore\n    }\n    throw error;\n  }\n};\n\nconst shouldUseCompression = (buf: Buffer, force?: boolean) =>\n  force === true || (force !== false && buf.byteLength > 1024);\n\n/** Derive on-disk path from config dir + namespace + key id. */\nconst cachePath = (cacheDir: string, id: string, ns?: string) =>\n  join(cacheDir, ns ? join(ns, id) : id);\n\n/** ------------------------- Local cache facade ------------------------- **/\n\nconst cacheMap = new Map<string, any>();\n\n/** Clears the in-memory portion of the disk cache without touching disk files. */\nexport const clearDiskCacheMemory = (): void => {\n  cacheMap.clear();\n};\n\nexport const cacheDisk = (\n  intlayerConfig: IntlayerConfig,\n  keys: CacheKey[],\n  options?: LocalCacheOptions\n) => {\n  const { cacheDir, tempDir } = intlayerConfig.system;\n  const buildCacheEnabled = intlayerConfig.build.cache ?? true;\n  const persistent =\n    options?.persistent === true ||\n    (typeof options?.persistent === 'undefined' && buildCacheEnabled);\n\n  const { compress, ttlMs, maxTimeMs, namespace } = {\n    ...DEFAULTS,\n    ...options,\n  };\n\n  // single stable id for this key tuple (works for both memory & disk)\n  const id = computeKeyId(keys);\n  const filePath = cachePath(cacheDir, id, namespace);\n\n  const readFromDisk = async (): Promise<unknown | undefined> => {\n    try {\n      const statValue = await stat(filePath).catch(() => undefined);\n\n      if (!statValue) return undefined;\n\n      if (typeof ttlMs === 'number' && ttlMs > 0) {\n        const age = Date.now() - statValue.mtimeMs;\n        if (age > ttlMs) return undefined;\n      }\n      let raw = await readFile(filePath);\n      // header: 1 byte flag (0x00 raw, 0x01 gzip)\n      const flag = raw[0];\n\n      raw = raw.subarray(1);\n\n      const payload = flag === 0x01 ? gunzipSync(raw) : raw;\n      const deserialized = deserialize(payload) as unknown;\n\n      let value: unknown;\n      const maybeObj = deserialized as Record<string, unknown> | null;\n      const isEnvelope =\n        !!maybeObj &&\n        typeof maybeObj === 'object' &&\n        typeof (maybeObj as any).version === 'string' &&\n        typeof (maybeObj as any).timestamp === 'number' &&\n        Object.hasOwn(maybeObj, 'data');\n\n      if (isEnvelope) {\n        const entry = maybeObj as CacheEnvelope;\n\n        if (entry.version !== configPackageJson.version) {\n          try {\n            await unlink(filePath);\n          } catch {}\n          return undefined;\n        }\n\n        if (typeof maxTimeMs === 'number' && maxTimeMs > 0) {\n          const age = Date.now() - entry.timestamp;\n          if (age > maxTimeMs) {\n            try {\n              await unlink(filePath);\n            } catch {}\n            return undefined;\n          }\n        }\n\n        value = entry.data;\n      } else {\n        // Backward compatibility: old entries had raw serialized value.\n        if (typeof maxTimeMs === 'number' && maxTimeMs > 0) {\n          const age = Date.now() - statValue.mtimeMs;\n          if (age > maxTimeMs) {\n            try {\n              await unlink(filePath);\n            } catch {}\n            return undefined;\n          }\n        }\n        value = deserialized;\n      }\n\n      // hydrate memory cache as well\n      cacheMap.set(id, value);\n      return value;\n    } catch {\n      return undefined;\n    }\n  };\n\n  const writeToDisk = async (value: unknown) => {\n    try {\n      await ensureDir(dirname(filePath));\n      const envelope: CacheEnvelope = {\n        version: configPackageJson.version,\n        timestamp: Date.now(),\n        data: value,\n      };\n      const payload = Buffer.from(serialize(envelope));\n\n      const gz = shouldUseCompression(payload, compress)\n        ? gzipSync(payload)\n        : payload;\n\n      // prepend a 1-byte header indicating compression\n      const buf = Buffer.concat([\n        Buffer.from([gz === payload ? 0x00 : 0x01]),\n        gz,\n      ]);\n\n      await atomicWriteFile(filePath, buf, tempDir);\n    } catch {\n      // swallow disk errors for cache writes\n    }\n  };\n\n  return {\n    /** In-memory first, then disk (if enabled), otherwise undefined. */\n    get: async <T>(): Promise<T | undefined> => {\n      const mem = cacheMap.get(id);\n\n      if (mem !== undefined) return mem as T;\n\n      if (persistent && buildCacheEnabled) {\n        return (await readFromDisk()) as T | undefined;\n      }\n      return undefined;\n    },\n    /** Sets in-memory (always) and persists to disk if enabled. */\n    set: async (value: unknown): Promise<void> => {\n      cacheMap.set(id, value);\n\n      if (persistent && buildCacheEnabled) {\n        await writeToDisk(value);\n      }\n    },\n    /** Clears only this entry from memory and disk. */\n    clear: async (): Promise<void> => {\n      cacheMap.delete(id);\n\n      try {\n        await unlink(filePath);\n      } catch {}\n    },\n    /** Clears ALL cached entries (memory Map and entire cacheDir namespace if persistent). */\n    clearAll: async (): Promise<void> => {\n      clearAllCache();\n      if (persistent && buildCacheEnabled) {\n        // remove only the current namespace (if provided), else the root dir\n        const base = namespace ? join(cacheDir, namespace) : cacheDir;\n\n        try {\n          await rm(base, { recursive: true, force: true });\n        } catch {}\n\n        try {\n          await mkdir(base, { recursive: true });\n        } catch {}\n      }\n    },\n    /** Expose the computed id (useful if you want to key other structures). */\n    isValid: async (): Promise<boolean> => {\n      const cachedValue = cacheMap.get(id);\n      if (cachedValue !== undefined) return true;\n\n      // If persistence is disabled or build cache disabled, only memory can be valid\n      if (!persistent || !buildCacheEnabled) return false;\n\n      try {\n        const statValue = await stat(filePath).catch(() => undefined);\n        if (!statValue) return false;\n\n        if (typeof ttlMs === 'number' && ttlMs > 0) {\n          const age = Date.now() - statValue.mtimeMs;\n          if (age > ttlMs) return false;\n        }\n\n        let raw = await readFile(filePath);\n        const flag = raw[0];\n        raw = raw.subarray(1);\n        const payload = flag === 0x01 ? gunzipSync(raw) : raw;\n        const deserialized = deserialize(payload) as unknown;\n\n        const maybeObj = deserialized as Record<string, unknown> | null;\n        const isEnvelope =\n          !!maybeObj &&\n          typeof maybeObj === 'object' &&\n          typeof maybeObj.version === 'string' &&\n          typeof maybeObj.timestamp === 'number' &&\n          Object.hasOwn(maybeObj, 'data');\n\n        if (isEnvelope) {\n          const entry = maybeObj as CacheEnvelope;\n          if (entry.version !== configPackageJson.version) return false;\n\n          if (typeof maxTimeMs === 'number' && maxTimeMs > 0) {\n            const age = Date.now() - entry.timestamp;\n            if (age > maxTimeMs) return false;\n          }\n          return true;\n        }\n\n        if (typeof maxTimeMs === 'number' && maxTimeMs > 0) {\n          const age = Date.now() - statValue.mtimeMs;\n          if (age > maxTimeMs) return false;\n        }\n        return true;\n      } catch {\n        return false;\n      }\n    },\n    /** Expose the computed id (useful if you want to key other structures). */\n    id,\n    /** Expose the absolute file path for debugging. */\n    filePath,\n  };\n};\n"],"mappings":"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"}