@truenas/api-client 2.0.0 → 3.0.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.
package/dist/index.cjs CHANGED
@@ -2521,7 +2521,6 @@ __export(v26_0_0_exports, {
2521
2521
  Aclmode: () => Aclmode,
2522
2522
  Acltype: () => Acltype,
2523
2523
  Advpowermgmt: () => Advpowermgmt,
2524
- Algorithm: () => Algorithm,
2525
2524
  AppContainerDetailsState: () => AppContainerDetailsState,
2526
2525
  AppContainerDetailsStateInput: () => AppContainerDetailsStateInput,
2527
2526
  Atime: () => Atime,
@@ -2659,11 +2658,13 @@ var ContainerNICDeviceTypeInput = {
2659
2658
  };
2660
2659
  var ContainerStatusState = {
2661
2660
  Running: "RUNNING",
2662
- Stopped: "STOPPED"
2661
+ Stopped: "STOPPED",
2662
+ Suspended: "SUSPENDED"
2663
2663
  };
2664
2664
  var ContainerStatusStateInput = {
2665
2665
  Running: "RUNNING",
2666
- Stopped: "STOPPED"
2666
+ Stopped: "STOPPED",
2667
+ Suspended: "SUSPENDED"
2667
2668
  };
2668
2669
  var Feature2 = {
2669
2670
  Apps: "APPS",
@@ -2743,7 +2744,6 @@ __export(v27_0_0_exports, {
2743
2744
  Aclmode: () => Aclmode,
2744
2745
  Acltype: () => Acltype,
2745
2746
  Advpowermgmt: () => Advpowermgmt,
2746
- Algorithm: () => Algorithm,
2747
2747
  AppContainerDetailsState: () => AppContainerDetailsState,
2748
2748
  AppContainerDetailsStateInput: () => AppContainerDetailsStateInput,
2749
2749
  Atime: () => Atime,
@@ -3066,6 +3066,10 @@ var AppState = /* @__PURE__ */ ((AppState2) => {
3066
3066
  AppState2["Stopped"] = "STOPPED";
3067
3067
  AppState2["Stopping"] = "STOPPING";
3068
3068
  AppState2["Deploying"] = "DEPLOYING";
3069
+ AppState2["Suspended"] = "SUSPENDED";
3070
+ AppState2["Suspending"] = "SUSPENDING";
3071
+ AppState2["Error"] = "ERROR";
3072
+ AppState2["Unknown"] = "UNKNOWN";
3069
3073
  return AppState2;
3070
3074
  })(AppState || {});
3071
3075
 
@@ -3076,7 +3080,10 @@ function toAppState(state) {
3076
3080
  return "RUNNING" /* Running */;
3077
3081
  case "STOPPED":
3078
3082
  return "STOPPED" /* Stopped */;
3083
+ // `ABORTING` is v25.10 only: the instance is being forced down, which ends
3084
+ // at `STOPPED` and is what `Stopping` already means.
3079
3085
  case "STOPPING":
3086
+ case "ABORTING":
3080
3087
  return "STOPPING" /* Stopping */;
3081
3088
  // `STARTING` is v25.10 `virt.instance` only, and folding it into `Stopped`
3082
3089
  // told callers a container that is coming up is at rest — enough for a UI
@@ -3085,8 +3092,35 @@ function toAppState(state) {
3085
3092
  case "STARTING":
3086
3093
  case "DEPLOYING":
3087
3094
  return "DEPLOYING" /* Deploying */;
3095
+ // v25.10's freezer states are the same idea as v26's `SUSPENDED`: the
3096
+ // processes are paused, not exited. `FROZEN` is that pause completed, so it
3097
+ // and `SUSPENDED` are the same answer.
3098
+ case "SUSPENDED":
3099
+ case "FROZEN":
3100
+ return "SUSPENDED" /* Suspended */;
3101
+ // `FREEZING` is on the way there and not there yet. It used to answer
3102
+ // `Suspended`, defended by analogy with `STARTING` — but `STARTING` maps to
3103
+ // `Deploying`, which is itself an in-progress word, so the analogy ran the
3104
+ // wrong way. Answering `Suspended` tells a caller polling for "the memory
3105
+ // is quiesced" that it is, one poll before it is true: the same false
3106
+ // claim this mapping was rewritten to stop making, one state along.
3107
+ // `ABORTING` had `Stopping` to land on; the freeze path needed its own.
3108
+ case "FREEZING":
3109
+ return "SUSPENDING" /* Suspending */;
3110
+ // `THAWED` is the freezer lifted: the processes are scheduled again, so
3111
+ // this is `RUNNING` reported through the transition rather than a state a
3112
+ // caller should act on differently.
3113
+ case "THAWED":
3114
+ return "RUNNING" /* Running */;
3115
+ case "ERROR":
3116
+ return "ERROR" /* Error */;
3117
+ // `UNKNOWN` is middleware's own word for it, and anything unrecognised is
3118
+ // the same situation: a state this mapping has no word for. Neither is
3119
+ // evidence the container is stopped.
3120
+ case "UNKNOWN":
3121
+ return "UNKNOWN" /* Unknown */;
3088
3122
  default:
3089
- return "STOPPED" /* Stopped */;
3123
+ return "UNKNOWN" /* Unknown */;
3090
3124
  }
3091
3125
  }
3092
3126
 
@@ -3306,7 +3340,7 @@ var VersionDiscovery = class {
3306
3340
  operators.map((versionStrings) => this.selectVersion(hostname, versionStrings)),
3307
3341
  operators.catchError((error) => {
3308
3342
  this.versionCache.delete(hostname);
3309
- this.logger.error("Version discovery failed", { hostname, error });
3343
+ this.logger.warn("Version discovery failed", { hostname, error });
3310
3344
  return rxjs.throwError(() => this.classify(error, hostname));
3311
3345
  }),
3312
3346
  operators.shareReplay(1)
@@ -3377,7 +3411,7 @@ var VersionDiscovery = class {
3377
3411
  });
3378
3412
  const parsedVersions = versionStrings.map(parseApiVersion).filter((v) => v !== null);
3379
3413
  if (parsedVersions.length === 0) {
3380
- this.logger.error("No valid versions in response", {
3414
+ this.logger.warn("No valid versions in response", {
3381
3415
  hostname,
3382
3416
  versionStrings
3383
3417
  });
@@ -3479,29 +3513,34 @@ async function createTrueNasClient(opts) {
3479
3513
  `Cannot create client for system ${uuid}: hostnames array is empty`
3480
3514
  );
3481
3515
  }
3482
- const primaryHostname = hostnames[0];
3483
3516
  const versionDiscovery = new VersionDiscovery(logger);
3484
3517
  logger.info("Creating versioned API client", {
3485
3518
  uuid: uuid.slice(0, 8),
3486
- hostname: primaryHostname,
3519
+ hostnames: hostnames.join(", "),
3487
3520
  systemName
3488
3521
  });
3489
3522
  let version;
3490
3523
  try {
3491
- version = await rxjs.firstValueFrom(
3492
- versionDiscovery.discoverVersion(primaryHostname)
3524
+ const winner = await discoverVersionFromAnyHostname(
3525
+ hostnames,
3526
+ versionDiscovery
3493
3527
  );
3528
+ version = winner.version;
3494
3529
  logger.info("API version discovered, instantiating client", {
3495
3530
  uuid: uuid.slice(0, 8),
3531
+ // Which hostname answered is log context only. The client is built with
3532
+ // the full hostname list regardless — the websocket connection races all
3533
+ // of them anyway.
3534
+ hostname: winner.hostname,
3496
3535
  version: version.version,
3497
3536
  websocketPath: version.websocketPath
3498
3537
  });
3499
3538
  } catch (error) {
3500
3539
  const errorMessage = errorMessageOrDefault(error, "Unknown error");
3501
3540
  if (!(error instanceof VersionDiscoveryNetworkError)) {
3502
- logger.error("Version discovery failed", {
3541
+ logger.error("Version discovery failed on every hostname", {
3503
3542
  uuid: uuid.slice(0, 8),
3504
- hostname: primaryHostname,
3543
+ hostnames: hostnames.join(", "),
3505
3544
  error: errorMessage,
3506
3545
  errorType: error instanceof Error ? error.constructor.name : typeof error
3507
3546
  });
@@ -3512,7 +3551,7 @@ async function createTrueNasClient(opts) {
3512
3551
  if (!fallbackVersion) {
3513
3552
  logger.error("Invalid fallback version configuration", {
3514
3553
  uuid: uuid.slice(0, 8),
3515
- hostname: primaryHostname,
3554
+ hostnames: hostnames.join(", "),
3516
3555
  fallbackVersion: fallbackVersionString
3517
3556
  });
3518
3557
  throw error;
@@ -3521,7 +3560,7 @@ async function createTrueNasClient(opts) {
3521
3560
  "Version discovery failed with a network error (possible CORS or network issue), falling back to assumed version",
3522
3561
  {
3523
3562
  uuid: uuid.slice(0, 8),
3524
- hostname: primaryHostname,
3563
+ hostnames: hostnames.join(", "),
3525
3564
  fallbackVersion: fallbackVersionString,
3526
3565
  originalError: errorMessage,
3527
3566
  warning: "A network error has multiple causes (CORS, network down, DNS failure). The connection may still fail during the WebSocket handshake."
@@ -3531,6 +3570,27 @@ async function createTrueNasClient(opts) {
3531
3570
  }
3532
3571
  return instantiateClientForVersion(version, opts, logger);
3533
3572
  }
3573
+ async function discoverVersionFromAnyHostname(hostnames, versionDiscovery) {
3574
+ const attempts = hostnames.map(
3575
+ (hostname) => rxjs.firstValueFrom(versionDiscovery.discoverVersion(hostname)).then(
3576
+ (version) => ({ hostname, version })
3577
+ )
3578
+ );
3579
+ try {
3580
+ return await Promise.any(attempts);
3581
+ } catch (error) {
3582
+ const failures = error instanceof AggregateError ? error.errors : [error];
3583
+ throw selectRepresentativeFailure(failures);
3584
+ }
3585
+ }
3586
+ function selectRepresentativeFailure(failures) {
3587
+ const isVersionError = (error) => (
3588
+ // cases: valid response, but the given versions won't work for us
3589
+ error instanceof VersionTooOldError || error instanceof VersionTooNewError || error instanceof NoCompatibleVersionsError || error instanceof VersionEndpointNotFoundError
3590
+ );
3591
+ const isNetworkError = (error) => error instanceof VersionDiscoveryNetworkError;
3592
+ return failures.find(isVersionError) ?? failures.find(isNetworkError) ?? failures[0];
3593
+ }
3534
3594
  var CLIENT_BY_VERSION_KEY = {
3535
3595
  "25.10": TrueNasApiClientV2510,
3536
3596
  "26": TrueNasApiClientV26