@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
80 lines • 3.43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* A debounced file load/watcher for use with the {@link FileDataSource}.
*
* The file loader will load all files specified and keep the string representations in memory.
* Whenever a change is made to any of the files, then that file will be reloaded and the in
* memory version updated.
*
* Updates to many files, which occur within 10ms of each other, will be coalesced into
* a single callback.
*
* @internal
*/
class FileLoader {
constructor(_filesystem, _paths, _watch, _callback) {
this._filesystem = _filesystem;
this._paths = _paths;
this._watch = _watch;
this._callback = _callback;
this._watchers = [];
this._fileData = {};
this._fileTimestamps = {};
}
/**
* Load all the files and start watching them if watching is enabled.
*/
async loadAndWatch() {
const promises = this._paths.map(async (path) => {
const data = await this._filesystem.readFile(path);
const timeStamp = await this._filesystem.getFileTimestamp(path);
return { data, path, timeStamp };
});
// This promise could be rejected, let the caller handle it.
const results = await Promise.all(promises);
results.forEach((res) => {
this._fileData[res.path] = res.data;
this._fileTimestamps[res.path] = res.timeStamp;
});
this._callback(results);
// If we are watching, then setup watchers and notify of any changes.
if (this._watch) {
this._paths.forEach((path) => {
const watcher = this._filesystem.watch(path, async (_, updatePath) => {
const timeStamp = await this._filesystem.getFileTimestamp(updatePath);
// The modification time is the same, so we are going to ignore this update.
// In some implementations watch might be triggered multiple times for a single update.
if (timeStamp === this._fileTimestamps[updatePath]) {
return;
}
this._fileTimestamps[updatePath] = timeStamp;
const data = await this._filesystem.readFile(updatePath);
this._fileData[updatePath] = data;
this._debounceCallback();
});
this._watchers.push(watcher);
});
}
}
close() {
this._watchers.forEach((watcher) => watcher.close());
}
_debounceCallback() {
// If there is a handle, then we have already started the debounce process.
if (!this._debounceHandle) {
this._debounceHandle = setTimeout(() => {
this._debounceHandle = undefined;
this._callback(Object.entries(this._fileData).reduce((acc, [path, data]) => {
acc.push({ path, data });
return acc;
}, []));
}, 10);
// The 10ms delay above is arbitrary - we just don't want to have the number be zero,
// because in a case where multiple watch events are fired off one after another,
// we want the reload to happen only after all of the event handlers have executed.
}
}
}
exports.default = FileLoader;
//# sourceMappingURL=FileLoader.js.map