@andersbakken/fisk 5.0.18 → 5.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,6 +47,36 @@ var require$$8__default = /*#__PURE__*/_interopDefaultLegacy(require$$8);
47
47
  var child_process__default = /*#__PURE__*/_interopDefaultLegacy(child_process);
48
48
  var require$$1__default$4 = /*#__PURE__*/_interopDefaultLegacy(require$$1$5);
49
49
 
50
+ /******************************************************************************
51
+ Copyright (c) Microsoft Corporation.
52
+
53
+ Permission to use, copy, modify, and/or distribute this software for any
54
+ purpose with or without fee is hereby granted.
55
+
56
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
57
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
58
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
59
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
60
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
61
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
62
+ PERFORMANCE OF THIS SOFTWARE.
63
+ ***************************************************************************** */
64
+
65
+ function __awaiter(thisArg, _arguments, P, generator) {
66
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
67
+ return new (P || (P = Promise))(function (resolve, reject) {
68
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
69
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
70
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
71
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
72
+ });
73
+ }
74
+
75
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
76
+ var e = new Error(message);
77
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
78
+ };
79
+
50
80
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
51
81
 
52
82
  function getDefaultExportFromCjs (x) {
@@ -8211,6 +8241,97 @@ class Job extends EventEmitter__default["default"] {
8211
8241
  }
8212
8242
  }
8213
8243
 
8244
+ const Version = 5;
8245
+ // 6: objects now carry the client's real source path and compilation dir,
8246
+ // baked in at compile time instead of the builder's /compiles paths, and the
8247
+ // stored response no longer keeps the paths the client used to patch with.
8248
+ const ObjectCacheFormatVersion = 6;
8249
+ function cacheDir(option) {
8250
+ let dir = option("cache-dir");
8251
+ if (!dir) {
8252
+ dir = path__default["default"].join(os__default["default"].homedir(), ".cache", "fisk", path__default["default"].basename(option.prefix || ""));
8253
+ }
8254
+ return dir;
8255
+ }
8256
+ function validateCache(option) {
8257
+ const dir = cacheDir(option);
8258
+ const file = path__default["default"].join(dir, "version");
8259
+ // console.log(dir);
8260
+ let version;
8261
+ try {
8262
+ version = fs$3.readFileSync(file);
8263
+ if (version.readUInt32BE() === Version) {
8264
+ return;
8265
+ }
8266
+ }
8267
+ catch (err) {
8268
+ /* */
8269
+ }
8270
+ if (version) {
8271
+ console.log(`Wrong version. Destroying cache ${dir}`);
8272
+ }
8273
+ fs$3.removeSync(dir);
8274
+ fs$3.mkdirpSync(dir);
8275
+ const buf = Buffer.allocUnsafe(4);
8276
+ buf.writeUInt32BE(Version);
8277
+ fs$3.writeFileSync(file, buf);
8278
+ }
8279
+ function validateObjectCache(option) {
8280
+ const dir = cacheDir(option);
8281
+ const objectCacheDir = option.string("object-cache-dir") || path__default["default"].join(dir, "objectcache");
8282
+ const file = path__default["default"].join(objectCacheDir, "version");
8283
+ let version;
8284
+ try {
8285
+ version = fs$3.readFileSync(file);
8286
+ if (version.readUInt32BE() === ObjectCacheFormatVersion) {
8287
+ return;
8288
+ }
8289
+ }
8290
+ catch (err) {
8291
+ /* */
8292
+ }
8293
+ if (version) {
8294
+ console.log(`Wrong object cache version. Destroying object cache ${objectCacheDir}`);
8295
+ }
8296
+ fs$3.removeSync(objectCacheDir);
8297
+ fs$3.mkdirpSync(objectCacheDir);
8298
+ const buf = Buffer.allocUnsafe(4);
8299
+ buf.writeUInt32BE(ObjectCacheFormatVersion);
8300
+ fs$3.writeFileSync(file, buf);
8301
+ }
8302
+ // The listen backlog is the depth of the kernel's accept queue. When it fills
8303
+ // -- which is what happens whenever the event loop stalls long enough to stop
8304
+ // calling accept() -- Linux does not refuse the connection, it silently drops
8305
+ // the SYN, and the client's first retransmit is a second later. A client with a
8306
+ // sub-second handshake budget sees that as a timeout with no server-side trace.
8307
+ // listen(2) clamps to net.core.somaxconn, so following somaxconn is both the
8308
+ // largest useful value and the one an operator can actually tune.
8309
+ function defaultBacklog() {
8310
+ try {
8311
+ return parseInt(fs$3.readFileSync("/proc/sys/net/core/somaxconn", "utf8")) || 511;
8312
+ }
8313
+ catch (err) {
8314
+ // Not Linux, or procfs isn't mounted. 511 is node's own default.
8315
+ return 511;
8316
+ }
8317
+ }
8318
+ // Only the builder keeps an object cache on disk. The scheduler tracks which
8319
+ // builder holds which sha1 in memory, and the daemon uses cacheDir purely for
8320
+ // the default socket path -- validating an object cache for either created a
8321
+ // directory they never read and, on a format bump, tried to destroy one they do
8322
+ // not necessarily own.
8323
+ function common$2(option, hasObjectCache = false) {
8324
+ validateCache(option);
8325
+ if (hasObjectCache) {
8326
+ validateObjectCache(option);
8327
+ }
8328
+ return {
8329
+ cacheDir: cacheDir.bind(undefined, option),
8330
+ Version,
8331
+ ObjectCacheFormatVersion
8332
+ };
8333
+ }
8334
+
8214
8335
  /**
8215
8336
  * Check if we're required to add a port number.
8216
8337
  *
@@ -54881,8 +55002,14 @@ class Server extends EventEmitter__default["default"] {
54881
55002
  this.emit("listen", this.app);
54882
55003
  this.port = this.option.int("port", 8096);
54883
55004
  this.server = require$$1__default$1["default"].createServer(this.app);
54884
- this.ws = new ws.Server({ noServer: true, backlog: this.option.int("backlog", 50) });
54885
- this.server.listen({ port: this.port, backlog: this.option.int("backlog", 50), host: "0.0.0.0" });
55005
+ // No backlog here on purpose: ws only uses that option when it creates
55006
+ // its own http server, which noServer mode explicitly does not do.
55007
+ this.ws = new ws.Server({ noServer: true });
55008
+ this.server.listen({
55009
+ port: this.port,
55010
+ backlog: this.option.int("backlog", defaultBacklog()),
55011
+ host: "0.0.0.0"
55012
+ });
54886
55013
  this.server.on("upgrade", (req, socket, head) => {
54887
55014
  assert__default["default"](this.ws, "Must have ws");
54888
55015
  this.ws.handleUpgrade(req, socket, head, (ws) => {
@@ -55061,47 +55188,59 @@ class CompileJob extends EventEmitter__default["default"] {
55061
55188
  this.dir = path__default["default"].join(vm.root, "compiles", String(this.id));
55062
55189
  this.vmDir = path__default["default"].join("/", "compiles", String(this.id));
55063
55190
  this.sourceFileName = sourcePath ? path__default["default"].basename(sourcePath) : "sourcefile";
55064
- fs$3.mkdirpSync(this.dir);
55065
- this.fd = fs$3.openSync(path__default["default"].join(this.dir, this.sourceFileName), "w");
55066
55191
  this.cppSize = 0;
55067
55192
  this.startCompile = undefined;
55193
+ this.opened = fs$3.mkdirp(this.dir).then(() => fs$3.open(path__default["default"].join(this.dir, this.sourceFileName), "w"));
55194
+ // Nothing awaits this until feed(), and an unhandled rejection in the
55195
+ // meantime would take the builder down.
55196
+ this.opened.catch(() => {
55197
+ /* reported by feed */
55198
+ });
55068
55199
  }
