@jeffusion/bungee 6.11.2 → 6.12.0

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.
Files changed (2) hide show
  1. package/dist/index.js +82 -47
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -11960,7 +11960,7 @@ var require_winston = __commonJS(function(exports) {
11960
11960
 
11961
11961
  // ../../node_modules/object-hash/index.js
11962
11962
  var require_object_hash = __commonJS(function(exports, module) {
11963
- var crypto = __require("crypto");
11963
+ var crypto2 = __require("crypto");
11964
11964
  exports = module.exports = objectHash;
11965
11965
  function objectHash(object, options) {
11966
11966
  options = applyDefaults(object, options);
@@ -11978,7 +11978,7 @@ var require_object_hash = __commonJS(function(exports, module) {
11978
11978
  exports.keysMD5 = function(object) {
11979
11979
  return objectHash(object, { algorithm: "md5", encoding: "hex", excludeValues: true });
11980
11980
  };
11981
- var hashes = crypto.getHashes ? crypto.getHashes().slice() : ["sha1", "md5"];
11981
+ var hashes = crypto2.getHashes ? crypto2.getHashes().slice() : ["sha1", "md5"];
11982
11982
  hashes.push("passthrough");
11983
11983
  var encodings = ["buffer", "hex", "binary", "base64"];
11984
11984
  function applyDefaults(object, sourceOptions) {
@@ -12024,7 +12024,7 @@ var require_object_hash = __commonJS(function(exports, module) {
12024
12024
  function hash(object, options) {
12025
12025
  var hashingStream;
12026
12026
  if (options.algorithm !== "passthrough") {
12027
- hashingStream = crypto.createHash(options.algorithm);
12027
+ hashingStream = crypto2.createHash(options.algorithm);
12028
12028
  } else {
12029
12029
  hashingStream = new PassThrough;
12030
12030
  }
@@ -16112,7 +16112,7 @@ var require_FileStreamRotator = __commonJS(function(exports, module) {
16112
16112
  var fs = __require("fs");
16113
16113
  var path = __require("path");
16114
16114
  var moment = require_moment();
16115
- var crypto = __require("crypto");
16115
+ var crypto2 = __require("crypto");
16116
16116
  var EventEmitter = __require("events");
16117
16117
  var FileStreamRotator = {};
16118
16118
  module.exports = FileStreamRotator;
@@ -16241,7 +16241,7 @@ var require_FileStreamRotator = __commonJS(function(exports, module) {
16241
16241
  }
16242
16242
  };
16243
16243
  function removeFile(file, verbose) {
16244
- if (file.hash === crypto.createHash(file.hashType).update(file.name + "LOG_FILE" + file.date).digest("hex")) {
16244
+ if (file.hash === crypto2.createHash(file.hashType).update(file.name + "LOG_FILE" + file.date).digest("hex")) {
16245
16245
  try {
16246
16246
  if (fs.existsSync(file.name)) {
16247
16247
  fs.unlinkSync(file.name);
@@ -16308,7 +16308,7 @@ var require_FileStreamRotator = __commonJS(function(exports, module) {
16308
16308
  audit.files.push({
16309
16309
  date: time,
16310
16310
  name: logfile,
16311
- hash: crypto.createHash(audit.hashType).update(logfile + "LOG_FILE" + time).digest("hex")
16311
+ hash: crypto2.createHash(audit.hashType).update(logfile + "LOG_FILE" + time).digest("hex")
16312
16312
  });
16313
16313
  if (audit.keep.days) {
16314
16314
  var oldestDate = moment().subtract(audit.keep.amount, "days").valueOf();
@@ -17800,7 +17800,7 @@ function formatDaemonFileAclError(error) {
17800
17800
  return `acl_operation=${operation} outcome=${outcome}` + ` last_phase=${lastPhase} kill_returned_true=${diagnostic.kill_returned_true === true}` + ` spawn_event=${diagnostic.spawn_event === true} exit_event=${diagnostic.exit_event === true}` + ` close_event=${diagnostic.close_event === true}` + ` target_kind=${targetKind} exit_code=${exitCode}`;
17801
17801
  }
17802
17802
  function boundedElapsed(startedAt, deadlineMs) {
17803
- return Math.min(deadlineMs, Math.max(0, Date.now() - startedAt));
17803
+ return Math.min(deadlineMs, Math.max(0, Math.round(performance.now() - startedAt)));
17804
17804
  }
17805
17805
  function selectWindowsAclExecutable(environment) {
17806
17806
  const programFiles = environment.ProgramFiles;
@@ -17834,7 +17834,7 @@ function processDiagnostic(operation, startedAt, phase, environment, deadlineMs,
17834
17834
  };
17835
17835
  }
17836
17836
  async function runPowerShell(operation, script, environment, deadlineMs) {
17837
- const startedAt = Date.now();
17837
+ const startedAt = performance.now();
17838
17838
  let phase = null;
17839
17839
  let child;
17840
17840
  try {
@@ -21683,12 +21683,10 @@ async function upgradeCommand(options = {}) {
21683
21683
  }
21684
21684
 
21685
21685
  // src/commands/config-io.ts
21686
- var OPERATION_TIMEOUT_MS = 15000;
21687
- var OPERATION_POLL_INTERVAL_MS = 100;
21688
21686
  function endpoint(options, path) {
21689
21687
  const host = options.host ?? "localhost";
21690
21688
  const port = options.port ?? DEFAULT_MANAGEMENT_PORT;
21691
- return `http://${host}:${port}/api/config/${path}`;
21689
+ return `http://${host}:${port}/api/config${path === "" ? "" : `/${path}`}`;
21692
21690
  }
21693
21691
  async function writeFile(path, content) {
21694
21692
  await Bun.write(path, content);
@@ -21698,6 +21696,31 @@ function headers(token) {
21698
21696
  ...token === undefined ? {} : { authorization: `Bearer ${token}` }
21699
21697
  };
21700
21698
  }
21699
+ function acknowledgement(value) {
21700
+ if (typeof value !== "object" || value === null)
21701
+ return null;
21702
+ const { operation_id, revision } = value;
21703
+ if (typeof operation_id !== "string" || operation_id.length === 0 || typeof revision !== "number" || !Number.isSafeInteger(revision) || revision <= 0)
21704
+ return null;
21705
+ return { operation_id, revision };
21706
+ }
21707
+ function currentRevision(value) {
21708
+ if (typeof value !== "object" || value === null)
21709
+ return null;
21710
+ const revision = value.revision;
21711
+ return typeof revision === "number" && Number.isSafeInteger(revision) && revision > 0 ? revision : null;
21712
+ }
21713
+ function unknownOutcome(mutationId, cause) {
21714
+ return new Error(`Import result is unknown; check whether the configuration was saved before retrying. Reference ${mutationId}`, { cause });
21715
+ }
21716
+ function reportedResultUnknown(body) {
21717
+ try {
21718
+ const value = JSON.parse(body);
21719
+ return value.error === "result_unknown";
21720
+ } catch {
21721
+ return false;
21722
+ }
21723
+ }
21701
21724
  async function exportCommand(options) {
21702
21725
  const response = await fetch(endpoint(options, "export"), {
21703
21726
  headers: headers(options.token)
@@ -21711,52 +21734,64 @@ async function exportCommand(options) {
21711
21734
  }
21712
21735
  async function importCommand(options) {
21713
21736
  const snapshot = await Bun.file(options.file).text();
21737
+ let envelope;
21738
+ try {
21739
+ envelope = JSON.parse(snapshot);
21740
+ } catch (cause) {
21741
+ throw new Error(`Import failed: ${options.file} is not valid JSON`, { cause });
21742
+ }
21743
+ const currentResponse = await fetch(endpoint(options, ""), {
21744
+ headers: headers(options.token)
21745
+ });
21746
+ if (!currentResponse.ok) {
21747
+ const body = await currentResponse.text();
21748
+ throw new Error(`Import failed: cannot read current revision: ${currentResponse.status} ${body}`);
21749
+ }
21750
+ let current;
21751
+ try {
21752
+ current = await currentResponse.json();
21753
+ } catch (cause) {
21754
+ throw new Error("Import failed: current revision response is invalid", { cause });
21755
+ }
21756
+ const expectedRevision = currentRevision(current);
21757
+ if (expectedRevision === null) {
21758
+ throw new Error("Import failed: current revision response is invalid");
21759
+ }
21760
+ const mutationId = crypto.randomUUID();
21714
21761
  const response = await fetch(endpoint(options, "import"), {
21715
21762
  method: "POST",
21716
21763
  headers: { ...headers(options.token), "content-type": "application/json" },
21717
- body: snapshot
21764
+ body: JSON.stringify({ expected_revision: expectedRevision, mutation_id: mutationId, envelope })
21765
+ }).catch((cause) => {
21766
+ throw unknownOutcome(mutationId, cause);
21718
21767
  });
21719
21768
  if (!response.ok) {
21720
- const body = await response.text();
21769
+ let body;
21770
+ try {
21771
+ body = await response.text();
21772
+ } catch (cause) {
21773
+ throw unknownOutcome(mutationId, cause);
21774
+ }
21775
+ if (reportedResultUnknown(body))
21776
+ throw unknownOutcome(mutationId);
21721
21777
  throw new Error(`Import failed: ${response.status} ${body}`);
21722
21778
  }
21723
- const accepted = await response.json();
21724
- const deadline = Date.now() + OPERATION_TIMEOUT_MS;
21725
- let state = accepted;
21726
- while (true) {
21727
- switch (state.operation.state) {
21728
- case "converged":
21729
- console.log(`Config imported from ${options.file}: revision ${accepted.revision} operation ${accepted.operation_id}`);
21730
- return;
21731
- case "degraded":
21732
- throw new Error(`Import failed: operation ${accepted.operation_id} degraded: ${state.operation.error_detail}`);
21733
- case "committed":
21734
- case "publishing":
21735
- case "draining":
21736
- break;
21737
- }
21738
- const remainingMs = deadline - Date.now();
21739
- if (remainingMs <= 0) {
21740
- throw new Error(`Import failed: operation ${accepted.operation_id} timed out after ${OPERATION_TIMEOUT_MS}ms`);
21741
- }
21742
- const poll = await fetch(endpoint(options, `operations/${accepted.operation_id}`), {
21743
- headers: headers(options.token),
21744
- signal: AbortSignal.timeout(remainingMs)
21745
- });
21746
- if (!poll.ok) {
21747
- const body = await poll.text();
21748
- throw new Error(`Import failed: operation poll returned ${poll.status} ${body}`);
21749
- }
21750
- state = await poll.json();
21751
- if (state.operation.result_status === null) {
21752
- await Bun.sleep(Math.min(OPERATION_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
21753
- }
21779
+ let value;
21780
+ try {
21781
+ value = await response.json();
21782
+ } catch (cause) {
21783
+ throw unknownOutcome(mutationId, cause);
21784
+ }
21785
+ const accepted = acknowledgement(value);
21786
+ if (accepted === null || accepted.operation_id !== mutationId) {
21787
+ throw unknownOutcome(mutationId);
21754
21788
  }
21789
+ console.log(`Config saved from ${options.file}`);
21755
21790
  }
21756
21791
  // package.json
21757
21792
  var package_default = {
21758
21793
  name: "@jeffusion/bungee",
21759
- version: "6.11.2",
21794
+ version: "6.12.0",
21760
21795
  description: "High-performance reverse proxy server CLI",
21761
21796
  type: "module",
21762
21797
  bin: {
@@ -21922,8 +21957,8 @@ Usage counts are replacement totals for the specified attempt, applied to its or
21922
21957
  program.command("status").description("Show daemon status and health").action(statusCommand);
21923
21958
  program.command("logs").description("Show daemon logs").option("-f, --follow", "Follow log output").option("-n, --lines <number>", "Number of lines to show", "50").action(logsCommand);
21924
21959
  program.command("ui").description("Open web dashboard from the Management server").option("-p, --port <port>", "Management server port", DEFAULT_MANAGEMENT_PORT).option("-H, --host <host>", "Management server host", "localhost").action(uiCommand);
21925
- program.command("export").description("Export current configuration from the Management server as a versioned snapshot JSON").requiredOption("-o, --file <path>", "Output file path").option("-p, --port <port>", "Management server port", DEFAULT_MANAGEMENT_PORT).option("-H, --host <host>", "Management server host", "localhost").option("-t, --token <token>", "Management auth token").action(exportCommand);
21926
- program.command("import").description("Restore configuration on the Management server from a versioned snapshot JSON").requiredOption("-f, --file <path>", "Snapshot file path").option("-p, --port <port>", "Management server port", DEFAULT_MANAGEMENT_PORT).option("-H, --host <host>", "Management server host", "localhost").option("-t, --token <token>", "Management auth token").action(importCommand);
21960
+ program.command("export").description("Export the current configuration from the Management server as a JSON file").requiredOption("-o, --file <path>", "Output file path").option("-p, --port <port>", "Management server port", DEFAULT_MANAGEMENT_PORT).option("-H, --host <host>", "Management server host", "localhost").option("-t, --token <token>", "Management auth token").action(exportCommand);
21961
+ program.command("import").description("Save a configuration file on the Management server").requiredOption("-f, --file <path>", "Configuration file path").option("-p, --port <port>", "Management server port", DEFAULT_MANAGEMENT_PORT).option("-H, --host <host>", "Management server host", "localhost").option("-t, --token <token>", "Management auth token").action(importCommand);
21927
21962
  program.command("upgrade").description("Upgrade Bungee binary to the latest version").option("-f, --force", "Force re-download even if already up to date").action(upgradeCommand);
21928
21963
  return program;
21929
21964
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffusion/bungee",
3
- "version": "6.11.2",
3
+ "version": "6.12.0",
4
4
  "description": "High-performance reverse proxy server CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,8 +13,8 @@
13
13
  "test": "bun test"
14
14
  },
15
15
  "dependencies": {
16
- "@jeffusion/bungee-core": "^6.11.2",
17
- "@jeffusion/bungee-types": "^6.11.2",
16
+ "@jeffusion/bungee-core": "^6.12.0",
17
+ "@jeffusion/bungee-types": "^6.12.0",
18
18
  "cli-progress": "^3.12.0",
19
19
  "commander": "^14.0.1"
20
20
  },