UNPKG

@intlayer/chokidar

Version:

Uses chokidar to scan and build Intlayer declaration files into dictionaries based on Intlayer configuration.

1 lines 6.52 kB
{"version":3,"file":"runOnce.cjs","names":["packageJson"],"sources":["../../../src/utils/runOnce.ts"],"sourcesContent":["import { mkdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport packageJson from '@intlayer/core/package.json' with { type: 'json' };\n\ntype RunOnceOptions = {\n /**\n * The function to execute when the sentinel is not found or is older than the cache timeout.\n */\n onIsCached?: () => void | Promise<void>;\n /**\n * The time window in milliseconds during which the sentinel is considered valid.\n *\n * @default 60000 = 1 minute\n */\n cacheTimeoutMs?: number;\n /**\n * If true, the callback will always run. If undefined, the callback will run only if the sentinel is older than the cache timeout.\n *\n * @default false\n */\n forceRun?: boolean;\n};\n\nconst DEFAULT_RUN_ONCE_OPTIONS = {\n cacheTimeoutMs: 60 * 1000, // 1 minute in milliseconds,\n} satisfies RunOnceOptions;\n\ntype SentinelData = {\n version: string;\n timestamp: number;\n};\n\nconst writeSentinelFile = async (\n sentinelFilePath: string,\n currentTimestamp: number\n) => {\n // O_EXCL ensures only the *first* process can create the file\n const data: SentinelData = {\n version: packageJson.version,\n timestamp: currentTimestamp,\n };\n\n try {\n // Ensure the directory exists before writing the file\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n\n await writeFile(sentinelFilePath, JSON.stringify(data), { flag: 'wx' });\n } catch (err: any) {\n if (err.code === 'EEXIST') {\n // Another process already created it → we're done\n return;\n }\n // Optimization: If ENOENT occurs on write despite mkdir (race condition with external deletion), retry once.\n if (err.code === 'ENOENT') {\n try {\n await mkdir(dirname(sentinelFilePath), { recursive: true });\n await writeFile(sentinelFilePath, JSON.stringify(data), { flag: 'wx' });\n return;\n } catch (retryErr: any) {\n if (retryErr.code === 'EEXIST') return;\n }\n }\n throw err; // unexpected FS error\n }\n};\n\n/**\n * Ensures a callback function runs only once within a specified time window across multiple processes.\n * Uses a sentinel file to coordinate execution and prevent duplicate work.\n *\n * @param sentinelFilePath - Path to the sentinel file used for coordination\n * @param callback - The function to execute (should be async)\n * @param options - The options for the runOnce function\n *\n * @example\n * ```typescript\n * await runPrepareIntlayerOnce(\n * '/tmp/intlayer-sentinel',\n * async () => {\n * // Your initialization logic here\n * await prepareIntlayer();\n * },\n * 30 * 1000 // 30 seconds cache\n * );\n * ```\n *\n * @throws {Error} When there are unexpected filesystem errors\n */\nexport const runOnce = async (\n sentinelFilePath: string,\n callback: () => void | Promise<void>,\n options?: RunOnceOptions\n) => {\n const { onIsCached, cacheTimeoutMs, forceRun } = {\n ...DEFAULT_RUN_ONCE_OPTIONS,\n ...(options ?? {}),\n };\n const currentTimestamp = Date.now();\n\n try {\n // Check if sentinel file exists and get its stats\n const sentinelStats = await stat(sentinelFilePath);\n const sentinelAge = currentTimestamp - sentinelStats.mtime.getTime();\n\n // Determine if we should rebuild based on cache age, force flag, or version mismatch\n let shouldRebuild = Boolean(forceRun) || sentinelAge > cacheTimeoutMs!;\n\n if (!shouldRebuild) {\n try {\n const raw = await readFile(sentinelFilePath, 'utf8');\n let cachedVersion: string | undefined;\n try {\n const parsed = JSON.parse(raw) as Partial<SentinelData>;\n cachedVersion = parsed.version;\n } catch {\n // Legacy format (timestamp only). Force a rebuild once to write versioned sentinel.\n cachedVersion = undefined;\n }\n\n if (!cachedVersion || cachedVersion !== packageJson.version) {\n shouldRebuild = true;\n }\n } catch {\n // If we cannot read the file, err on the safe side and rebuild\n shouldRebuild = true;\n }\n }\n\n if (shouldRebuild) {\n try {\n await unlink(sentinelFilePath);\n } catch {}\n // Fall through to create new sentinel and rebuild\n } else {\n await onIsCached?.();\n // Sentinel is recent and versions match, no need to rebuild\n return;\n }\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n // File doesn't exist, continue to create it\n } else {\n throw err; // unexpected FS error\n }\n }\n\n // Write sentinel file before to block parallel processes\n // Added await here\n await writeSentinelFile(sentinelFilePath, currentTimestamp);\n\n try {\n await callback();\n\n // Write sentinel file after to ensure the first one has not been removed with cleanOutputDir\n // Added await here\n await writeSentinelFile(sentinelFilePath, currentTimestamp);\n } catch {\n try {\n await unlink(sentinelFilePath); // Remove sentinel file if an error occurs\n } catch {}\n }\n};\n"],"mappings":";;;;;;;;AAuBA,MAAM,2BAA2B,EAC/B,gBAAgB,KAAK,IACvB;AAOA,MAAM,oBAAoB,OACxB,kBACA,qBACG;CAEH,MAAM,OAAqB;EACzB,SAASA,oCAAY;EACrB,WAAW;CACb;CAEA,IAAI;EAEF,yDAAoB,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;EAE1D,sCAAgB,kBAAkB,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,KAAK,CAAC;CACxE,SAAS,KAAU;EACjB,IAAI,IAAI,SAAS,UAEf;EAGF,IAAI,IAAI,SAAS,UACf,IAAI;GACF,yDAAoB,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;GAC1D,sCAAgB,kBAAkB,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,KAAK,CAAC;GACtE;EACF,SAAS,UAAe;GACtB,IAAI,SAAS,SAAS,UAAU;EAClC;EAEF,MAAM;CACR;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,UAAU,OACrB,kBACA,UACA,YACG;CACH,MAAM,EAAE,YAAY,gBAAgB,aAAa;EAC/C,GAAG;EACH,GAAI,WAAW,CAAC;CAClB;CACA,MAAM,mBAAmB,KAAK,IAAI;CAElC,IAAI;EAGF,MAAM,cAAc,oBAAmB,iCADN,gBAAgB,GACI,MAAM,QAAQ;EAGnE,IAAI,gBAAgB,QAAQ,QAAQ,KAAK,cAAc;EAEvD,IAAI,CAAC,eACH,IAAI;GACF,MAAM,MAAM,qCAAe,kBAAkB,MAAM;GACnD,IAAI;GACJ,IAAI;IAEF,gBADe,KAAK,MAAM,GACL,EAAE;GACzB,QAAQ;IAEN,gBAAgB;GAClB;GAEA,IAAI,CAAC,iBAAiB,kBAAkBA,oCAAY,SAClD,gBAAgB;EAEpB,QAAQ;GAEN,gBAAgB;EAClB;EAGF,IAAI,eACF,IAAI;GACF,mCAAa,gBAAgB;EAC/B,QAAQ,CAAC;OAEJ;GACL,MAAM,aAAa;GAEnB;EACF;CACF,SAAS,KAAU;EACjB,IAAI,IAAI,SAAS,UAAU,CAE3B,OACE,MAAM;CAEV;CAIA,MAAM,kBAAkB,kBAAkB,gBAAgB;CAE1D,IAAI;EACF,MAAM,SAAS;EAIf,MAAM,kBAAkB,kBAAkB,gBAAgB;CAC5D,QAAQ;EACN,IAAI;GACF,mCAAa,gBAAgB;EAC/B,QAAQ,CAAC;CACX;AACF"}