55069
55200
  sendCallback(error) {
55070
55201
  if (error) {
55071
55202
  console.error("Got send error for", this.vmDir, this.id, this.commandLine);
55072
- const compileFinished = {
55073
- type: "compileFinished",
55074
- success: false,
55075
- id: this.id,
55076
- files: [],
55077
- exitCode: -1,
55078
- sourcePath: "",
55079
- error: error.toString()
55080
- };
55081
- this.vm.compileFinished(compileFinished);
55203
+ this.fail(error);
55082
55204
  }
55083
55205
  }
55084
55206
  feed(data) {
55085
- assert__default["default"](this.fd !== undefined, "Must have fd");
55086
- fs$3.writeSync(this.fd, data);
55087
- this.cppSize += data.length;
55088
- this.startCompile = Date.now();
55089
- fs$3.closeSync(this.fd);
55090
- this.fd = undefined;
55091
- this.vm.child.send({
55092
- type: "compile",
55093
- commandLine: this.commandLine,
55094
- argv0: this.argv0,
55095
- id: this.id,
55096
- dir: this.vmDir,
55097
- sourceFileName: this.sourceFileName,
55098
- clientSourcePath: this.sourcePath,
55099
- clientCwd: this.clientCwd
55100
- }, this.sendCallback.bind(this));
55207
+ this.opened
55208
+ .then((fd) => __awaiter(this, void 0, void 0, function* () {
55209
+ yield fs$3.write(fd, data);
55210
+ this.cppSize += data.length;
55211
+ this.startCompile = Date.now();
55212
+ yield fs$3.close(fd);
55213
+ this.vm.child.send({
55214
+ type: "compile",
55215
+ commandLine: this.commandLine,
55216
+ argv0: this.argv0,
55217
+ id: this.id,
55218
+ dir: this.vmDir,
55219
+ sourceFileName: this.sourceFileName,
55220
+ clientSourcePath: this.sourcePath,
55221
+ clientCwd: this.clientCwd
55222
+ }, this.sendCallback.bind(this));
55223
+ }))
55224
+ .catch((err) => {
55225
+ console.error("Failed to write source file for", this.vmDir, this.id, err);
55226
+ this.fail(err);
55227
+ });
55101
55228
  }
55102
55229
  cancel() {
55103
55230
  this.vm.child.send({ type: "cancel", id: this.id }, this.sendCallback.bind(this));
55104
55231
  }
55232
+ fail(error) {
55233
+ const compileFinished = {
55234
+ type: "compileFinished",
55235
+ success: false,
55236
+ id: this.id,
55237
+ files: [],
55238
+ exitCode: -1,
55239
+ sourcePath: "",
55240
+ error: error.toString()
55241
+ };
55242
+ this.vm.compileFinished(compileFinished);
55243
+ }
55105
55244
  }
55106
55245
 
55107
55246
  class VM extends EventEmitter__default["default"] {
@@ -55179,6 +55318,28 @@ class VM extends EventEmitter__default["default"] {
55179
55318
  console.error("Got some error", msg.error);
55180
55319
  }
55181
55320
  const now = Date.now();
55321
+ const dir = this.compiles[msg.id].dir;
55322
+ let released = false;
55323
+ const release = () => {
55324
+ if (released) {
55325
+ return;
55326
+ }
55327
+ released = true;
55328
+ clearTimeout(leakTimer);
55329
+ if (!this.keepCompiles) {
55330
+ fs$3.remove(dir).catch((err) => {
55331
+ console.error(`Failed to remove compile directory ${dir}`, err);
55332
+ });
55333
+ }
55334
+ };
55335
+ // A handler that throws before releasing would keep the directory
55336
+ // until the builder restarts and cleared its whole compiles root. The
55337
+ // reads it is holding the directory for take milliseconds, so anything
55338
+ // still outstanding this much later is a bug, not slow disk.
55339
+ const leakTimer = setTimeout(() => {
55340
+ console.error(`Compile directory ${dir} was never released, removing it anyway`);
55341
+ release();
55342
+ }, 5 * 60000).unref();
55182
55343
  const finishedEvent = {
55183
55344
  cppSize: compile.cppSize,
55184
55345
  compileDuration: now - (compile.startCompile || 0),
@@ -55191,13 +55352,19 @@ class VM extends EventEmitter__default["default"] {
55191
55352
  path: file.path,
55192
55353
  absolute: path__default["default"].join(this.root, file.mapped ? file.mapped : file.path)
55193
55354
  };
55194
- })
55355
+ }),
55356
+ release
55195
55357
  };
