UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge.

98 lines 3.54 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { randomBytes } from 'node:crypto'; import { copyFile, open, rename, unlink } from 'node:fs/promises'; import { isAbsolute, normalize } from 'node:path'; import { pid } from 'node:process'; import { Injectable } from '@nestjs/common'; import { readJson } from 'fs-extra/esm'; function assertSafeFsPath(path) { if (typeof path !== 'string' || path.length === 0) { throw new TypeError('Path must be a non-empty string'); } if (!isAbsolute(path) || normalize(path) !== path) { throw new Error('Refusing fs operation on non-normalised or relative path'); } } let JsonFileStoreService = class JsonFileStoreService { locks = new Map(); async read(path) { return readJson(path); } async mutate(path, mutator, opts = {}) { return this.withLock(path, async () => { let current = null; try { current = await readJson(path); } catch (e) { if (e?.code !== 'ENOENT') { throw e; } } const next = await mutator(current); if (next !== null && next !== undefined) { await this.atomicWrite(path, next, opts); } return next; }); } async write(path, data, opts = {}) { return this.withLock(path, () => this.atomicWrite(path, data, opts)); } async withLock(path, fn) { const previous = this.locks.get(path) ?? Promise.resolve(); const guarded = previous.then(fn, fn); const swallowed = guarded.catch(() => undefined); this.locks.set(path, swallowed); try { return await guarded; } finally { if (this.locks.get(path) === swallowed) { this.locks.delete(path); } } } async atomicWrite(path, data, opts) { assertSafeFsPath(path); if (opts.backupTo) { assertSafeFsPath(opts.backupTo); try { await copyFile(path, opts.backupTo); } catch (e) { if (e?.code !== 'ENOENT') { throw e; } } } const tmpPath = `${path}.tmp.${pid}.${randomBytes(6).toString('hex')}`; const body = `${JSON.stringify(data, null, opts.spaces ?? 4)}\n`; let handle; try { handle = await open(tmpPath, 'w'); await handle.writeFile(body, 'utf8'); await handle.sync(); } finally { await handle?.close(); } try { await rename(tmpPath, path); } catch (e) { await unlink(tmpPath).catch(() => undefined); throw e; } } }; JsonFileStoreService = __decorate([ Injectable() ], JsonFileStoreService); export { JsonFileStoreService }; //# sourceMappingURL=json-file-store.service.js.map