UNPKG

mockaton

Version:
118 lines (100 loc) 2.82 kB
import { join } from 'node:path' import { watch } from 'node:fs' import { EventEmitter } from 'node:events' import { HEADER_SYNC_VERSION, LONG_POLL_SERVER_TIMEOUT } from './ApiConstants.js' import { config } from './config.js' import { sendJSON } from './utils/http-response.js' import { isFile, isDirectory } from './utils/fs.js' import * as staticCollection from './staticCollection.js' import * as mockBrokerCollection from './mockBrokersCollection.js' /** * ARR Event = Add, Remove, or Rename Mock * * The emitter is debounced so it handles e.g. bulk deletes, * and also renames, which are two events (delete + add). */ const uiSyncVersion = new class extends EventEmitter { delay = Number(process.env.MOCKATON_WATCHER_DEBOUNCE_MS ?? 80) version = 0 increment = /** @type {function} */ this.#debounce(() => { this.version++ super.emit('ARR') }) subscribe(listener) { this.once('ARR', listener) } unsubscribe(listener) { this.removeListener('ARR', listener) } #debounce(fn) { // TESTME let timer return () => { clearTimeout(timer) timer = setTimeout(fn, this.delay) } } } export function watchMocksDir() { const dir = config.mocksDir watch(dir, { recursive: true, persistent: false }, (_, file) => { if (!file) return if (isDirectory(join(dir, file))) { mockBrokerCollection.init() uiSyncVersion.increment() } else if (!isFile(join(dir, file))) { // file deleted mockBrokerCollection.unregisterMock(file) uiSyncVersion.increment() } else if (mockBrokerCollection.registerMock(file, Boolean('isFromWatcher'))) uiSyncVersion.increment() else { // ignore file edits } }) } export function watchStaticDir() { const dir = config.staticDir if (!dir) return watch(dir, { recursive: true, persistent: false }, (_, file) => { if (!file) return if (isDirectory(join(dir, file))) { staticCollection.init() uiSyncVersion.increment() } else if (!isFile(join(dir, file))) { // file deleted staticCollection.unregisterMock(file) uiSyncVersion.increment() } else if (staticCollection.registerMock(file)) uiSyncVersion.increment() else { // ignore file edits } }) } /** Realtime notify ARR Events */ export function longPollClientSyncVersion(req, response) { const clientVersion = req.headers[HEADER_SYNC_VERSION] if (clientVersion !== undefined && uiSyncVersion.version !== Number(clientVersion)) { // e.g., tab was hidden while new mocks were added or removed sendJSON(response, uiSyncVersion.version) return } function onARR() { uiSyncVersion.unsubscribe(onARR) sendJSON(response, uiSyncVersion.version) } response.setTimeout(LONG_POLL_SERVER_TIMEOUT, onARR) req.on('error', () => { uiSyncVersion.unsubscribe(onARR) response.destroy() }) uiSyncVersion.subscribe(onARR) }