@kosdev-code/kos-ui-sdk 0.1.0-next.807 → 0.1.0-next.809

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/index.js CHANGED
@@ -2915,6 +2915,29 @@ const isKosModelReady = (model) => {
2915
2915
  }
2916
2916
  return kosModel2.isReady();
2917
2917
  };
2918
+ const getKosModelActivationState = (model) => {
2919
+ if (!model?.id) {
2920
+ return void 0;
2921
+ }
2922
+ const kosModel2 = KosCore.getInstance().modelManager.getModelById(model.id);
2923
+ if (!kosModel2) {
2924
+ return void 0;
2925
+ }
2926
+ return {
2927
+ get activeStatus() {
2928
+ return kosModel2.activeStatus;
2929
+ },
2930
+ get isActivating() {
2931
+ return kosModel2.activeStatus === KosModelState.ACTIVATING;
2932
+ },
2933
+ get isActive() {
2934
+ return kosModel2.activeStatus === KosModelState.ACTIVE;
2935
+ },
2936
+ get isFailed() {
2937
+ return kosModel2.activeStatus === KosModelState.FAILED;
2938
+ }
2939
+ };
2940
+ };
2918
2941
  const log$L = KosLog.createLogger({ name: "kos-model-factory" });
2919
2942
  const KosModelFactory = {
2920
2943
  byModelType: (modelType) => KosCore.getInstance().modelManager.getModelFactory(
@@ -6339,8 +6362,38 @@ const getMessageBody = (payload, skipParse) => {
6339
6362
  return payload.body || payload;
6340
6363
  }
6341
6364
  };
6365
+ const TRANSPORT_NOT_READY = "TRANSPORT_NOT_READY";
6366
+ class TransportNotReadyError extends Error {
6367
+ /** Stable discriminator; see {@link TRANSPORT_NOT_READY}. */
6368
+ code = TRANSPORT_NOT_READY;
6369
+ /**
6370
+ * The budget, in milliseconds, that expired while waiting. `undefined` when
6371
+ * the wait was cut short by a caller-supplied timeout signal rather than by
6372
+ * the request's own budget.
6373
+ */
6374
+ timeout;
6375
+ /** The URL of the request that was never sent. */
6376
+ url;
6377
+ constructor(details = {}) {
6378
+ super(
6379
+ `Transport not ready${details.timeout === void 0 ? "" : ` after ${details.timeout}ms`}${details.url ? ` - url: ${details.url}` : ""}`
6380
+ );
6381
+ this.name = "TransportNotReadyError";
6382
+ this.timeout = details.timeout;
6383
+ this.url = details.url;
6384
+ }
6385
+ }
6386
+ const isTransportNotReadyError = (error) => !!error && error.code === TRANSPORT_NOT_READY;
6342
6387
  const log$D = KosLog.createLogger({ name: "kos-fetch" });
6343
- const WS_TIMEOUT = process.env.KOS_WS_TIMEOUT ? parseInt(process.env.KOS_WS_TIMEOUT) : 3e4;
6388
+ const DEFAULT_WS_TIMEOUT = 3e4;
6389
+ const WS_TIMEOUT = (() => {
6390
+ const configured = process.env.KOS_WS_TIMEOUT ? parseInt(process.env.KOS_WS_TIMEOUT) : DEFAULT_WS_TIMEOUT;
6391
+ return Number.isNaN(configured) ? DEFAULT_WS_TIMEOUT : configured;
6392
+ })();
6393
+ const resolveTimeoutBudget = (timeout) => {
6394
+ const budget = timeout ?? WS_TIMEOUT;
6395
+ return Number.isFinite(budget) && budget > 0 ? budget : void 0;
6396
+ };
6344
6397
  const delay = () => new Promise((resolve) => {
6345
6398
  setTimeout(() => {
6346
6399
  resolve(true);
@@ -6374,6 +6427,14 @@ const combineSignals = (signals) => {
6374
6427
  }
6375
6428
  return controller.signal;
6376
6429
  };
6430
+ const createRequestSignal = (budget, callerSignal) => {
6431
+ const timeoutSignal = budget ? createTimeoutSignal(budget) : void 0;
6432
+ if (timeoutSignal && callerSignal) {
6433
+ return combineSignals([callerSignal, timeoutSignal]);
6434
+ }
6435
+ return timeoutSignal ?? callerSignal ?? new AbortController().signal;
6436
+ };
6437
+ const isTimeoutReason = (reason) => reason?.name === "TimeoutError";
6377
6438
  const fetchMessageFactory = (options) => {
6378
6439
  if (options?.studio) {
6379
6440
  return createStudioMessage;
@@ -6385,15 +6446,25 @@ const fetchMessageFactory = (options) => {
6385
6446
  };
6386
6447
  const kosFetchWs = async (url, options) => {
6387
6448
  const transport = KosCore.getInstance().transport;
6388
- await transport.whenReady();
6389
6449
  const requestId = uuid();
6390
6450
  const urlObj = new URL(url);
6391
6451
  const path = `${urlObj.pathname}${urlObj.search}`;
6392
6452
  log$D.debug(`path: ${path}`);
6393
- const TIMEOUT = options?.timeout || WS_TIMEOUT;
6453
+ const budget = resolveTimeoutBudget(options?.timeout);
6394
6454
  const messageFactory = fetchMessageFactory(options);
6395
- const timeoutSignal = createTimeoutSignal(TIMEOUT);
6396
- const signal = options?.signal ? combineSignals([options.signal, timeoutSignal]) : timeoutSignal;
6455
+ const signal = createRequestSignal(budget, options?.signal);
6456
+ try {
6457
+ await transport.whenReady({ signal });
6458
+ } catch (error) {
6459
+ if (isTimeoutReason(error)) {
6460
+ log$D.error(
6461
+ `Transport not ready${budget === void 0 ? "" : ` after ${budget}ms`} - request not sent - url: ${url}`
6462
+ );
6463
+ throw new TransportNotReadyError({ timeout: budget, url });
6464
+ }
6465
+ log$D.debug(`Request aborted while waiting for transport - url: ${url}`);
6466
+ throw error;
6467
+ }
6397
6468
  const processedBody = await processRequestBody(options?.body);
6398
6469
  const additionalHeaders = {};
6399
6470
  if (processedBody.contentType) {
@@ -6556,6 +6627,7 @@ const resolveBaseUrl = () => {
6556
6627
  };
6557
6628
  const log$C = KosLog.createLogger({ name: "kos-service-request" });
6558
6629
  const ERROR_UNKNOWN = "errUnknown";
6630
+ const ERROR_TRANSPORT_NOT_READY = "Transport not ready";
6559
6631
  const MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
6560
6632
  const RETRY_DEFAULTS = {
6561
6633
  maxAttempts: 3,
@@ -6605,6 +6677,10 @@ async function executeFetch(fullUrl, fetchOptions) {
6605
6677
  const payload = await response.json();
6606
6678
  return [null, payload.data ?? payload];
6607
6679
  } catch (error) {
6680
+ if (isTransportNotReadyError(error)) {
6681
+ log$C.error(`Transport not ready, request not sent: ${fullUrl}`);
6682
+ return [ERROR_TRANSPORT_NOT_READY, null];
6683
+ }
6608
6684
  if (error instanceof DOMException) {
6609
6685
  if (error.name === "TimeoutError") {
6610
6686
  log$C.error(`Request timed out: ${fullUrl}`);
@@ -6648,6 +6724,10 @@ async function executeFetchWithRetry(fullUrl, fetchOptions, config2) {
6648
6724
  await new Promise((resolve) => setTimeout(resolve, delayMs));
6649
6725
  }
6650
6726
  } catch (error) {
6727
+ if (isTransportNotReadyError(error)) {
6728
+ log$C.error(`Transport not ready, request not sent: ${fullUrl}`);
6729
+ return [ERROR_TRANSPORT_NOT_READY, null];
6730
+ }
6651
6731
  if (error instanceof DOMException) {
6652
6732
  if (error.name === "TimeoutError") {
6653
6733
  log$C.error(`Request timed out: ${fullUrl}`);
@@ -7459,6 +7539,38 @@ class FlowControlManager {
7459
7539
  return { ...this.stats };
7460
7540
  }
7461
7541
  }
7542
+ const abortReason = (signal) => signal.reason ?? new DOMException("Aborted", "AbortError");
7543
+ const abortableWhen = async (predicate, signal) => {
7544
+ if (predicate()) {
7545
+ return;
7546
+ }
7547
+ if (!signal) {
7548
+ await when(predicate);
7549
+ return;
7550
+ }
7551
+ if (signal.aborted) {
7552
+ throw abortReason(signal);
7553
+ }
7554
+ const pending = when(predicate);
7555
+ let onAbort;
7556
+ try {
7557
+ await Promise.race([
7558
+ // `cancel()` below rejects this promise with MobX's WHEN_CANCELLED
7559
+ // sentinel. That rejection *is* the cancellation, never a real failure,
7560
+ // so it is swallowed here; the abort branch supplies the thrown reason.
7561
+ pending.catch(() => void 0),
7562
+ new Promise((_, reject) => {
7563
+ onAbort = () => reject(abortReason(signal));
7564
+ signal.addEventListener("abort", onAbort, { once: true });
7565
+ })
7566
+ ]);
7567
+ } finally {
7568
+ if (onAbort) {
7569
+ signal.removeEventListener("abort", onAbort);
7570
+ }
7571
+ pending.cancel();
7572
+ }
7573
+ };
7462
7574
  class WebSocketBridgeTransport extends WebSocket {
7463
7575
  constructor(address) {
7464
7576
  super(address);
@@ -8091,15 +8203,26 @@ ${dstAddr}topics:${topic}
8091
8203
  }
8092
8204
  };
8093
8205
  }
8094
- async whenReady() {
8095
- const that = this;
8206
+ /**
8207
+ * Resolves once the transport can carry requests: the socket (and the FOS
8208
+ * socket, when used) is connected and the connection is authorized.
8209
+ *
8210
+ * Pass `options.signal` to bound the wait. `connectionEstablished` flips back
8211
+ * to `false` on socket close, so an unbounded wait parks indefinitely while
8212
+ * the transport is down — callers with a deadline must supply a signal.
8213
+ *
8214
+ * @param options - See {@link KosTransportReadyOptions}.
8215
+ * @throws The signal's abort reason if it aborts before the transport is ready.
8216
+ */
8217
+ async whenReady(options) {
8096
8218
  if (!this.webSocketSupported) {
8097
8219
  return {
8098
8220
  status: `not supported`
8099
8221
  };
8100
8222
  }
8101
- await when(
8102
- () => !!that.socket?.connectionEstablished && (!that.useFosTransport || !!that.fosSocket?.connectionEstablished) && that.authorized
8223
+ await abortableWhen(
8224
+ () => !!this.socket?.connectionEstablished && (!this.useFosTransport || !!this.fosSocket?.connectionEstablished) && this.authorized,
8225
+ options?.signal
8103
8226
  );
8104
8227
  return {
8105
8228
  status: `success`
@@ -26922,6 +27045,7 @@ export {
26922
27045
  Device,
26923
27046
  index$6 as DeviceServices,
26924
27047
  DomIntersectionStrategy,
27048
+ ERROR_TRANSPORT_NOT_READY,
26925
27049
  EVENT_KOS_MODEL_READY,
26926
27050
  EVENT_TROUBLE_ADDED,
26927
27051
  EVENT_TROUBLE_REMOVED,
@@ -27104,6 +27228,7 @@ export {
27104
27228
  TIMER_END,
27105
27229
  TIMER_EVENT,
27106
27230
  TOPIC_TIMER_TICK_EVENT,
27231
+ TRANSPORT_NOT_READY,
27107
27232
  TimerManager,
27108
27233
  TokenContext,
27109
27234
  TokenProvider,
@@ -27116,6 +27241,7 @@ export {
27116
27241
  TranslationContainerContext,
27117
27242
  TranslationContext,
27118
27243
  TransportFactory,
27244
+ TransportNotReadyError,
27119
27245
  Trouble,
27120
27246
  TroubleAwareSetup,
27121
27247
  TroubleContainer,
@@ -27211,6 +27337,7 @@ export {
27211
27337
  getKosLocalizationDescriptor,
27212
27338
  getKosMessageLogging,
27213
27339
  getKosModel$1 as getKosModel,
27340
+ getKosModelActivationState,
27214
27341
  getKosModelSync,
27215
27342
  getKosModelType,
27216
27343
  getKosServiceApi,
@@ -27253,6 +27380,7 @@ export {
27253
27380
  isLocalRefId,
27254
27381
  isNumber,
27255
27382
  isPrintable,
27383
+ isTransportNotReadyError,
27256
27384
  isTroubleAware,
27257
27385
  isValidDate,
27258
27386
  kosAction,