@truenas/api-client 1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/enums/truenas-endpoint.enum.ts","../src/types/api-error.type.ts","../src/types/job.type.ts","../src/utils/utils.ts","../src/utils/jsonrpc.utils.ts","../src/api/truenas-api.ts","../src/enums/truenas-auth-mechanism.enum.ts","../src/errors/auth.errors.ts","../src/auth/truenas-authenticator.ts","../src/logger.ts","../src/utils/truenas-connection.utils.ts","../src/connection/truenas-socket.ts","../src/connection/truenas-connection.ts","../src/config/api-version.config.ts","../src/types/api-version.type.ts","../src/utils/api-version.utils.ts","../src/client/truenas-api-client.ts","../src/client/truenas-api-client-v25-10.ts","../src/client/truenas-api-client-v26.ts","../src/errors/version-discovery.errors.ts","../src/version-discovery.ts","../src/factory.ts"],"names":["TrueNasEndpoint","JobState","filter","TrueNasAuthMechanism","AuthErrorCode","switchMap","of","BehaviorSubject","map","take","catchError","throwError","shareReplay","Observable","tap","VersionCompatibility","concat","from"],"mappings":"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","file":"index.js","sourcesContent":["export enum TrueNasEndpoint {\n UpdateStatus = 'update.status',\n AppQuery = 'app.query',\n AppStart = 'app.start',\n AppStop = 'app.stop',\n AppRedeploy = 'app.redeploy',\n AppUpgrade = 'app.upgrade',\n CoreSubscribe = 'core.subscribe',\n ReplicationQuery = 'replication.query',\n ReplicationRun = 'replication.run',\n InterfaceQuery = 'interface.query',\n CloudBackupQuery = 'cloud_backup.query',\n CloudSyncQuery = 'cloudsync.query',\n VirtualInstanceQuery = 'virt.instance.query',\n VirtualInstanceStop = 'virt.instance.stop',\n VirtualInstanceStart = 'virt.instance.start',\n VirtualInstanceRestart = 'virt.instance.restart',\n // Container endpoints (v26+)\n ContainerQuery = 'container.query',\n ContainerStart = 'container.start',\n ContainerStop = 'container.stop',\n VmQuery = 'vm.query',\n VmStart = 'vm.start',\n VmStop = 'vm.stop',\n VmRestart = 'vm.restart',\n CoreDownload = 'core.download',\n CoreGetJobs = 'core.get_jobs',\n SystemInfo = 'system.info',\n SystemProductType = 'system.product_type',\n ExtendedSystemInfo = 'webui.main.dashboard.sys_info',\n DiskQuery = 'disk.query',\n DeviceGetInfo = 'device.get_info',\n CorePing = 'core.ping',\n GenerateToken = 'auth.generate_token',\n AlertList = 'alert.list',\n AuthLogin = 'auth.login_ex',\n AuthLogout = 'auth.logout',\n ApiKeyQuery = 'api_key.query',\n ApiKeyCreate = 'api_key.create',\n ApiKeyDelete = 'api_key.delete',\n FailOverStatus = 'failover.status',\n FailOverNode = 'failover.node',\n FailOverDisabledReasons = 'failover.disabled.reasons',\n PoolQuery = 'pool.query',\n DatasetQuery = 'pool.dataset.query',\n DatasetCreate = 'pool.dataset.create',\n DatasetDelete = 'pool.dataset.delete',\n SnapshotTaskCreate = 'pool.snapshottask.create',\n SnapshotTaskQuery = 'pool.snapshottask.query',\n SnapshotTaskDelete = 'pool.snapshottask.delete',\n SnapshotCreate = 'pool.snapshot.create',\n SnapshotDelete = 'pool.snapshot.delete',\n ReplicationCountEligibleSnapshots = 'replication.count_eligible_manual_snapshots',\n BootGetState = 'boot.get_state',\n TnConnectUpdate = 'tn_connect.update',\n HardwareVirtualizationVariant = 'hardware.virtualization.variant',\n KeychainCredentialGenerateSshKeyPair = 'keychaincredential.generate_ssh_key_pair',\n KeychainCredentialCreate = 'keychaincredential.create',\n KeychainCredentialRemoteSshHostKeyScan = 'keychaincredential.remote_ssh_host_key_scan',\n KeychainCredentialDelete = 'keychaincredential.delete',\n ReplicationCreate = 'replication.create',\n UserQuery = 'user.query',\n UserUpdate = 'user.update',\n NetworkConfiguration = 'network.configuration.config',\n ServiceQuery = 'service.query',\n ServiceUpdate = 'service.update',\n ServiceControl = 'service.control',\n}\n","/**\n * API error types for versioned TrueNAS API\n *\n * Handles both JSON-RPC 2.0 standard errors and TrueNAS custom error formats.\n */\n\n/**\n * JSON-RPC 2.0 standard error format\n * @see https://www.jsonrpc.org/specification#error_object\n */\nexport interface JsonRpcError {\n /** Error code indicating the error type */\n code: number;\n /** Human-readable error message */\n message: string;\n /** Optional additional error data */\n data?: unknown;\n}\n\n/**\n * TrueNAS custom error format\n * Used in some API responses alongside JSON-RPC errors\n */\nexport interface TrueNasError {\n /** Additional error properties */\n [key: string]: unknown;\n /** Human-readable error reason (TrueNAS-specific field) */\n reason: string;\n}\n\n/**\n * Union type for all possible API error formats\n */\nexport type ApiError = JsonRpcError | TrueNasError;\n\n/**\n * Type guard to check if an error is a JSON-RPC error\n */\nexport function isJsonRpcError(error: unknown): error is JsonRpcError {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'message' in error &&\n typeof (error as JsonRpcError).message === 'string'\n );\n}\n\n/**\n * Type guard to check if an error is a TrueNAS error\n */\nexport function isTrueNasError(error: unknown): error is TrueNasError {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'reason' in error &&\n typeof (error as TrueNasError).reason === 'string'\n );\n}\n\n/**\n * Extract a human-readable error message from an API error\n *\n * Handles multiple error formats:\n * - JSON-RPC 2.0 with nested TrueNAS data: uses `data.reason` field\n * - JSON-RPC 2.0 standard: uses `message` field\n * - TrueNAS custom: uses `reason` field\n * - Unknown format: returns fallback message\n *\n * @param error - The error object from API response\n * @param fallback - Fallback message if error format is unrecognized\n * @returns Human-readable error message\n */\nexport function getApiErrorMessage(\n error: unknown,\n fallback = 'API call failed'\n): string {\n // Check for JSON-RPC error with nested TrueNAS data (most specific)\n // Format: { code, message, data: { reason: \"actual error\" } }\n if (isJsonRpcError(error) && error.data && isTrueNasError(error.data)) {\n return error.data.reason;\n }\n\n // Check for direct TrueNAS error format\n if (isTrueNasError(error)) {\n return error.reason;\n }\n\n // Fall back to JSON-RPC standard message\n if (isJsonRpcError(error)) {\n return error.message;\n }\n\n return fallback;\n}\n","import { TrueNasDate } from '@/types/truenas-date.type';\n\nexport interface Job {\n id: number;\n method: string;\n arguments: string[];\n description: string | null;\n abortable: boolean;\n logs_path: string | null;\n logs_excerpt: string | null;\n progress: JobProgress;\n error: string | null;\n time_started: TrueNasDate;\n time_finished: TrueNasDate | null;\n state: JobState;\n /**\n * Array of JSON-RPC request IDs that triggered this job.\n * Used in v26+ to correlate API calls with their jobs.\n */\n message_ids?: string[];\n}\n\nexport interface JobProgress {\n percent: number;\n description: string;\n}\n\nexport enum JobState {\n Pending = 'PENDING',\n Running = 'RUNNING',\n Hold = 'HOLD',\n Error = 'ERROR',\n Failed = 'FAILED',\n Aborted = 'ABORTED',\n Success = 'SUCCESS',\n Finished = 'FINISHED',\n Locked = 'LOCKED',\n Waiting = 'WAITING',\n}\n","import { filter } from 'rxjs';\nimport { TrueNasMessage } from '@/types/truenas-message.type';\n\n/**\n * RxJS operator that keeps only the message whose JSON-RPC `id` matches `id`.\n * Used to correlate a request with its response over the shared socket stream.\n */\nexport const withId = (id: string) =>\n filter((msg: TrueNasMessage) => msg.id === id);\n\n/**\n * Generate a UUID. Uses `crypto.randomUUID()` when available, falling back to a\n * manual v4 generator (built on `crypto.getRandomValues`) for environments where\n * `crypto.randomUUID` is absent — e.g. insecure `http://` (non-localhost) pages,\n * which are not \"secure contexts\", and older runtimes. Feature-detecting rather\n * than sniffing the environment keeps this correct across the broader set of hosts\n * a redistributable library runs in.\n */\nexport function randomUUID(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n if (typeof crypto === 'undefined' || typeof crypto.getRandomValues !== 'function') {\n throw new Error('Web Crypto API unavailable: cannot generate a UUID');\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (crypto.getRandomValues(new Uint8Array(1))[0] % 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","/**\n * JSON-RPC 2.0 utility functions for versioned TrueNAS API\n */\n\nimport { TrueNasMessage } from '@/types/truenas-message.type';\nimport { randomUUID } from '@/utils/utils';\n\n/**\n * Create a JSON-RPC 2.0 formatted message\n *\n * @param method - The method name to call (e.g., 'core.ping', 'core.set_options')\n * @param params - Optional parameters for the method call. Defaults to empty array.\n * @returns A properly formatted JSON-RPC 2.0 message with generated UUID\n */\nexport function createJsonRpcMessage(\n method: string,\n params?: unknown\n): TrueNasMessage {\n return {\n jsonrpc: '2.0',\n id: randomUUID(),\n method,\n params: params ?? [],\n };\n}\n","import {\n BehaviorSubject,\n Observable,\n filter,\n map,\n merge,\n of,\n share,\n switchMap,\n take,\n takeWhile,\n} from 'rxjs';\nimport { TrueNasEndpoint } from '@/enums/truenas-endpoint.enum';\nimport {\n ApiCallMethod,\n ApiCallParams,\n ApiCallResponse,\n} from '@/types/api-call-directory.type';\nimport { getApiErrorMessage } from '@/types/api-error.type';\nimport { Job, JobState } from '@/types/job.type';\nimport { createJsonRpcMessage } from '@/utils/jsonrpc.utils';\nimport { withId } from '@/utils/utils';\nimport { TrueNasConnection } from '@/connection/truenas-connection';\n\n/**\n * Type for JSON-RPC 2.0 collection_update event params\n */\ninterface CollectionUpdateParams {\n msg: string;\n collection: string;\n id: number;\n fields: Job;\n}\n\n/**\n * TrueNAS API handler using the JSON-RPC 2.0 protocol.\n *\n * It handles:\n * - JSON-RPC 2.0 request formatting\n * - JSON-RPC 2.0 response parsing (result/error)\n * - Event subscriptions\n * - Job tracking\n */\nexport class TrueNasApi {\n /**\n * Stream of job events from websocket.\n * JSON-RPC 2.0 events have structure: { method: 'collection_update', params: { collection, fields, ... } }\n */\n private jobEvents = this.connection.messages().pipe(\n filter(\n res =>\n res.method === 'collection_update' &&\n (res.params as CollectionUpdateParams)?.collection === 'core.get_jobs'\n ),\n map(event => (event.params as CollectionUpdateParams).fields),\n filter(job => !!job?.id),\n share()\n );\n\n constructor(\n public authenticated: BehaviorSubject<boolean>,\n public connection: TrueNasConnection\n ) {\n this.initializeJobEventsSubscription();\n }\n\n call<M extends ApiCallMethod>(\n method: M,\n params?: ApiCallParams<M>\n ): Observable<ApiCallResponse<M>> {\n const message = createJsonRpcMessage(method, params);\n\n this.connection.ws.next(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n // JSON-RPC 2.0 response format\n if (msg.error) {\n // Handle both JSON-RPC 2.0 standard error (message) and TrueNAS error (reason)\n const errorMessage = getApiErrorMessage(msg.error, 'API call failed');\n throw new Error(errorMessage);\n }\n return msg.result as ApiCallResponse<M>;\n }),\n take(1)\n );\n }\n\n /**\n * Makes an API call and returns the job ID from the websocket event.\n * Used for v26 where API calls return null but job events contain the job ID.\n *\n * The job ID is extracted from the first job event where message_ids contains\n * the original request ID.\n *\n * @param method The API method to call\n * @param params The parameters for the API call\n * @returns Observable that emits the job ID when received from websocket events\n */\n callAndGetJobId<M extends ApiCallMethod>(\n method: M,\n params?: ApiCallParams<M>\n ): Observable<number> {\n const message = createJsonRpcMessage(method, params);\n\n this.connection.ws.next(message);\n\n // createJsonRpcMessage always returns a message with an id\n const requestId = message.id ?? '';\n\n // Listen for job events that contain our request ID in message_ids\n return this.jobEvents.pipe(\n filter(job => job.message_ids?.includes(requestId) ?? false),\n map(job => job.id),\n take(1)\n );\n }\n\n events(eventName: string) {\n this.authenticated.pipe(filter(Boolean), take(1)).subscribe(() => {\n const message = createJsonRpcMessage('core.subscribe', [eventName]);\n this.connection.ws.next(message);\n });\n\n return this.connection.messages().pipe(\n filter(res => {\n // JSON-RPC 2.0 collection_update format\n const params = res.params as CollectionUpdateParams | undefined;\n return (\n res.method === 'collection_update' &&\n params?.collection === eventName &&\n ['added', 'changed', 'removed'].includes(params?.msg || '') &&\n params.fields !== undefined\n );\n })\n );\n }\n\n /**\n * Convenience wrapper for auth.generate_token API call.\n */\n generateToken(\n ttl = 600,\n matchOrigin = false,\n singleUse = true\n ): Observable<string> {\n return this.call(TrueNasEndpoint.GenerateToken, [\n ttl,\n {},\n matchOrigin,\n singleUse,\n ]);\n }\n\n trackJob(jobId: number): Observable<Job> {\n const completedStates = [\n JobState.Success,\n JobState.Failed,\n JobState.Aborted,\n JobState.Error,\n JobState.Finished,\n ];\n\n // First, get the current job state\n const currentJobState$ = this.call('core.get_jobs' as ApiCallMethod, [\n [['id', '=', jobId]],\n ]).pipe(\n map(jobs => (jobs as Job[])[0]),\n filter(job => job !== undefined)\n );\n\n // Then track ongoing updates\n const jobUpdates$ = this.jobEvents.pipe(\n filter(job => job.id === jobId),\n takeWhile(job => !completedStates.includes(job.state), true) // Include the final state\n );\n\n // Start with current state, then merge with updates\n // This ensures we don't miss already-completed jobs\n return currentJobState$.pipe(\n switchMap(currentJob => {\n // If job is already complete, just return it\n if (completedStates.includes(currentJob.state)) {\n return of(currentJob);\n }\n // Otherwise, return current state and continue tracking\n return merge(of(currentJob), jobUpdates$);\n })\n );\n }\n\n private initializeJobEventsSubscription() {\n this.authenticated.pipe(filter(Boolean), take(1)).subscribe(() => {\n const message = createJsonRpcMessage('core.subscribe', ['core.get_jobs']);\n this.connection.ws.next(message);\n });\n }\n}\n","export enum TrueNasAuthMechanism {\n ApiKey = 'API_KEY_PLAIN',\n Password = 'PASSWORD_PLAIN',\n Otp = 'OTP_TOKEN',\n Token = 'TOKEN_PLAIN',\n}\n","/**\n * Stable, framework-agnostic authentication error codes. The authenticator throws\n * `AuthError` with one of these codes; consumers translate the code to a localized\n * message (the `message` here is a plain-English default / fallback).\n */\nexport enum AuthErrorCode {\n PasswordAuthFailed = 'PASSWORD_AUTH_FAILED',\n OtpAuthFailed = 'OTP_AUTH_FAILED',\n ApiKeyAuthFailed = 'API_KEY_AUTH_FAILED',\n FullAdminRequired = 'FULL_ADMIN_REQUIRED',\n}\n\nexport class AuthError extends Error {\n constructor(\n readonly code: AuthErrorCode,\n message: string,\n ) {\n super(message);\n this.name = 'AuthError';\n // Preserve `instanceof AuthError` across down-level compilation targets.\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n","import {\n BehaviorSubject,\n filter,\n finalize,\n map,\n of,\n switchMap,\n take,\n tap,\n throwError,\n} from 'rxjs';\nimport { TrueNasConnection } from '@/connection/truenas-connection';\nimport { TrueNasAuthMechanism } from '@/enums/truenas-auth-mechanism.enum';\nimport { UserRole } from '@/enums/user-role.enum';\nimport { AuthError, AuthErrorCode } from '@/errors/auth.errors';\nimport { getApiErrorMessage } from '@/types/api-error.type';\nimport { ApiKeyCreate } from '@/types/api-key-create.type';\nimport { AuthResponse, AuthResponseType } from '@/types/auth.type';\nimport { createJsonRpcMessage } from '@/utils/jsonrpc.utils';\nimport { randomUUID, withId } from '@/utils/utils';\n\nconst throwOnAuthenticationFailure = (code: AuthErrorCode, message: string) =>\n switchMap((response: AuthResponse) => {\n if (response.response_type === AuthResponseType.AuthErr) {\n return throwError(() => new AuthError(code, message));\n }\n\n return of(response);\n });\n\n/**\n * TrueNAS authenticator using the JSON-RPC 2.0 protocol.\n *\n * It handles authentication using the JSON-RPC 2.0 message format.\n */\nexport class TrueNasAuthenticator {\n static readonly DefaultSessionLifetime = 300; // in seconds (5 minutes)\n\n /**\n * whether or not the system is currently authenticated and accessible.\n * this is a `BehaviorSubject` instead of a signal, since we use its `getValue` method\n * throughout a lot of the codebase.\n */\n authenticated$ = new BehaviorSubject(false);\n\n /**\n * `true` when the system is currently being authenticated to, and `false` otherwise.\n * does not indicate whatsoever if authentication is successful or not, just whether the process\n * is ongoing.\n */\n authenticating$ = new BehaviorSubject(false);\n\n credentials = { username: '', password: '', key: '' };\n sessionLifetime = TrueNasAuthenticator.DefaultSessionLifetime;\n\n constructor(private connection: TrueNasConnection) {\n this.connection.opened\n .pipe(\n filter(\n isOpen =>\n !!(\n isOpen &&\n this.credentials?.username &&\n (this.credentials.password || this.credentials.key)\n )\n ),\n switchMap(() => {\n if (this.credentials.password) {\n return this.loginWithUserPass(\n this.credentials.username,\n this.credentials.password\n );\n }\n return this.loginWithApiKey({\n username: this.credentials.username,\n key: this.credentials.key,\n });\n })\n )\n .subscribe();\n connection.closed.subscribe(() => {\n this.sessionLifetime = TrueNasAuthenticator.DefaultSessionLifetime;\n this.authenticated$.next(false);\n });\n }\n\n loginWithUserPass(username: string, password: string) {\n // Versioned API uses auth.login_ex with a single object parameter\n const message = createJsonRpcMessage('auth.login_ex', [\n {\n mechanism: TrueNasAuthMechanism.Password,\n username,\n password,\n },\n ]);\n\n this.authenticating$.next(true);\n this.connection.send(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n if (msg.error) {\n const errorMessage = getApiErrorMessage(\n msg.error,\n 'Authentication failed'\n );\n throw new Error(errorMessage);\n }\n return msg.result as AuthResponse;\n }),\n throwOnAuthenticationFailure(\n AuthErrorCode.PasswordAuthFailed,\n 'TrueNAS authentication failed. Please verify your TrueNAS user credentials and try again.'\n ),\n tap(res => {\n if (res.response_type === AuthResponseType.Success) {\n if (\n res.user_info?.privilege.roles.$set.includes(UserRole.FullAdmin)\n ) {\n this.credentials.username = username;\n this.credentials.password = password;\n this.sessionLifetime =\n res.user_info?.attributes?.preferences?.lifetime ??\n TrueNasAuthenticator.DefaultSessionLifetime;\n this.authenticated$.next(true);\n } else {\n this.logout();\n this.authenticated$.next(false);\n throw new AuthError(\n AuthErrorCode.FullAdminRequired,\n 'User account must have full admin privileges'\n );\n }\n }\n }),\n finalize(() => {\n this.authenticating$.next(false);\n }),\n take(1)\n );\n }\n\n loginWithOtp(code: string) {\n // Versioned API uses auth.login_ex with a single object parameter\n const message = createJsonRpcMessage('auth.login_ex', [\n {\n mechanism: TrueNasAuthMechanism.Otp,\n otp_token: code,\n },\n ]);\n\n this.authenticating$.next(true);\n this.connection.send(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n if (msg.error) {\n const errorMessage = getApiErrorMessage(\n msg.error,\n 'Authentication failed'\n );\n throw new Error(errorMessage);\n }\n return msg.result as AuthResponse;\n }),\n tap(res => {\n if (res?.response_type === AuthResponseType.Success) {\n this.sessionLifetime =\n res.user_info?.attributes?.preferences?.lifetime ??\n TrueNasAuthenticator.DefaultSessionLifetime;\n this.authenticated$.next(true);\n }\n }),\n throwOnAuthenticationFailure(\n AuthErrorCode.OtpAuthFailed,\n 'TrueNAS authentication failed. Please verify your one-time passcode and try again.'\n ),\n finalize(() => {\n this.authenticating$.next(false);\n }),\n take(1)\n );\n }\n\n loginWithApiKey(credentials: { username: string; key: string }) {\n const { username, key } = credentials;\n // Versioned API uses auth.login_ex with a single object parameter\n const message = createJsonRpcMessage('auth.login_ex', [\n {\n mechanism: TrueNasAuthMechanism.ApiKey,\n username,\n api_key: key,\n },\n ]);\n\n this.authenticating$.next(true);\n this.connection.send(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n if (msg.error) {\n const errorMessage = getApiErrorMessage(\n msg.error,\n 'Authentication failed'\n );\n throw new Error(errorMessage);\n }\n return msg.result as AuthResponse;\n }),\n throwOnAuthenticationFailure(\n AuthErrorCode.ApiKeyAuthFailed,\n 'TrueNAS authentication failed. Has the TrueNAS Connect API key been removed from your TrueNAS server?'\n ),\n tap(res => {\n this.credentials.username = username;\n this.credentials.key = key;\n this.sessionLifetime =\n res.user_info?.attributes?.preferences?.lifetime ??\n TrueNasAuthenticator.DefaultSessionLifetime;\n this.authenticated$.next(true);\n }),\n finalize(() => {\n this.authenticating$.next(false);\n }),\n take(1)\n );\n }\n\n newApiKey(username: string) {\n const message = createJsonRpcMessage('api_key.create', [\n { name: `tnc-${randomUUID()}`, username },\n ]);\n\n this.connection.send(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n if (msg.error) {\n const errorMessage = getApiErrorMessage(\n msg.error,\n 'Failed to create API key'\n );\n throw new Error(errorMessage);\n }\n return msg.result as ApiKeyCreate;\n }),\n take(1)\n );\n }\n\n logout() {\n const message = createJsonRpcMessage('auth.logout');\n\n this.connection.send(message);\n\n // createJsonRpcMessage always returns a message with an id\n const messageId = message.id ?? '';\n\n return this.connection.messages().pipe(\n withId(messageId),\n map(msg => {\n if (msg.error) {\n return false;\n }\n return msg.result as boolean;\n }),\n tap(success => {\n this.sessionLifetime = TrueNasAuthenticator.DefaultSessionLifetime;\n this.authenticated$.next(!success);\n }),\n take(1)\n );\n }\n}\n","/**\n * Minimal logging surface the client writes to. Consumers inject their own\n * implementation; the package never depends on a concrete logging library.\n * Method signatures mirror the TrueNAS Connect UI `Log` service so call sites\n * port unchanged.\n */\nexport interface Logger {\n trace(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\n/** Logger that forwards to the global `console`. */\nexport const consoleLogger: Logger = {\n trace: (message, ...args) => console.trace(message, ...args),\n debug: (message, ...args) => console.debug(message, ...args),\n info: (message, ...args) => console.info(message, ...args),\n warn: (message, ...args) => console.warn(message, ...args),\n error: (message, ...args) => console.error(message, ...args),\n};\n\nconst noop = (): void => undefined;\n\n/** Logger that discards everything. The default when no logger is provided. */\nexport const noopLogger: Logger = {\n trace: noop,\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n};\n","export function isHttpStatusError(reason: string): boolean {\n return ['404', '502', '503'].some(code => reason.includes(code));\n}\n\nexport function getHttpError(reason: string): string {\n if (reason.includes('404')) {\n return 'API endpoint not found - System may not support versioned API';\n }\n if (reason.includes('502')) {\n return 'Bad Gateway - System may be starting up';\n }\n if (reason.includes('503')) {\n return 'Service Unavailable - System may be overloaded';\n }\n return '';\n}\n\nexport function getWebSocketError(code: number): string {\n switch (code) {\n case 1000: // Normal closure\n return 'Connection closed normally';\n case 1001: // Going away\n return 'Server is shutting down or restarting';\n case 1002: // Protocol error\n return 'Protocol error - Invalid WebSocket communication';\n case 1003: // Unsupported data\n return 'Unsupported data format received';\n case 1006: // Abnormal closure\n return 'Connection lost unexpectedly - Check network connectivity';\n case 1007: // Invalid frame payload data\n return 'Invalid data received from server';\n case 1008: // Policy violation\n return 'Connection terminated due to policy violation';\n case 1009: // Message too big\n return 'Message size exceeded server limits';\n case 1011: // Internal error\n return 'Server encountered an internal error';\n case 1012: // Service restart\n return 'Service is restarting';\n case 1013: // Try again later\n return 'Server temporarily overloaded - Try again later';\n case 1015: // TLS handshake failure\n return 'TLS/Certificate error - Certificate may be expired';\n default:\n // Fallback for unknown codes or network issues\n return 'Network connection failed - Check IP address and connectivity';\n }\n}\n","import { webSocket, WebSocketSubjectConfig } from 'rxjs/webSocket';\nimport { TrueNasMessage } from '@/types/truenas-message.type';\n\nexport class TrueNasSocket {\n socket = webSocket(this.config);\n\n constructor(public config: WebSocketSubjectConfig<TrueNasMessage>) {}\n\n messages() {\n return this.socket;\n }\n\n next(msg: TrueNasMessage) {\n this.socket.next(msg);\n }\n\n complete() {\n this.socket.complete();\n }\n}\n","import {\n BehaviorSubject,\n Observable,\n Subject,\n timeout,\n takeUntil,\n retry,\n race,\n take,\n interval,\n combineLatest,\n tap,\n filter,\n switchMap,\n catchError,\n of,\n startWith,\n map,\n concat,\n throwError,\n shareReplay,\n distinctUntilChanged,\n} from 'rxjs';\nimport { Logger, noopLogger } from '@/logger';\nimport { TrueNasMessage } from '@/types/truenas-message.type';\nimport { createJsonRpcMessage } from '@/utils/jsonrpc.utils';\nimport { getHttpError, getWebSocketError, isHttpStatusError } from '@/utils/truenas-connection.utils';\nimport { TrueNasSocket } from '@/connection/truenas-socket';\n\n// helper types\ninterface ActiveConnection {\n ws: TrueNasSocket;\n hostname: string;\n}\n\ninterface ConnectionError extends Error {\n hostname?: string;\n}\n\ntype Connection =\n | ActiveConnection & { state: 'active' }\n | ConnectionError & { state: 'error' }\n | { state: 'closed' };\n\nconst tenSeconds = 10 * 1000;\nconst twentySeconds = 20 * 1000;\n\nexport class TrueNasConnection {\n // compatibility properties\n opened = new BehaviorSubject(false);\n closed = new Subject<void>();\n hostname = new BehaviorSubject<string>('');\n ws!: TrueNasSocket;\n connectionAttempts = new BehaviorSubject<number>(0);\n lastErrorMessage = new BehaviorSubject<string | null>(null);\n\n /**\n * emits when the connection is manually closed.\n */\n closeConnection = new Subject<void>();\n\n /**\n * the connection only establishes a socket while this gate is `true`. consumers\n * flip it via `setEnabled()` (the app maps `SystemState.Active -> true`).\n */\n private enabled$ = new BehaviorSubject<boolean>(false);\n\n /**\n * observable which emits the current gate value and only emits again when it changes.\n */\n enabledChange$ = this.enabled$.pipe(distinctUntilChanged());\n\n /**\n * observable that handles the entire connection lifecycle.\n */\n connection$ = this.enabledChange$.pipe(\n switchMap(enabled => {\n if (enabled) {\n // if enabled, establish a connection and push it down the pipeline.\n return this.connect().pipe(\n map((conn) => makeActiveConnection(conn.ws, conn.hostname)),\n );\n }\n\n // if not enabled, emit a closed connection.\n return of(closedConnection);\n }),\n switchMap((connection): Observable<Connection> => {\n // if the connection we received is a closed connection from upstream,\n // we just need to pass that along.\n if (connection.state !== 'active') {\n return of(connection);\n }\n\n // initialize the websocket if it's active and, upon\n // receiving confirmation that it's active, return it.\n const initMessage = createJsonRpcMessage('core.set_options', [\n { legacy_jobs: false },\n ]);\n\n connection.ws.next(initMessage);\n return connection.ws.messages().pipe(\n filter(msg => msg.id === initMessage.id),\n take(1),\n map(() => connection),\n )\n }),\n // in the event of an error, do two things:\n // 1. emit an errored connection downstream to inform consumers that\n // the connection is dead.\n // 2. re-throw an error so the downstream `retry` will re-subscribe.\n catchError((err: ConnectionError): Observable<Connection> => {\n this.logger.error('All connections failed - retrying', { message: err?.message, hostname: err?.hostname });\n return concat(\n of<Connection>(makeConnectionError(err.message, err.hostname)),\n // since this error will immediately get caught by `retry` there's no reason to build a real error.\n throwError(() => null)\n );\n }),\n retry(),\n // start with a closed connection.\n startWith<Connection>(closedConnection),\n // prevent multiple subscriptions from re-evaluating the entire pipeline.\n shareReplay({ bufferSize: 1, refCount: false }),\n takeUntil(this.closeConnection),\n );\n\n /**\n * observable which is either an instance of the current `TrueNasSocket` used for the connection\n * or `null` if the connection is closed or errored for any reason.\n */\n ws$: Observable<TrueNasSocket | null> = this.connection$.pipe(\n map(conn => conn.state === 'active' ? conn.ws : null),\n );\n\n /**\n * current hostname we used to build the URL for this connection.\n */\n hostname$: Observable<string | null> = this.connection$.pipe(\n map(conn => conn.state === 'active' ? conn.hostname : null),\n );\n\n /**\n * emits error messages from the connection pipeline. this is used to derive the\n * corresponding compatibility property.\n */\n lastErrorMessage$: Observable<string | null> = this.connection$.pipe(\n map(conn => conn.state === 'error' ? conn.message : null),\n );\n\n /**\n * emits when the connection state *changes*, so it'll never emit `true` twice\n * in a row nor `false` twice in a row.\n */\n opened$: Observable<boolean> = this.connection$.pipe(\n map(conn => conn.state),\n distinctUntilChanged(),\n map(state => state === 'active'),\n );\n\n /**\n * emits when the connection state goes from open to closed.\n */\n closed$: Observable<void> = this.connection$.pipe(\n map(conn => conn.state),\n distinctUntilChanged(),\n filter(state => state === 'closed' || state === 'error'),\n map(() => { }),\n );\n\n /**\n * whether to display a connection error to the user — the **live** signal.\n *\n * `true` only while `connection$` is currently a `ConnectionError`, meaning the entire race +\n * retry cycle has been exhausted for all hostnames right now; it flips back to `false` once a\n * connection is re-established. individual socket losses during a race are expected and not surfaced.\n *\n * NOTE: this is NOT the same as the `hasExhaustedRetries()` method, which is a **cumulative**\n * snapshot (see there). The two can disagree — prefer this observable for \"is the connection\n * errored right now?\".\n */\n hasConnectionError$: Observable<boolean> = this.connection$.pipe(\n map(conn => conn.state === 'error'),\n takeUntil(this.closeConnection),\n );\n\n /**\n * observable which always emits messages from the current socket.\n */\n messages$ = this.ws$.pipe(\n filter(ws => ws !== null),\n switchMap(ws => ws.messages()),\n );\n\n constructor(\n initialEnabled: boolean,\n private readonly hostnames: string[],\n readonly systemUuid: string,\n readonly websocketPath: string,\n readonly systemName?: string,\n readonly retryDelay: number = tenSeconds,\n readonly maxRetry: number = 3,\n readonly logger: Logger = noopLogger,\n ) {\n // create a ping observable which only stops when the connection is *manually* closed.\n // we can safely do this since we can assume that if `closeConnection` fires, then\n // this connection is probably dead and will be recreated.\n //\n // ping behavior across connection failures is resilient, because `ws$` is resilient\n combineLatest([\n interval(twentySeconds),\n this.ws$,\n ]).pipe(\n takeUntil(this.closeConnection),\n ).subscribe(([, ws]) => {\n if (ws) {\n const pingMessage = createJsonRpcMessage('core.ping');\n ws.next(pingMessage);\n }\n });\n\n // compatibility property subscriptions\n this.opened$.subscribe(val => this.opened.next(val));\n this.closed$.subscribe(() => this.closed.next());\n this.hostname$.pipe(filter(Boolean)).subscribe(name => this.hostname.next(name));\n // we assign `ws` here for compatibility with downstream consumers, since they expect\n // a plain property. ideally, this would be reactive, but this is a compat property.\n this.ws$.pipe(filter(Boolean)).subscribe(ws => this.ws = ws);\n this.lastErrorMessage$.subscribe(msg => this.lastErrorMessage.next(msg));\n\n this.enabled$.next(initialEnabled);\n }\n\n /**\n * whether the connection has exhausted its retries — the **cumulative** snapshot, read\n * synchronously. (Formerly `hasConnectionError()`; renamed to disambiguate it from the\n * live `hasConnectionError$` observable, with which it can disagree.)\n *\n * `true` when the lifetime `connectionAttempts` count exceeds `hostnames.length * maxRetry`,\n * OR when an error message is currently set. Ported from the source (tncui) behavior.\n *\n * CAVEAT (pre-existing tncui behavior, preserved here): `connectionAttempts` only ever grows —\n * it is never reset on a successful (re)connection — so over a long-lived connection with\n * reconnect churn this can latch to `true` even while the connection is currently healthy. For\n * \"is it errored right now?\" use the live `hasConnectionError$` observable instead.\n */\n hasExhaustedRetries(): boolean {\n const attemptsExhausted =\n this.connectionAttempts.value > this.hostnames.length * this.maxRetry;\n\n const hasErrorMessage = this.lastErrorMessage.value !== null;\n\n return attemptsExhausted || hasErrorMessage;\n }\n\n /**\n * enables or disables the connection gate. the app calls this when its `SystemState`\n * changes (mapping `SystemState.Active -> true`, everything else -> `false`).\n */\n setEnabled(enabled: boolean): void {\n this.enabled$.next(enabled);\n }\n\n /**\n * compatibility method which just returns the public `messages$` observable.\n * this is used in a few places in the codebase that relied upon the old implementation.\n */\n messages() {\n return this.messages$;\n }\n\n /**\n * sends a message over the current websocket OR queues it to send once\n * the next websocket is opened. messages sent via `send` will not be lost until after\n * the next `closed$` emission.\n */\n send(message: TrueNasMessage) {\n return this.ws$.pipe(\n filter(ws => ws !== null),\n take(1),\n ).subscribe(ws => ws.next(message))\n }\n\n /**\n * manually closes this connection and prevents it from retrying/opening any more.\n */\n close() {\n this.closeConnection.next();\n this.closeConnection.complete();\n }\n\n /**\n * helper method which creates an `Observable` which emits an `ActiveConnection`\n * after it establishes a connection to the given hostname. the observable will emit an\n * error if the connection is never established and will not complete until unsubscribed from or closed.\n */\n private createSocket(hostname: string): Observable<ActiveConnection> {\n const url = `wss://${hostname}${this.websocketPath}`;\n return new Observable<ActiveConnection>(subscriber => {\n const ws = new TrueNasSocket({\n url,\n openObserver: {\n next: () => {\n subscriber.next({ ws, hostname, });\n },\n },\n closeObserver: {\n next: (event: CloseEvent) => {\n const reason = event.reason || '';\n let errorMessage: string;\n if (isHttpStatusError(reason)) {\n errorMessage = getHttpError(reason);\n } else {\n errorMessage = getWebSocketError(event.code);\n }\n\n // we let individual sockets update the total connection attempts, since\n // this can be safely done in parallel and also the compatibility property\n // `hasExhaustedRetries` wants to check the *total* number.\n this.connectionAttempts.next(this.connectionAttempts.value + 1);\n\n subscriber.error(makeConnectionError(errorMessage, hostname));\n }\n }\n });\n\n // create a subscription to get the websocket to open, since it's lazy,\n // but ignore all errors since this is just to kick `openObserver` off.\n // when the socket dies, this will complete.\n const startupSub = ws.socket.subscribe({\n error: () => { }\n });\n\n return () => {\n // make sure we clean up the startup subscription here\n startupSub.unsubscribe();\n ws.complete();\n }\n }).pipe(\n // retry logic:\n // * if a connection is not established in 10 seconds, consider that an error\n timeout({ first: tenSeconds }),\n // * if an error happens, wait `retryDelay` before trying again.\n // after `maxRetry` retries, give up.\n retry({ count: this.maxRetry, delay: this.retryDelay }),\n );\n }\n\n /**\n * helper function which actually performs the parallel connection `race`.\n */\n private connect(): Observable<ActiveConnection> {\n return race(\n this.hostnames.map(this.createSocket.bind(this))\n ).pipe(\n tap(conn => this.logger.debug(`TrueNas socket opened to ${conn.hostname}.`)),\n takeUntil(this.closeConnection),\n );\n }\n}\n\n/**\n * helper function which wraps a socket and hostname into an active `Connection`.\n */\nconst makeActiveConnection = (ws: TrueNasSocket, hostname: string): Connection => ({\n ws,\n hostname,\n state: 'active',\n});\n\n/**\n * helper function which wraps a message and hostname into an errored `Connection`.\n */\nconst makeConnectionError = (message: string, hostname?: string): Connection => ({\n name: 'ConnectionError',\n message,\n hostname,\n state: 'error',\n});\n\n/**\n * the canonical closed connection.\n */\nconst closedConnection: Connection = { state: 'closed' };\n","/**\n * API Version Configuration\n * Defines the supported range of TrueNAS API versions.\n *\n * Update these values as new API versions are released and old ones are deprecated.\n *\n * Version format:\n * - Legacy (v25.x): vYY.MM.PATCH where MM is month (01-12)\n * - Example: v25.10.0 = October 2025, patch 0\n * - New (v26+): vYY.MINOR.PATCH where MINOR is minor version (0-99)\n * - Example: v26.0.0 = 2026, minor 0, patch 0\n * - Breaking changes only in yearly releases (v26.0.0, v27.0.0, etc.)\n */\nexport const apiVersionConfig = {\n /**\n * Minimum supported API version.\n * Systems with older API versions will be rejected.\n */\n MIN_SUPPORTED_VERSION: 'v25.10.0',\n\n /**\n * Maximum supported API version.\n * Systems with newer API versions will be rejected.\n */\n MAX_SUPPORTED_VERSION: 'v26.0.0',\n\n /**\n * Fallback version to use when version discovery fails due to CORS/network errors.\n * When /api/versions returns HTTP status 0 (CORS block, network down, etc.),\n * the system will attempt to connect using this version as a best-effort fallback.\n *\n * WARNING: Status 0 errors have multiple causes:\n * - CORS policy blocking the request\n * - Network disconnected\n * - DNS lookup failure\n * - Server unreachable\n *\n * Using this fallback means systems with genuine network issues will still\n * attempt connection (and fail during WebSocket handshake instead of immediately).\n */\n FALLBACK_VERSION: 'v25.10.0',\n} as const;\n","/**\n * Response from the /api/versions endpoint\n * Returns an array of version strings directly\n * Example: [\"v25.10.0\", \"v25.10.1\", \"v26.0.0\"]\n */\nexport type ApiVersionResponse = string[];\n\n/**\n * Parsed API version information\n *\n * Version format:\n * - Legacy (v25.x): vYY.MM.PATCH (e.g., v25.10.0 = October 2025, patch 0)\n * - New (v26+): vYY.MINOR.PATCH (e.g., v26.0.0 = 2026, minor 0, patch 0)\n *\n * Note: The second segment has different semantics based on the year:\n * - Year 25: month (1-12)\n * - Year 26+: minor version (0-99)\n */\nexport interface ApiVersion {\n /** Full version string (e.g., \"v26.0.0\") */\n version: string;\n /** Two-digit year (e.g., 26 = 2026) */\n year: number;\n /**\n * Second version segment (semantics depend on year):\n * - For v25.x: month (1-12, e.g., 10 = October)\n * - For v26+: minor version (0-99)\n */\n minor: number;\n /** Patch version number (e.g., 0, 1, 2) */\n patch: number;\n /** WebSocket path for this version (e.g., \"/api/v26.0.0\") */\n websocketPath: string;\n}\n\n/**\n * Version compatibility status\n */\nexport enum VersionCompatibility {\n Compatible = 'compatible',\n TooOld = 'too-old',\n TooNew = 'too-new',\n Invalid = 'invalid',\n}\n","import { apiVersionConfig } from '@/config/api-version.config';\nimport {\n ApiVersion,\n VersionCompatibility,\n} from '@/types/api-version.type';\n\nexport const legacyCutoffYear = 25;\n\n/**\n * Parses a version string into an ApiVersion object.\n *\n * Supports two versioning schemes:\n * - Legacy (v25.x): vYY.MM.PATCH where MM is month (1-12)\n * - New (v26+): vYY.MINOR.PATCH where MINOR is minor version (0-99)\n *\n * @param versionString - Version string (e.g., \"v25.10.0\" or \"v26.0.0\")\n * @returns Parsed ApiVersion object, or null if invalid format\n *\n * @example\n * parseApiVersion('v25.10.0')\n * // Returns: { version: 'v25.10.0', year: 25, minor: 10, patch: 0, websocketPath: '/api/v25.10.0' }\n *\n * parseApiVersion('v26.0.0')\n * // Returns: { version: 'v26.0.0', year: 26, minor: 0, patch: 0, websocketPath: '/api/v26.0.0' }\n */\nexport function parseApiVersion(versionString: string): ApiVersion | null {\n // Version format: vYY.MINOR.PATCH (where MINOR is 1-2 digits)\n const regex = /^v(\\d{2})\\.(\\d+)\\.(\\d+)$/;\n const match = versionString.match(regex);\n\n if (!match) {\n return null;\n }\n\n const [, yearStr, minorStr, patchStr] = match;\n const year = parseInt(yearStr, 10);\n const minor = parseInt(minorStr, 10);\n const patch = parseInt(patchStr, 10);\n\n // For legacy v25.x and similar versions, validate month range (1-12)\n // For v26+ versions, minor can be 0-99\n if (year <= legacyCutoffYear) {\n if (minor < 1 || minor > 12) {\n return null;\n }\n } else if (year >= 26) {\n if (minor < 0 || minor > 99) {\n return null;\n }\n }\n\n return {\n version: versionString,\n year,\n minor,\n patch,\n websocketPath: `/api/${versionString}`,\n };\n}\n\n/**\n * Compares two API versions.\n *\n * @param a - First version\n * @param b - Second version\n * @returns Negative if a < b, zero if a === b, positive if a > b\n *\n * @example\n * compareVersions(v25_10_0, v26_0_0) // Returns negative (v25.10.0 < v26.0.0)\n * compareVersions(v26_1_0, v26_0_0) // Returns positive (v26.1.0 > v26.0.0)\n */\nexport function compareVersions(a: ApiVersion, b: ApiVersion): number {\n if (a.year !== b.year) {\n return a.year - b.year;\n }\n if (a.minor !== b.minor) {\n return a.minor - b.minor;\n }\n return a.patch - b.patch;\n}\n\n/**\n * Checks the compatibility status of a version against supported range.\n *\n * @param version - Version to check\n * @returns VersionCompatibility status\n *\n * @example\n * checkVersionCompatibility(v24_04_0) // Returns VersionCompatibility.TooOld\n * checkVersionCompatibility(v26_0_0) // Returns VersionCompatibility.Compatible\n * checkVersionCompatibility(v27_0_0) // Returns VersionCompatibility.TooNew\n */\nexport function checkVersionCompatibility(\n version: ApiVersion\n): VersionCompatibility {\n const minVersion = parseApiVersion(apiVersionConfig.MIN_SUPPORTED_VERSION);\n const maxVersion = parseApiVersion(apiVersionConfig.MAX_SUPPORTED_VERSION);\n\n if (!minVersion || !maxVersion) {\n return VersionCompatibility.Invalid;\n }\n\n if (compareVersions(version, minVersion) < 0) {\n return VersionCompatibility.TooOld;\n }\n\n if (compareVersions(version, maxVersion) > 0) {\n return VersionCompatibility.TooNew;\n }\n\n return VersionCompatibility.Compatible;\n}\n\n/**\n * Checks if a version is within the supported range.\n *\n * @param version - Version to check\n * @returns True if version is supported (compatible)\n *\n * @example\n * isVersionSupported(v26_0_0) // Returns true\n * isVersionSupported(v24_04_0) // Returns false\n */\nexport function isVersionSupported(version: ApiVersion): boolean {\n return checkVersionCompatibility(version) === VersionCompatibility.Compatible;\n}\n\n/**\n * Filters an array of versions to only those within the supported range.\n *\n * @param versions - Array of versions to filter\n * @returns Array containing only compatible versions\n *\n * @example\n * filterCompatibleVersions([v24_04_0, v25_10_0, v27_04_0])\n * // Returns: [v25_10_0] (only compatible version)\n */\nexport function filterCompatibleVersions(versions: ApiVersion[]): ApiVersion[] {\n return versions.filter(isVersionSupported);\n}\n\n/**\n * Selects the latest compatible version from an array of version strings.\n *\n * @param versionStrings - Array of version strings\n * @returns Latest compatible version, or null if none found\n *\n * @example\n * selectLatestCompatibleVersion(['v25.10.0', 'v25.10.1', 'v26.0.0'])\n * // Returns: { version: 'v26.0.0', ... } (highest compatible version)\n */\nexport function selectLatestCompatibleVersion(\n versionStrings: string[]\n): ApiVersion | null {\n // Parse all version strings\n const parsedVersions = versionStrings\n .map(parseApiVersion)\n .filter((v): v is ApiVersion => v !== null);\n\n if (parsedVersions.length === 0) {\n return null;\n }\n\n // Filter to only compatible versions\n const compatibleVersions = filterCompatibleVersions(parsedVersions);\n\n if (compatibleVersions.length === 0) {\n return null;\n }\n\n // Sort by version (highest first) and return the latest\n const sortedVersions = compatibleVersions.sort((a, b) =>\n compareVersions(b, a)\n );\n return sortedVersions[0];\n}\n\n/**\n * Gets the WebSocket path for a given API version.\n *\n * @param version - API version\n * @returns WebSocket path (e.g., \"/api/v26.0.0\")\n *\n * @example\n * getWebSocketPath(v26_0_0) // Returns \"/api/v26.0.0\"\n */\nexport function getWebSocketPath(version: ApiVersion): string {\n return version.websocketPath;\n}\n","/**\n * Abstract base class for TrueNAS API clients\n *\n * Provides common implementation for all API versions. Version-specific clients\n * extend this class and override factory methods to provide custom implementations\n * when needed.\n *\n * This class manages the lifecycle of all TrueNAS connection components:\n * - TrueNasConnection (WebSocket connection management)\n * - TrueNasApi (API call handling)\n * - TrueNasAuthenticator (authentication)\n */\n\nimport { TrueNasApi } from '@/api/truenas-api';\nimport { TrueNasAuthenticator } from '@/auth/truenas-authenticator';\nimport { TrueNasConnection } from '@/connection/truenas-connection';\nimport { Logger, noopLogger } from '@/logger';\nimport { ApiVersion } from '@/types/api-version.type';\nimport { OperationMappings } from '@/types/operation-mappings.interface';\nimport { getWebSocketPath } from '@/utils/api-version.utils';\n\nexport abstract class TrueNasApiClient {\n /** API version information for this client */\n readonly version: ApiVersion;\n\n /** WebSocket connection manager */\n readonly connection: TrueNasConnection;\n\n /** Authentication manager */\n readonly authenticator: TrueNasAuthenticator;\n\n /** API call handler */\n readonly api: TrueNasApi;\n\n /**\n * Version-agnostic operation mappings\n *\n * Provides a unified interface for operations that differ between API versions.\n * Each version-specific client implements these operations using its own endpoints.\n *\n * Usage:\n * ```typescript\n * truenas.ops.containerQuery().subscribe(containers => ...);\n * truenas.ops.containerStart(id).subscribe(jobId => ...);\n * ```\n */\n readonly ops: OperationMappings;\n\n /** System UUID */\n protected readonly uuid: string;\n\n /** System hostnames (primary and fallback) */\n protected readonly hostnames: string[];\n\n /**\n * Initial connection gate, forwarded to the connection. The app flips it later\n * via `connection.setEnabled()` (mapping its `SystemState.Active -> true`).\n */\n protected readonly enabled: boolean;\n\n /** System name (optional) */\n protected readonly systemName: string | undefined;\n\n /** Logger forwarded to the connection (defaults to a no-op). */\n protected readonly logger: Logger;\n\n constructor(\n uuid: string,\n hostnames: string[],\n version: ApiVersion,\n enabled: boolean,\n systemName?: string,\n logger: Logger = noopLogger\n ) {\n this.uuid = uuid;\n this.hostnames = hostnames;\n this.version = version;\n this.enabled = enabled;\n this.systemName = systemName;\n this.logger = logger;\n\n // Initialize components using factory methods\n // Subclasses can override factory methods to provide version-specific implementations\n //\n // IMPORTANT: Initialization order matters! Do not reorder these lines.\n // Each component may depend on previous ones:\n // 1. connection (no dependencies)\n // 2. authenticator (depends on connection)\n // 3. api (depends on authenticator and connection)\n // 4. ops (depends on api)\n this.connection = this.createConnection();\n this.authenticator = this.createAuthenticator();\n this.api = this.createApi();\n this.ops = this.createOperations();\n }\n\n /**\n * Get current connection status.\n * @returns true if WebSocket is connected\n */\n get connected(): boolean {\n return this.connection.opened.getValue();\n }\n\n /**\n * Get current authentication status.\n * @returns true if authenticated with the system\n */\n get authenticated(): boolean {\n return this.authenticator.authenticated$.getValue();\n }\n\n /**\n * Close the WebSocket connection.\n * Connection will automatically retry if retry is enabled.\n */\n close(): void {\n this.connection.close();\n }\n\n /**\n * Factory method to create the WebSocket connection.\n * Override in subclasses to provide version-specific connection handling.\n */\n protected createConnection(): TrueNasConnection {\n const websocketPath = getWebSocketPath(this.version);\n return new TrueNasConnection(\n this.enabled,\n this.hostnames,\n this.uuid,\n websocketPath,\n this.systemName,\n undefined, // retryDelay (use default)\n undefined, // maxRetry (use default)\n this.logger\n );\n }\n\n /**\n * Factory method to create the API handler.\n * Override in subclasses to provide version-specific API implementations.\n */\n protected createApi(): TrueNasApi {\n return new TrueNasApi(this.authenticator.authenticated$, this.connection);\n }\n\n /**\n * Factory method to create the authenticator.\n * Override in subclasses to provide version-specific authentication.\n */\n protected createAuthenticator(): TrueNasAuthenticator {\n return new TrueNasAuthenticator(this.connection);\n }\n\n /**\n * Factory method to create version-specific operation mappings.\n * Must be implemented by subclasses to provide version-specific implementations.\n *\n * This is the primary extension point for handling API differences between versions.\n */\n protected abstract createOperations(): OperationMappings;\n}\n","/**\n * TrueNAS API Client for v25.10.x\n *\n * Handles all v25.10 patch versions (v25.10.0, v25.10.1, v25.10.2, etc.).\n * Patch versions are backward compatible, so one client implementation\n * handles all patches within the v25.10.x series.\n *\n * To add version-specific behavior, override the factory methods:\n * - createConnection() - for connection-specific changes\n * - createApi() - for API method changes\n * - createAuthenticator() - for authentication changes\n * - createOperations() - for version-specific operation mappings\n */\n\nimport { map, switchMap } from 'rxjs';\nimport { TrueNasApiClient } from '@/client/truenas-api-client';\nimport { TrueNasEndpoint } from '@/enums/truenas-endpoint.enum';\nimport { Container } from '@/types/container.type';\nimport { OperationMappings } from '@/types/operation-mappings.interface';\nimport {\n VirtualInstanceQuery,\n VirtualInstanceType,\n} from '@/types/virtual-instance-query.type';\n\n/**\n * API client for TrueNAS API v25.10.x\n *\n * Protocol: JSON-RPC 2.0\n * WebSocket Path: /api/v25.10.{patch}\n *\n * Container operations use virt.instance.* APIs:\n * - containerQuery → virt.instance.query (filtered by type=CONTAINER)\n * - containerStart → virt.instance.start (emits Job updates)\n * - containerStop → virt.instance.stop (emits Job updates)\n * - containerRestart → virt.instance.restart (emits Job updates)\n */\nexport class TrueNasApiClientV2510 extends TrueNasApiClient {\n /**\n * Create v25.10-specific operation mappings\n *\n * virt.instance.* APIs return boolean (true) but emit job events via websocket.\n * We use callAndGetJobId to capture the job ID, then track the job.\n * All operations emit Job updates until the operation completes.\n */\n protected createOperations(): OperationMappings {\n return {\n containerQuery: () =>\n this.api\n .call(TrueNasEndpoint.VirtualInstanceQuery, [\n [['type', '=', VirtualInstanceType.Container]],\n ])\n .pipe(map(instances => instances.map(this.toContainer))),\n\n containerStart: (id: string) =>\n this.api\n .callAndGetJobId(TrueNasEndpoint.VirtualInstanceStart, [id])\n .pipe(switchMap(jobId => this.api.trackJob(jobId))),\n\n containerStop: (id, options) =>\n this.api\n .callAndGetJobId(TrueNasEndpoint.VirtualInstanceStop, [id, options])\n .pipe(switchMap(jobId => this.api.trackJob(jobId))),\n\n containerRestart: (id, options) =>\n this.api\n .callAndGetJobId(TrueNasEndpoint.VirtualInstanceRestart, [\n id,\n options,\n ])\n .pipe(switchMap(jobId => this.api.trackJob(jobId))),\n };\n }\n\n /**\n * Transform VirtualInstanceQuery to unified Container type\n */\n private toContainer(instance: VirtualInstanceQuery): Container {\n return {\n id: instance.id,\n name: instance.name,\n status: instance.status,\n autostart: instance.autostart,\n cpu: instance.cpu,\n memory: instance.memory,\n image: instance.image,\n };\n }\n}\n","/**\n * TrueNAS API Client for v26.X.Y\n *\n * Handles all v26 versions (v26.0.0, v26.0.1, v26.1.2, etc.).\n * Minor AND patch versions are backward compatible, so one client implementation\n * handles all patches within the v26 series.\n *\n * To add version-specific behavior, override the factory methods:\n * - createConnection() - for connection-specific changes\n * - createApi() - for API method changes\n * - createAuthenticator() - for authentication changes\n * - createOperations() - for version-specific operation mappings\n */\n\nimport { concat, from, map, switchMap, toArray } from 'rxjs';\nimport { TrueNasApiClient } from '@/client/truenas-api-client';\nimport { TrueNasEndpoint } from '@/enums/truenas-endpoint.enum';\nimport { AppState } from '@/types/app-query.type';\nimport { Container, ContainerQueryV26 } from '@/types/container.type';\nimport { OperationMappings } from '@/types/operation-mappings.interface';\n\n/**\n * API client for TrueNAS API v26\n *\n * Protocol: JSON-RPC 2.0\n * WebSocket Path: /api/v26.{minor}.{patch}\n *\n * Container operations use native container.* APIs:\n * - containerQuery → container.query (with response transformation)\n * - containerStart → container.start (synchronous, emits null)\n * - containerStop → container.stop (emits Job updates)\n * - containerRestart → container.stop + container.start (emits Job, then null)\n */\nexport class TrueNasApiClientV26 extends TrueNasApiClient {\n /**\n * Map v26 status state string to AppState enum\n */\n private static mapStatus(state: string): AppState {\n switch (state.toUpperCase()) {\n case 'RUNNING':\n return AppState.Running;\n case 'STOPPED':\n return AppState.Stopped;\n case 'STOPPING':\n return AppState.Stopping;\n default:\n return AppState.Stopped;\n }\n }\n\n /**\n * Create v26-specific operation mappings\n *\n * Operations return Observable<Job | null>:\n * - Async operations emit Job updates until complete\n * - Sync operations emit null once\n */\n protected createOperations(): OperationMappings {\n return {\n containerQuery: () =>\n this.api\n .call(TrueNasEndpoint.ContainerQuery, [[]])\n .pipe(map(containers => containers.map(this.toContainer))),\n\n // container.start is synchronous in v26.0.0 - emit null\n containerStart: (id: string) =>\n this.api\n .call(TrueNasEndpoint.ContainerStart, [parseInt(id, 10)])\n .pipe(map(() => null)),\n\n // container.stop emits job updates\n containerStop: (id, options) =>\n this.api\n .callAndGetJobId(TrueNasEndpoint.ContainerStop, [\n parseInt(id, 10),\n {\n force: options.force,\n force_after_timeout: options.force,\n },\n ])\n .pipe(switchMap(jobId => this.api.trackJob(jobId))),\n\n // v26.0.0 doesn't have container.restart - chain stop + start\n // Emits Job updates during stop, then null when start completes\n containerRestart: (id, options) => {\n const numericId = parseInt(id, 10);\n return this.api\n .callAndGetJobId(TrueNasEndpoint.ContainerStop, [\n numericId,\n {\n force: options.force,\n force_after_timeout: options.force,\n },\n ])\n .pipe(\n // Track stop job until it completes\n switchMap(stopJobId => this.api.trackJob(stopJobId)),\n // Collect all job updates to ensure stop fully completes\n toArray(),\n // Re-emit job updates, then call start after stop is done\n switchMap(jobUpdates =>\n concat(\n from(jobUpdates),\n this.api\n .call(TrueNasEndpoint.ContainerStart, [numericId])\n .pipe(map(() => null))\n )\n )\n );\n },\n };\n }\n\n /**\n * Transform v26 ContainerQueryV26 to unified Container type\n */\n private toContainer(container: ContainerQueryV26): Container {\n return {\n id: container.id.toString(),\n name: container.name,\n status: TrueNasApiClientV26.mapStatus(container.status.state),\n autostart: container.autostart,\n description: container.description,\n // cpu and memory are not available in v26 container.query\n // image is not available in v26 container.query\n };\n }\n}\n","import { apiVersionConfig } from '@/config/api-version.config';\n\n/**\n * Base class for all version discovery errors\n */\nexport abstract class VersionDiscoveryError extends Error {\n constructor(\n message: string,\n readonly hostname: string\n ) {\n super(message);\n this.name = this.constructor.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when all available API versions are older than minimum supported version\n */\nexport class VersionTooOldError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly availableVersions: string[],\n readonly minRequired: string = apiVersionConfig.MIN_SUPPORTED_VERSION\n ) {\n super(\n `System API version is too old. Minimum supported: ${minRequired}. Available versions: ${availableVersions.join(\n ', '\n )}`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when all available API versions are newer than maximum supported version\n */\nexport class VersionTooNewError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly availableVersions: string[],\n readonly maxSupported: string = apiVersionConfig.MAX_SUPPORTED_VERSION\n ) {\n super(\n `System API version is too new. Maximum supported: ${maxSupported}. Available versions: ${availableVersions.join(\n ', '\n )}`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when the /api/versions endpoint returns 404\n */\nexport class VersionEndpointNotFoundError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly statusCode: number = 404\n ) {\n super(\n `Version discovery endpoint not found: ${hostname} (HTTP ${String(\n statusCode\n )})`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when version discovery request times out\n */\nexport class VersionDiscoveryTimeoutError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly timeoutMs: number = 5000\n ) {\n super(\n `Cannot connect to system (timeout after ${String(\n timeoutMs\n )}ms): ${hostname}`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when version discovery fails due to network error\n */\nexport class VersionDiscoveryNetworkError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly originalError: Error\n ) {\n super(\n `Network error connecting to ${hostname}: ${originalError.message}`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when no compatible versions are found in the response\n */\nexport class NoCompatibleVersionsError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly availableVersions: string[],\n readonly supportedRange: { min: string; max: string } = {\n min: apiVersionConfig.MIN_SUPPORTED_VERSION,\n max: apiVersionConfig.MAX_SUPPORTED_VERSION,\n }\n ) {\n super(\n `No compatible API versions available. Supported: ${\n supportedRange.min\n } to ${supportedRange.max}. Available: ${availableVersions.join(', ')}`,\n hostname\n );\n }\n}\n\n/**\n * Thrown when the API response is malformed or invalid\n */\nexport class InvalidVersionResponseError extends VersionDiscoveryError {\n constructor(\n hostname: string,\n readonly reason: string\n ) {\n super(`Invalid API response format from ${hostname}: ${reason}`, hostname);\n }\n}\n","import { Observable, defer, from, throwError } from 'rxjs';\nimport { catchError, map, shareReplay } from 'rxjs/operators';\nimport { apiVersionConfig } from '@/config/api-version.config';\nimport {\n InvalidVersionResponseError,\n NoCompatibleVersionsError,\n VersionDiscoveryError,\n VersionDiscoveryNetworkError,\n VersionDiscoveryTimeoutError,\n VersionEndpointNotFoundError,\n VersionTooNewError,\n VersionTooOldError,\n} from '@/errors/version-discovery.errors';\nimport { Logger, noopLogger } from '@/logger';\nimport {\n ApiVersion,\n ApiVersionResponse,\n VersionCompatibility,\n} from '@/types/api-version.type';\nimport {\n checkVersionCompatibility,\n compareVersions,\n filterCompatibleVersions,\n parseApiVersion,\n} from '@/utils/api-version.utils';\n\nconst discoveryTimeoutMs = 5000;\n\n/** True when `error` looks like `{ name }` equal to `expected` (robust to DOMException). */\nfunction hasErrorName(error: unknown, expected: string): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'name' in error &&\n (error as { name?: unknown }).name === expected\n );\n}\n\n/**\n * Discovers available API versions from TrueNAS systems.\n *\n * Performs a `fetch` GET to the `/api/versions` endpoint and:\n * - Parses version strings\n * - Filters to compatible versions (within MIN/MAX range)\n * - Selects the latest compatible version\n * - Caches results per hostname\n * - Classifies failures into typed {@link VersionDiscoveryError} subclasses\n *\n * This is the framework-agnostic replacement for the app's Angular `HttpClient`\n * service. Because `fetch` resolves (rather than rejects) on non-2xx responses and\n * throws a `TypeError` on network/CORS/unreachable failures, the error contract\n * differs from the original: a network/CORS/unreachable failure surfaces as a\n * {@link VersionDiscoveryNetworkError} — the sentinel the client factory keys on for\n * its CORS fallback (replacing the old `HttpErrorResponse.status === 0` check).\n */\nexport class VersionDiscovery {\n private versionCache = new Map<string, Observable<ApiVersion>>();\n\n constructor(private readonly logger: Logger = noopLogger) {}\n\n /**\n * Discovers the API version for a given hostname.\n *\n * Makes a GET request to `https://{hostname}/api/versions` and returns the latest\n * compatible version. Results are cached per hostname; the cache entry is removed\n * on failure so the next call retries.\n *\n * @param hostname - The TrueNAS system hostname (e.g., \"truenas.local\")\n * @returns Observable that emits the selected ApiVersion\n * @throws VersionDiscoveryError subclasses for specific failure scenarios\n */\n discoverVersion(hostname: string): Observable<ApiVersion> {\n const cached = this.versionCache.get(hostname);\n if (cached) {\n this.logger.info('Version discovery cache hit', { hostname });\n return cached;\n }\n\n const url = `https://${hostname}/api/versions`;\n this.logger.info('Starting version discovery', { hostname, url });\n\n const discovery$ = defer(() => from(this.fetchVersions(hostname))).pipe(\n map(versionStrings => this.selectVersion(hostname, versionStrings)),\n catchError((error: unknown) => {\n // Remove from cache on error to allow retry on next call.\n this.versionCache.delete(hostname);\n this.logger.error('Version discovery failed', { hostname, error });\n return throwError(() => this.classify(error, hostname));\n }),\n shareReplay(1)\n );\n\n this.versionCache.set(hostname, discovery$);\n return discovery$;\n }\n\n /**\n * Clears the version cache for a specific hostname or all hostnames.\n *\n * @param hostname - Optional hostname to clear. If omitted, clears all cached versions.\n */\n clearCache(hostname?: string): void {\n if (hostname) {\n this.versionCache.delete(hostname);\n } else {\n this.versionCache.clear();\n }\n }\n\n /**\n * Performs the `fetch` with a 5-second `AbortController` timeout and inspects the\n * *resolved* Response (fetch does not reject on non-2xx). Throws\n * `VersionEndpointNotFoundError` on 404 and `InvalidVersionResponseError` on any\n * other non-2xx or a body that is not an array of strings; lets `TypeError`\n * (network) and `AbortError` (timeout) bubble to `classify`.\n *\n * The element-type check matters: a reachable server returning an array of\n * non-strings (e.g. `[1, 2, 3]`) would otherwise reach `parseApiVersion`, whose\n * `.match()` throws a `TypeError` on a non-string — which `classify` would then\n * misfile as a network error. Validating here keeps it an `InvalidVersionResponseError`.\n */\n private async fetchVersions(hostname: string): Promise<ApiVersionResponse> {\n const url = `https://${hostname}/api/versions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), discoveryTimeoutMs);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n\n if (response.status === 404) {\n throw new VersionEndpointNotFoundError(hostname);\n }\n if (!response.ok) {\n throw new InvalidVersionResponseError(\n hostname,\n `HTTP ${String(response.status)}`\n );\n }\n\n const body: unknown = await response.json();\n if (!Array.isArray(body) || !body.every(v => typeof v === 'string')) {\n throw new InvalidVersionResponseError(\n hostname,\n 'Response was not an array of version strings'\n );\n }\n\n return body as ApiVersionResponse;\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Parses, filters, and selects the latest compatible version from the raw response\n * array. Throws the appropriate typed error when no valid/compatible version exists.\n */\n private selectVersion(\n hostname: string,\n versionStrings: ApiVersionResponse\n ): ApiVersion {\n this.logger.info('Version discovery response received', {\n hostname,\n versionCount: versionStrings.length,\n versions: versionStrings,\n });\n\n const parsedVersions = versionStrings\n .map(parseApiVersion)\n .filter((v): v is ApiVersion => v !== null);\n\n if (parsedVersions.length === 0) {\n this.logger.error('No valid versions in response', {\n hostname,\n versionStrings,\n });\n throw new InvalidVersionResponseError(\n hostname,\n 'No valid API versions found in response'\n );\n }\n\n const compatibleVersions = filterCompatibleVersions(parsedVersions);\n\n this.logger.info('Version compatibility check', {\n hostname,\n total: parsedVersions.length,\n compatible: compatibleVersions.length,\n supportedRange: {\n min: apiVersionConfig.MIN_SUPPORTED_VERSION,\n max: apiVersionConfig.MAX_SUPPORTED_VERSION,\n },\n });\n\n if (compatibleVersions.length === 0) {\n const allTooOld = parsedVersions.every(\n v => checkVersionCompatibility(v) === VersionCompatibility.TooOld\n );\n const allTooNew = parsedVersions.every(\n v => checkVersionCompatibility(v) === VersionCompatibility.TooNew\n );\n\n const availableVersions = parsedVersions.map(v => v.version);\n\n if (allTooOld) {\n this.logger.warn('All available versions too old', {\n hostname,\n availableVersions,\n });\n throw new VersionTooOldError(hostname, availableVersions);\n } else if (allTooNew) {\n this.logger.warn('All available versions too new', {\n hostname,\n availableVersions,\n });\n throw new VersionTooNewError(hostname, availableVersions);\n } else {\n this.logger.warn('No compatible versions found', {\n hostname,\n availableVersions,\n });\n throw new NoCompatibleVersionsError(hostname, availableVersions);\n }\n }\n\n // Select latest compatible version (sort by highest first).\n const sortedVersions = compatibleVersions.sort((a, b) =>\n compareVersions(b, a)\n );\n const selectedVersion = sortedVersions[0];\n\n this.logger.info('Version selected', {\n hostname,\n selected: selectedVersion.version,\n websocketPath: selectedVersion.websocketPath,\n });\n\n return selectedVersion;\n }\n\n /**\n * Maps a raw failure to a typed {@link VersionDiscoveryError}. Errors thrown by\n * `fetchVersions`/`selectVersion` are already typed and pass through unchanged; a\n * `fetch` `TypeError` becomes {@link VersionDiscoveryNetworkError} (the CORS/network\n * sentinel), an `AbortError` becomes {@link VersionDiscoveryTimeoutError}, and any\n * other failure (e.g. an unparseable body's `SyntaxError`) becomes\n * {@link InvalidVersionResponseError}.\n */\n private classify(error: unknown, hostname: string): Error {\n // Already a typed version-discovery error (from fetchVersions or selectVersion).\n if (error instanceof VersionDiscoveryError) {\n return error;\n }\n\n // Abort → timeout.\n if (hasErrorName(error, 'AbortError')) {\n this.logger.warn('Version discovery timeout', { hostname });\n return new VersionDiscoveryTimeoutError(hostname);\n }\n\n // fetch network/CORS/unreachable failures reject with a TypeError. This is the\n // new \"status 0\" sentinel the client factory detects for its CORS fallback.\n if (error instanceof TypeError) {\n this.logger.warn('Version discovery network error', {\n hostname,\n message: error.message,\n });\n return new VersionDiscoveryNetworkError(hostname, error);\n }\n\n // Unparseable body (a `SyntaxError` from `response.json()`) or anything else.\n const reason = error instanceof Error ? error.message : 'Unknown error';\n this.logger.warn('Version discovery failed with unknown error', {\n hostname,\n reason,\n });\n return new InvalidVersionResponseError(\n hostname,\n `Versioned API not available. This system may be running an older TrueNAS version (requires v25.10.0+). ${reason}`\n );\n }\n}\n","import { firstValueFrom } from 'rxjs';\nimport { TrueNasApiClient } from '@/client/truenas-api-client';\nimport { TrueNasApiClientV2510 } from '@/client/truenas-api-client-v25-10';\nimport { TrueNasApiClientV26 } from '@/client/truenas-api-client-v26';\nimport { apiVersionConfig } from '@/config/api-version.config';\nimport { VersionDiscoveryNetworkError } from '@/errors/version-discovery.errors';\nimport { Logger, noopLogger } from '@/logger';\nimport { ApiVersion } from '@/types/api-version.type';\nimport { legacyCutoffYear, parseApiVersion } from '@/utils/api-version.utils';\nimport { VersionDiscovery } from '@/version-discovery';\n\n/** Options for {@link createTrueNasClient}. */\nexport interface CreateClientOptions {\n /** System UUID. */\n uuid: string;\n /** Hostnames to connect to — primary first, then fallbacks. */\n hostnames: string[];\n /**\n * Initial connection gate. The client only opens a socket while this is `true`;\n * flip it later via `client.connection.setEnabled()`. (The app maps its\n * `SystemState.Active -> true`.)\n */\n enabled: boolean;\n /** Optional system name (used only for log context). */\n systemName?: string;\n /**\n * Optional logger; defaults to a no-op. Forwarded to version discovery and,\n * through the client, to the connection.\n */\n logger?: Logger;\n}\n\n/**\n * Creates a version-specific TrueNAS API client.\n *\n * 1. Discovers the API version from the primary hostname (`GET /api/versions`).\n * 2. Selects the matching client implementation (`v25.10.x` -> `TrueNasApiClientV2510`,\n * `v26.x.y` -> `TrueNasApiClientV26`).\n * 3. Instantiates and returns it.\n *\n * Resolves exactly once with a single client instance — dispose of it with\n * `client.close()` when done.\n *\n * @returns a Promise that resolves with the created client, or rejects with a\n * {@link VersionDiscoveryError} subclass (or a client-selection error).\n * Rejects if `hostnames` is empty.\n */\nexport async function createTrueNasClient(\n opts: CreateClientOptions\n): Promise<TrueNasApiClient> {\n const { uuid, hostnames, systemName } = opts;\n const logger = opts.logger ?? noopLogger;\n\n if (!hostnames || hostnames.length === 0) {\n throw new Error(\n `Cannot create client for system ${uuid}: hostnames array is empty`\n );\n }\n\n const primaryHostname = hostnames[0];\n const versionDiscovery = new VersionDiscovery(logger);\n\n logger.info('Creating versioned API client', {\n uuid: uuid.slice(0, 8),\n hostname: primaryHostname,\n systemName,\n });\n\n let version: ApiVersion;\n try {\n version = await firstValueFrom(\n versionDiscovery.discoverVersion(primaryHostname)\n );\n logger.info('API version discovered, instantiating client', {\n uuid: uuid.slice(0, 8),\n version: version.version,\n websocketPath: version.websocketPath,\n });\n } catch (error) {\n const errorMessage = errorMessageOrDefault(error, 'Unknown error');\n\n // CORS / network fallback (load-bearing).\n //\n // `fetch` surfaces network/CORS/unreachable failures as a\n // `VersionDiscoveryNetworkError` (the replacement for the Angular\n // `HttpClient`'s `status === 0`). IMPORTANT: TrueNAS v25.10.0 does not\n // have CORS enabled for the /api/versions endpoint, so discovery is\n // blocked there. This fallback MUST remain until v25.10.0 is no longer in\n // the supported range (i.e. once MIN_SUPPORTED_VERSION > v25.10.0).\n if (!(error instanceof VersionDiscoveryNetworkError)) {\n // For other errors (version too old/too new, invalid response, etc.), re-throw.\n logger.error('Version discovery failed', {\n uuid: uuid.slice(0, 8),\n hostname: primaryHostname,\n error: errorMessage,\n errorType:\n error instanceof Error ? error.constructor.name : typeof error,\n });\n throw error;\n }\n\n const fallbackVersionString = apiVersionConfig.FALLBACK_VERSION;\n const fallbackVersion = parseApiVersion(fallbackVersionString);\n\n if (!fallbackVersion) {\n logger.error('Invalid fallback version configuration', {\n uuid: uuid.slice(0, 8),\n hostname: primaryHostname,\n fallbackVersion: fallbackVersionString,\n });\n throw error;\n }\n\n logger.warn(\n 'Version discovery failed with a network error (possible CORS or ' +\n 'network issue), falling back to assumed version',\n {\n uuid: uuid.slice(0, 8),\n hostname: primaryHostname,\n fallbackVersion: fallbackVersionString,\n originalError: errorMessage,\n warning:\n 'A network error has multiple causes (CORS, network down, DNS ' +\n 'failure). The connection may still fail during the WebSocket handshake.',\n }\n );\n\n version = fallbackVersion;\n }\n\n return instantiateClientForVersion(version, opts, logger);\n}\n\n/**\n * Maps a discovered version to its client implementation by `year.month` (v25.x)\n * or `year` (v26+): `25.10` -> V2510, `26` -> V26.\n */\nfunction instantiateClientForVersion(\n version: ApiVersion,\n opts: CreateClientOptions,\n logger: Logger\n): TrueNasApiClient {\n const { uuid, hostnames, enabled, systemName } = opts;\n\n let versionKey: string;\n if (version.year <= legacyCutoffYear) {\n // Legacy scheme (vYY.MM): all patches of a month share one client.\n const monthPadded = version.minor.toString().padStart(2, '0');\n versionKey = `${version.year.toString()}.${monthPadded}`;\n } else {\n // Yearly scheme (v26+): the year selects the client.\n versionKey = version.year.toString();\n }\n\n switch (versionKey) {\n case '25.10':\n logger.info('Instantiating TrueNasApiClientV2510', {\n uuid: uuid.slice(0, 8),\n version: version.version,\n versionKey,\n });\n return new TrueNasApiClientV2510(\n uuid,\n hostnames,\n version,\n enabled,\n systemName,\n logger\n );\n\n case '26':\n logger.info('Instantiating TrueNasApiClientV26', {\n uuid: uuid.slice(0, 8),\n version: version.version,\n versionKey,\n });\n return new TrueNasApiClientV26(\n uuid,\n hostnames,\n version,\n enabled,\n systemName,\n logger\n );\n\n default:\n // Should not happen: discovery only yields compatible versions. Defensive.\n logger.error('No client implementation for version', {\n uuid: uuid.slice(0, 8),\n version: version.version,\n versionKey,\n });\n throw new Error(\n `No client implementation for API version ${version.version}. ` +\n `Supported versions: v25.10.x, v26.x.y. Version key: ${versionKey}`\n );\n }\n}\n\n/**\n * Extracts a user-facing message from an unknown error, falling back to `fallback`.\n * (Inlined pure subset of the app's `errorMessageOrDefault`.)\n */\nfunction errorMessageOrDefault(error: unknown, fallback: string): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (\n typeof error === 'object' &&\n error !== null &&\n 'message' in error &&\n typeof (error as { message: unknown }).message === 'string'\n ) {\n return (error as { message: string }).message;\n }\n return fallback;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@truenas/api-client",
3
+ "version": "1.0.0",
4
+ "description": "Framework-agnostic TypeScript client for the TrueNAS JSON-RPC 2.0 WebSocket API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "sideEffects": false,
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "prepack": "yarn build",
28
+ "dev": "tsup --watch",
29
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.spec.json",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "test:coverage": "vitest run --coverage",
33
+ "lint": "eslint .",
34
+ "check:dist": "node scripts/check-dist.mjs",
35
+ "check:package": "publint && yarn pack -o package.tgz && attw package.tgz && rm -f package.tgz",
36
+ "clean": "rm -rf dist"
37
+ },
38
+ "peerDependencies": {
39
+ "rxjs": "^7.8.0"
40
+ },
41
+ "devDependencies": {
42
+ "@arethetypeswrong/cli": "^0.18.5",
43
+ "@eslint/js": "^9.18.0",
44
+ "@semantic-release/github": "^11.0.1",
45
+ "@types/node": "^22.0.0",
46
+ "@vitest/coverage-v8": "^3.0.0",
47
+ "conventional-changelog-conventionalcommits": "^8.0.0",
48
+ "eslint": "^9.18.0",
49
+ "publint": "^0.3.21",
50
+ "rxjs": "~7.8.0",
51
+ "semantic-release": "^24.2.1",
52
+ "tsup": "^8.3.5",
53
+ "typescript": "~5.9.3",
54
+ "typescript-eslint": "^8.20.0",
55
+ "vitest": "^3.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=22"
59
+ },
60
+ "packageManager": "yarn@4.10.3",
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/truenas/api-client-ts.git"
67
+ },
68
+ "keywords": [
69
+ "truenas",
70
+ "api",
71
+ "client",
72
+ "websocket",
73
+ "json-rpc"
74
+ ]
75
+ }