@jeffusion/bungee-core 5.6.0 → 5.6.1

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/main.js +163 -71
  2. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -8126,10 +8126,10 @@ function parseNormalizeCompileAggregate(input, options) {
8126
8126
  if (!hasLogicalConfiguration) {
8127
8127
  context.add("required", "logical_configuration", "Required field is missing");
8128
8128
  }
8129
- const pluginActivations = parsePluginActivations(root.plugin_activations, "plugin_activations" in root, context, options?.availablePlugins ?? (options ? new Set(options.pluginSchemas.keys()) : undefined));
8129
+ let pluginActivations = parsePluginActivations(root.plugin_activations, "plugin_activations" in root, context, options?.availablePlugins ?? (options ? new Set(options.pluginSchemas.keys()) : undefined));
8130
8130
  if (options?.pluginDependencies) {
8131
8131
  try {
8132
- options.pluginDependencies.assertClosed(pluginActivations.map(({ plugin_name }) => plugin_name));
8132
+ pluginActivations = options.pluginDependencies.closure(pluginActivations.map(({ plugin_name }) => plugin_name)).map((plugin_name) => ({ plugin_name })).sort(comparePluginNames);
8133
8133
  } catch (error) {
8134
8134
  context.add("invalid_value", "plugin_activations", error instanceof Error ? error.message : String(error));
8135
8135
  }
@@ -12455,7 +12455,7 @@ class PluginDependencyGraph {
12455
12455
  }
12456
12456
  }
12457
12457
  function updatePluginActivations(graph, activated, name, enable) {
12458
- const active = new Set(activated);
12458
+ const active = new Set(graph.closure(activated));
12459
12459
  graph.dependenciesOf(name);
12460
12460
  if (enable) {
12461
12461
  for (const dependency of graph.closure([name]))
@@ -12479,7 +12479,7 @@ var package_default;
12479
12479
  var init_package = __esm(() => {
12480
12480
  package_default = {
12481
12481
  name: "@jeffusion/bungee-core",
12482
- version: "5.6.0",
12482
+ version: "5.6.1",
12483
12483
  description: "Bungee reverse proxy core engine",
12484
12484
  main: "./dist/main.js",
12485
12485
  types: "./dist/main.d.ts",
@@ -222853,10 +222853,38 @@ var init_drain_workers = __esm(() => {
222853
222853
  function validStatus(value) {
222854
222854
  return typeof value.retired_id === "string" && RETIRED_ID.test(value.retired_id) && Number.isSafeInteger(value.pending) && value.pending >= 0 && typeof value.complete === "boolean" && Number.isSafeInteger(value.remaining_ms) && value.remaining_ms >= 0 && (!value.complete || value.pending === 0);
222855
222855
  }
222856
+ function waitBeforePoll(scheduler, delay, signal) {
222857
+ return new Promise((resolve) => {
222858
+ let settled = false;
222859
+ let timeout;
222860
+ const finish = (value) => {
222861
+ if (settled)
222862
+ return;
222863
+ settled = true;
222864
+ bestEffort(() => timeout?.cancel());
222865
+ signal?.removeEventListener("abort", abort);
222866
+ resolve(value);
222867
+ };
222868
+ const abort = () => finish("cancelled");
222869
+ signal?.addEventListener("abort", abort, { once: true });
222870
+ if (signal?.aborted) {
222871
+ abort();
222872
+ return;
222873
+ }
222874
+ try {
222875
+ timeout = scheduler.schedule(delay, () => finish("poll"));
222876
+ } catch {
222877
+ finish("unavailable");
222878
+ }
222879
+ if (settled)
222880
+ bestEffort(() => timeout?.cancel());
222881
+ });
222882
+ }
222856
222883
  async function waitForAdmissionHandoff(prepared, scheduler, hasRetiredWorkers, signal) {
222857
222884
  if (!hasRetiredWorkers && prepared.handoffStatus === undefined)
222858
222885
  return { kind: "complete" };
222859
222886
  let retiredId = null;
222887
+ let statusFailures = 0;
222860
222888
  while (true) {
222861
222889
  if (signal?.aborted)
222862
222890
  return { kind: "unknown", reason: "cancelled" };
@@ -222873,26 +222901,19 @@ async function waitForAdmissionHandoff(prepared, scheduler, hasRetiredWorkers, s
222873
222901
  return { kind: "complete" };
222874
222902
  if (status.remaining_ms === 0)
222875
222903
  return { kind: "unknown", reason: "expired" };
222876
- const resumed = await new Promise((resolve) => {
222877
- let settled = false;
222878
- const finish = (value) => {
222879
- if (settled)
222880
- return;
222881
- settled = true;
222882
- timeout.cancel();
222883
- signal?.removeEventListener("abort", abort);
222884
- resolve(value);
222885
- };
222886
- const timeout = scheduler.schedule(Math.max(1, Math.min(100, status.remaining_ms)), () => finish(true));
222887
- const abort = () => finish(false);
222888
- signal?.addEventListener("abort", abort, { once: true });
222889
- if (signal?.aborted)
222890
- abort();
222891
- });
222892
- if (!resumed)
222904
+ const resumed = await waitBeforePoll(scheduler, Math.max(1, Math.min(100, status.remaining_ms)), signal);
222905
+ if (resumed !== "poll")
222906
+ return { kind: "unknown", reason: resumed };
222907
+ } catch (error) {
222908
+ if (signal?.aborted)
222893
222909
  return { kind: "unknown", reason: "cancelled" };
222894
- } catch {
222895
- return { kind: "unknown", reason: signal?.aborted ? "cancelled" : "unavailable" };
222910
+ const code = typeof error === "object" && error !== null ? error.code : undefined;
222911
+ const retryable = code === undefined || ["network", "timeout", "unavailable", "control_recovering", "outcome_unknown"].includes(String(code));
222912
+ if (!retryable || ++statusFailures > 2)
222913
+ return { kind: "unknown", reason: "unavailable" };
222914
+ const resumed = await waitBeforePoll(scheduler, 100, signal);
222915
+ if (resumed !== "poll")
222916
+ return { kind: "unknown", reason: resumed };
222896
222917
  }
222897
222918
  }
222898
222919
  }
@@ -223026,14 +223047,15 @@ function command(active, attempt, pluginCatalogHash) {
223026
223047
  function failureDetail(failures) {
223027
223048
  return failures.map(({ slot, code }) => `${slot}:${code}`).join(", ").slice(0, 512) || "publication failed";
223028
223049
  }
223029
- function publicationPhase(options, active, phase, boundary) {
223050
+ function publicationPhase(options, active, phase, boundary, handoffReason) {
223030
223051
  try {
223031
223052
  (options.stderr ?? process.stderr).write(`${JSON.stringify({
223032
223053
  event: "publication_phase",
223033
223054
  phase,
223034
223055
  boundary,
223035
223056
  mutation_id: active.operation.mutation_id,
223036
- revision: active.snapshot.revision
223057
+ revision: active.snapshot.revision,
223058
+ ...handoffReason === undefined ? {} : { handoff_reason: handoffReason }
223037
223059
  })}
223038
223060
  `);
223039
223061
  } catch {}
@@ -223324,10 +223346,22 @@ async function runPublication(options, active, oldWorkers) {
223324
223346
  }
223325
223347
  throw error;
223326
223348
  }
223327
- const handoff = await waitForAdmissionHandoff(preparedAdmission, options.scheduler, oldWorkers.length > 0, options.signal);
223349
+ publicationPhase(options, refreshed, "admission.handoff", "enter");
223350
+ let handoff;
223351
+ try {
223352
+ handoff = await waitForAdmissionHandoff(preparedAdmission, options.scheduler, oldWorkers.length > 0, options.signal);
223353
+ } finally {
223354
+ publicationPhase(options, refreshed, "admission.handoff", "exit", handoff?.kind === "unknown" ? handoff.reason : undefined);
223355
+ }
223328
223356
  if (handoff.kind === "unknown")
223329
223357
  return retiredOutcomeUnknown(options, oldWorkers, new Error(`retired ingress handoff is unknown: ${handoff.reason}`));
223330
- const drainEvidence = await drainWorkersUntilKnown(oldWorkers, options.scheduler, publicationPolicy, () => throwIfPublicationCancelled(options.signal, admissionCommitMayHaveBeenSent));
223358
+ publicationPhase(options, refreshed, "drainWorkers", "enter");
223359
+ let drainEvidence;
223360
+ try {
223361
+ drainEvidence = await drainWorkersUntilKnown(oldWorkers, options.scheduler, publicationPolicy, () => throwIfPublicationCancelled(options.signal, admissionCommitMayHaveBeenSent));
223362
+ } finally {
223363
+ publicationPhase(options, refreshed, "drainWorkers", "exit");
223364
+ }
223331
223365
  throwIfPublicationCancelled(options.signal, admissionCommitMayHaveBeenSent);
223332
223366
  const failuresDuringDrain = drainFailures(drainEvidence);
223333
223367
  const recoveringWithoutOldOwnership = options.recoveringMaster && oldWorkers.length === 0 && await options.confirmPreviousWorkersExited?.(options.owned.serving()) !== true;
@@ -225434,7 +225468,7 @@ async function togglePluginActivation(request, pluginName, enable, options) {
225434
225468
  return json({ error: "unauthorized" }, 401);
225435
225469
  const activations = active.aggregate.plugin_activations;
225436
225470
  const activated = activations.some((activation) => activation.plugin_name === pluginName);
225437
- if (activated === enable)
225471
+ if (!options.pluginDependencies && activated === enable)
225438
225472
  return unchangedMutationResponse(options, active.revision);
225439
225473
  let nextActivations;
225440
225474
  if (options.pluginDependencies) {
@@ -225447,6 +225481,8 @@ async function togglePluginActivation(request, pluginName, enable, options) {
225447
225481
  } else {
225448
225482
  nextActivations = enable ? [...activations, { plugin_name: pluginName }].sort((left, right) => left.plugin_name.localeCompare(right.plugin_name)) : activations.filter((activation) => activation.plugin_name !== pluginName);
225449
225483
  }
225484
+ if (nextActivations.length === activations.length && nextActivations.every((value, index) => value.plugin_name === activations[index]?.plugin_name))
225485
+ return unchangedMutationResponse(options, active.revision);
225450
225486
  const next = { ...active.aggregate, plugin_activations: nextActivations };
225451
225487
  return commitConfigurationMutation(request, options, {
225452
225488
  active,
@@ -225481,12 +225517,15 @@ async function importConfig(request, options) {
225481
225517
  const active = activeResult;
225482
225518
  if (!await options.managementAuth?.recheck(request) || !matchesCurrentAuth(request, active, options))
225483
225519
  return json({ error: "unauthorized" }, 401);
225484
- const parsed = options.parseAggregate(envelope.aggregate);
225520
+ const exported = parseNormalizeCompileAggregate(envelope.aggregate);
225521
+ if (!exported.ok)
225522
+ return json({ error: "invalid_configuration", errors: exported.errors }, 422);
225523
+ if (envelope.content_hash !== hashConfigurationContent(exported.value))
225524
+ return json({ error: "invalid_snapshot" }, 400);
225525
+ const parsed = options.parseAggregate(exported.value);
225485
225526
  if (!parsed.ok)
225486
225527
  return json({ error: "invalid_configuration", errors: parsed.errors }, 422);
225487
225528
  freezeJson3(parsed.value);
225488
- if (envelope.content_hash !== hashConfigurationContent(parsed.value))
225489
- return json({ error: "invalid_snapshot" }, 400);
225490
225529
  return commitConfigurationMutation(request, options, {
225491
225530
  active,
225492
225531
  next: parsed.value,
@@ -225727,6 +225766,7 @@ var init_control_api = __esm(() => {
225727
225766
  init_config_storage();
225728
225767
  init_repository_validation();
225729
225768
  init_content_hash();
225769
+ init_aggregate();
225730
225770
  init_plugin_name();
225731
225771
  init_logger();
225732
225772
  init_error_chain();
@@ -228420,6 +228460,39 @@ var init_master_plugin_catalog_api = __esm(() => {
228420
228460
  ];
228421
228461
  });
228422
228462
 
228463
+ // src/master-runtime/reconcile-plugin-dependencies.ts
228464
+ import { randomUUID as randomUUID17 } from "crypto";
228465
+ function reconcilePluginDependencies(repository, options, workerCount, now, managementProviders) {
228466
+ const current = repository.getSnapshot();
228467
+ if (!options.pluginDependencies)
228468
+ return current;
228469
+ const required = options.pluginDependencies.closure(current.aggregate.plugin_activations.map((value) => value.plugin_name));
228470
+ if (required.length === current.aggregate.plugin_activations.length)
228471
+ return current;
228472
+ const parsed = parseNormalizeCompileAggregate(current.aggregate, options);
228473
+ if (!parsed.ok)
228474
+ throw new ConfigRepositoryError("invalid_configuration", "configuration dependency reconciliation failed", parsed.errors);
228475
+ const active = new Set(current.aggregate.plugin_activations.map((value) => value.plugin_name));
228476
+ if (required.some((name) => managementProviders.has(name) && !active.has(name))) {
228477
+ throw new ConfigRepositoryError("invalid_configuration", "dependency activation changes management authentication; enable the provider through management setup before upgrading");
228478
+ }
228479
+ const result = repository.commit({
228480
+ mutation_id: randomUUID17(),
228481
+ expected_revision: current.revision,
228482
+ aggregate: parsed.value,
228483
+ kind: "config",
228484
+ created_at: now,
228485
+ target_worker_slots: Array.from({ length: workerCount }, (_, slot) => slot)
228486
+ });
228487
+ if (result.kind !== "committed") {
228488
+ throw new ConfigRepositoryError("invalid_operation", `configuration dependency reconciliation blocked: ${result.kind}`);
228489
+ }
228490
+ return result.snapshot;
228491
+ }
228492
+ var init_reconcile_plugin_dependencies = __esm(() => {
228493
+ init_config_storage();
228494
+ });
228495
+
228423
228496
  // src/ui/assets.ts
228424
228497
  function decodeBase64Asset(content) {
228425
228498
  return new Blob([Uint8Array.from(atob(content), (character) => character.charCodeAt(0))]);
@@ -228917,7 +228990,7 @@ var init_daemon_control2 = __esm(() => {
228917
228990
 
228918
228991
  // src/master-runtime/composition.ts
228919
228992
  import { AsyncLocalStorage } from "async_hooks";
228920
- import { randomUUID as randomUUID17, createHash as createHash6 } from "crypto";
228993
+ import { randomUUID as randomUUID18, createHash as createHash6 } from "crypto";
228921
228994
  import { resolve as resolve14 } from "path";
228922
228995
  function cleanupWorkersAfterStartupFailure(factory, disposition) {
228923
228996
  if (disposition === undefined || disposition.kind === "shutdown_safe_empty") {
@@ -229098,7 +229171,7 @@ class DurableRouteProtections {
229098
229171
  const previous = this.state?.get(name), value = { plugin: name, routeIds: [...routeIds] };
229099
229172
  if (this.state && JSON.stringify(previous?.value) !== JSON.stringify(value))
229100
229173
  this.state.execute({
229101
- commandId: randomUUID17(),
229174
+ commandId: randomUUID18(),
229102
229175
  mutations: [{ key: name, expectedVersion: previous?.version ?? 0, value }]
229103
229176
  });
229104
229177
  if (routeIds.length)
@@ -229153,7 +229226,7 @@ async function startMasterComposition(dependencies, daemonBootstrap = null) {
229153
229226
  if (createClaim === undefined || bootstrap.claimControllerWithCapability === undefined) {
229154
229227
  throw new MasterRuntimeError("startup_incomplete", "supervision claim is unavailable");
229155
229228
  }
229156
- supervisionState = bootstrap.claimControllerWithCapability(createClaim(configLock, accessLock), randomUUID17(), dependencies.clock.now());
229229
+ supervisionState = bootstrap.claimControllerWithCapability(createClaim(configLock, accessLock), randomUUID18(), dependencies.clock.now());
229157
229230
  await bootstrap.close();
229158
229231
  resources.repository = null;
229159
229232
  }
@@ -229182,6 +229255,10 @@ async function startMasterComposition(dependencies, daemonBootstrap = null) {
229182
229255
  if (dependencies.createIngressController !== undefined && supervisionState === null) {
229183
229256
  throw new MasterRuntimeError("startup_incomplete", "supervision state was not claimed");
229184
229257
  }
229258
+ if (compileOptions.pluginDependencies) {
229259
+ const managementProviders = new Set((catalog.records?.() ?? []).filter((record) => record.manifest.management).map((record) => record.name));
229260
+ reconcilePluginDependencies(resources.repository, compileOptions, options.workerCount, dependencies.clock.now(), managementProviders);
229261
+ }
229185
229262
  const configDatabase = resources.repository.getDatabase?.();
229186
229263
  const accessDatabase = resources.stats?.getDatabase?.();
229187
229264
  const secretStores = configDatabase === undefined ? {
@@ -229198,10 +229275,10 @@ async function startMasterComposition(dependencies, daemonBootstrap = null) {
229198
229275
  const rpcIdentityState = durableState?.forNamespace("core-master-state-rpc");
229199
229276
  let rpcIdentity = rpcIdentityState?.get("identity");
229200
229277
  if (rpcIdentityState && !rpcIdentity) {
229201
- rpcIdentity = rpcIdentityState.execute({ commandId: randomUUID17(), mutations: [{ key: "identity", expectedVersion: 0, value: { process_instance_id: supervisionState?.instance_id ?? randomUUID17(), boot_nonce: randomUUID17() } }] })[0];
229278
+ rpcIdentity = rpcIdentityState.execute({ commandId: randomUUID18(), mutations: [{ key: "identity", expectedVersion: 0, value: { process_instance_id: supervisionState?.instance_id ?? randomUUID18(), boot_nonce: randomUUID18() } }] })[0];
229202
229279
  }
229203
229280
  const stableIdentity = rpcIdentity?.value;
229204
- const masterStateIdentity = { role: "ingress", process_instance_id: stableIdentity?.process_instance_id ?? randomUUID17(), boot_nonce: stableIdentity?.boot_nonce ?? randomUUID17() };
229281
+ const masterStateIdentity = { role: "ingress", process_instance_id: stableIdentity?.process_instance_id ?? randomUUID18(), boot_nonce: stableIdentity?.boot_nonce ?? randomUUID18() };
229205
229282
  let publishPolicy = async () => false;
229206
229283
  const managementOrigin = parseManagementOrigin(process.env.BUNGEE_PUBLIC_ORIGIN);
229207
229284
  const trustedProxyAddresses = parseTrustedManagementProxies(process.env.BUNGEE_TRUSTED_MANAGEMENT_PROXIES);
@@ -229296,7 +229373,7 @@ async function startMasterComposition(dependencies, daemonBootstrap = null) {
229296
229373
  stateLeases.set(record.key, record.value);
229297
229374
  const saveStateLease = (id, lease) => {
229298
229375
  const previous = leaseState?.get(id);
229299
- leaseState?.execute({ commandId: randomUUID17(), mutations: [{ key: id, expectedVersion: previous?.version ?? 0, value: lease ? { ...lease } : null }] });
229376
+ leaseState?.execute({ commandId: randomUUID18(), mutations: [{ key: id, expectedVersion: previous?.version ?? 0, value: lease ? { ...lease } : null }] });
229300
229377
  if (lease)
229301
229378
  stateLeases.set(id, lease);
229302
229379
  else
@@ -229371,7 +229448,7 @@ async function startMasterComposition(dependencies, daemonBootstrap = null) {
229371
229448
  if (!lease)
229372
229449
  saveStateLease(id, { worker: workerId, principal, plugin: call.plugin });
229373
229450
  if (!requestLease)
229374
- requestLeaseState?.execute({ commandId: randomUUID17(), mutations: [{ key: requestLeaseId, expectedVersion: 0, value: { worker: workerId, principal, plugin: call.plugin } }] });
229451
+ requestLeaseState?.execute({ commandId: randomUUID18(), mutations: [{ key: requestLeaseId, expectedVersion: 0, value: { worker: workerId, principal, plugin: call.plugin } }] });
229375
229452
  }
229376
229453
  if (call.method === "settle")
229377
229454
  await resources.ingressController.freezePluginKey(call.plugin, call.target.principal.keyId);
@@ -230747,6 +230824,7 @@ var init_composition = __esm(() => {
230747
230824
  init_plugin_control();
230748
230825
  init_master_http_bridge();
230749
230826
  init_master_plugin_catalog_api();
230827
+ init_reconcile_plugin_dependencies();
230750
230828
  init_server();
230751
230829
  init_daemon_control2();
230752
230830
  NO_INGRESS_STARTUP_FAILURE_DISPOSITION = Object.freeze({
@@ -230837,7 +230915,7 @@ var init_headers = __esm(() => {
230837
230915
  });
230838
230916
 
230839
230917
  // src/public-listener/forwarding.ts
230840
- import { randomUUID as randomUUID18 } from "crypto";
230918
+ import { randomUUID as randomUUID19 } from "crypto";
230841
230919
  function errorResponse4(status) {
230842
230920
  const error = status === 503 ? "service_unavailable" : status === 502 ? "bad_gateway" : "client_closed_request";
230843
230921
  const headers = new Headers(JSON_HEADERS6);
@@ -230859,7 +230937,7 @@ async function forwardToSelectedWorker(request, options, trustedPeer) {
230859
230937
  });
230860
230938
  }
230861
230939
  const principal = options.authenticate?.(request) ?? ANONYMOUS_PRINCIPAL;
230862
- const identity = { requestId: randomUUID18(), principal };
230940
+ const identity = { requestId: randomUUID19(), principal };
230863
230941
  const lease = options.admission.acquire();
230864
230942
  const worker = lease.worker;
230865
230943
  if (worker === null) {
@@ -230893,7 +230971,7 @@ async function forwardToSelectedWorker(request, options, trustedPeer) {
230893
230971
  try {
230894
230972
  logger.warn({ ingressForwarding: {
230895
230973
  phase: "private_response_headers",
230896
- correlation_id: randomUUID18(),
230974
+ correlation_id: randomUUID19(),
230897
230975
  error_code: errorCode,
230898
230976
  elapsed_ms: Math.max(0, Math.round(performance.now() - startedAt))
230899
230977
  } }, "Ingress private request forwarding failed");
@@ -231157,7 +231235,7 @@ var init_admission_registry2 = __esm(() => {
231157
231235
  });
231158
231236
 
231159
231237
  // src/ingress/supervision-http.ts
231160
- import { randomUUID as randomUUID19 } from "crypto";
231238
+ import { randomUUID as randomUUID20 } from "crypto";
231161
231239
  function optionalAdmission(value) {
231162
231240
  return value === null ? null : parseAdmissionSet(value);
231163
231241
  }
@@ -231716,7 +231794,7 @@ class IngressControllerClient {
231716
231794
  }
231717
231795
  return value;
231718
231796
  }
231719
- async challenge(authority, requestId = randomUUID19(), sequence = 1, signal) {
231797
+ async challenge(authority, requestId = randomUUID20(), sequence = 1, signal) {
231720
231798
  if (signal?.aborted)
231721
231799
  throw signal.reason;
231722
231800
  this.statusRequestSequence = Math.max(this.statusRequestSequence, sequence + 1);
@@ -231731,7 +231809,7 @@ class IngressControllerClient {
231731
231809
  async status(authority, sequence = this.statusRequestSequence, signal) {
231732
231810
  if (signal?.aborted)
231733
231811
  throw signal.reason;
231734
- const requestId = randomUUID19();
231812
+ const requestId = randomUUID20();
231735
231813
  const message = signSupervisionMessage({
231736
231814
  protocol: "bungee-supervision-v1",
231737
231815
  kind: "status",
@@ -231749,13 +231827,13 @@ class IngressControllerClient {
231749
231827
  throw signal.reason;
231750
231828
  return this.verifyStatus(response, authority, requestId);
231751
231829
  }
231752
- async fence(authority, sequence, requestId = randomUUID19(), signal) {
231830
+ async fence(authority, sequence, requestId = randomUUID20(), signal) {
231753
231831
  const response = await this.command(authority, sequence, "/admission/fence", null, requestId, signal);
231754
231832
  if (signal?.aborted)
231755
231833
  throw signal.reason;
231756
231834
  return this.verifyStatus(response, authority, requestId);
231757
231835
  }
231758
- async attach(challenge, authority, sequence = 1, requestId = randomUUID19(), signal) {
231836
+ async attach(challenge, authority, sequence = 1, requestId = randomUUID20(), signal) {
231759
231837
  if (signal?.aborted)
231760
231838
  throw signal.reason;
231761
231839
  this.statusRequestSequence = Math.max(this.statusRequestSequence, sequence + 1);
@@ -231774,7 +231852,7 @@ class IngressControllerClient {
231774
231852
  throw signal.reason;
231775
231853
  return this.verifyStatus(response, authority, requestId);
231776
231854
  }
231777
- async lease(authority, leaseExpiresAt, sequence, requestId = randomUUID19(), signal) {
231855
+ async lease(authority, leaseExpiresAt, sequence, requestId = randomUUID20(), signal) {
231778
231856
  if (signal?.aborted)
231779
231857
  throw signal.reason;
231780
231858
  this.statusRequestSequence = Math.max(this.statusRequestSequence, sequence + 1);
@@ -231793,7 +231871,7 @@ class IngressControllerClient {
231793
231871
  throw signal.reason;
231794
231872
  return this.verifyStatus(response, authority, requestId);
231795
231873
  }
231796
- async command(authority, sequence, path, body, requestId = randomUUID19(), signal) {
231874
+ async command(authority, sequence, path, body, requestId = randomUUID20(), signal) {
231797
231875
  if (signal?.aborted)
231798
231876
  throw signal.reason;
231799
231877
  this.statusRequestSequence = Math.max(this.statusRequestSequence, sequence + 1);
@@ -232251,7 +232329,7 @@ var init_ingress2 = __esm(async () => {
232251
232329
  });
232252
232330
 
232253
232331
  // src/master-runtime/supervised-worker-client.ts
232254
- import { randomUUID as randomUUID20 } from "crypto";
232332
+ import { randomUUID as randomUUID21 } from "crypto";
232255
232333
  function plain8(value) {
232256
232334
  if (typeof value !== "object" || value === null || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
232257
232335
  throw new WorkerControllerClientError("protocol", "supervision response must be a plain object");
@@ -232730,7 +232808,7 @@ class WorkerControllerClient {
232730
232808
  try {
232731
232809
  if (this.stopped || this.terminating || controller.signal.aborted || Date.now() >= deadline)
232732
232810
  throw new WorkerControllerClientError("timeout", "worker runtime snapshot timed out");
232733
- const requestId = randomUUID20();
232811
+ const requestId = randomUUID21();
232734
232812
  const sequence = this.nextSequence();
232735
232813
  const message = signSupervisionMessage({
232736
232814
  protocol: "bungee-supervision-v1",
@@ -232756,7 +232834,7 @@ class WorkerControllerClient {
232756
232834
  }, controller.signal, deadline).finally(finish);
232757
232835
  }
232758
232836
  async challenge(signal = this.ordinaryAbort.signal) {
232759
- const requestId = randomUUID20();
232837
+ const requestId = randomUUID21();
232760
232838
  const sequence = this.nextSequence();
232761
232839
  const response = plain8(await this.fetchJson("/__supervision/challenge", {
232762
232840
  controller_epoch: this.options.authority.controller_epoch,
@@ -232772,7 +232850,7 @@ class WorkerControllerClient {
232772
232850
  }
232773
232851
  async attachOnce(signal = this.ordinaryAbort.signal) {
232774
232852
  const challengeNonce = await this.challenge(signal);
232775
- const attachId = randomUUID20();
232853
+ const attachId = randomUUID21();
232776
232854
  const attachSequence = this.nextSequence();
232777
232855
  const attach = signSupervisionMessage({
232778
232856
  protocol: "bungee-supervision-v1",
@@ -232799,7 +232877,7 @@ class WorkerControllerClient {
232799
232877
  throw error;
232800
232878
  }
232801
232879
  }
232802
- const leaseId = randomUUID20();
232880
+ const leaseId = randomUUID21();
232803
232881
  const leaseSequence = this.nextSequence();
232804
232882
  const leaseStartedAt = performance.now();
232805
232883
  const lease = signSupervisionMessage({
@@ -232844,7 +232922,7 @@ class WorkerControllerClient {
232844
232922
  }, delayMs);
232845
232923
  }
232846
232924
  async renewOnce(signal = this.ordinaryAbort.signal) {
232847
- const requestId = randomUUID20();
232925
+ const requestId = randomUUID21();
232848
232926
  const sequence = this.nextSequence();
232849
232927
  const leaseStartedAt = performance.now();
232850
232928
  const lease = signSupervisionMessage({
@@ -232889,12 +232967,11 @@ class WorkerControllerClient {
232889
232967
  return this.enqueueRenewal((signal) => this.renewOnce(signal));
232890
232968
  }
232891
232969
  status(timeoutMs = this.timeoutMs) {
232892
- const budget = this.leaseDeadlineAt === null ? timeoutMs : Math.max(1, Math.min(timeoutMs, this.leaseDeadlineAt - performance.now()));
232893
- const deadline = performance.now() + budget;
232970
+ const deadline = performance.now() + timeoutMs;
232894
232971
  return this.enqueue(() => deadline <= performance.now() ? Promise.reject(new WorkerControllerClientError("timeout", "worker status deadline expired")) : this.statusOnce(this.ordinaryAbort.signal, deadline));
232895
232972
  }
232896
232973
  async statusOnce(signal = this.ordinaryAbort.signal, deadline) {
232897
- const requestId = randomUUID20();
232974
+ const requestId = randomUUID21();
232898
232975
  const sequence = this.nextSequence();
232899
232976
  const message = signSupervisionMessage({
232900
232977
  protocol: "bungee-supervision-v1",
@@ -232927,7 +233004,7 @@ class WorkerControllerClient {
232927
233004
  });
232928
233005
  }
232929
233006
  async commandOnce(path, body, signal, allowTerminating = false, deadline) {
232930
- const requestId = randomUUID20();
233007
+ const requestId = randomUUID21();
232931
233008
  const sequence = this.nextSequence();
232932
233009
  const message = signSupervisionMessage({
232933
233010
  protocol: "bungee-supervision-v1",
@@ -232976,7 +233053,7 @@ class WorkerControllerClient {
232976
233053
  for (const controller of this.runtimeControllers)
232977
233054
  controller.abort("disconnected");
232978
233055
  if (expireLease && !this.terminating && this.state === "attached") {
232979
- const requestId = randomUUID20();
233056
+ const requestId = randomUUID21();
232980
233057
  const sequence = this.nextSequence();
232981
233058
  const lease = signSupervisionMessage({
232982
233059
  protocol: "bungee-supervision-v1",
@@ -233214,6 +233291,7 @@ class SupervisedConfigWorkerProcessAdapter {
233214
233291
  stopped = false;
233215
233292
  drainPoll = null;
233216
233293
  drainCommand = null;
233294
+ drainStatusFailureReported = false;
233217
233295
  exitPublication = null;
233218
233296
  kernelBootId;
233219
233297
  constructor(options) {
@@ -233416,7 +233494,23 @@ class SupervisedConfigWorkerProcessAdapter {
233416
233494
  return status;
233417
233495
  }
233418
233496
  async drainStatus(timeoutMs) {
233419
- const status = await this.status(timeoutMs);
233497
+ const startedAt = performance.now();
233498
+ let status;
233499
+ try {
233500
+ status = await this.status(timeoutMs);
233501
+ this.drainStatusFailureReported = false;
233502
+ } catch (error) {
233503
+ if (!this.drainStatusFailureReported) {
233504
+ this.drainStatusFailureReported = true;
233505
+ recordShutdownFailure("worker_drain_status", {
233506
+ pid: this.pid,
233507
+ origin: this.origin,
233508
+ elapsedMs: shutdownElapsedMs(startedAt),
233509
+ ...timeoutMs === undefined ? {} : { timeoutMs }
233510
+ }, error);
233511
+ }
233512
+ throw error;
233513
+ }
233420
233514
  if (status.evidence.kind === "draining" && status.evidence.message?.status === "worker-draining") {
233421
233515
  return status.evidence.message;
233422
233516
  }
@@ -235409,7 +235503,7 @@ var init_management_listener = __esm(() => {
235409
235503
  });
235410
235504
 
235411
235505
  // src/ingress/master-controller.ts
235412
- import { randomUUID as randomUUID21 } from "crypto";
235506
+ import { randomUUID as randomUUID22 } from "crypto";
235413
235507
  import { spawn as spawnChild2 } from "child_process";
235414
235508
  function immutableRegistry(registry) {
235415
235509
  const immutableAdmission = (set) => set === null ? null : Object.freeze({
@@ -235493,7 +235587,7 @@ class MasterIngressController {
235493
235587
  requirePositive(this.startupTimeoutMs, "startupTimeoutMs");
235494
235588
  requirePositive(this.leaseDurationMs, "leaseDurationMs");
235495
235589
  this.authority = { controller_epoch: options.controllerEpoch, controller_id: options.controllerId };
235496
- this.childCredential = deriveSupervisionProcessKey(options.rootKey, options.instanceId, "ingress", randomUUID21(), randomUUID21());
235590
+ this.childCredential = deriveSupervisionProcessKey(options.rootKey, options.instanceId, "ingress", randomUUID22(), randomUUID22());
235497
235591
  this.fetch = options.fetch ?? fetch;
235498
235592
  this.identityControl = options.processIdentity ?? DEFAULT_INGRESS_PROCESS_IDENTITY;
235499
235593
  this.probeSleep = options.probeSleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
@@ -235988,8 +236082,6 @@ class MasterIngressController {
235988
236082
  evidence.lastProbe = probe;
235989
236083
  if (probe === "dead" || probe === "mismatch")
235990
236084
  return probe;
235991
- if (probe === "unknown")
235992
- return "unverified";
235993
236085
  const remaining = deadline - this.now();
235994
236086
  if (remaining <= 0)
235995
236087
  break;
@@ -236050,7 +236142,7 @@ class MasterIngressController {
236050
236142
  const lifecycle = this.lifecycleGeneration;
236051
236143
  if (!this.isRecoveryEligible(lifecycle))
236052
236144
  return null;
236053
- this.childCredential = deriveSupervisionProcessKey(this.options.rootKey, this.options.instanceId, "ingress", randomUUID21(), randomUUID21());
236145
+ this.childCredential = deriveSupervisionProcessKey(this.options.rootKey, this.options.instanceId, "ingress", randomUUID22(), randomUUID22());
236054
236146
  const childEnvironment = { ...this.options.environment ?? process.env };
236055
236147
  for (const name of [
236056
236148
  "BUNGEE_PLUGIN_SECRETS_KEY",
@@ -236080,9 +236172,9 @@ class MasterIngressController {
236080
236172
  throw this.mapDiscoveryFailure(cause);
236081
236173
  }
236082
236174
  this.assertRecoveryEligible(lifecycle);
236083
- const challenge = await client.challenge(this.authority, randomUUID21(), 1, signal);
236175
+ const challenge = await client.challenge(this.authority, randomUUID22(), 1, signal);
236084
236176
  this.assertRecoveryEligible(lifecycle);
236085
- await client.attach(challenge, this.authority, this.nextSequence(signal), randomUUID21(), signal);
236177
+ await client.attach(challenge, this.authority, this.nextSequence(signal), randomUUID22(), signal);
236086
236178
  this.assertRecoveryEligible(lifecycle);
236087
236179
  const attachedStatus = await client.status(this.authority, this.nextSequence(signal), signal);
236088
236180
  this.assertRecoveryEligible(lifecycle);
@@ -236393,10 +236485,10 @@ class MasterIngressController {
236393
236485
  await client.identity(signal);
236394
236486
  if (!this.isRecoveryEligible(lifecycle))
236395
236487
  return null;
236396
- const challenge = await client.challenge(this.authority, randomUUID21(), 1, signal);
236488
+ const challenge = await client.challenge(this.authority, randomUUID22(), 1, signal);
236397
236489
  if (!this.isRecoveryEligible(lifecycle))
236398
236490
  return null;
236399
- await client.attach(challenge, this.authority, this.nextSequence(signal), randomUUID21(), signal);
236491
+ await client.attach(challenge, this.authority, this.nextSequence(signal), randomUUID22(), signal);
236400
236492
  if (!this.isRecoveryEligible(lifecycle))
236401
236493
  return null;
236402
236494
  const attachedStatus = await client.status(this.authority, this.nextSequence(signal), signal);
@@ -237103,7 +237195,7 @@ var init_master_controller = __esm(() => {
237103
237195
  });
237104
237196
 
237105
237197
  // src/master.ts
237106
- import { randomUUID as randomUUID22 } from "crypto";
237198
+ import { randomUUID as randomUUID23 } from "crypto";
237107
237199
  import { resolve as resolve17 } from "path";
237108
237200
  function environment() {
237109
237201
  return Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
@@ -237187,7 +237279,7 @@ var init_master = __esm(async () => {
237187
237279
  deriveTransportSecret: deriveWorkerTransportSecret,
237188
237280
  resolveWorkerLaunch,
237189
237281
  createWorkerFactory: (options) => new SupervisedConfigWorkerFactory(options),
237190
- createMasterGeneration: randomUUID22,
237282
+ createMasterGeneration: randomUUID23,
237191
237283
  createCoordinator: (options) => new MasterConfigPublicationCoordinator(options),
237192
237284
  createManagementListener,
237193
237285
  createControllerClaim: mintControllerClaimCapability,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffusion/bungee-core",
3
- "version": "5.6.0",
3
+ "version": "5.6.1",
4
4
  "description": "Bungee reverse proxy core engine",
5
5
  "main": "./dist/main.js",
6
6
  "types": "./dist/main.d.ts",
@@ -12,7 +12,7 @@
12
12
  "test": "bun test"
13
13
  },
14
14
  "dependencies": {
15
- "@jeffusion/bungee-types": "^5.6.0",
15
+ "@jeffusion/bungee-types": "^5.6.1",
16
16
  "@types/lodash-es": "^4.17.12",
17
17
  "acorn": "^8.15.0",
18
18
  "canonicalize": "3.0.0",