{"version":3,"sources":["../src/store.ts"],"sourcesContent":["import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { Store } from '@walkeros/core';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type { FsStoreSettings } from './types';\n\nfunction isValidKey(key: string): boolean {\n  if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n  return !key.split(/[/\\\\]/).includes('..');\n}\n\nexport const storeFsInit: Store.Init<Store.Types<FsStoreSettings>> = (\n  context,\n) => {\n  const settings = context.config.settings as FsStoreSettings;\n  const basePath = path.resolve(settings.basePath);\n  // One mode per instance, decided at init. file:true persists raw bytes\n  // byte-exact; the default structured mode round-trips StoreValue through the\n  // shared core codec (utf8 JSON, binary leaves base64-tagged).\n  const fileMode = context.config.file === true;\n\n  function resolvePath(key: string): string | undefined {\n    if (!isValidKey(key)) {\n      context.logger.warn('Path traversal rejected', { key });\n      return undefined;\n    }\n    const resolved = path.join(basePath, key);\n    if (!resolved.startsWith(basePath + path.sep) && resolved !== basePath)\n      return undefined;\n    return resolved;\n  }\n\n  return {\n    type: 'fs',\n    config: context.config as Store.Config<Store.Types<FsStoreSettings>>,\n\n    async get(key: string): Promise<Store.StoreValue | undefined> {\n      const filePath = resolvePath(key);\n      if (!filePath) return undefined;\n      let bytes: Buffer;\n      try {\n        bytes = await fs.readFile(filePath);\n      } catch {\n        return undefined;\n      }\n      // File mode hands the raw bytes back untouched (Buffer is a Uint8Array,\n      // a valid StoreValue leaf). Structured mode decodes the utf8-JSON payload\n      // to a StoreValue (binary leaves become Uint8Array). A corrupt or empty\n      // payload degrades to a miss rather than throwing a raw SyntaxError.\n      if (fileMode) return bytes;\n      try {\n        return deserializeStoreValue(bytes);\n      } catch (err) {\n        context.logger.debug('structured decode failed, degrading to miss', {\n          key,\n          error: err instanceof Error ? err.message : String(err),\n        });\n        return undefined;\n      }\n    },\n\n    async set(key: string, value: Store.StoreValue): Promise<void> {\n      const filePath = resolvePath(key);\n      if (!filePath) return;\n      await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n      if (fileMode) {\n        // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n        if (!(value instanceof Uint8Array) && typeof value !== 'string') {\n          throw new Error(\n            `fs store in file mode persists Uint8Array or string values only, received ${typeof value}`,\n          );\n        }\n        await fs.writeFile(filePath, value);\n        return;\n      }\n\n      // Structured mode: any StoreValue serializes to a utf8-JSON byte payload.\n      await fs.writeFile(filePath, serializeStoreValue(value));\n    },\n\n    async delete(key: string): Promise<void> {\n      const filePath = resolvePath(key);\n      if (!filePath) return;\n      try {\n        await fs.unlink(filePath);\n      } catch {\n        /* ignore */\n      }\n    },\n  };\n};\n"],"mappings":";AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,SAAS,qBAAqB,6BAA6B;AAG3D,SAAS,WAAW,KAAsB;AACxC,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AAChE,SAAO,CAAC,IAAI,MAAM,OAAO,EAAE,SAAS,IAAI;AAC1C;AAEO,IAAM,cAAwD,CACnE,YACG;AACH,QAAM,WAAW,QAAQ,OAAO;AAChC,QAAM,WAAgB,aAAQ,SAAS,QAAQ;AAI/C,QAAM,WAAW,QAAQ,OAAO,SAAS;AAEzC,WAAS,YAAY,KAAiC;AACpD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAQ,OAAO,KAAK,2BAA2B,EAAE,IAAI,CAAC;AACtD,aAAO;AAAA,IACT;AACA,UAAM,WAAgB,UAAK,UAAU,GAAG;AACxC,QAAI,CAAC,SAAS,WAAW,WAAgB,QAAG,KAAK,aAAa;AAC5D,aAAO;AACT,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,QAAQ;AAAA,IAEhB,MAAM,IAAI,KAAoD;AAC5D,YAAM,WAAW,YAAY,GAAG;AAChC,UAAI,CAAC,SAAU,QAAO;AACtB,UAAI;AACJ,UAAI;AACF,gBAAQ,MAAS,YAAS,QAAQ;AAAA,MACpC,QAAQ;AACN,eAAO;AAAA,MACT;AAKA,UAAI,SAAU,QAAO;AACrB,UAAI;AACF,eAAO,sBAAsB,KAAK;AAAA,MACpC,SAAS,KAAK;AACZ,gBAAQ,OAAO,MAAM,+CAA+C;AAAA,UAClE;AAAA,UACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AACD,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,IAAI,KAAa,OAAwC;AAC7D,YAAM,WAAW,YAAY,GAAG;AAChC,UAAI,CAAC,SAAU;AACf,YAAS,SAAW,aAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAE1D,UAAI,UAAU;AAEZ,YAAI,EAAE,iBAAiB,eAAe,OAAO,UAAU,UAAU;AAC/D,gBAAM,IAAI;AAAA,YACR,6EAA6E,OAAO,KAAK;AAAA,UAC3F;AAAA,QACF;AACA,cAAS,aAAU,UAAU,KAAK;AAClC;AAAA,MACF;AAGA,YAAS,aAAU,UAAU,oBAAoB,KAAK,CAAC;AAAA,IACzD;AAAA,IAEA,MAAM,OAAO,KAA4B;AACvC,YAAM,WAAW,YAAY,GAAG;AAChC,UAAI,CAAC,SAAU;AACf,UAAI;AACF,cAAS,UAAO,QAAQ;AAAA,MAC1B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}