55196
- compile.emit("finished", finishedEvent);
55197
- if (!this.keepCompiles) {
55198
- fs$3.remove(this.compiles[msg.id].dir);
55199
- }
55200
55358
  delete this.compiles[msg.id];
55359
+ // The directory used to be removed right here, which forced the
55360
+ // handler to read and compress every output file synchronously to beat
55361
+ // the cleanup. It owns the directory now and releases it when done.
55362
+ if (compile.listenerCount("finished")) {
55363
+ compile.emit("finished", finishedEvent);
55364
+ }
55365
+ else {
55366
+ release();
55367
+ }
55201
55368
  }
55202
55369
  destroy() {
55203
55370
  this.destroying = true;
@@ -55219,81 +55386,6 @@ class VM extends EventEmitter__default["default"] {
55219
55386
  }
55220
55387
  }
55221
55388
 
55222
- const Version = 5;
55223
- // 6: objects now carry the client's real source path and compilation dir,
55224
- // baked in at compile time instead of the builder's /compiles paths, and the
55225
- // stored response no longer keeps the paths the client used to patch with.
55226
- const ObjectCacheFormatVersion = 6;
55227
- function cacheDir(option) {
55228
- let dir = option("cache-dir");
55229
- if (!dir) {
55230
- dir = path__default["default"].join(os__default["default"].homedir(), ".cache", "fisk", path__default["default"].basename(option.prefix || ""));
55231
- }
55232
- return dir;
55233
- }
55234
- function validateCache(option) {
55235
- const dir = cacheDir(option);
55236
- const file = path__default["default"].join(dir, "version");
55237
- // console.log(dir);
55238
- let version;
55239
- try {
55240
- version = fs$3.readFileSync(file);
55241
- if (version.readUInt32BE() === Version) {
55242
- return;
55243
- }
55244
- }
55245
- catch (err) {
55246
- /* */
55247
- }
55248
- if (version) {
55249
- console.log(`Wrong version. Destroying cache ${dir}`);
55250
- }
55251
- fs$3.removeSync(dir);
55252
- fs$3.mkdirpSync(dir);
55253
- const buf = Buffer.allocUnsafe(4);
55254
- buf.writeUInt32BE(Version);
55255
- fs$3.writeFileSync(file, buf);
55256
- }
55257
- function validateObjectCache(option) {
55258
- const dir = cacheDir(option);
55259
- const objectCacheDir = option.string("object-cache-dir") || path__default["default"].join(dir, "objectcache");
55260
- const file = path__default["default"].join(objectCacheDir, "version");
55261
- let version;
55262
- try {
55263
- version = fs$3.readFileSync(file);
55264
- if (version.readUInt32BE() === ObjectCacheFormatVersion) {
55265
- return;
55266
- }
55267
- }
55268
- catch (err) {
55269
- /* */
55270
- }
55271
- if (version) {
55272
- console.log(`Wrong object cache version. Destroying object cache ${objectCacheDir}`);
55273
- }
55274
- fs$3.removeSync(objectCacheDir);
55275
- fs$3.mkdirpSync(objectCacheDir);
55276
- const buf = Buffer.allocUnsafe(4);
55277
- buf.writeUInt32BE(ObjectCacheFormatVersion);
55278
- fs$3.writeFileSync(file, buf);
55279
- }
55280
- // Only the builder keeps an object cache on disk. The scheduler tracks which
55281
- // builder holds which sha1 in memory, and the daemon uses cacheDir purely for
55282
- // the default socket path -- validating an object cache for either created a
55283
- // directory they never read and, on a format bump, tried to destroy one they do
55284
- // not necessarily own.
55285
- function common$2(option, hasObjectCache = false) {
55286
- validateCache(option);
55287
- if (hasObjectCache) {
55288
- validateObjectCache(option);
55289
- }
55290
- return {
55291
- cacheDir: cacheDir.bind(undefined, option),
55292
- Version,
55293
- ObjectCacheFormatVersion
55294
- };
55295
- }
55296
-
55297
55389
  var resolve;
55298
55390
  var hasRequiredResolve;
55299
55391
 
@@ -60174,6 +60266,14 @@ const option = createOptions({
60174
60266
  additionalFiles: ["fisk/builder.conf.override"]
60175
60267
  });
60176
60268
  const common = common$2(option, true);
60269
+ // Reads, writes, gzip and gunzip all run on libuv's threadpool, and now that
60270
+ // the job and object cache paths use the asynchronous forms they share it.
60271
+ // Four threads is not enough to keep as many jobs as we have slots moving.
60272
+ // libuv reads this when it first creates the pool, so it has to be set before
60273
+ // anything asynchronous has been submitted, which is why it lives up here.
60274
+ if (!process.env.UV_THREADPOOL_SIZE) {
60275
+ process.env.UV_THREADPOOL_SIZE = String(Math.min(128, Math.max(8, os__default["default"].cpus().length)));
60276
+ }
60177
60277
  if (process.getuid() !== 0) {
60178
60278
  console.error("fisk builder needs to run as root to be able to chroot");
60179
60279
  process.exit(1);
@@ -60271,26 +60371,34 @@ function getFromCache(job, cb) {
60271
60371
  else {
60272
60372
  // console.log("got good response from file", file);
60273
60373
  // console.log("sending some data", buffer.length, fileIdx, item.response.index.length);
60274
- let sendBuffer = buffer;
60275
- // Decompress if client doesn't support compressed responses from cache
60276
- if (!sendCompressed && buffer.byteLength > 0) {
60277
- try {
60278
- sendBuffer = zlib__default["default"].gunzipSync(buffer);
60374
+ const sendAndContinue = (sendBuffer) => {
60375
+ job.send(sendBuffer);
60376
+ pos += read;
60377
+ if (++fileIdx < item.response.index.length) {
60378
+ work();
60279
60379
  }
60280
- catch (gunzipErr) {
60281
- assert__default["default"](objectCache, "Must have objectCache");
60282
- console.error(`Failed to gunzip ${path__default["default"].join(objectCache.dir, item.response.sha1)} for file index ${fileIdx}:`, gunzipErr);
60283
- finish(gunzipErr);
60284
- return;
60380
+ else {
60381
+ finish();
60285
60382
  }
60286
- }
60287
- job.send(sendBuffer);
60288
- pos += read;
60289
- if (++fileIdx < item.response.index.length) {
60290
- work();
60383
+ };
60384
+ // Decompress if client doesn't support compressed responses
60385
+ // from cache. Asynchronously: a cache hit is the fast path
60386
+ // and used to spend it inflating megabytes on the event
60387
+ // loop, once per file. Files are still sent in index order
60388
+ // because the next read only starts from sendAndContinue.
60389
+ if (!sendCompressed && buffer.byteLength > 0) {
60390
+ zlib__default["default"].gunzip(buffer, (gunzipErr, inflated) => {
60391
+ if (gunzipErr) {
60392
+ assert__default["default"](objectCache, "Must have objectCache");
60393
+ console.error(`Failed to gunzip ${path__default["default"].join(objectCache.dir, item.response.sha1)} for file index ${fileIdx}:`, gunzipErr);
60394
+ finish(gunzipErr);
60395
+ return;
60396
+ }
60397
+ sendAndContinue(inflated);
60398
+ });
60291
60399
  }
60292
60400
  else {
60293
- finish();
60401
+ sendAndContinue(buffer);
60294
60402
  }
60295
60403
  }
60296
60404
  });
@@ -60848,6 +60956,19 @@ server.on("listen", (app) => {
60848
60956
  }
60849
60957
  });
60850
60958
  });
