{"version":3,"file":"filestorage.cjs","names":["process","jsonStringifySafe","toValidFilename","cloneObject","Buffer"],"sources":["../../src/node/filestorage.ts"],"sourcesContent":["import type { Json, ObjectStorage } from '../common/types'\nimport { Buffer } from 'node:buffer'\nimport { mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport process from 'node:process'\nimport { jsonStringifySafe } from '../common/data/json'\nimport { toValidFilename } from '../common/data/path'\nimport { cloneObject } from '../common/data/utils'\n\nexport interface FileStorageOptions {\n  pretty?: boolean\n  path?: string\n  extension?: string\n  objectFromString?: (data: string) => any\n  objectToString?: (data: any) => string\n  keyToFilename?: (key: string) => string\n}\n\nexport class FileStorage<T = Json> implements ObjectStorage<T> {\n  private store: Record<string, T | null> = {}\n  private dirname: string\n  private fileKeys?: string[] = undefined\n  private pretty = false\n  private extension: string\n  private extensionLength: number\n  private objectFromString: (data: string) => any\n  private objectToString: (data: any) => string\n  private keyToFilename: (key: string) => string\n\n  constructor(opt: FileStorageOptions = {}) {\n    this.dirname = resolve(process.cwd(), opt.path ?? '.fileStorage')\n    this.pretty = !!opt.pretty\n    this.extension = opt.extension ?? '.json'\n\n    if (opt.extension && !this.extension.startsWith('.'))\n      this.extension = `.${this.extension}`\n\n    this.extensionLength = this.extension.length\n\n    this.objectToString = opt.objectToString ?? ((data: any): string => {\n      return this.pretty\n        ? jsonStringifySafe(data, null, 2)\n        : jsonStringifySafe(data)\n    })\n\n    this.objectFromString = opt.objectFromString ?? ((data: string) => {\n      try {\n        return JSON.parse(data)\n      }\n      catch (err) {\n        // log.warn(`fileStorage parse error '${err}' in`, data)\n      }\n    })\n\n    this.keyToFilename = opt.keyToFilename ?? toValidFilename\n  }\n\n  setItem(key: string, value: T): void {\n    this.store[key] = cloneObject(value)\n    try {\n      const data = this.objectToString(value)\n      const path = this.getPath(key)\n      mkdirSync(dirname(path), { recursive: true })\n      writeFileSync(path, data, 'utf8')\n    }\n    catch (err) {\n      // log.error('setItem error', err)\n    }\n  }\n\n  getPath(key: string): string {\n    return resolve(this.dirname, this.keyToFilename(key) + this.extension)\n  }\n\n  getBuffer(key: string): any {\n    const path = this.getPath(key)\n    return Buffer.from(readFileSync(path))\n  }\n\n  getItem(key: string): T | undefined {\n    const value = this.store[key]\n\n    //  null is an indicator for not existing!\n    if (value === null)\n      return\n\n    if (value != null)\n      return cloneObject(value) // this.objectFromString(value)\n\n    try {\n      const path = this.getPath(key)\n      const data = readFileSync(path, 'utf8')\n      if (data != null) {\n        const value = this.objectFromString(data)\n        this.store[key] = value\n        return value\n      }\n    }\n    catch (err) {\n      // log.warn(\"getItem error\", err)\n      this.store[key] = null // do not retry next time\n    }\n  }\n\n  removeItem(key: string): void {\n    delete this.store[key]\n    if (this.fileKeys != null) {\n      const index: number = this.fileKeys.indexOf(key)\n      if (index !== -1)\n        this.fileKeys.splice(index, 1)\n    }\n    try {\n      const path = this.getPath(key)\n      unlinkSync(path)\n    }\n    catch (err) {}\n  }\n\n  clear(): void {\n    this.fileKeys = []\n    this.store = {}\n    rmSync(this.dirname, { recursive: true, force: true })\n  }\n\n  allKeys(): string[] {\n    if (this.fileKeys == null) {\n      try {\n        this.fileKeys\n          = readdirSync(this.dirname, { withFileTypes: true })\n            .filter(\n              item =>\n                !item.isDirectory() && item.name.endsWith(this.extension),\n            )\n            .map(item => item.name.slice(0, -this.extensionLength)) || []\n      }\n      catch (err) {}\n    }\n    const keys = [...(this.fileKeys || [])]\n    for (const key of Object.keys(this.store)) {\n      if (!keys.includes(key))\n        keys.push(key)\n    }\n    keys.sort()\n    return keys\n  }\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,cAAb,MAA+D;CAC7D,AAAQ,QAAkC,EAAE;CAC5C,AAAQ;CACR,AAAQ,WAAsB;CAC9B,AAAQ,SAAS;CACjB,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,MAA0B,EAAE,EAAE;AACxC,OAAK,iCAAkBA,qBAAQ,KAAK,EAAE,IAAI,QAAQ,eAAe;AACjE,OAAK,SAAS,CAAC,CAAC,IAAI;AACpB,OAAK,YAAY,IAAI,aAAa;AAElC,MAAI,IAAI,aAAa,CAAC,KAAK,UAAU,WAAW,IAAI,CAClD,MAAK,YAAY,IAAI,KAAK;AAE5B,OAAK,kBAAkB,KAAK,UAAU;AAEtC,OAAK,iBAAiB,IAAI,oBAAoB,SAAsB;AAClE,UAAO,KAAK,SACRC,2CAAkB,MAAM,MAAM,EAAE,GAChCA,2CAAkB,KAAK;;AAG7B,OAAK,mBAAmB,IAAI,sBAAsB,SAAiB;AACjE,OAAI;AACF,WAAO,KAAK,MAAM,KAAK;YAElB,KAAK;;AAKd,OAAK,gBAAgB,IAAI,iBAAiBC;;CAG5C,QAAQ,KAAa,OAAgB;AACnC,OAAK,MAAM,OAAOC,sCAAY,MAAM;AACpC,MAAI;GACF,MAAM,OAAO,KAAK,eAAe,MAAM;GACvC,MAAM,OAAO,KAAK,QAAQ,IAAI;AAC9B,iDAAkB,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,8BAAc,MAAM,MAAM,OAAO;WAE5B,KAAK;;CAKd,QAAQ,KAAqB;AAC3B,gCAAe,KAAK,SAAS,KAAK,cAAc,IAAI,GAAG,KAAK,UAAU;;CAGxE,UAAU,KAAkB;EAC1B,MAAM,OAAO,KAAK,QAAQ,IAAI;AAC9B,SAAOC,mBAAO,+BAAkB,KAAK,CAAC;;CAGxC,QAAQ,KAA4B;EAClC,MAAM,QAAQ,KAAK,MAAM;AAGzB,MAAI,UAAU,KACZ;AAEF,MAAI,SAAS,KACX,QAAOD,sCAAY,MAAM;AAE3B,MAAI;GAEF,MAAM,iCADO,KAAK,QAAQ,IAAI,EACE,OAAO;AACvC,OAAI,QAAQ,MAAM;IAChB,MAAM,QAAQ,KAAK,iBAAiB,KAAK;AACzC,SAAK,MAAM,OAAO;AAClB,WAAO;;WAGJ,KAAK;AAEV,QAAK,MAAM,OAAO;;;CAItB,WAAW,KAAmB;AAC5B,SAAO,KAAK,MAAM;AAClB,MAAI,KAAK,YAAY,MAAM;GACzB,MAAM,QAAgB,KAAK,SAAS,QAAQ,IAAI;AAChD,OAAI,UAAU,GACZ,MAAK,SAAS,OAAO,OAAO,EAAE;;AAElC,MAAI;AAEF,2BADa,KAAK,QAAQ,IAAI,CACd;WAEX,KAAK;;CAGd,QAAc;AACZ,OAAK,WAAW,EAAE;AAClB,OAAK,QAAQ,EAAE;AACf,sBAAO,KAAK,SAAS;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;;CAGxD,UAAoB;AAClB,MAAI,KAAK,YAAY,KACnB,KAAI;AACF,QAAK,oCACW,KAAK,SAAS,EAAE,eAAe,MAAM,CAAC,CACjD,QACC,SACE,CAAC,KAAK,aAAa,IAAI,KAAK,KAAK,SAAS,KAAK,UAAU,CAC5D,CACA,KAAI,SAAQ,KAAK,KAAK,MAAM,GAAG,CAAC,KAAK,gBAAgB,CAAC,IAAI,EAAE;WAE5D,KAAK;EAEd,MAAM,OAAO,CAAC,GAAI,KAAK,YAAY,EAAE,CAAE;AACvC,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,MAAM,CACvC,KAAI,CAAC,KAAK,SAAS,IAAI,CACrB,MAAK,KAAK,IAAI;AAElB,OAAK,MAAM;AACX,SAAO"}