@q9labsai/chalk-client 5.2.1 → 5.2.2

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.
@@ -249,8 +249,11 @@ var SAFE_REASONS = /* @__PURE__ */ new Set([
249
249
  function safeSyncCloseReason(reason) {
250
250
  return reason ? SAFE_REASONS.has(reason) ? reason : "redacted" : null;
251
251
  }
252
+ function syncConnectFailureCode(diagnostic) {
253
+ return diagnostic?.stage === "upgrade" ? "sync_start_failed" : "sync_hello_timeout";
254
+ }
252
255
  function formatSyncConnectDiagnostic(diagnostic) {
253
- return `[${diagnostic.stage}; open=${diagnostic.opened ? 1 : 0}; close=${diagnostic.closeCode ?? "none"}; reason=${safeSyncCloseReason(diagnostic.closeReason ?? void 0) ?? "none"}; ${Math.round(diagnostic.durationMs)}ms]`;
256
+ return `[${diagnostic.stage}; open=${diagnostic.opened ? 1 : 0}; close=${diagnostic.closeCode ?? "none"}; reason=${safeSyncCloseReason(diagnostic.closeReason ?? void 0) ?? "none"}; ${diagnostic.stage === "upgrade" && diagnostic.closeCode !== null ? "cause=closed_before_open; " : ""}${diagnostic.attempt === void 0 ? "" : `attempt=${diagnostic.attempt}; `}${diagnostic.attempts === void 0 ? "" : `attempts=${diagnostic.attempts}; `}${Math.round(diagnostic.durationMs)}ms]`;
254
257
  }
255
258
 
256
259
  // src/sync/v1-health.ts
@@ -3024,10 +3027,11 @@ var V1SyncClient = class {
3024
3027
  #cancelDial;
3025
3028
  #dialTimeoutCount = 0;
3026
3029
  #connectStartedAt = null;
3030
+ #connectAttemptCount = 0;
3027
3031
  #opened = false;
3028
3032
  #connectFailure;
3029
3033
  #connectDiagnostic() {
3030
- return { stage: this.#opened ? "hello" : "upgrade", opened: this.#opened, closeCode: null, closeReason: null, durationMs: Math.max(0, this.#clock().now() - (this.#connectStartedAt ?? this.#clock().now())) };
3034
+ return { stage: this.#opened ? "hello" : "upgrade", opened: this.#opened, closeCode: null, closeReason: null, durationMs: Math.max(0, this.#clock().now() - (this.#connectStartedAt ?? this.#clock().now())), attempt: this.#connectAttemptCount };
3031
3035
  }
3032
3036
  #reconnectAttempt = 0;
3033
3037
  #episodeEndTimer;
@@ -3090,6 +3094,8 @@ var V1SyncClient = class {
3090
3094
  async start() {
3091
3095
  if (this.#started) return;
3092
3096
  this.#started = true;
3097
+ this.#connectAttemptCount = 0;
3098
+ this.#connectFailure = void 0;
3093
3099
  const startupGeneration = ++this.#startupGeneration;
3094
3100
  let stored;
3095
3101
  try {
@@ -3288,9 +3294,10 @@ var V1SyncClient = class {
3288
3294
  #connect() {
3289
3295
  if (!this.#started || !this.#transportAvailable || this.#socket || this.#phase.phase === "terminal") return;
3290
3296
  this.#phase = { phase: "connecting" };
3297
+ this.#connectAttemptCount += 1;
3291
3298
  this.#connectStartedAt = this.#clock().now();
3292
3299
  this.#opened = false;
3293
- this.#connectFailure = void 0;
3300
+ if (this.#connectFailure?.opened) this.#connectFailure = void 0;
3294
3301
  this.#emit();
3295
3302
  this.#entryNegotiated = false;
3296
3303
  let socket;
@@ -3299,7 +3306,7 @@ var V1SyncClient = class {
3299
3306
  } catch {
3300
3307
  this.#connectFailure = this.#connectDiagnostic();
3301
3308
  this.#emit();
3302
- throw new V1SyncError("Sync WebSocket could not be created", "sync_hello_timeout");
3309
+ throw new V1SyncError("Sync WebSocket could not be created", "sync_start_failed");
3303
3310
  }
3304
3311
  const connectionGeneration = ++this.#connectionGeneration;
3305
3312
  this.#socket = socket;
@@ -3341,7 +3348,7 @@ var V1SyncClient = class {
3341
3348
  };
3342
3349
  socket.onerror = () => {
3343
3350
  if (socket === this.#socket && (this.#phase.phase === "connecting" || this.#phase.phase === "recovering")) {
3344
- this.#connectFailure = this.#connectDiagnostic();
3351
+ this.#connectFailure ??= this.#connectDiagnostic();
3345
3352
  this.#emit();
3346
3353
  }
3347
3354
  socket.close(CLIENT_RESTART_CLOSE_CODE, "transport error");
@@ -4046,6 +4053,7 @@ function createV1SyncClient(options) {
4046
4053
  }
4047
4054
 
4048
4055
  export {
4056
+ syncConnectFailureCode,
4049
4057
  formatSyncConnectDiagnostic,
4050
4058
  UuidSchema,
4051
4059
  ReactionValues,
@@ -18,8 +18,9 @@ import {
18
18
  createBrowserWebSocketFactory,
19
19
  createV1SyncClient,
20
20
  formatSyncConnectDiagnostic,
21
- scheduleSyncDialDeadline
22
- } from "./chunk-TVNI2XUZ.js";
21
+ scheduleSyncDialDeadline,
22
+ syncConnectFailureCode
23
+ } from "./chunk-AIJ5Z6HA.js";
23
24
 
24
25
  // src/access/manager.ts
25
26
  import { Clock, Context, Data, Effect, Layer, SynchronizedRef } from "effect";
@@ -2551,7 +2552,7 @@ var ConnectionDiagnostics = class {
2551
2552
 
2552
2553
  // src/connection/lifecycle.ts
2553
2554
  var MEDIA_RECOVERY_TIMEOUT_MS = 6e4;
2554
- var START_TIMEOUT_MS = 1e4;
2555
+ var START_TIMEOUT_MS = 3e4;
2555
2556
  var LEAVE_TIMEOUT_MS = 5e3;
2556
2557
  var RECOVERY_BUDGET_MS = 1e4;
2557
2558
  var MAX_RECOVERY_ATTEMPTS = 3;
@@ -2768,7 +2769,7 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2768
2769
  const failure2 = snapshot.connection.connectFailure;
2769
2770
  if (failure2 && failure2 !== observedConnectFailure) {
2770
2771
  observedConnectFailure = failure2;
2771
- diagnostics.record({ event: "sync_connect_failed", state: model.state, epoch: model.epoch, code: "sync_hello_timeout", sync: failure2 });
2772
+ diagnostics.record({ event: "sync_connect_failed", state: model.state, epoch: model.epoch, code: syncConnectFailureCode(failure2), sync: failure2 });
2772
2773
  }
2773
2774
  const count = snapshot.connection.dialTimeoutCount ?? 0;
2774
2775
  if (count > observedDialTimeouts) {
@@ -2844,7 +2845,7 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2844
2845
  const span = diagnostics.startSpan({ step, state: model.state, epoch: model.epoch });
2845
2846
  return effect.pipe(
2846
2847
  Effect2.tap(() => Effect2.sync(() => span.end({ state: model.state, epoch: model.epoch, outcome: "succeeded" }))),
2847
- Effect2.tapError(() => Effect2.sync(() => span.end({ state: model.state, epoch: model.epoch, outcome: "failed" })))
2848
+ Effect2.tapError((cause) => Effect2.sync(() => span.end({ state: model.state, epoch: model.epoch, outcome: "failed", code: failureFromCause(cause, "internal_error", "Connection step failed").code })))
2848
2849
  );
2849
2850
  };
2850
2851
  const confirmSyncEntry = (sync) => {
@@ -2921,7 +2922,7 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2921
2922
  Effect2.matchEffect({
2922
2923
  onFailure: (cause) => Effect2.gen(function* () {
2923
2924
  stopStream(stream);
2924
- const failure2 = failureFromCause(cause, "sync_start_failed", "The Connection could not start");
2925
+ const failure2 = failureFromCause(cause, "internal_error", "The Connection could not start");
2925
2926
  model.failure = toFailure(failure2);
2926
2927
  if (model.joinCancelled) {
2927
2928
  span.end({ state: model.state, epoch: model.epoch, outcome: "cancelled", code: "invalid_state" });
@@ -3584,9 +3585,11 @@ function syncTerminalFailure(snapshot) {
3584
3585
  return lifecycleFailure("sync_start_failed", true, "The Sync transport needs recovery");
3585
3586
  }
3586
3587
  function syncStartupFailure(snapshot) {
3587
- const sync = snapshot.connection.connectFailure ?? snapshot.connection.connectAttempt;
3588
+ const evidence = snapshot.connection.connectFailure ?? snapshot.connection.connectAttempt;
3589
+ const attempts = snapshot.connection.connectAttempt?.attempt;
3590
+ const sync = evidence ? { ...evidence, ...attempts === void 0 ? {} : { attempts } } : void 0;
3588
3591
  const message = sync?.opened === false ? "Sync could not reach the call service" : "Sync could not connect";
3589
- return new ConnectionLifecycleFailure({ code: "sync_hello_timeout", recoverable: true, message: `${message}${sync ? ` ${formatSyncConnectDiagnostic(sync)}` : " before the startup deadline"}`, ...sync ? { sync: Object.freeze({ ...sync }) } : {} });
3592
+ return new ConnectionLifecycleFailure({ code: syncConnectFailureCode(sync), recoverable: true, message: `${message}${sync ? ` ${formatSyncConnectDiagnostic(sync)}` : " before the startup deadline"}`, ...sync ? { sync: Object.freeze({ ...sync }) } : {} });
3590
3593
  }
3591
3594
  function syncAutoRecoverable(snapshot, failure2) {
3592
3595
  return failure2.recoverable && snapshot.connection.terminalReason !== "entry_timeout";
@@ -3631,6 +3634,7 @@ var INVALID_PAYLOAD_CODE_BY_ACTION = /* @__PURE__ */ new Map([
3631
3634
  ]);
3632
3635
  var MEDIA_TIMEOUT_FAILURE = { code: "media.timeout", recoverable: true, message: "Media did not respond in time. Try again." };
3633
3636
  var SYNC_FAILURES = /* @__PURE__ */ new Map([
3637
+ ["sync_start_failed", { failure: { code: "connection.sync_start_failed", recoverable: true, message: "A connection to the call service could not be opened. Try again." } }],
3634
3638
  ["sync_hello_timeout", { failure: { code: "connection.sync_hello_timeout", recoverable: true, message: "Sync could not reach the call service. Try again." } }],
3635
3639
  ["not_live", { failure: { code: "connection.invalid_state", recoverable: true, message: "Wait for the call to connect, then try again." } }],
3636
3640
  ["signaling_timeout", { failure: MEDIA_TIMEOUT_FAILURE }],
package/dist/effect.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  makeSpaceClientCoreLayer,
18
18
  makeSpaceClientCoreLayerFromServices,
19
19
  makeSpaceStoreLayer
20
- } from "./chunk-RVUN6LW4.js";
20
+ } from "./chunk-XPW3OCPO.js";
21
21
  import "./chunk-DWKTGBHR.js";
22
22
  import {
23
23
  APIKeyListSchema,
@@ -829,7 +829,7 @@ import {
829
829
  WhiteboardStorageUnavailableErrorWireSchema,
830
830
  createChalkEffectClient
831
831
  } from "./chunk-GA3JDPFI.js";
832
- import "./chunk-TVNI2XUZ.js";
832
+ import "./chunk-AIJ5Z6HA.js";
833
833
 
834
834
  // src/space-client/effect.ts
835
835
  import { Context, Effect, Exit, Layer, Scope } from "effect";
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export * from "./sync";
6
6
  export * from "./whiteboard";
7
7
  export * from "./public-invites";
8
8
  export { formatSyncConnectDiagnostic } from "./sync/connect-diagnostics";
9
+ export { getConnectionFailureDetail } from "./space-client/failure-detail";
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  createSpaceClient,
16
16
  decodeWhiteboardV1ServerFrame,
17
17
  encodeWhiteboardV1ClientFrame
18
- } from "./chunk-RVUN6LW4.js";
18
+ } from "./chunk-XPW3OCPO.js";
19
19
  import {
20
20
  CloudflareSFUClient,
21
21
  CloudflareSFUError,
@@ -69,7 +69,7 @@ import {
69
69
  optimisticV1Control,
70
70
  reactionFromFrame,
71
71
  restoreV1Snapshot
72
- } from "./chunk-TVNI2XUZ.js";
72
+ } from "./chunk-AIJ5Z6HA.js";
73
73
 
74
74
  // src/space-client/reload-rejoin.ts
75
75
  import { Schema } from "effect";
@@ -115,6 +115,23 @@ function reloadRejoinAccess(access) {
115
115
  const parsed = parseParsedAccessGrant(access);
116
116
  return { tenantId: parsed.subject.tenantId, episodeId: parsed.subject.episodeId, participantId: parsed.subject.participantId, participantGeneration: parsed.subject.participantGeneration, mediaProof: parsed.media.token };
117
117
  }
118
+
119
+ // src/space-client/failure-detail.ts
120
+ var DEFAULT_FAILURE_DETAIL = "Something went wrong while joining. Try again.";
121
+ var FAILURE_DETAILS = {
122
+ "access.invalid": "Access to this call was rejected. Refresh the page and try again.",
123
+ "access.unavailable": "Couldn\u2019t get access to the call. Try again.",
124
+ "connection.sync_start_failed": "Couldn\u2019t reach the call service. Try again.",
125
+ "connection.sync_hello_timeout": "The call service did not respond in time. Try again.",
126
+ "connection.sync_recovery_exhausted": "The call connection could not recover. Try again.",
127
+ "connection.media_start_failed": "Audio and video could not connect. Try again.",
128
+ "connection.media_recovery_exhausted": "Audio and video could not reconnect. Try again.",
129
+ "environment.unsupported": "This device cannot start the call. Use an up-to-date browser over HTTPS, or update the app."
130
+ };
131
+ function getConnectionFailureDetail({ code, sync }) {
132
+ if (sync?.stage === "upgrade") return sync.closeCode !== null ? "The connection closed before the call service could be reached. Try again." : "A connection to the call service could not be opened. Try again.";
133
+ return (code && FAILURE_DETAILS[code]) ?? DEFAULT_FAILURE_DETAIL;
134
+ }
118
135
  export {
119
136
  ArrivalInvalidHandleError,
120
137
  ArrivalUnavailableError,
@@ -166,6 +183,7 @@ export {
166
183
  encodeV1ClientFrame,
167
184
  encodeWhiteboardV1ClientFrame,
168
185
  formatSyncConnectDiagnostic,
186
+ getConnectionFailureDetail,
169
187
  optimisticV1Control,
170
188
  parseCloudflareSFUPublicationID,
171
189
  parseInviteCode,
@@ -0,0 +1,6 @@
1
+ import type { ClientFailure, ErrorCode } from "./types";
2
+ /** Safe, actionable failure copy shared by web and native call screens. */
3
+ export declare function getConnectionFailureDetail({ code, sync }: {
4
+ readonly code?: ErrorCode;
5
+ readonly sync?: ClientFailure["sync"];
6
+ }): string;
@@ -5,6 +5,12 @@ export type SyncConnectDiagnostic = {
5
5
  readonly closeCode: number | null;
6
6
  readonly closeReason: string | null;
7
7
  readonly durationMs: number;
8
+ /** One-based socket attempt since Sync start; absent in older SDK evidence. */
9
+ readonly attempt?: number;
10
+ /** Total sockets started when the join failed, including a pending replacement. */
11
+ readonly attempts?: number;
8
12
  };
9
13
  export declare function safeSyncCloseReason(reason: string | undefined): string | null;
14
+ /** Keep published error codes stable while separating upgrade from hello failures. */
15
+ export declare function syncConnectFailureCode(diagnostic: SyncConnectDiagnostic | undefined): "sync_start_failed" | "sync_hello_timeout";
10
16
  export declare function formatSyncConnectDiagnostic(diagnostic: SyncConnectDiagnostic): string;
@@ -22,7 +22,7 @@ import {
22
22
  encodeV1ClientFrame,
23
23
  optimisticV1Control,
24
24
  restoreV1Snapshot
25
- } from "../chunk-TVNI2XUZ.js";
25
+ } from "../chunk-AIJ5Z6HA.js";
26
26
  export {
27
27
  AsyncStorageV1PendingTargetStore,
28
28
  InMemoryV1PendingTargetStore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q9labsai/chalk-client",
3
- "version": "5.2.1",
3
+ "version": "5.2.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",