60959
+ const gzip = require$$1$3.promisify(zlib__default["default"].gzip);
60960
+ // Always compressed, because that is the form the object cache stores. The
60961
+ // uncompressed buffer is kept alongside it since the response index reports
60962
+ // both sizes and a client that cannot take compressed data gets that one.
60963
+ function readAndCompress(files) {
60964
+ return __awaiter(this, void 0, void 0, function* () {
60965
+ return Promise.all(files.map((f) => __awaiter(this, void 0, void 0, function* () {
60966
+ const uncompressed = yield fs$3.promises.readFile(f.absolute);
60967
+ const contents = uncompressed.byteLength > 0 ? yield gzip(uncompressed) : uncompressed;
60968
+ return { contents, uncompressed, path: f.path };
60969
+ })));
60970
+ });
60971
+ }
60851
60972
  function startPending() {
60852
60973
  // console.log(`startPending called ${jobQueue.length}`);
60853
60974
  for (let idx = 0; idx < jobQueue.length; ++idx) {
@@ -60958,6 +61079,7 @@ server.on("job", (job) => {
60958
61079
  j.op.on("finished", (event) => {
60959
61080
  j.done = true;
60960
61081
  if (j.aborted) {
61082
+ event.release();
60961
61083
  return;
60962
61084
  }
60963
61085
  const end = Date.now();
@@ -60968,108 +61090,131 @@ server.on("job", (job) => {
60968
61090
  }
60969
61091
  else {
60970
61092
  console.error("Can't find j?");
61093
+ event.release();
60971
61094
  return;
60972
61095
  }
60973
- // this can't be async, the directory is removed after the event is fired
60974
- // Always compress for cache storage, but send based on client preference
60975
- const contents = event.files.map((f) => {
60976
- const uncompressed = fs$3.readFileSync(f.absolute);
60977
- const compressed = uncompressed.byteLength > 0 ? zlib__default["default"].gzipSync(uncompressed) : uncompressed;
60978
- return {
60979
- contents: compressed,
60980
- uncompressed,
60981
- path: f.path
60982
- };
60983
- });
60984
- // Prepare data to send to client (compressed or uncompressed based on preference and capability)
60985
- // Only send compressed if client wants it AND supports compressed responses
60986
- const sendCompressed = j.job.compressed && j.job.supportsCompressedResponse;
60987
- const toSend = contents.map((item) => {
60988
- assert__default["default"](item.uncompressed, "Must have uncompressed data");
60989
- return {
60990
- contents: sendCompressed ? item.contents : item.uncompressed,
60991
- path: item.path
60992
- };
61096
+ // Reading and gzipping every output file synchronously blocked
61097
+ // the event loop once per finished job, which on a full builder
61098
+ // is once per core per compile wave, and that is what stopped
61099
+ // handshakes from being answered. The compile directory is ours
61100
+ // until we release it, so this can take as long as it needs to.
61101
+ readAndCompress(event.files)
61102
+ .then((contents) => {
61103
+ if (j.aborted) {
61104
+ return;
61105
+ }
61106
+ respond(contents);
61107
+ })
61108
+ .catch((err) => {
61109
+ console.error(`Failed to collect output for job ${j.id}`, err);
61110
+ if (!j.aborted) {
61111
+ jobJob.send({
61112
+ type: "response",
61113
+ index: [],
61114
+ success: false,
61115
+ exitCode: 1,
61116
+ error: `Failed to collect output: ${err.message}`,
61117
+ sha1: jobJob.sha1,
61118
+ sourcePath: j.op.sourceFileName,
61119
+ stderr: j.stderr,
61120
+ stdout: j.stdout
61121
+ });
61122
+ }
61123
+ })
61124
+ .finally(() => {
61125
+ event.release();
61126
+ startPending();
60993
61127
  });
60994
- const response = {
60995
- type: "response",
60996
- index: toSend.map((item) => {
60997
- const original = contents.find((c) => c.path === item.path);
60998
- assert__default["default"](original, "Must have original contents");
60999
- assert__default["default"](original.uncompressed, "Must have uncompressed data");
61000
- const ret = {
61001
- path: item.path,
61002
- bytes: item.contents.length,
61003
- uncompressedSize: original.uncompressed.byteLength
61128
+ function respond(contents) {
61129
+ // Prepare data to send to client (compressed or uncompressed based on preference and capability)
61130
+ // Only send compressed if client wants it AND supports compressed responses
61131
+ const sendCompressed = j.job.compressed && j.job.supportsCompressedResponse;
61132
+ const toSend = contents.map((item) => {
61133
+ assert__default["default"](item.uncompressed, "Must have uncompressed data");
61134
+ return {
61135
+ contents: sendCompressed ? item.contents : item.uncompressed,
61136
+ path: item.path
61004
61137
  };
61005
- return ret;
61006
- }),
61007
- success: event.success,
61008
- exitCode: event.exitCode,
61009
- sha1: jobJob.sha1,
61010
- // Just the basename. This goes into the object cache and is
61011
- // replayed on every hit, so it has to still mean something
61012
- // later: the /compiles/<id> directory belongs to this one
61013
- // job's chroot, and the requesting client's own path belongs
61014
- // to whichever client happened to compile it first. Only the
61015
- // file name survives being shared.
61016
- sourcePath: j.op.sourceFileName,
61017
- stderr: j.stderr,
61018
- stdout: j.stdout
61019
- };
61020
- if (event.error) {
61021
- response.error = event.error;
61022
- }
61023
- if (debug) {
61024
- console.log("Sending response", jobJob.ip, jobJob.hostname, response);
61025
- }
61026
- jobJob.send(response);
61027
- if (response.exitCode === 0 &&
61028
- event.success &&
61029
- objectCache &&
61030
- response.sha1 &&
61031
- objectCache.state(response.sha1) === "none") {
61032
- // Cache metadata needs to reflect compressed sizes since we store compressed
61033
- const cacheResponse = Object.assign(Object.assign({}, response), { index: contents.map((item) => {
61034
- assert__default["default"](item.uncompressed, "Must have uncompressed data");
61035
- return {
61138
+ });
61139
+ const response = {
61140
+ type: "response",
61141
+ index: toSend.map((item) => {
61142
+ const original = contents.find((c) => c.path === item.path);
61143
+ assert__default["default"](original, "Must have original contents");
61144
+ assert__default["default"](original.uncompressed, "Must have uncompressed data");
61145
+ const ret = {
61036
61146
  path: item.path,
61037
61147
  bytes: item.contents.length,
61038
- uncompressedSize: item.uncompressed.byteLength
61148
+ uncompressedSize: original.uncompressed.byteLength
61039
61149
  };
61040
- }) });
61041
- cacheResponse.commandLine = jobJob.commandLine;
61042
- cacheResponse.environment = jobJob.hash;
61043
- objectCache.add(cacheResponse, contents);
61044
- }
61045
- toSend.forEach((x) => {
61046
- if (x.contents && x.contents.byteLength) {
61047
- jobJob.send(x.contents);
61150
+ return ret;
61151
+ }),
61152
+ success: event.success,
61153
+ exitCode: event.exitCode,
61154
+ sha1: jobJob.sha1,
61155
+ // Just the basename. This goes into the object cache and is
61156
+ // replayed on every hit, so it has to still mean something
61157
+ // later: the /compiles/<id> directory belongs to this one
61158
+ // job's chroot, and the requesting client's own path belongs
61159
+ // to whichever client happened to compile it first. Only the
61160
+ // file name survives being shared.
61161
+ sourcePath: j.op.sourceFileName,
61162
+ stderr: j.stderr,
61163
+ stdout: j.stdout
61164
+ };
61165
+ if (event.error) {
61166
+ response.error = event.error;
61048
61167
  }
61049
- });
61050
- // console.log("GOT ID", j);
61051
- assert__default["default"](uploadDuration !== undefined, "Must have uploadDuration");
61052
- if (event.success) {
61053
- client.send("jobFinished", {
61054
- id: j.id,
61055
- cppSize: event.cppSize,
61056
- compileDuration: event.compileDuration,
61057
- compileSpeed: event.cppSize / event.compileDuration,
61058
- uploadDuration: uploadDuration,
61059
- uploadSpeed: event.cppSize / uploadDuration
61060
- });
61061
- }
61062
- else {
61063
- client.send("jobAborted", {
61064
- id: j.id,
61065
- cppSize: event.cppSize,
61066
- compileDuration: event.compileDuration,
61067
- compileSpeed: event.cppSize / event.compileDuration,
61068
- uploadDuration: uploadDuration,
61069
- uploadSpeed: event.cppSize / uploadDuration
61168
+ if (debug) {
61169
+ console.log("Sending response", jobJob.ip, jobJob.hostname, response);
61170
+ }
61171
+ jobJob.send(response);
61172
+ if (response.exitCode === 0 &&
61173
+ event.success &&
61174
+ objectCache &&
61175
+ response.sha1 &&
61176
+ objectCache.state(response.sha1) === "none") {
61177
+ // Cache metadata needs to reflect compressed sizes since we store compressed
61178
+ const cacheResponse = Object.assign(Object.assign({}, response), { index: contents.map((item) => {
61179
+ assert__default["default"](item.uncompressed, "Must have uncompressed data");
61180
+ return {
61181
+ path: item.path,
61182
+ bytes: item.contents.length,
61183
+ uncompressedSize: item.uncompressed.byteLength
61184
+ };
61185
+ }) });
61186
+ cacheResponse.commandLine = jobJob.commandLine;
61187
+ cacheResponse.environment = jobJob.hash;
61188
+ objectCache.add(cacheResponse, contents);
61189
+ }
61190
+ toSend.forEach((x) => {
61191
+ if (x.contents && x.contents.byteLength) {
61192
+ jobJob.send(x.contents);
61193
+ }
61070
61194
  });
61195
+ // console.log("GOT ID", j);
61196
+ assert__default["default"](uploadDuration !== undefined, "Must have uploadDuration");
61197
+ if (event.success) {
61198
+ client.send("jobFinished", {
61199
+ id: j.id,
61200
+ cppSize: event.cppSize,
61201
+ compileDuration: event.compileDuration,
61202
+ compileSpeed: event.cppSize / event.compileDuration,
61203
+ uploadDuration: uploadDuration,
61204
+ uploadSpeed: event.cppSize / uploadDuration
61205
+ });
61206
+ }
61207
+ else {
61208
+ client.send("jobAborted", {
61209
+ id: j.id,
61210
+ cppSize: event.cppSize,
61211
+ compileDuration: event.compileDuration,
61212
+ compileSpeed: event.cppSize / event.compileDuration,
61213
+ uploadDuration: uploadDuration,
61214
+ uploadSpeed: event.cppSize / uploadDuration
61215
+ });
61216
+ }
61071
61217
  }
61072
- startPending();
61073
61218
  });
61074
61219
  },
61075
61220
  cancel: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andersbakken/fisk",
3
- "version": "5.0.18",
3
+ "version": "5.0.19",
4
4
  "description": "Fisk, a distributed compile system",
5
5
  "scripts": {
6
6
  "lint": "eslint . --ext .ts",
@@ -3843,7 +3843,7 @@ class SHA1Data {
3843
3843
  constructor(fileSize, node) {
3844
3844
  this.fileSize = fileSize;
3845
3845
  this.fileSize = fileSize;
3846
- this.nodes = [node];
3846
+ this.nodes = new Set([node]);
3847
3847
  }
3848
3848
  }
3849
3849
 
@@ -3974,10 +3974,8 @@ function prettySize(bytes) {
3974
3974
  function addToSHA1Map(bySHA1, sha1, fileSize, node) {
3975
3975
  const data = bySHA1.get(sha1);
3976
3976
  if (data) {
3977
- if (data.nodes.indexOf(node) === -1) {
3978
- data.nodes.push(node);
3979
- }
3980
- return data.nodes.length;
3977
+ data.nodes.add(node);
3978
+ return data.nodes.size;
3981
3979
  }
3982
3980
  bySHA1.set(sha1, new SHA1Data(fileSize, node));
3983
3981
  return 1;
@@ -3985,10 +3983,8 @@ function addToSHA1Map(bySHA1, sha1, fileSize, node) {
3985
3983
  function removeFromSHA1Map(bySHA1, sha1, node) {
3986
3984
  const data = bySHA1.get(sha1);
3987
3985
  if (data) {
3988
- const idx = data.nodes.indexOf(node);
3989
- if (idx !== -1) {
3990
- data.nodes.splice(idx, 1);
3991
- if (data.nodes.length === 0) {
3986
+ if (data.nodes.delete(node)) {
3987
+ if (data.nodes.size === 0) {
3992
3988
  bySHA1.delete(sha1);
3993
3989
  }
3994
3990
  }
@@ -4009,7 +4005,8 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4009
4005
  this.hits = 0;
4010
4006
  this.bySHA1 = new Map();
4011
4007
  this.byNode = new Map();
4012
- this.pendingTransfers = new Set();
4008
+ this.pendingBySHA1 = new Map();
4009
+ this.pendingByNode = new Map();
4013
4010
  this.pendingTransferTimers = new Map();
4014
4011
  this.redundancy = option.int("object-cache-redundancy", 1);
4015
4012
  if (this.redundancy <= 0) {
@@ -4024,7 +4021,8 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4024
4021
  for (const timer of this.pendingTransferTimers.values()) {
4025
4022
  clearTimeout(timer);
4026
4023
  }
4027
- this.pendingTransfers.clear();
4024
+ this.pendingBySHA1.clear();
4025
+ this.pendingByNode.clear();
4028
4026
  this.pendingTransferTimers.clear();
4029
4027
  this.emit("cleared");
4030
4028
  }
@@ -4131,7 +4129,7 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4131
4129
  // console.log(key, value);
4132
4130
  sha1[key] = {
4133
4131
  fileSize: prettySize(value.fileSize),
4134
- nodes: value.nodes.map((node) => node.ip + ":" + node.port)
4132
+ nodes: Array.from(value.nodes, (node) => node.ip + ":" + node.port)
4135
4133
  };
4136
4134
  });
4137
4135
  ret.sha1 = sha1;
@@ -4171,11 +4169,15 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4171
4169
  return;
4172
4170
  }
4173
4171
  const pendingCount = this.pendingCountForSha1(sha);
4174
- const totalCopies = value.nodes.length + pendingCount;
4172
+ const totalCopies = value.nodes.size + pendingCount;
4175
4173
  const needed = Math.min(redundancy + 1 - totalCopies, this.byNode.size - 1);
4176
4174
  if (needed > 0) {
4177
4175
  let firstIdx;
4178
4176
  let found = 0;
4177
+ // Only materialised if we actually pick a source, since
4178
+ // most objects already have enough copies and never get
4179
+ // this far.
4180
+ let sources;
4179
4181
  while (found < needed) {
4180
4182
  if (++nodeIdx === nodes.length) {
4181
4183
  nodeIdx = 0;
@@ -4187,7 +4189,7 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4187
4189
  break;
4188
4190
  }
4189
4191
  const node = nodes[nodeIdx];
4190
- if (value.nodes.indexOf(node) !== -1) {
4192
+ if (value.nodes.has(node)) {
4191
4193
  continue;
4192
4194
  }
4193
4195
  if (this.hasPendingTransfer(sha, node)) {
@@ -4215,7 +4217,10 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4215
4217
  }
4216
4218
  ++found;
4217
4219
  data.available -= value.fileSize;
4218
- const src = value.nodes[roundRobinIndex++ % value.nodes.length];
4220
+ if (!sources) {
4221
+ sources = Array.from(value.nodes);
4222
+ }
4223
+ const src = sources[roundRobinIndex++ % sources.length];
4219
4224
  data.objects.push({ source: src.ip + ":" + src.port, sha1: sha });
4220
4225
  if (max !== undefined && !--max) {
4221
4226
  break;
@@ -4259,48 +4264,64 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
4259
4264
  }
4260
4265
  }
4261
4266
  addPendingTransfer(sha1, node) {
4267
+ let nodes = this.pendingBySHA1.get(sha1);
4268
+ if (!nodes) {
4269
+ nodes = new Set();
4270
+ this.pendingBySHA1.set(sha1, nodes);
4271
+ }
4272
+ nodes.add(node);
4273
+ let sha1s = this.pendingByNode.get(node);
4274
+ if (!sha1s) {
4275
+ sha1s = new Set();
4276
+ this.pendingByNode.set(node, sha1s);
4277
+ }
4278
+ sha1s.add(sha1);
4262
4279
  const key = pendingKey(sha1, node);
4263
- this.pendingTransfers.add(key);
4280
+ const existing = this.pendingTransferTimers.get(key);
4281
+ if (existing) {
4282
+ clearTimeout(existing);
4283
+ }
4264
4284
  const timer = setTimeout(() => {
4265
- this.pendingTransfers.delete(key);
4266
- this.pendingTransferTimers.delete(key);
4285
+ this.forgetPendingTransfer(sha1, node);
4267
4286
  }, this.pendingTransferTimeoutMs);
4268
4287
  timer.unref();
4269
4288
  this.pendingTransferTimers.set(key, timer);
4270
4289
  }
4271
4290
  clearPendingTransfer(sha1, node) {
4272
- const key = pendingKey(sha1, node);
4273
- this.pendingTransfers.delete(key);
4274
- const timer = this.pendingTransferTimers.get(key);
4291
+ const timer = this.pendingTransferTimers.get(pendingKey(sha1, node));
4275
4292
  if (timer) {
4276
4293
  clearTimeout(timer);
4277
- this.pendingTransferTimers.delete(key);
4278
4294
  }
4295
+ this.forgetPendingTransfer(sha1, node);
4296
+ }
4297
+ forgetPendingTransfer(sha1, node) {
4298
+ const nodes = this.pendingBySHA1.get(sha1);
4299
+ if (nodes && nodes.delete(node) && nodes.size === 0) {
4300
+ this.pendingBySHA1.delete(sha1);
4301
+ }
4302
+ const sha1s = this.pendingByNode.get(node);
4303
+ if (sha1s && sha1s.delete(sha1) && sha1s.size === 0) {
4304
+ this.pendingByNode.delete(node);
4305
+ }
4306
+ this.pendingTransferTimers.delete(pendingKey(sha1, node));
4279
4307
  }
4280
4308
  clearAllPendingForNode(node) {
4281
- const suffix = ":" + node.ip + ":" + node.port;
4282
- for (const key of this.pendingTransfers) {
4283
- if (key.endsWith(suffix)) {
4284
- this.pendingTransfers.delete(key);
4285
- const timer = this.pendingTransferTimers.get(key);
4286
- if (timer) {
4287
- clearTimeout(timer);
4288
- this.pendingTransferTimers.delete(key);
4289
- }
4290
- }
4309
+ const sha1s = this.pendingByNode.get(node);
4310
+ if (!sha1s) {
4311
+ return;
4312
+ }
4313
+ for (const sha1 of Array.from(sha1s)) {
4314
+ this.clearPendingTransfer(sha1, node);
4291
4315
  }
4316
+ this.pendingByNode.delete(node);
4292
4317
  }
4293
4318
  pendingCountForSha1(sha1) {
4294
- let count = 0;
4295
- for (const key of this.pendingTransfers) {
4296
- if (key.startsWith(sha1 + ":")) {
4297
- ++count;
4298
- }
4299
- }
4300
- return count;
4319
+ var _a, _b;
4320
+ return (_b = (_a = this.pendingBySHA1.get(sha1)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0;
4301
4321
  }
4302
4322
  hasPendingTransfer(sha1, node) {
4303
- return this.pendingTransfers.has(pendingKey(sha1, node));
4323
+ var _a, _b;
4324
+ return (_b = (_a = this.pendingBySHA1.get(sha1)) === null || _a === void 0 ? void 0 : _a.has(node)) !== null && _b !== void 0 ? _b : false;
4304
4325
  }
4305
4326
  }
4306
4327
 
@@ -4621,6 +4642,97 @@ class DaemonConnection extends Client {
4621
4642
  }
4622
4643
  }
4623
4644
 
4645
+ const Version = 5;
4646
+ // 6: objects now carry the client's real source path and compilation dir,
4647
+ // baked in at compile time instead of the builder's /compiles paths, and the
4648
+ // stored response no longer keeps the paths the client used to patch with.
4649
+ const ObjectCacheFormatVersion = 6;
4650
+ function cacheDir(option) {
4651
+ let dir = option("cache-dir");
4652
+ if (!dir) {
4653
+ dir = path__default["default"].join(require$$1__default$1["default"].homedir(), ".cache", "fisk", path__default["default"].basename(option.prefix || ""));
4654
+ }
4655
+ return dir;
4656
+ }
4657
+ function validateCache(option) {
4658
+ const dir = cacheDir(option);
4659
+ const file = path__default["default"].join(dir, "version");
4660
+ // console.log(dir);
4661
+ let version;
4662
+ try {
4663
+ version = fs$4.readFileSync(file);
4664
+ if (version.readUInt32BE() === Version) {
4665
+ return;
4666
+ }
4667
+ }
4668
+ catch (err) {
4669
+ /* */
4670
+ }
4671
+ if (version) {
4672
+ console.log(`Wrong version. Destroying cache ${dir}`);
4673
+ }
4674
+ fs$4.removeSync(dir);
4675
+ fs$4.mkdirpSync(dir);
4676
+ const buf = Buffer.allocUnsafe(4);
4677
+ buf.writeUInt32BE(Version);
4678
+ fs$4.writeFileSync(file, buf);
4679
+ }
4680
+ function validateObjectCache(option) {
4681
+ const dir = cacheDir(option);
4682
+ const objectCacheDir = option.string("object-cache-dir") || path__default["default"].join(dir, "objectcache");
4683
+ const file = path__default["default"].join(objectCacheDir, "version");
4684
+ let version;
4685
+ try {
4686
+ version = fs$4.readFileSync(file);
4687
+ if (version.readUInt32BE() === ObjectCacheFormatVersion) {
4688
+ return;
4689
+ }
4690
+ }
4691
+ catch (err) {
4692
+ /* */
4693
+ }
4694
+ if (version) {
4695
+ console.log(`Wrong object cache version. Destroying object cache ${objectCacheDir}`);
4696
+ }
4697
+ fs$4.removeSync(objectCacheDir);
4698
+ fs$4.mkdirpSync(objectCacheDir);
4699
+ const buf = Buffer.allocUnsafe(4);
4700
+ buf.writeUInt32BE(ObjectCacheFormatVersion);
4701
+ fs$4.writeFileSync(file, buf);
4702
+ }
4703
+ // The listen backlog is the depth of the kernel's accept queue. When it fills
4704
+ // -- which is what happens whenever the event loop stalls long enough to stop
4705
+ // calling accept() -- Linux does not refuse the connection, it silently drops
4706
+ // the SYN, and the client's first retransmit is a second later. A client with a
4707
+ // sub-second handshake budget sees that as a timeout with no server-side trace.
4708
+ // listen(2) clamps to net.core.somaxconn, so following somaxconn is both the
4709
+ // largest useful value and the one an operator can actually tune.
4710
+ function defaultBacklog() {
4711
+ try {
4712
+ return parseInt(fs$4.readFileSync("/proc/sys/net/core/somaxconn", "utf8")) || 511;
4713
+ }
4714
+ catch (err) {
4715
+ // Not Linux, or procfs isn't mounted. 511 is node's own default.
4716
+ return 511;
4717
+ }
4718
+ }
4719
+ // Only the builder keeps an object cache on disk. The scheduler tracks which
4720
+ // builder holds which sha1 in memory, and the daemon uses cacheDir purely for
4721
+ // the default socket path -- validating an object cache for either created a
4722
+ // directory they never read and, on a format bump, tried to destroy one they do
4723
+ // not necessarily own.
4724
+ function common$1(option, hasObjectCache = false) {
4725
+ validateCache(option);
4726
+ if (hasObjectCache) {
4727
+ validateObjectCache(option);
4728
+ }
4729
+ return {
4730
+ cacheDir: cacheDir.bind(undefined, option),
4731
+ Version,
4732
+ ObjectCacheFormatVersion
4733
+ };
4734
+ }
4735
+
4624
4736
  /**
4625
4737
  * Check if we're required to add a port number.
4626
4738
  *
@@ -56177,14 +56289,7 @@ class Server extends require$$0__default$2["default"] {
56177
56289
  return;
56178
56290
  }
56179
56291
  }
56180
- let defaultBacklog = 128;
56181
- try {
56182
- defaultBacklog = parseInt(fs__default["default"].readFileSync("/proc/sys/net/core/somaxconn", "utf8")) || 128;
56183
- }
56184
- catch (err) {
56185
- /* */
56186
- }
56187
- const backlog = this.option.int("backlog", defaultBacklog);
56292
+ const backlog = this.option.int("backlog", defaultBacklog());
56188
56293
  this.ws = new ws.Server({ noServer: true });
56189
56294
  let waitingServers = 1;
56190
56295
  const port = this.option.int("port", 8097);
@@ -56639,81 +56744,6 @@ class Server extends require$$0__default$2["default"] {
56639
56744
  }
56640
56745
  }
56641
56746
 
56642
- const Version = 5;
56643
- // 6: objects now carry the client's real source path and compilation dir,
56644
- // baked in at compile time instead of the builder's /compiles paths, and the
56645
- // stored response no longer keeps the paths the client used to patch with.
56646
- const ObjectCacheFormatVersion = 6;
56647
- function cacheDir(option) {
56648
- let dir = option("cache-dir");
56649
- if (!dir) {
56650
- dir = path__default["default"].join(require$$1__default$1["default"].homedir(), ".cache", "fisk", path__default["default"].basename(option.prefix || ""));
56651
- }
56652
- return dir;
56653
- }
56654
- function validateCache(option) {
56655
- const dir = cacheDir(option);
56656
- const file = path__default["default"].join(dir, "version");
56657
- // console.log(dir);
56658
- let version;
56659
- try {
56660
- version = fs$4.readFileSync(file);
56661
- if (version.readUInt32BE() === Version) {
56662
- return;
56663
- }
56664
- }
56665
- catch (err) {
56666
- /* */
56667
- }
56668
- if (version) {
56669
- console.log(`Wrong version. Destroying cache ${dir}`);
56670
- }
56671
- fs$4.removeSync(dir);
56672
- fs$4.mkdirpSync(dir);
56673
- const buf = Buffer.allocUnsafe(4);
56674
- buf.writeUInt32BE(Version);
56675
- fs$4.writeFileSync(file, buf);
56676
- }
56677
- function validateObjectCache(option) {
56678
- const dir = cacheDir(option);
56679
- const objectCacheDir = option.string("object-cache-dir") || path__default["default"].join(dir, "objectcache");
56680
- const file = path__default["default"].join(objectCacheDir, "version");
56681
- let version;
56682
- try {
56683
- version = fs$4.readFileSync(file);
56684
- if (version.readUInt32BE() === ObjectCacheFormatVersion) {
56685
- return;
56686
- }
56687
- }
56688
- catch (err) {
56689
- /* */
56690
- }
56691
- if (version) {
56692
- console.log(`Wrong object cache version. Destroying object cache ${objectCacheDir}`);
56693
- }
56694
- fs$4.removeSync(objectCacheDir);
56695
- fs$4.mkdirpSync(objectCacheDir);
56696
- const buf = Buffer.allocUnsafe(4);
56697
- buf.writeUInt32BE(ObjectCacheFormatVersion);
56698
- fs$4.writeFileSync(file, buf);
56699
- }
56700
- // Only the builder keeps an object cache on disk. The scheduler tracks which
56701
- // builder holds which sha1 in memory, and the daemon uses cacheDir purely for
56702
- // the default socket path -- validating an object cache for either created a
56703
- // directory they never read and, on a format bump, tried to destroy one they do
56704
- // not necessarily own.
56705
- function common$1(option, hasObjectCache = false) {
56706
- validateCache(option);
56707
- if (hasObjectCache) {
56708
- validateObjectCache(option);
56709
- }
56710
- return {
56711
- cacheDir: cacheDir.bind(undefined, option),
56712
- Version,
56713
- ObjectCacheFormatVersion
56714
- };
56715
- }
56716
-
56717
56747
  var bytesExports = requireBytes();
56718
56748
  var bytes = /*@__PURE__*/getDefaultExportFromCjs(bytesExports);
56719
56749