@truenas/api-client 9.0.0 → 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -4
- package/dist/{chunk-LFNAVARU.js → chunk-K7BL6J3Z.js} +205 -49
- package/dist/chunk-K7BL6J3Z.js.map +1 -0
- package/dist/{chunk-TSO2EHF3.cjs → chunk-U6V2Y3AE.cjs} +205 -47
- package/dist/chunk-U6V2Y3AE.cjs.map +1 -0
- package/dist/index.cjs +40 -36
- package/dist/index.d.cts +40 -5
- package/dist/index.d.ts +40 -5
- package/dist/index.js +1 -1
- package/dist/testing/index.cjs +28 -16
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +15 -2
- package/dist/testing/index.d.ts +15 -2
- package/dist/testing/index.js +18 -6
- package/dist/testing/index.js.map +1 -1
- package/dist/{truenas-auth-mechanism.enum-CW5Wwz1s.d.cts → truenas-auth-mechanism.enum-Bp2XMMwK.d.cts} +65 -18
- package/dist/{truenas-auth-mechanism.enum-CW5Wwz1s.d.ts → truenas-auth-mechanism.enum-Bp2XMMwK.d.ts} +65 -18
- package/package.json +2 -2
- package/dist/chunk-LFNAVARU.js.map +0 -1
- package/dist/chunk-TSO2EHF3.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -89,7 +89,9 @@ client.api.call('alert.dismiss', ['uuid-1']); // params checked against the me
|
|
|
89
89
|
client.api.call('nope.nope'); // ✗ compile error
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
A failed call errors with middleware's own
|
|
92
|
+
A failed call errors with a `TrueNasApiError`. Its `message` is middleware's own
|
|
93
|
+
reason, and the rest of the error is there too: `errname` (such as `EINVAL`),
|
|
94
|
+
and `data.extra`, where a validation error names the fields it rejected.
|
|
93
95
|
|
|
94
96
|
## Queries
|
|
95
97
|
|
|
@@ -116,7 +118,9 @@ client.api.job('pool.dataset.export_key', ['tank/encrypted']).subscribe(job => {
|
|
|
116
118
|
```
|
|
117
119
|
|
|
118
120
|
The stream emits each state and completes when the job finishes. A failed job
|
|
119
|
-
completes too, with `state` and `error` set.
|
|
121
|
+
completes too, with `state` and `error` set. A job method refused before it
|
|
122
|
+
starts a job, such as for a validation error, errors with a `TrueNasApiError`
|
|
123
|
+
instead, because there is no job to report.
|
|
120
124
|
|
|
121
125
|
## Events
|
|
122
126
|
|
|
@@ -129,7 +133,20 @@ client.api.events('app.query').subscribe(change => {
|
|
|
129
133
|
|
|
130
134
|
Changes are typed per collection and discriminated on `msg`. One server
|
|
131
135
|
subscription is shared by every subscriber and re-registered after each
|
|
132
|
-
reconnect.
|
|
136
|
+
reconnect. When the last subscriber leaves, it is released with
|
|
137
|
+
`core.unsubscribe`.
|
|
138
|
+
|
|
139
|
+
Event sources take their arguments after the name, typed from the method's
|
|
140
|
+
`subscriptionParams`:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
client.api.events('filesystem.file_tail_follow', { path: '/var/log/messages' })
|
|
144
|
+
.subscribe(line => append(line.fields.data));
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A source with different arguments is a separate subscription. If middleware
|
|
148
|
+
ends a source, the stream completes. If it rejects the arguments, the stream
|
|
149
|
+
errors with a `TrueNasApiError`.
|
|
133
150
|
|
|
134
151
|
## Working across versions
|
|
135
152
|
|
|
@@ -164,6 +181,17 @@ client.ops.smbStatus({ infoLevel: 'SESSIONS' });
|
|
|
164
181
|
Per-version details, such as permissions that differ between releases, are in
|
|
165
182
|
the `OperationMappings` reference.
|
|
166
183
|
|
|
184
|
+
Some behaviour differs between versions in ways no type can express, because a
|
|
185
|
+
default is not part of a TypeScript signature. One worth knowing: at v28.0.0,
|
|
186
|
+
`config.save()` with no arguments includes the secret seed in the backup, where
|
|
187
|
+
at v25.10 and v27.0 the same call left it out. That seed decrypts every
|
|
188
|
+
password, private key and API key the backup contains, so treat such a backup
|
|
189
|
+
as a secret; pass `{ secretseed: false }` for the old behaviour, bearing in mind
|
|
190
|
+
a backup saved without it cannot be uploaded. These are recorded as they are
|
|
191
|
+
found in
|
|
192
|
+
[`src/generated/MANIFEST.md`](https://github.com/truenas/api-client-ts/blob/main/src/generated/MANIFEST.md),
|
|
193
|
+
which is in the repository rather than the published package.
|
|
194
|
+
|
|
167
195
|
## Connection
|
|
168
196
|
|
|
169
197
|
```typescript
|
|
@@ -212,7 +240,8 @@ the client is doing.
|
|
|
212
240
|
|
|
213
241
|
## Errors
|
|
214
242
|
|
|
215
|
-
- **Calls** error with
|
|
243
|
+
- **Calls, queries and job starts** error with `TrueNasApiError`. Its `message`
|
|
244
|
+
is middleware's reason, and `errname`, `code` and `data` carry the rest.
|
|
216
245
|
- **Logins** error with `AuthError`. Check `code` against `AuthErrorCode`.
|
|
217
246
|
- **`createTrueNasClient`** rejects with a `VersionDiscoveryError` subclass, such
|
|
218
247
|
as `VersionTooOldError`, `VersionTooNewError` or
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BehaviorSubject, filter, switchMap, catchError, EMPTY, tap, map, finalize, take, Subject, distinctUntilChanged, of, startWith, concat, throwError, retry, race, timer, skip, takeUntil, shareReplay, interval, Observable, timeout, TimeoutError, toArray, from, defer, firstValueFrom, share, merge, takeWhile } from 'rxjs';
|
|
1
|
+
import { BehaviorSubject, filter, switchMap, catchError, EMPTY, tap, map, finalize, take, Subject, distinctUntilChanged, of, startWith, concat, throwError, retry, race, timer, skip, takeUntil, shareReplay, interval, Observable, timeout, TimeoutError, toArray, from, defer, firstValueFrom, share, ignoreElements, merge, takeWhile, skipWhile, pairwise } from 'rxjs';
|
|
2
2
|
import { webSocket } from 'rxjs/webSocket';
|
|
3
3
|
import { map as map$1, catchError as catchError$1, shareReplay as shareReplay$1 } from 'rxjs/operators';
|
|
4
4
|
|
|
@@ -28,6 +28,34 @@ function getApiErrorMessage(error, fallback = "API call failed") {
|
|
|
28
28
|
return fallback;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// src/errors/api.errors.ts
|
|
32
|
+
var TrueNasApiError = class _TrueNasApiError extends Error {
|
|
33
|
+
constructor(frame, method) {
|
|
34
|
+
super(getApiErrorMessage(frame, "API call failed"));
|
|
35
|
+
this.frame = frame;
|
|
36
|
+
this.method = method;
|
|
37
|
+
this.name = "TrueNasApiError";
|
|
38
|
+
Object.setPrototypeOf(this, _TrueNasApiError.prototype);
|
|
39
|
+
}
|
|
40
|
+
/** The JSON-RPC code: `-32001` for a refused call, others for a malformed one. */
|
|
41
|
+
get code() {
|
|
42
|
+
return this.frame.code;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The TrueNAS payload — `errname`, `reason`, `extra`, `trace`.
|
|
46
|
+
*
|
|
47
|
+
* `undefined` for the codes middleware sends without one, such as
|
|
48
|
+
* `-32601 "Method does not exist"`.
|
|
49
|
+
*/
|
|
50
|
+
get data() {
|
|
51
|
+
return this.frame.data;
|
|
52
|
+
}
|
|
53
|
+
/** `data.errname`, e.g. `EINVAL`, or `undefined` when there is no payload. */
|
|
54
|
+
get errname() {
|
|
55
|
+
return this.frame.data?.errname;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
31
59
|
// src/types/job.type.ts
|
|
32
60
|
var JobState = /* @__PURE__ */ ((JobState2) => {
|
|
33
61
|
JobState2["Pending"] = "PENDING";
|
|
@@ -2222,6 +2250,19 @@ function createJsonRpcMessage(method, params) {
|
|
|
2222
2250
|
};
|
|
2223
2251
|
}
|
|
2224
2252
|
|
|
2253
|
+
// src/utils/subscription.utils.ts
|
|
2254
|
+
function subscriptionName(event, params) {
|
|
2255
|
+
return params === void 0 ? event : `${event}:${JSON.stringify(params, sortKeys)}`;
|
|
2256
|
+
}
|
|
2257
|
+
function sortKeys(_key, value) {
|
|
2258
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2259
|
+
return value;
|
|
2260
|
+
}
|
|
2261
|
+
return Object.fromEntries(
|
|
2262
|
+
Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2225
2266
|
// src/api/truenas-api.ts
|
|
2226
2267
|
var EVENT_KINDS = ["added", "changed", "removed"];
|
|
2227
2268
|
var TrueNasApi = class {
|
|
@@ -2231,8 +2272,13 @@ var TrueNasApi = class {
|
|
|
2231
2272
|
/**
|
|
2232
2273
|
* Stream of job events from websocket.
|
|
2233
2274
|
* JSON-RPC 2.0 events have structure: { method: 'collection_update', params: { collection, fields, ... } }
|
|
2275
|
+
*
|
|
2276
|
+
* Reads `messages()` when first subscribed, not when this is built, as every
|
|
2277
|
+
* other verb does. A consumer that wraps the connection's `messages()` after
|
|
2278
|
+
* the client is created — a debug panel mixing in mocked answers — is
|
|
2279
|
+
* otherwise heard by `call` and `events` but not by job tracking.
|
|
2234
2280
|
*/
|
|
2235
|
-
this.jobEvents = this.connection.messages().pipe(
|
|
2281
|
+
this.jobEvents = defer(() => this.connection.messages()).pipe(
|
|
2236
2282
|
filter(
|
|
2237
2283
|
(res) => res.method === "collection_update" && res.params?.collection === "core.get_jobs"
|
|
2238
2284
|
),
|
|
@@ -2241,11 +2287,20 @@ var TrueNasApi = class {
|
|
|
2241
2287
|
share()
|
|
2242
2288
|
);
|
|
2243
2289
|
/**
|
|
2244
|
-
* One shared stream per
|
|
2245
|
-
*
|
|
2246
|
-
* for lives on the server and is per socket, not per caller.
|
|
2290
|
+
* One shared stream per subscription — see {@link events}. Keyed by the
|
|
2291
|
+
* name *with* its arguments: a tail of one file is not a tail of another.
|
|
2247
2292
|
*/
|
|
2248
2293
|
this.eventStreams = /* @__PURE__ */ new Map();
|
|
2294
|
+
/**
|
|
2295
|
+
* Releases awaiting their `core.unsubscribe` reply, per name. Middleware
|
|
2296
|
+
* sends a source's release notice (`terminate`) before that reply, and a
|
|
2297
|
+
* notice names only the collection; so an error-free one arriving inside
|
|
2298
|
+
* that window belongs to the release, not to a later subscription. One per
|
|
2299
|
+
* window, which closes on the reply whether or not a notice came.
|
|
2300
|
+
*/
|
|
2301
|
+
this.releasing = /* @__PURE__ */ new Map();
|
|
2302
|
+
/** The live subscription per name, told when the server ends it. */
|
|
2303
|
+
this.serverEnds = /* @__PURE__ */ new Map();
|
|
2249
2304
|
this.initializeJobEventsSubscription();
|
|
2250
2305
|
}
|
|
2251
2306
|
/**
|
|
@@ -2283,11 +2338,7 @@ var TrueNasApi = class {
|
|
|
2283
2338
|
withId(messageId),
|
|
2284
2339
|
map((msg) => {
|
|
2285
2340
|
if (msg.error) {
|
|
2286
|
-
|
|
2287
|
-
msg.error,
|
|
2288
|
-
"API call failed"
|
|
2289
|
-
);
|
|
2290
|
-
throw new Error(errorMessage);
|
|
2341
|
+
throw new TrueNasApiError(msg.error, method);
|
|
2291
2342
|
}
|
|
2292
2343
|
return msg.result;
|
|
2293
2344
|
}),
|
|
@@ -2343,18 +2394,29 @@ var TrueNasApi = class {
|
|
|
2343
2394
|
* differs by version (v25.10 answers with the id, v27 with `null`), while
|
|
2344
2395
|
* the event correlation holds for both.
|
|
2345
2396
|
*
|
|
2346
|
-
* @returns
|
|
2397
|
+
* @returns the job's id; errors with a {@link TrueNasApiError} if the method is refused before a job starts
|
|
2347
2398
|
*/
|
|
2348
2399
|
callAndGetJobId(method, ...params) {
|
|
2349
2400
|
return defer(() => {
|
|
2350
2401
|
const message = createJsonRpcMessage(method, params[0]);
|
|
2351
|
-
const
|
|
2402
|
+
const started = this.jobEvents.pipe(
|
|
2352
2403
|
filter((job) => job.message_ids?.includes(message.id ?? "") ?? false),
|
|
2353
|
-
map((job) => job.id)
|
|
2354
|
-
|
|
2404
|
+
map((job) => job.id)
|
|
2405
|
+
);
|
|
2406
|
+
const refused = this.connection.messages().pipe(
|
|
2407
|
+
withId(message.id ?? ""),
|
|
2408
|
+
map((reply) => {
|
|
2409
|
+
if (reply.error) {
|
|
2410
|
+
throw new TrueNasApiError(reply.error, method);
|
|
2411
|
+
}
|
|
2412
|
+
}),
|
|
2413
|
+
ignoreElements()
|
|
2355
2414
|
);
|
|
2356
2415
|
const sending = this.connection.send(message);
|
|
2357
|
-
return
|
|
2416
|
+
return merge(started, refused).pipe(
|
|
2417
|
+
take(1),
|
|
2418
|
+
finalize(() => sending.unsubscribe())
|
|
2419
|
+
);
|
|
2358
2420
|
});
|
|
2359
2421
|
}
|
|
2360
2422
|
/**
|
|
@@ -2373,41 +2435,118 @@ var TrueNasApi = class {
|
|
|
2373
2435
|
switchMap((jobId) => this.trackJob(jobId))
|
|
2374
2436
|
);
|
|
2375
2437
|
}
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
* api.events('app.query').subscribe(event => {
|
|
2381
|
-
* if (event.msg === 'removed') return drop(event.id);
|
|
2382
|
-
* render(event.fields); // only reachable once narrowed
|
|
2383
|
-
* });
|
|
2384
|
-
* ```
|
|
2385
|
-
*
|
|
2386
|
-
* Narrow before reading `fields`: `removed` events almost never carry it.
|
|
2387
|
-
* Sources taking subscribe-time arguments are excluded; see {@link EventName}.
|
|
2388
|
-
*/
|
|
2389
|
-
events(event) {
|
|
2390
|
-
const existing = this.eventStreams.get(event);
|
|
2438
|
+
events(event, params) {
|
|
2439
|
+
const name = subscriptionName(event, params);
|
|
2440
|
+
this.listenForEndNotices();
|
|
2441
|
+
const existing = this.eventStreams.get(name);
|
|
2391
2442
|
if (existing) return existing;
|
|
2443
|
+
const forget = () => {
|
|
2444
|
+
if (this.eventStreams.get(name) === stream) this.eventStreams.delete(name);
|
|
2445
|
+
};
|
|
2392
2446
|
const stream = defer(() => {
|
|
2393
|
-
|
|
2394
|
-
|
|
2447
|
+
let subscriptionId;
|
|
2448
|
+
let released = false;
|
|
2449
|
+
let serverEnded = false;
|
|
2450
|
+
const ended = new Subject();
|
|
2451
|
+
this.serverEnds.set(name, ended);
|
|
2452
|
+
const subscribing = this.authenticated.pipe(distinctUntilChanged()).subscribe((isAuthenticated) => {
|
|
2453
|
+
subscriptionId = void 0;
|
|
2454
|
+
if (!isAuthenticated) return;
|
|
2455
|
+
const request = createJsonRpcMessage("core.subscribe", [name]);
|
|
2456
|
+
this.connection.messages().pipe(
|
|
2457
|
+
withId(request.id ?? ""),
|
|
2458
|
+
take(1),
|
|
2459
|
+
takeUntil(this.authenticated.pipe(filter((isUp) => !isUp)))
|
|
2460
|
+
).subscribe((reply) => {
|
|
2461
|
+
if (typeof reply.result !== "string") return;
|
|
2462
|
+
if (!released) subscriptionId = reply.result;
|
|
2463
|
+
else if (!serverEnded) this.unsubscribe(name, reply.result);
|
|
2464
|
+
});
|
|
2465
|
+
this.connection.send(request);
|
|
2395
2466
|
});
|
|
2396
|
-
|
|
2397
|
-
filter((res) => res.method === "collection_update"),
|
|
2398
|
-
map((res) => res.params),
|
|
2467
|
+
const updates = this.connection.messages().pipe(
|
|
2399
2468
|
filter(
|
|
2400
|
-
(
|
|
2469
|
+
(res) => res.method === "collection_update" && res.params?.collection === name
|
|
2401
2470
|
),
|
|
2471
|
+
map((res) => res.params),
|
|
2472
|
+
filter((params2) => EVENT_KINDS.includes(params2.msg)),
|
|
2473
|
+
// `merge` below ends only when both inputs do. The connection closing
|
|
2474
|
+
// for good ends this one; `ended` has to follow, or the stream never
|
|
2475
|
+
// completes and nothing tears down the resubscription.
|
|
2476
|
+
finalize(() => ended.complete())
|
|
2477
|
+
);
|
|
2478
|
+
const end = ended.pipe(
|
|
2479
|
+
map((notice) => {
|
|
2480
|
+
subscriptionId = void 0;
|
|
2481
|
+
serverEnded = true;
|
|
2482
|
+
if (notice.error) {
|
|
2483
|
+
throw new TrueNasApiError(
|
|
2484
|
+
{ code: -32001, message: notice.error.reason, data: notice.error },
|
|
2485
|
+
name
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
})
|
|
2489
|
+
);
|
|
2490
|
+
return merge(updates, end).pipe(
|
|
2491
|
+
takeWhile((change) => change !== void 0),
|
|
2402
2492
|
// `collection` is the subscription the caller already named; what is
|
|
2403
2493
|
// left is exactly the payload the directory declares, plus its tag.
|
|
2404
2494
|
map(({ collection, ...change }) => change),
|
|
2405
|
-
|
|
2495
|
+
// An ended stream stays ended (below), so the next `events()` call for
|
|
2496
|
+
// this name needs a new one.
|
|
2497
|
+
tap({ complete: forget, error: forget }),
|
|
2498
|
+
finalize(() => {
|
|
2499
|
+
released = true;
|
|
2500
|
+
subscribing.unsubscribe();
|
|
2501
|
+
if (this.serverEnds.get(name) === ended) this.serverEnds.delete(name);
|
|
2502
|
+
if (subscriptionId) this.unsubscribe(name, subscriptionId);
|
|
2503
|
+
})
|
|
2406
2504
|
);
|
|
2407
|
-
}).pipe(
|
|
2408
|
-
|
|
2505
|
+
}).pipe(
|
|
2506
|
+
share({
|
|
2507
|
+
resetOnRefCountZero: true,
|
|
2508
|
+
resetOnComplete: false,
|
|
2509
|
+
resetOnError: false
|
|
2510
|
+
})
|
|
2511
|
+
);
|
|
2512
|
+
this.eventStreams.set(name, stream);
|
|
2409
2513
|
return stream;
|
|
2410
2514
|
}
|
|
2515
|
+
/**
|
|
2516
|
+
* Release a subscription by the id `core.subscribe` answered with, holding
|
|
2517
|
+
* its notice window (see {@link releasing}) open until the reply.
|
|
2518
|
+
*/
|
|
2519
|
+
unsubscribe(name, subscriptionId) {
|
|
2520
|
+
const release = { noticed: false };
|
|
2521
|
+
this.releasing.set(name, [...this.releasing.get(name) ?? [], release]);
|
|
2522
|
+
const request = createJsonRpcMessage("core.unsubscribe", [subscriptionId]);
|
|
2523
|
+
this.connection.messages().pipe(
|
|
2524
|
+
withId(request.id ?? ""),
|
|
2525
|
+
take(1),
|
|
2526
|
+
takeUntil(this.authenticated.pipe(filter((isUp) => !isUp))),
|
|
2527
|
+
finalize(() => {
|
|
2528
|
+
const open = (this.releasing.get(name) ?? []).filter((r) => r !== release);
|
|
2529
|
+
if (open.length) this.releasing.set(name, open);
|
|
2530
|
+
else this.releasing.delete(name);
|
|
2531
|
+
})
|
|
2532
|
+
).subscribe();
|
|
2533
|
+
this.connection.send(request);
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Route each `notify_unsubscribed` once: an error-free one inside an open
|
|
2537
|
+
* release window is that release's, anything else ends the name's live
|
|
2538
|
+
* subscription. Installed on the first `events()` call, before any stream
|
|
2539
|
+
* listens.
|
|
2540
|
+
*/
|
|
2541
|
+
listenForEndNotices() {
|
|
2542
|
+
if (this.endNotices) return;
|
|
2543
|
+
this.endNotices = this.connection.messages().pipe(filter((res) => res.method === "notify_unsubscribed")).subscribe((res) => {
|
|
2544
|
+
const notice = res.params;
|
|
2545
|
+
const release = notice.error ? void 0 : this.releasing.get(notice.collection)?.find((r) => !r.noticed);
|
|
2546
|
+
if (release) release.noticed = true;
|
|
2547
|
+
else this.serverEnds.get(notice.collection)?.next(notice);
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2411
2550
|
/**
|
|
2412
2551
|
* Convenience wrapper for auth.generate_token API call.
|
|
2413
2552
|
*
|
|
@@ -2440,12 +2579,10 @@ var TrueNasApi = class {
|
|
|
2440
2579
|
filter((job) => job.id === jobId),
|
|
2441
2580
|
map((job) => job)
|
|
2442
2581
|
);
|
|
2443
|
-
const
|
|
2444
|
-
|
|
2445
|
-
]).pipe(
|
|
2446
|
-
map((jobs) => jobs[0]),
|
|
2447
|
-
share()
|
|
2582
|
+
const readJob = () => this.dispatch("core.get_jobs", [[["id", "=", jobId]]]).pipe(
|
|
2583
|
+
map((jobs) => jobs[0])
|
|
2448
2584
|
);
|
|
2585
|
+
const read$ = readJob().pipe(share());
|
|
2449
2586
|
const missing$ = read$.pipe(
|
|
2450
2587
|
filter((job) => job === void 0),
|
|
2451
2588
|
takeUntil(updates$)
|
|
@@ -2454,10 +2591,29 @@ var TrueNasApi = class {
|
|
|
2454
2591
|
filter((job) => job !== void 0),
|
|
2455
2592
|
takeUntil(updates$)
|
|
2456
2593
|
);
|
|
2457
|
-
|
|
2594
|
+
const reread$ = this.authenticated.pipe(
|
|
2595
|
+
distinctUntilChanged(),
|
|
2596
|
+
skipWhile((isUp) => !isUp),
|
|
2597
|
+
pairwise(),
|
|
2598
|
+
filter(([wasUp, isUp]) => !wasUp && isUp),
|
|
2599
|
+
switchMap(
|
|
2600
|
+
() => readJob().pipe(
|
|
2601
|
+
// Stale once events flow again, as the opening read is; they carry the end.
|
|
2602
|
+
takeUntil(updates$),
|
|
2603
|
+
// A failed read waits for the next reconnect.
|
|
2604
|
+
catchError(() => EMPTY)
|
|
2605
|
+
)
|
|
2606
|
+
),
|
|
2607
|
+
share()
|
|
2608
|
+
);
|
|
2609
|
+
const gone$ = reread$.pipe(filter((job) => job === void 0));
|
|
2610
|
+
const rereadJob$ = reread$.pipe(
|
|
2611
|
+
filter((job) => job !== void 0)
|
|
2612
|
+
);
|
|
2613
|
+
return merge(snapshot$, updates$, rereadJob$).pipe(
|
|
2458
2614
|
takeWhile((job) => !isJobFinished(job), true),
|
|
2459
2615
|
// Include the final state
|
|
2460
|
-
takeUntil(missing$)
|
|
2616
|
+
takeUntil(merge(missing$, gone$))
|
|
2461
2617
|
);
|
|
2462
2618
|
}
|
|
2463
2619
|
/**
|
|
@@ -4356,6 +4512,6 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
|
|
|
4356
4512
|
return UserRole2;
|
|
4357
4513
|
})(UserRole || {});
|
|
4358
4514
|
|
|
4359
|
-
export { AppState, AuthError, AuthErrorCode, AuthResponseType, InvalidVersionResponseError, JobState, NoCompatibleVersionsError, SUPPORTED_API_VERSIONS, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV27, TrueNasApiClientV28, TrueNasAuthMechanism, TrueNasAuthenticator, TrueNasConnection, UserRole, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, clientClassFor, consoleLogger, createTrueNasClient, getApiErrorMessage, getCloseMessage, isJobFinished, noopLogger, parseApiVersion, policyViolationCloseCode, v25_10_0_exports, v25_10_1_exports, v25_10_2_exports, v25_10_3_exports, v25_10_4_exports, v25_10_5_exports, v27_0_0_exports, v28_0_0_exports };
|
|
4360
|
-
//# sourceMappingURL=chunk-
|
|
4361
|
-
//# sourceMappingURL=chunk-
|
|
4515
|
+
export { AppState, AuthError, AuthErrorCode, AuthResponseType, InvalidVersionResponseError, JobState, NoCompatibleVersionsError, SUPPORTED_API_VERSIONS, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV27, TrueNasApiClientV28, TrueNasApiError, TrueNasAuthMechanism, TrueNasAuthenticator, TrueNasConnection, UserRole, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, clientClassFor, consoleLogger, createTrueNasClient, getApiErrorMessage, getCloseMessage, isJobFinished, noopLogger, parseApiVersion, policyViolationCloseCode, subscriptionName, v25_10_0_exports, v25_10_1_exports, v25_10_2_exports, v25_10_3_exports, v25_10_4_exports, v25_10_5_exports, v27_0_0_exports, v28_0_0_exports };
|
|
4516
|
+
//# sourceMappingURL=chunk-K7BL6J3Z.js.map
|
|
4517
|
+
//# sourceMappingURL=chunk-K7BL6J3Z.js.map
|