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