{"version":3,"sources":["../src/record.translator.ts","../src/record.utils.ts","../src/RecordHook.ts","../src/record.hook.plugin.ts"],"names":["RecordTranslator","records","FlatfileRecords","XRecords","record","rawData","k","v","links","link","linkedRawData","lk","lv","metadata","FlatfileRecord","recordWithInfo","values","messages","info","config","deepEqual","obj1","obj2","options","isObject","keys1","keys2","key","handleValuesComparison","values1","values2","valueKeys","hasChanges","unchangedKeys","value","cleanRecord","deleteKeys","obj","keys","message","completeCommit","event","debug","commitId","trackChanges","logInfo","logError","prepareFlatfileRecords","prepareXRecords","startTimer","label","endTimer","RecordHook","handler","concurrency","BulkRecordHook","promises","promise","data","batch","asyncBatch","modifiedRecords","originalRecord","original","e","recordHookPlugin","sheetSlug","callback","listener","bulkRecordHookPlugin"],"mappings":";;;;;AAUO,IAAMA,CAAAA,CAAN,KAEL,CACA,WAA6BC,CAAAA,CAAAA,CAAc,CAAd,IAAA,CAAA,OAAA,CAAAA,EAC3B,IAAK,CAAA,OAAA,CAAUA,EACjB,CAEA,iBAAoB,CAAA,IAA4B,CAC9C,GAAI,IAAA,CAAK,OAAmBC,YAAAA,qBAAAA,CAC1B,OAAO,IAAA,CAAK,QACP,CACL,IAAMC,CAAW,CAAA,IAAA,CAAK,OAgCtB,CAAA,OA9BkB,IAAID,qBAAAA,CACpBC,CAAS,CAAA,GAAA,CAAKC,CAAqC,EAAA,CACjD,IAAIC,CAAAA,CAAgC,EACpC,CAAA,IAAA,GAAS,CAACC,CAAAA,CAAGC,CAAC,CAAA,GAAK,OAAO,OAAQH,CAAAA,CAAAA,CAAO,MAAM,CAAA,CAC7C,GAAMG,CAAAA,CAAE,OAAO,MAAUA,EAAAA,CAAAA,CAAE,KAAO,CAAA,CAChC,IAAMC,CAAAA,CAAQD,CAAE,CAAA,KAAA,CAAM,GAAKE,CAAAA,CAAAA,EAAS,CAClC,IAAIC,CAA6B,CAAA,GACjC,IAAS,GAAA,CAACC,CAAIC,CAAAA,CAAE,CAAK,GAAA,MAAA,CAAO,QAAQH,CAAK,CAAA,MAAM,CAC7CC,CAAAA,CAAAA,CAAcC,CAAE,CAAA,CAAIC,EAAG,KAEzB,CAAA,OAAOF,CACT,CAAC,CACDL,CAAAA,CAAAA,CAAQC,CAAC,CAAA,CAAI,CACX,KAAA,CAAOC,CAAE,CAAA,KAAA,CACT,KAAAC,CAAAA,CACF,EACF,CACEH,KAAAA,CAAAA,CAAQC,CAAC,CAAA,CAAIC,CAAE,CAAA,KAAA,CAGnB,IAAMM,CAAWT,CAAAA,CAAAA,CAAO,QAOxB,CAAA,OANkB,CAChB,KAAA,CAAOA,EAAO,EACd,CAAA,OAAA,CAAAC,CACA,CAAA,QAAA,CAAAQ,CACF,CAGF,CAAC,CACH,CAEF,CACF,CAEA,CAAA,UAAA,CAAa,IACP,IAAA,CAAK,QAAQ,CAAC,CAAA,WAAaC,oBACX,CAAA,IAAA,CAAK,OACN,CAAA,GAAA,CAAKV,GAA2B,CAC/C,IAAMW,CAAiBX,CAAAA,CAAAA,CAAO,MAAO,EAAA,CACjCY,EAAc,EAAC,CACnB,IAAS,GAAA,CAACV,CAAGC,CAAAA,CAAC,CAAK,GAAA,MAAA,CAAO,OAAQQ,CAAAA,CAAAA,CAAe,GAAI,CAAA,OAAO,CAAG,CAAA,CAC7D,IAAME,CAAWF,CAAAA,CAAAA,CAAe,IAC7B,CAAA,MAAA,CAAQG,CAASA,EAAAA,CAAAA,CAAK,QAAUZ,CAAC,CAAA,CACjC,GAAKY,CAAAA,CAAAA,GAAU,CACd,OAAA,CAASA,EAAK,OACd,CAAA,IAAA,CAAMA,CAAK,CAAA,KAAA,CACX,MAAQ,CAAA,cAAA,CACR,IAAMA,CAAAA,CAAAA,CAAK,IACb,CAAA,CAAE,CAEJF,CAAAA,CAAAA,CAAOV,CAAC,CAAA,CAAI,CACV,KACEC,CAAAA,CAAAA,GAAM,IAAQ,EAAA,OAAOA,CAAM,EAAA,QAAA,EAAY,CAAC,KAAA,CAAM,OAAQA,CAAAA,CAAC,CACnDA,CAAAA,CAAAA,CAAE,KACFA,CAAAA,CAAAA,CACN,SAAUU,CACZ,EACF,CACA,IAAMJ,CAAWE,CAAAA,CAAAA,CAAe,GAAI,CAAA,QAAA,CAC9BI,CAASJ,CAAAA,CAAAA,CAAe,MAC9B,CAAA,OAAO,CACL,EAAA,CAAI,OAAOX,CAAO,CAAA,KAAK,CACvB,CAAA,MAAA,CAAAY,CACA,CAAA,QAAA,CAAAH,EACA,MAAAM,CAAAA,CACF,CACF,CAAC,CAEM,CAAA,IAAA,CAAK,OAGlB,CCvFO,CAAA,SAASC,CACdC,CAAAA,CAAAA,CACAC,CACAC,CAAAA,CAAAA,CACS,CAET,GAAIF,CAASC,GAAAA,CAAAA,CAAM,OAAO,CAAA,CAAA,CAE1B,GAAI,CAACE,EAASH,CAAI,CAAA,EAAK,CAACG,CAAAA,CAASF,CAAI,CAAA,CAAG,OAAO,CAE/C,CAAA,CAAA,IAAMG,CAAQ,CAAA,MAAA,CAAO,IAAKJ,CAAAA,CAAI,EACxBK,CAAQ,CAAA,MAAA,CAAO,IAAKJ,CAAAA,CAAI,CAC9B,CAAA,OAAIG,CAAM,CAAA,MAAA,GAAWC,CAAM,CAAA,MAAA,CAAe,CAEnCD,CAAAA,CAAAA,CAAAA,CAAM,KAAOE,CAAAA,CAAAA,EACdA,IAAQ,QAAiBC,CAAAA,CAAAA,CAAuBP,CAAMC,CAAAA,CAAI,CACvDI,CAAAA,CAAAA,CAAM,SAASC,CAAG,CAAA,EAAKP,CAAUC,CAAAA,CAAAA,CAAKM,CAAG,CAAA,CAAGL,EAAKK,CAAG,CAAU,CACtE,CACH,CAEA,SAASC,CAAAA,CAAuBP,CAAWC,CAAAA,CAAAA,CAAoB,CAC7D,IAAMO,CAAUR,CAAAA,CAAAA,CAAK,OACfS,CAAUR,CAAAA,CAAAA,CAAK,MACfS,CAAAA,CAAAA,CAAY,MAAO,CAAA,IAAA,CAAKF,CAAO,CAAA,CAEjCG,CAAa,CAAA,CAAA,CAAA,CACXC,CAAgB,CAAA,EAGtB,CAAA,OAAAF,EAAU,OAASJ,CAAAA,CAAAA,EAAQ,CACrBP,CAAAA,CAAUS,CAAQF,CAAAA,CAAG,CAAGG,CAAAA,CAAAA,CAAQH,CAAG,CAAC,CACtCM,CAAAA,CAAAA,CAAc,IAAKN,CAAAA,CAAG,EAEtBK,CAAa,CAAA,CAAA,EAEjB,CAAC,CAAA,CAGDC,CAAc,CAAA,OAAA,CAASN,GAAQ,CAC7B,OAAOE,CAAQF,CAAAA,CAAG,CAClB,CAAA,OAAOG,EAAQH,CAAG,EACpB,CAAC,CAAA,CAGG,MAAO,CAAA,IAAA,CAAKE,CAAO,CAAA,CAAE,MAAW,GAAA,CAAA,GAClC,OAAOR,CAAAA,CAAK,MACZ,CAAA,OAAOC,EAAK,MAGP,CAAA,CAAA,CAACU,CACV,CAEA,SAASR,CAAAA,CAASU,EAAqB,CACrC,OAAO,OAAOA,CAAAA,EAAU,QAAYA,EAAAA,CAAAA,GAAU,IAChD,CAeO,SAASC,CAAY/B,CAAAA,CAAAA,CAA8C,CACnEA,CAAAA,GAID,MAAO,CAAA,IAAA,CAAKA,CAAM,CAAA,CAAE,QAAS,CAAA,OAAO,CACtC,EAAA,OAAOA,EAAO,KAGhBgC,CAAAA,CAAAA,CAAWhC,CAAO,CAAA,MAAA,CAAQ,CAAC,WAAA,CAAa,OAAO,CAAC,CAAA,EAClD,CAEA,SAASgC,CAAWC,CAAAA,CAAAA,CAAKC,EAAM,CACzB,OAAOD,CAAQ,EAAA,QAAA,EAAYA,CAAQ,GAAA,IAAA,EAIvC,MAAO,CAAA,IAAA,CAAKA,CAAG,CAAA,CAAE,OAASV,CAAAA,CAAAA,EAAQ,CAC5BW,CAAAA,CAAK,SAASX,CAAG,CAAA,CACnB,OAAOU,CAAAA,CAAIV,CAAG,CAAA,CACLA,CAAQ,GAAA,UAAA,CAEb,KAAM,CAAA,OAAA,CAAQU,CAAI,CAAA,QAAW,CAC/BA,GAAAA,CAAAA,CAAI,SAAcA,CAAI,CAAA,QAAA,CAAY,MAC/BE,CAAAA,CAAAA,EAAYA,CAAQ,CAAA,MAAA,GAAW,cAClC,CAAA,CAAA,CAEO,OAAOF,CAAAA,CAAIV,CAAG,CAAA,EAAM,QAAYU,EAAAA,CAAAA,CAAIV,CAAG,CAAM,GAAA,IAAA,EAEtDS,CAAWC,CAAAA,CAAAA,CAAIV,CAAG,CAAA,CAAGW,CAAI,EAE7B,CAAC,EACH,CAEA,eAAsBE,CAAAA,CACpBC,EACAC,CACe,CAAA,CACf,GAAM,CAAE,QAAAC,CAAAA,CAAS,CAAIF,CAAAA,CAAAA,CAAM,OACrB,CAAA,CAAE,YAAAG,CAAAA,CAAa,CAAIH,CAAAA,CAAAA,CAAM,QAE/B,GAAIG,CAAAA,CACF,GAAI,CACF,MAAMH,CAAAA,CAAM,MAAM,CAAcE,WAAAA,EAAAA,CAAQ,CAAa,SAAA,CAAA,CAAA,CACnD,MAAQ,CAAA,MACV,CAAC,CACGD,CAAAA,CAAAA,EACFG,kBAAQ,CAAA,8BAAA,CAAgC,+BAA+B,EAE3E,CAAY,KAAA,CACVC,mBACE,CAAA,8BAAA,CACA,CAA2BH,wBAAAA,EAAAA,CAAQ,CACrC,CAAA,EACF,CAGJ,CAEA,eAAsBI,CACpB9C,CAAAA,CAAAA,CACoC,CAEpC,OADqB,IAAID,CAAsCC,CAAAA,CAAO,CAClD,CAAA,UAAA,EACtB,CAEA,eAAsB+C,CACpB/C,CAAAA,CAAAA,CAC+B,CAE/B,OADc,IAAID,CAAAA,CAA2CC,CAAO,CAAA,CACvD,iBAAkB,EACjC,CAEO,SAASgD,CAAWC,CAAAA,CAAAA,CAAeR,EAAgB,CACxDA,CAAAA,EAAS,OAAQ,CAAA,IAAA,CAAK,CAAOQ,cAAAA,EAAAA,CAAK,EAAE,EACtC,CAEO,SAASC,CAAAA,CAASD,CAAeR,CAAAA,CAAAA,CAAgB,CACtDA,CAAS,EAAA,OAAA,CAAQ,OAAQ,CAAA,CAAA,cAAA,EAAOQ,CAAK,CAAA,CAAE,EACzC,CCxIaE,IAAAA,CAAAA,CAAa,MACxBX,CAAAA,CACAY,CACA9B,CAAAA,CAAAA,CAA6B,EAC1B,GAAA,CACH,GAAM,CAAE,WAAA+B,CAAAA,CAAAA,CAAc,EAAG,CAAI/B,CAAAA,CAAAA,CAC7B,OAAOgC,CAAAA,CACLd,CACA,CAAA,MAAOxC,EAASwC,CAAU,GAAA,CACxB,IAAMe,CAAAA,CAAW,IAAI,GAAA,CAErB,IAAWpD,IAAAA,CAAAA,IAAUH,CAAS,CAAA,CAC5B,IAAMwD,CAAAA,CAAU,OAAQ,CAAA,OAAA,CAAQJ,EAAQjD,CAAQqC,CAAAA,CAAK,CAAC,CAAA,CAAE,OAAQ,CAAA,IAC9De,EAAS,MAAOC,CAAAA,CAAO,CACzB,CAAA,CACAD,CAAS,CAAA,GAAA,CAAIC,CAAO,CAEhBD,CAAAA,CAAAA,CAAS,IAAQF,EAAAA,CAAAA,EACnB,MAAM,OAAA,CAAQ,IAAKE,CAAAA,CAAQ,EAE/B,CAEA,OAAO,MAAM,OAAQ,CAAA,GAAA,CAAIA,CAAQ,CACnC,CAAA,CACAjC,CACF,CACF,CAQagC,CAAAA,CAAAA,CAAiB,MAC5Bd,CACAY,CAAAA,CAAAA,CAIA9B,CAAiC,CAAA,EAC9B,GAAA,CACH,GAAM,CAAE,KAAA,CAAAmB,CAAQ,CAAA,CAAA,CAAM,CAAInB,CAAAA,CAAAA,CAE1B,GAAI,CACF0B,CAAW,CAAA,YAAA,CAAcP,CAAK,CAAA,CAC9B,IAAMgB,CAAAA,CAAO,MAAMjB,CAAM,CAAA,KAAA,CAAM,IAC7B,CAAA,MAAA,CACA,SAAgD,CAC9C,GAAI,CACF,IAAMiB,CAAAA,CAAO,MAAMjB,CAAAA,CAAM,IACzB,CAAA,OAAOiB,EAAK,OAAWA,EAAAA,CAAAA,CAAK,OAAQ,CAAA,MAAA,CAASA,CAAK,CAAA,OAAA,CAAU,KAC9D,CAAA,CAAA,KAAY,CACV,MAAM,IAAI,KAAA,CAAM,wBAAwB,CAC1C,CACF,CACF,CAAA,CAEA,GAAI,CAACA,CAAQA,EAAAA,CAAAA,CAAK,SAAW,CAAG,CAAA,CAC9Bb,kBAAQ,CAAA,8BAAA,CAAgC,uBAAuB,CAAA,CAC/D,MAAML,CAAeC,CAAAA,CAAAA,CAAOC,CAAK,CAAA,CACjC,MACF,CAEA,IAAMiB,CAAAA,CAAQ,MAAMlB,CAAAA,CAAM,KAAM,CAAA,IAAA,CAC9B,SACA,CAAA,SAAY,MAAMO,CAAgBU,CAAAA,CAAI,CACxC,CAAA,CACAP,CAAS,CAAA,YAAA,CAAcT,CAAK,CAG5BO,CAAAA,CAAAA,CAAW,aAAeP,CAAAA,CAAK,CAC/B,CAAA,MAAMkB,sBAAWD,CAAM,CAAA,OAAA,CAASN,CAAS9B,CAAAA,CAAAA,CAASkB,CAAK,CAAA,CACvDU,CAAS,CAAA,aAAA,CAAeT,CAAK,CAAA,CAE7BD,CAAM,CAAA,QAAA,CAAS,SAAY,CACzBQ,EAAW,yBAA2BP,CAAAA,CAAK,CAC3C,CAAA,GAAM,CAAE,OAAA,CAASiB,CAAM,CACrBlB,CAAAA,CAAAA,CAAM,KAAM,CAAA,GAAA,CAA0B,SAAS,CAAA,CAC3CxC,EACJ,MAAM8C,CAAAA,CAAuBY,CAAK,CAAA,CAE9BD,CAAO,CAAA,MAAMjB,CAAM,CAAA,KAAA,CAAM,GAA+B,CAAA,MAAM,CAC9DoB,CAAAA,CAAAA,CAA6C5D,CAAQ,CAAA,MAAA,CACxDG,GAAqC,CACpC,IAAM0D,CACJJ,CAAAA,CAAAA,CAAK,IACFK,CAAAA,CAAAA,EAAuCA,CAAS,CAAA,EAAA,GAAO3D,CAAO,CAAA,EACjE,CACF,CAAA,OAAA+B,CAAY2B,CAAAA,CAAc,EACP,CAAC1C,CAAAA,CAAUhB,CAAQ0D,CAAAA,CAAAA,CAAgB,CACpD,eAAA,CAAiB,CACnB,CAAA,CAAC,CAEH,CACF,CAEA,CAAA,GAAI,CAACD,CAAAA,EAAmBA,EAAgB,MAAW,GAAA,CAAA,CAAG,CACpDhB,kBAAAA,CAAQ,8BAAgC,CAAA,qBAAqB,EAC7D,MAAML,CAAAA,CAAeC,CAAOC,CAAAA,CAAK,CACjC,CAAA,MACF,CACAS,CAAS,CAAA,yBAAA,CAA2BT,CAAK,CAAA,CAEzCG,kBACE,CAAA,8BAAA,CACA,CAAGgB,EAAAA,CAAAA,CAAgB,MAAM,CAAA,iBAAA,CAC3B,CAEA,CAAA,GAAI,CACFZ,CAAAA,CAAW,0BAA2BP,CAAK,CAAA,CAC3C,MAAMD,CAAAA,CAAM,MAAOoB,CAAAA,CAAe,EAClCV,CAAS,CAAA,yBAAA,CAA2BT,CAAK,CAAA,CACzC,MACF,CAAA,KAAY,CACV,MAAM,IAAI,KAAM,CAAA,wBAAwB,CAC1C,CACF,CAAC,EACH,CAASsB,MAAAA,CAAAA,CAAG,CACVlB,mBAAAA,CAAS,8BAAiCkB,CAAAA,CAAAA,CAAY,OAAO,CAC7D,CAAA,MAAMxB,CAAeC,CAAAA,CAAAA,CAAOC,CAAK,CAAA,CACjC,MACF,CACF,ECzIauB,IAAAA,CAAAA,CAAmB,CAC9BC,CAAAA,CACAC,EAIA5C,CAA6B,CAAA,EAErB6C,GAAAA,CAAAA,EAA+B,CACrCA,CAAAA,CAAS,EAAG,CAAA,gBAAA,CAAkB,CAAE,SAAA,CAAAF,CAAU,CAAA,CAAIzB,CAC5CW,EAAAA,CAAAA,CAAWX,EAAO0B,CAAU5C,CAAAA,CAAO,CACrC,EACF,CAGW8C,CAAAA,CAAAA,CAAuB,CAClCH,CAAAA,CACAC,CAIA5C,CAAAA,CAAAA,CAAiC,EAAC,GAE1B6C,CAA+B,EAAA,CACrCA,EAAS,EAAG,CAAA,gBAAA,CAAkB,CAAE,SAAA,CAAAF,CAAU,CAAA,CAAIzB,CAC5Cc,EAAAA,CAAAA,CAAed,CAAO0B,CAAAA,CAAAA,CAAU5C,CAAO,CACzC,EACF","file":"index.browser.cjs","sourcesContent":["import type { Flatfile } from '@flatfile/api'\nimport type {\n  IRawRecord,\n  TPrimitive,\n  TRecordData,\n  TRecordDataWithLinks,\n  TRecordValue,\n} from '@flatfile/hooks'\nimport { FlatfileRecord, FlatfileRecords } from '@flatfile/hooks'\n\nexport class RecordTranslator<\n  T extends FlatfileRecord | Flatfile.RecordWithLinks,\n> {\n  constructor(private readonly records: T[]) {\n    this.records = records\n  }\n\n  toFlatfileRecords = (): FlatfileRecords<any> => {\n    if (this.records instanceof FlatfileRecords) {\n      return this.records\n    } else {\n      const XRecords = this.records as Flatfile.RecordWithLinks[]\n\n      const FFRecords = new FlatfileRecords(\n        XRecords.map((record: Flatfile.RecordWithLinks) => {\n          let rawData: TRecordDataWithLinks = {}\n          for (let [k, v] of Object.entries(record.values)) {\n            if (!!v.links?.length && v.value) {\n              const links = v.links.map((link) => {\n                let linkedRawData: TRecordData = {}\n                for (let [lk, lv] of Object.entries(link.values)) {\n                  linkedRawData[lk] = lv.value as TPrimitive\n                }\n                return linkedRawData\n              })\n              rawData[k] = {\n                value: v.value as TRecordValue,\n                links,\n              }\n            } else {\n              rawData[k] = v.value as TPrimitive\n            }\n          }\n          const metadata = record.metadata\n          const rawRecord = {\n            rowId: record.id,\n            rawData,\n            metadata,\n          } as IRawRecord\n\n          return rawRecord\n        })\n      )\n      return FFRecords\n    }\n  }\n\n  toXRecords = (): Flatfile.RecordsWithLinks => {\n    if (this.records[0] instanceof FlatfileRecord) {\n      const FFRecords = this.records as FlatfileRecord[]\n      return FFRecords.map((record: FlatfileRecord) => {\n        const recordWithInfo = record.toJSON()\n        let values: any = {}\n        for (let [k, v] of Object.entries(recordWithInfo.row.rawData)) {\n          const messages = recordWithInfo.info\n            .filter((info) => info.field === k)\n            .map((info) => ({\n              message: info.message,\n              type: info.level,\n              source: 'custom-logic',\n              path: info.path,\n            }))\n\n          values[k] = {\n            value:\n              v !== null && typeof v === 'object' && !Array.isArray(v)\n                ? v.value\n                : v,\n            messages: messages,\n          }\n        }\n        const metadata = recordWithInfo.row.metadata\n        const config = recordWithInfo.config\n        return {\n          id: String(record.rowId),\n          values,\n          metadata,\n          config,\n        }\n      }) as Flatfile.RecordsWithLinks\n    } else {\n      return this.records as Flatfile.RecordsWithLinks\n    }\n  }\n}\n","import type { Flatfile } from '@flatfile/api'\nimport { FlatfileRecord, FlatfileRecords } from '@flatfile/hooks'\nimport type { FlatfileEvent } from '@flatfile/listener'\nimport { logError, logInfo } from '@flatfile/util-common'\nimport { RecordTranslator } from './record.translator'\n\n// deepEqual is a generic object comparison function that is ambivalent to the key order\nexport function deepEqual(\n  obj1: any,\n  obj2: any,\n  options?: { removeUnchanged: boolean }\n): boolean {\n  // Handle primitive comparisons\n  if (obj1 === obj2) return true\n\n  if (!isObject(obj1) || !isObject(obj2)) return false\n\n  const keys1 = Object.keys(obj1)\n  const keys2 = Object.keys(obj2)\n  if (keys1.length !== keys2.length) return false\n\n  return keys1.every((key) => {\n    if (key === 'values') return handleValuesComparison(obj1, obj2)\n    return keys2.includes(key) && deepEqual(obj1[key], obj2[key], options)\n  })\n}\n\nfunction handleValuesComparison(obj1: any, obj2: any): boolean {\n  const values1 = obj1.values\n  const values2 = obj2.values\n  const valueKeys = Object.keys(values1)\n\n  let hasChanges = false\n  const unchangedKeys = []\n\n  // Compare each value and track unchanged ones\n  valueKeys.forEach((key) => {\n    if (deepEqual(values1[key], values2[key])) {\n      unchangedKeys.push(key)\n    } else {\n      hasChanges = true\n    }\n  })\n\n  // Remove unchanged values\n  unchangedKeys.forEach((key) => {\n    delete values1[key]\n    delete values2[key]\n  })\n\n  // Clean up empty values object\n  if (Object.keys(values1).length === 0) {\n    delete obj1.values\n    delete obj2.values\n  }\n\n  return !hasChanges\n}\n\nfunction isObject(value: any): boolean {\n  return typeof value === 'object' && value !== null\n}\n\n/**\n * This function is used to clean a the original record before it compared to the modified/updated record.\n *\n * @remarks\n * Prior to the recordHook's callback being called, the RecordTranslator's toFlatfileRecords() method is called\n * which converts the records into a FlatfileRecords object. This removes fields that the recordHook's callback is\n * expected to check and re-add (i.e. custom-logic messages) and also removes system fields like 'updatedAt'. After all\n * callbacks have run, the toXRecords() method is called to convert the records back into the original format, adds\n * 'source: custom-logic' to all messages and sets all fields to 'valid: true'.\n *\n * This function is intended to remove fields that will never be set on a modified record, specifically 'valid' at the\n * the root level, updatedAt at all levels, and any message that does not have a source of 'custom-logic'.\n */\nexport function cleanRecord(record: Flatfile.RecordWithLinks | undefined) {\n  if (!record) {\n    return\n  }\n  // Remove `valid` at the root level of the Record, but not on the Record's fields\n  if (Object.keys(record).includes('valid')) {\n    delete record.valid\n  }\n  // Remove `updatedAt` and `valid` at all levels of the Record\n  deleteKeys(record.values, ['updatedAt', 'valid'])\n}\n\nfunction deleteKeys(obj, keys) {\n  if (typeof obj !== 'object' || obj === null) {\n    return\n  }\n\n  Object.keys(obj).forEach((key) => {\n    if (keys.includes(key)) {\n      delete obj[key]\n    } else if (key === 'messages') {\n      // Filters out messages not from 'custom-logic'\n      if (Array.isArray(obj['messages'])) {\n        obj['messages'] = obj['messages'].filter(\n          (message) => message.source === 'custom-logic'\n        )\n      }\n    } else if (typeof obj[key] === 'object' && obj[key] !== null) {\n      // Apply cleaning recursively for nested objects\n      deleteKeys(obj[key], keys)\n    }\n  })\n}\n\nexport async function completeCommit(\n  event: FlatfileEvent,\n  debug: boolean\n): Promise<void> {\n  const { commitId } = event.context\n  const { trackChanges } = event.payload\n\n  if (trackChanges) {\n    try {\n      await event.fetch(`v1/commits/${commitId}/complete`, {\n        method: 'POST',\n      })\n      if (debug) {\n        logInfo('@flatfile/plugin-record-hook', 'Commit completed successfully')\n      }\n    } catch (e) {\n      logError(\n        '@flatfile/plugin-record-hook',\n        `Error completing commit ${commitId}`\n      )\n    }\n  }\n  return\n}\n\nexport async function prepareFlatfileRecords(\n  records: any\n): Promise<Flatfile.RecordsWithLinks> {\n  const fromFlatfile = new RecordTranslator<FlatfileRecord<any>>(records)\n  return fromFlatfile.toXRecords()\n}\n\nexport async function prepareXRecords(\n  records: any\n): Promise<FlatfileRecords<any>> {\n  const fromX = new RecordTranslator<Flatfile.RecordWithLinks>(records)\n  return fromX.toFlatfileRecords()\n}\n\nexport function startTimer(label: string, debug: boolean) {\n  debug && console.time(`⏱️  ${label}`)\n}\n\nexport function endTimer(label: string, debug: boolean) {\n  debug && console.timeEnd(`⏱️  ${label}`)\n}\n","import type { Flatfile } from '@flatfile/api'\nimport type { FlatfileRecord, FlatfileRecords } from '@flatfile/hooks'\nimport type { FlatfileEvent } from '@flatfile/listener'\nimport { asyncBatch, logError, logInfo } from '@flatfile/util-common'\nimport {\n  cleanRecord,\n  completeCommit,\n  deepEqual,\n  endTimer,\n  prepareFlatfileRecords,\n  prepareXRecords,\n  startTimer,\n} from './record.utils'\n\nexport interface RecordHookOptions {\n  concurrency?: number\n  debug?: boolean\n}\n\nexport const RecordHook = async (\n  event: FlatfileEvent,\n  handler: (record: FlatfileRecord, event: FlatfileEvent) => any | Promise<any>,\n  options: RecordHookOptions = {}\n) => {\n  const { concurrency = 10 } = options\n  return BulkRecordHook(\n    event,\n    async (records, event) => {\n      const promises = new Set<Promise<any>>()\n\n      for (const record of records) {\n        const promise = Promise.resolve(handler(record, event)).finally(() =>\n          promises.delete(promise)\n        )\n        promises.add(promise)\n\n        if (promises.size >= concurrency) {\n          await Promise.race(promises)\n        }\n      }\n\n      return await Promise.all(promises)\n    },\n    options\n  )\n}\n\nexport interface BulkRecordHookOptions {\n  chunkSize?: number\n  parallel?: number\n  debug?: boolean\n}\n\nexport const BulkRecordHook = async (\n  event: FlatfileEvent,\n  handler: (\n    records: FlatfileRecord[],\n    event: FlatfileEvent\n  ) => any | Promise<any>,\n  options: BulkRecordHookOptions = {}\n) => {\n  const { debug = false } = options\n\n  try {\n    startTimer('fetch data', debug)\n    const data = await event.cache.init<Flatfile.RecordsWithLinks>(\n      'data',\n      async (): Promise<Flatfile.RecordsWithLinks> => {\n        try {\n          const data = await event.data\n          return data.records && data.records.length ? data.records : undefined\n        } catch (e) {\n          throw new Error('Error fetching records')\n        }\n      }\n    )\n\n    if (!data || data.length === 0) {\n      logInfo('@flatfile/plugin-record-hook', 'No records to process')\n      await completeCommit(event, debug)\n      return\n    }\n\n    const batch = await event.cache.init<FlatfileRecords<any>>(\n      'records',\n      async () => await prepareXRecords(data)\n    )\n    endTimer('fetch data', debug)\n\n    // Execute client-defined data hooks\n    startTimer('run handler', debug)\n    await asyncBatch(batch.records, handler, options, event)\n    endTimer('run handler', debug)\n\n    event.afterAll(async () => {\n      startTimer('filter modified records', debug)\n      const { records: batch } =\n        event.cache.get<FlatfileRecords<any>>('records')\n      const records: Flatfile.RecordsWithLinks =\n        await prepareFlatfileRecords(batch)\n\n      const data = await event.cache.get<Flatfile.RecordsWithLinks>('data')\n      const modifiedRecords: Flatfile.RecordsWithLinks = records.filter(\n        (record: Flatfile.RecordWithLinks) => {\n          const originalRecord: Flatfile.RecordWithLinks | undefined =\n            data.find(\n              (original: Flatfile.RecordWithLinks) => original.id === record.id\n            )\n          cleanRecord(originalRecord) // Remove fields that should not be compared\n          const hasChanges = !deepEqual(record, originalRecord, {\n            removeUnchanged: true,\n          })\n          return hasChanges\n        }\n      )\n\n      if (!modifiedRecords || modifiedRecords.length === 0) {\n        logInfo('@flatfile/plugin-record-hook', 'No records modified')\n        await completeCommit(event, debug)\n        return\n      }\n      endTimer('filter modified records', debug)\n\n      logInfo(\n        '@flatfile/plugin-record-hook',\n        `${modifiedRecords.length} modified records`\n      )\n\n      try {\n        startTimer('update modified records', debug)\n        await event.update(modifiedRecords)\n        endTimer('update modified records', debug)\n        return\n      } catch (e) {\n        throw new Error('Error updating records')\n      }\n    })\n  } catch (e) {\n    logError('@flatfile/plugin-record-hook', (e as Error).message)\n    await completeCommit(event, debug)\n    return\n  }\n}\n","import type { FlatfileRecord } from '@flatfile/hooks'\nimport type { FlatfileEvent, FlatfileListener } from '@flatfile/listener'\nimport type { BulkRecordHookOptions, RecordHookOptions } from './RecordHook'\nimport { BulkRecordHook, RecordHook } from './RecordHook'\n\nexport const recordHookPlugin = (\n  sheetSlug: string,\n  callback: (\n    record: FlatfileRecord,\n    event: FlatfileEvent\n  ) => any | Promise<any>,\n  options: RecordHookOptions = {}\n) => {\n  return (listener: FlatfileListener) => {\n    listener.on('commit:created', { sheetSlug }, (event: FlatfileEvent) =>\n      RecordHook(event, callback, options)\n    )\n  }\n}\n\nexport const bulkRecordHookPlugin = (\n  sheetSlug: string,\n  callback: (\n    records: FlatfileRecord[],\n    event: FlatfileEvent\n  ) => any | Promise<any>,\n  options: BulkRecordHookOptions = {}\n) => {\n  return (listener: FlatfileListener) => {\n    listener.on('commit:created', { sheetSlug }, (event: FlatfileEvent) =>\n      BulkRecordHook(event, callback, options)\n    )\n  }\n}\n\nexport {\n  bulkRecordHookPlugin as bulkRecordHook,\n  recordHookPlugin as recordHook,\n}\n"]}