@q9labsai/chalk-client 4.1.12 → 4.1.14

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.
Files changed (51) hide show
  1. package/dist/access/manager.d.ts +2 -0
  2. package/dist/access/manager.d.ts.map +1 -1
  3. package/dist/{chunk-DFLTEYWX.js → chunk-2NUTY2VP.js} +3 -3
  4. package/dist/{chunk-JPNE4K2L.js → chunk-HE6CLPZM.js} +51 -2
  5. package/dist/{chunk-D6W4ON4A.js → chunk-JCNUF3DP.js} +116 -46
  6. package/dist/{chunk-QVHR3RMB.js → chunk-NKOKY2AE.js} +21 -1
  7. package/dist/{chunk-5VXGLI3T.js → chunk-PWUU6T3R.js} +8 -1
  8. package/dist/{chunk-C4BIONCP.js → chunk-S5OUHFGC.js} +174 -13
  9. package/dist/client.d.ts +12 -1
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/connection/dependencies.d.ts +3 -1
  12. package/dist/connection/dependencies.d.ts.map +1 -1
  13. package/dist/connection/index.d.ts +2 -0
  14. package/dist/connection/index.d.ts.map +1 -1
  15. package/dist/connection/lifecycle.d.ts.map +1 -1
  16. package/dist/effect.js +17 -5
  17. package/dist/generated/http-api.d.ts +12 -1
  18. package/dist/generated/http-api.d.ts.map +1 -1
  19. package/dist/generated/schemas.d.ts +53 -1
  20. package/dist/generated/schemas.d.ts.map +1 -1
  21. package/dist/index.js +26 -26
  22. package/dist/media/client.d.ts.map +1 -1
  23. package/dist/media/index.js +1 -1
  24. package/dist/media/transport.d.ts.map +1 -1
  25. package/dist/media/types.d.ts +12 -1
  26. package/dist/media/types.d.ts.map +1 -1
  27. package/dist/public-invites/index.js +3 -3
  28. package/dist/public-invites/types.d.ts +1 -0
  29. package/dist/public-invites/types.d.ts.map +1 -1
  30. package/dist/space-client/core.d.ts +2 -0
  31. package/dist/space-client/core.d.ts.map +1 -1
  32. package/dist/sync/index.js +1 -1
  33. package/dist/sync/v1-client.d.ts.map +1 -1
  34. package/dist/telemetry/attributes.d.ts +1 -1
  35. package/dist/telemetry/attributes.d.ts.map +1 -1
  36. package/dist/telemetry/index.js +1 -1
  37. package/dist/telemetry/rtc.d.ts +6 -0
  38. package/dist/telemetry/rtc.d.ts.map +1 -1
  39. package/dist/whiteboard/types.d.ts +1 -0
  40. package/dist/whiteboard/types.d.ts.map +1 -1
  41. package/dist/whiteboard/v1-client.d.ts +2 -1
  42. package/dist/whiteboard/v1-client.d.ts.map +1 -1
  43. package/dist/whiteboard/v1-codec.d.ts +1 -2
  44. package/dist/whiteboard/v1-codec.d.ts.map +1 -1
  45. package/package.json +2 -2
  46. package/dist/episode-diagnostic-runtime.test.helpers.d.ts +0 -3
  47. package/dist/episode-diagnostic-runtime.test.helpers.d.ts.map +0 -1
  48. package/dist/space-client/controller-parity.test.helpers.d.ts +0 -151
  49. package/dist/space-client/controller-parity.test.helpers.d.ts.map +0 -1
  50. package/dist/space-client/media-controller.test.helpers.d.ts +0 -105
  51. package/dist/space-client/media-controller.test.helpers.d.ts.map +0 -1
@@ -10,6 +10,7 @@ export declare class ConnectionAccessFailure extends ConnectionAccessFailure_bas
10
10
  }> {
11
11
  }
12
12
  export type ConnectionAccessEffectProvider = (request: ConnectionAccessRequest) => Effect.Effect<ParsedAccessGrant, ConnectionAccessFailure>;
13
+ export type AutomaticMediaReplacementListener = (access: ParsedAccessGrant) => void;
13
14
  export type ConnectionAccessEffectService = {
14
15
  readonly current: Effect.Effect<ParsedAccessGrant | null>;
15
16
  readonly currentUnsafe: () => ParsedAccessGrant | null;
@@ -19,6 +20,7 @@ export type ConnectionAccessEffectService = {
19
20
  readonly refreshAfterRejection: () => Effect.Effect<ParsedAccessGrant, ConnectionAccessFailure>;
20
21
  readonly getSyncToken: (reason?: ConnectionAccessReason) => Effect.Effect<ParticipantSyncCredential, ConnectionAccessFailure>;
21
22
  readonly getMediaToken: () => Effect.Effect<ParticipantMediaCredential, ConnectionAccessFailure>;
23
+ readonly subscribeAutomaticMediaReplacement: (listener: AutomaticMediaReplacementListener) => Effect.Effect<() => void>;
22
24
  readonly requiresRefresh: Effect.Effect<boolean>;
23
25
  readonly millisecondsUntilRefresh: Effect.Effect<number | null>;
24
26
  readonly clear: Effect.Effect<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/access/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAQ,MAAM,EAAE,KAAK,EAAmB,MAAM,QAAQ,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AACxG,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;;;;AAIlG,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CAAC;IACvD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;AAEL,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,EAAE,uBAAuB,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;AAE7I,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IACrH,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IACrH,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,sBAAsB,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IAClK,QAAQ,CAAC,qBAAqB,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IAChG,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB,KAAK,MAAM,CAAC,MAAM,CAAC,yBAAyB,EAAE,uBAAuB,CAAC,CAAC;IAC9H,QAAQ,CAAC,aAAa,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,0BAA0B,EAAE,uBAAuB,CAAC,CAAC;IACjG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACrC,CAAC;;AAEF,qBAAa,uBAAwB,SAAQ,4BAA2G;CAAG;AAI3J;;;GAGG;AACH,eAAO,MAAM,yBAAyB,aAAc,8BAA8B,iFAqD/E,CAAC;AAEJ,eAAO,MAAM,6BAA6B,aAvDU,8BAA8B,iFAuDZ,CAAC"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/access/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAQ,MAAM,EAAE,KAAK,EAAmB,MAAM,QAAQ,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AACxG,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;;;;AAIlG,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CAAC;IACvD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;AAEL,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,EAAE,uBAAuB,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;AAC7I,MAAM,MAAM,iCAAiC,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAEpF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IACrH,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IACrH,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,sBAAsB,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IAClK,QAAQ,CAAC,qBAAqB,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;IAChG,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB,KAAK,MAAM,CAAC,MAAM,CAAC,yBAAyB,EAAE,uBAAuB,CAAC,CAAC;IAC9H,QAAQ,CAAC,aAAa,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,0BAA0B,EAAE,uBAAuB,CAAC,CAAC;IACjG,QAAQ,CAAC,kCAAkC,EAAE,CAAC,QAAQ,EAAE,iCAAiC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACxH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACrC,CAAC;;AAEF,qBAAa,uBAAwB,SAAQ,4BAA2G;CAAG;AAY3J;;;GAGG;AACH,eAAO,MAAM,yBAAyB,aAAc,8BAA8B,iFAuC/E,CAAC;AAEJ,eAAO,MAAM,6BAA6B,aAzCU,8BAA8B,iFAyCZ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createChalkEffectClient,
3
3
  parseAccessGrant
4
- } from "./chunk-JPNE4K2L.js";
4
+ } from "./chunk-HE6CLPZM.js";
5
5
 
6
6
  // src/public-invites/client.ts
7
7
  import { Effect } from "effect";
@@ -59,7 +59,7 @@ function createChalkPublicClient(options) {
59
59
  const response = await Effect.runPromise(
60
60
  client.spaces.refreshSpacePublicInviteAccess({
61
61
  headers: { "X-Chalk-Arrival-Handle": normalized.arrivalHandle },
62
- payload: { media_proof: required(normalized.mediaProof, "mediaProof") }
62
+ payload: { media_proof: required(normalized.mediaProof, "mediaProof"), replace_media_connection: normalized.replaceMediaConnection ?? false }
63
63
  })
64
64
  );
65
65
  return parseAccessGrant(response);
@@ -109,7 +109,7 @@ function arrivalReference(reference) {
109
109
  return { arrivalHandle: required(reference.arrivalHandle, "arrivalHandle"), guestCredential: reference.guestCredential };
110
110
  }
111
111
  function refreshInput(input, arrival) {
112
- const values = typeof input === "string" ? { arrivalHandle: arrival?.arrivalHandle, guestCredential: arrival?.guestCredential, mediaProof: input } : { arrivalHandle: input.arrivalHandle ?? arrival?.arrivalHandle, guestCredential: input.guestCredential ?? arrival?.guestCredential, mediaProof: input.mediaProof };
112
+ const values = typeof input === "string" ? { arrivalHandle: arrival?.arrivalHandle, guestCredential: arrival?.guestCredential, mediaProof: input } : { arrivalHandle: input.arrivalHandle ?? arrival?.arrivalHandle, guestCredential: input.guestCredential ?? arrival?.guestCredential, mediaProof: input.mediaProof, replaceMediaConnection: input.replaceMediaConnection };
113
113
  return {
114
114
  ...values,
115
115
  arrivalHandle: required(values.arrivalHandle ?? "", "arrivalHandle")
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  journeyHeaders
3
- } from "./chunk-QVHR3RMB.js";
3
+ } from "./chunk-NKOKY2AE.js";
4
4
 
5
5
  // src/generated/schemas.ts
6
6
  import { Schema, SchemaGetter } from "effect";
@@ -757,7 +757,8 @@ var RecordingReservationSchema = Schema.Struct({
757
757
  updated_at: DateTimeStringSchema
758
758
  });
759
759
  var RefreshSpacePublicInviteAccessRequestSchema = Schema.Struct({
760
- media_proof: Schema.String.check(Schema.isMinLength(1))
760
+ media_proof: Schema.String.check(Schema.isMinLength(1)),
761
+ replace_media_connection: Schema.Boolean
761
762
  });
762
763
  var RegionsSchema = Schema.Struct({
763
764
  regions: Schema.Array(
@@ -2149,6 +2150,44 @@ var AccessInvalidPasswordErrorSchema = AccessInvalidPasswordErrorWireSchema.pipe
2149
2150
  encode: SchemaGetter.transform((error) => ({ error: error.error }))
2150
2151
  })
2151
2152
  );
2153
+ var AccessMediaProofExpiredError = class extends Schema.TaggedErrorClass()("AccessMediaProofExpiredError", {
2154
+ error: Schema.Struct({
2155
+ code: Schema.Literal("access.media_proof_expired"),
2156
+ message: Schema.String
2157
+ })
2158
+ }) {
2159
+ };
2160
+ var AccessMediaProofExpiredErrorWireSchema = Schema.Struct({
2161
+ error: Schema.Struct({
2162
+ code: Schema.Literal("access.media_proof_expired"),
2163
+ message: Schema.String
2164
+ })
2165
+ });
2166
+ var AccessMediaProofExpiredErrorSchema = AccessMediaProofExpiredErrorWireSchema.pipe(
2167
+ Schema.decodeTo(AccessMediaProofExpiredError, {
2168
+ decode: SchemaGetter.transform((wire) => ({ _tag: "AccessMediaProofExpiredError", ...wire })),
2169
+ encode: SchemaGetter.transform((error) => ({ error: error.error }))
2170
+ })
2171
+ );
2172
+ var AccessMediaProofRejectedError = class extends Schema.TaggedErrorClass()("AccessMediaProofRejectedError", {
2173
+ error: Schema.Struct({
2174
+ code: Schema.Literal("access.media_proof_rejected"),
2175
+ message: Schema.String
2176
+ })
2177
+ }) {
2178
+ };
2179
+ var AccessMediaProofRejectedErrorWireSchema = Schema.Struct({
2180
+ error: Schema.Struct({
2181
+ code: Schema.Literal("access.media_proof_rejected"),
2182
+ message: Schema.String
2183
+ })
2184
+ });
2185
+ var AccessMediaProofRejectedErrorSchema = AccessMediaProofRejectedErrorWireSchema.pipe(
2186
+ Schema.decodeTo(AccessMediaProofRejectedError, {
2187
+ decode: SchemaGetter.transform((wire) => ({ _tag: "AccessMediaProofRejectedError", ...wire })),
2188
+ encode: SchemaGetter.transform((error) => ({ error: error.error }))
2189
+ })
2190
+ );
2152
2191
  var AccessRecentAuthRequiredError = class extends Schema.TaggedErrorClass()("AccessRecentAuthRequiredError", {
2153
2192
  error: Schema.Struct({
2154
2193
  code: Schema.Literal("access.recent_auth_required"),
@@ -5367,6 +5406,8 @@ var RefreshIntegrationConnectionErrorSchema = Schema.Union([
5367
5406
  TenantInvalidIdErrorSchema
5368
5407
  ]);
5369
5408
  var RefreshSpacePublicInviteAccessErrorSchema = Schema.Union([
5409
+ AccessMediaProofExpiredErrorSchema,
5410
+ AccessMediaProofRejectedErrorSchema,
5370
5411
  ArrivalInvalidHandleErrorSchema,
5371
5412
  ArrivalUnavailableErrorSchema,
5372
5413
  EpisodeCapacityExceededErrorSchema,
@@ -7374,8 +7415,10 @@ var spacesGroup = HttpApiGroup.make("spaces").add(
7374
7415
  ArrivalInvalidHandleErrorSchema.pipe(HttpApiSchema.status(400)),
7375
7416
  RequestInvalidErrorSchema.pipe(HttpApiSchema.status(400)),
7376
7417
  RequestInvalidIdempotencyKeyErrorSchema.pipe(HttpApiSchema.status(400)),
7418
+ AccessMediaProofRejectedErrorSchema.pipe(HttpApiSchema.status(401)),
7377
7419
  ArrivalUnavailableErrorSchema.pipe(HttpApiSchema.status(404)),
7378
7420
  SpacePublicInviteUnavailableErrorSchema.pipe(HttpApiSchema.status(404)),
7421
+ AccessMediaProofExpiredErrorSchema.pipe(HttpApiSchema.status(409)),
7379
7422
  EpisodeCapacityExceededErrorSchema.pipe(HttpApiSchema.status(409)),
7380
7423
  RequestIdempotencyConflictErrorSchema.pipe(HttpApiSchema.status(409)),
7381
7424
  RequestPayloadTooLargeErrorSchema.pipe(HttpApiSchema.status(413)),
@@ -8421,6 +8464,12 @@ export {
8421
8464
  AccessInvalidPasswordError,
8422
8465
  AccessInvalidPasswordErrorWireSchema,
8423
8466
  AccessInvalidPasswordErrorSchema,
8467
+ AccessMediaProofExpiredError,
8468
+ AccessMediaProofExpiredErrorWireSchema,
8469
+ AccessMediaProofExpiredErrorSchema,
8470
+ AccessMediaProofRejectedError,
8471
+ AccessMediaProofRejectedErrorWireSchema,
8472
+ AccessMediaProofRejectedErrorSchema,
8424
8473
  AccessRecentAuthRequiredError,
8425
8474
  AccessRecentAuthRequiredErrorWireSchema,
8426
8475
  AccessRecentAuthRequiredErrorSchema,
@@ -2,21 +2,21 @@ import {
2
2
  CloudflareRTKClient,
3
3
  CloudflareSFUClient,
4
4
  createCloudflareSFUHTTPTransport
5
- } from "./chunk-C4BIONCP.js";
5
+ } from "./chunk-S5OUHFGC.js";
6
6
  import {
7
7
  AccessGrantError,
8
8
  requireParsedAccessGrant,
9
9
  validEpisodeDiagnosticCredential
10
- } from "./chunk-JPNE4K2L.js";
10
+ } from "./chunk-HE6CLPZM.js";
11
+ import {
12
+ createTraceContext,
13
+ parseTraceparent
14
+ } from "./chunk-NKOKY2AE.js";
11
15
  import {
12
16
  createBrowserSyncLifecycle,
13
17
  createBrowserWebSocketFactory,
14
18
  createV1SyncClient
15
- } from "./chunk-5VXGLI3T.js";
16
- import {
17
- createTraceContext,
18
- parseTraceparent
19
- } from "./chunk-QVHR3RMB.js";
19
+ } from "./chunk-PWUU6T3R.js";
20
20
 
21
21
  // src/access/manager.ts
22
22
  import { Clock, Context, Data, Effect, Layer, SynchronizedRef } from "effect";
@@ -29,28 +29,10 @@ var makeConnectionAccessLayer = (provider, refreshWindowMs = DEFAULT_REFRESH_WIN
29
29
  ConnectionAccessService,
30
30
  Effect.gen(function* () {
31
31
  const state = yield* SynchronizedRef.make({ access: null });
32
- const fetch = (reason, replaceMediaConnection, force) => SynchronizedRef.modifyEffect(
33
- state,
34
- (current) => Effect.gen(function* () {
35
- const now = yield* Clock.currentTimeMillis;
36
- if (!force && current.access && millisecondsUntil(current.access, now, refreshWindowMs) > 0) return [current.access, current];
37
- const request = Object.freeze({
38
- reason,
39
- replaceMediaConnection,
40
- ...current.access ? {
41
- currentMediaToken: current.access.media.token,
42
- expectedParticipantGeneration: current.access.subject.participantGeneration
43
- } : {}
44
- });
45
- const next = yield* provider(request);
46
- try {
47
- validateExpiration(next, now);
48
- if (current.access) validateRefresh(current.access, next, replaceMediaConnection);
49
- } catch (cause) {
50
- return yield* Effect.fail(new ConnectionAccessFailure({ code: "access.invalid", cause }));
51
- }
52
- return [next, { access: next }];
53
- })
32
+ const automaticMediaReplacementListeners = /* @__PURE__ */ new Set();
33
+ const fetch = (reason, replaceMediaConnection, force) => SynchronizedRef.modifyEffect(state, (current) => fetchAccessGrant(provider, { current, reason, replaceMediaConnection, force, refreshWindowMs })).pipe(
34
+ Effect.tap((result) => Effect.sync(() => notifyAutomaticMediaReplacement(automaticMediaReplacementListeners, result))),
35
+ Effect.map((result) => result.access)
54
36
  );
55
37
  const initialize = (reason = "join") => fetch(reason, false, false).pipe(Effect.catchTag("ConnectionAccessFailure", (failure2) => failure2.code === "access.invalid" && SynchronizedRef.getUnsafe(state).access === null ? fetch(reason, false, true) : Effect.fail(failure2)));
56
38
  const millisecondsUntilRefresh = Effect.gen(function* () {
@@ -68,6 +50,10 @@ var makeConnectionAccessLayer = (provider, refreshWindowMs = DEFAULT_REFRESH_WIN
68
50
  refreshAfterRejection: () => fetch("access_retry", true, true),
69
51
  getSyncToken: (reason = "sync_recovery") => ensureFresh(reason).pipe(Effect.map((access) => access.sync.token)),
70
52
  getMediaToken: () => ensureFresh("scheduled_refresh").pipe(Effect.map((access) => access.media.token)),
53
+ subscribeAutomaticMediaReplacement: (listener) => Effect.sync(() => {
54
+ automaticMediaReplacementListeners.add(listener);
55
+ return () => automaticMediaReplacementListeners.delete(listener);
56
+ }),
71
57
  requiresRefresh: millisecondsUntilRefresh.pipe(Effect.map((milliseconds) => milliseconds === null || milliseconds === 0)),
72
58
  millisecondsUntilRefresh,
73
59
  clear: SynchronizedRef.set(state, { access: null })
@@ -75,6 +61,46 @@ var makeConnectionAccessLayer = (provider, refreshWindowMs = DEFAULT_REFRESH_WIN
75
61
  })
76
62
  );
77
63
  var makeFakeConnectionAccessLayer = makeConnectionAccessLayer;
64
+ function fetchAccessGrant(provider, input) {
65
+ return Effect.gen(function* () {
66
+ const now = yield* Clock.currentTimeMillis;
67
+ const currentAccess = input.current.access;
68
+ if (!input.force && currentAccess && millisecondsUntil(currentAccess, now, input.refreshWindowMs) > 0) {
69
+ return [{ access: currentAccess, automaticMediaReplacement: false }, input.current];
70
+ }
71
+ const mediaExpired = currentAccess !== null && expiresAt(currentAccess.media.expiresAt) <= now;
72
+ const shouldReplaceMediaConnection = input.replaceMediaConnection || mediaExpired;
73
+ const next = yield* provider(connectionAccessRequest(input, shouldReplaceMediaConnection));
74
+ yield* Effect.try({
75
+ try: () => validateFetchedAccess(currentAccess, next, now, shouldReplaceMediaConnection),
76
+ catch: (cause) => new ConnectionAccessFailure({ code: "access.invalid", cause })
77
+ });
78
+ return [{ access: next, automaticMediaReplacement: mediaExpired && !input.replaceMediaConnection }, { access: next }];
79
+ });
80
+ }
81
+ function connectionAccessRequest(input, replaceMediaConnection) {
82
+ return Object.freeze({
83
+ reason: input.reason,
84
+ replaceMediaConnection,
85
+ ...input.current.access ? {
86
+ currentMediaToken: input.current.access.media.token,
87
+ expectedParticipantGeneration: input.current.access.subject.participantGeneration
88
+ } : {}
89
+ });
90
+ }
91
+ function validateFetchedAccess(previous, next, now, replaceMediaConnection) {
92
+ validateExpiration(next, now);
93
+ if (previous) validateRefresh(previous, next, replaceMediaConnection);
94
+ }
95
+ function notifyAutomaticMediaReplacement(listeners, result) {
96
+ if (!result.automaticMediaReplacement) return;
97
+ for (const listener of listeners) {
98
+ try {
99
+ listener(result.access);
100
+ } catch {
101
+ }
102
+ }
103
+ }
78
104
  function millisecondsUntil(access, now, refreshWindowMs) {
79
105
  return Math.max(0, Math.min(expiresAt(access.sync.expiresAt), expiresAt(access.media.expiresAt)) - now - refreshWindowMs);
80
106
  }
@@ -82,12 +108,15 @@ function validateExpiration(access, now) {
82
108
  if (expiresAt(access.sync.expiresAt) <= now || expiresAt(access.media.expiresAt) <= now) throw new TypeError("Access grant is expired");
83
109
  }
84
110
  function validateRefresh(previous, next, replaceMediaConnection) {
111
+ if (!hasSameSubject(previous, next)) throw new TypeError("Access grant refresh changed its subject");
112
+ const mediaConnectionChanged = previous.media.provider !== next.media.provider || mediaBinding(previous) !== mediaBinding(next);
113
+ if (replaceMediaConnection === mediaConnectionChanged) return;
114
+ throw new TypeError(replaceMediaConnection ? "Access grant replacement did not change its media binding" : "Access grant refresh unexpectedly replaced its media connection");
115
+ }
116
+ function hasSameSubject(previous, next) {
85
117
  const left = previous.subject;
86
118
  const right = next.subject;
87
- if (left.tenantId !== right.tenantId || left.spaceId !== right.spaceId || left.episodeId !== right.episodeId || left.participantId !== right.participantId || left.participantGeneration !== right.participantGeneration) {
88
- throw new TypeError("Access grant refresh changed its subject");
89
- }
90
- if (!replaceMediaConnection && (previous.media.provider !== next.media.provider || mediaBinding(previous) !== mediaBinding(next))) throw new TypeError("Access grant refresh unexpectedly replaced its media connection");
119
+ return left.tenantId === right.tenantId && left.spaceId === right.spaceId && left.episodeId === right.episodeId && left.participantId === right.participantId && left.participantGeneration === right.participantGeneration;
91
120
  }
92
121
  function mediaBinding(access) {
93
122
  switch (access.media.provider) {
@@ -879,12 +908,15 @@ var CLIENT_RESTART_CLOSE_CODE = 4e3;
879
908
  var DEPENDENCY_UNAVAILABLE_CLOSE_CODE = 1012;
880
909
  var HEARTBEAT_INTERVAL_MS = 2e4;
881
910
  var MAX_MISSED_HEARTBEATS = 2;
911
+ var DEFAULT_RECONNECT_DELAY_MS = 250;
912
+ var MAX_RECONNECT_DELAY_MS = 5e3;
882
913
  var encoder2 = new TextEncoder();
883
914
  var ChalkWhiteboardV1Client = class {
884
915
  files;
885
916
  #options;
886
917
  #store;
887
918
  #listeners = /* @__PURE__ */ new Set();
919
+ #summaryListeners = /* @__PURE__ */ new Set();
888
920
  #operations = /* @__PURE__ */ new Map();
889
921
  #reservedOperationIds = /* @__PURE__ */ new Set();
890
922
  #awaitingOperationIds = /* @__PURE__ */ new Set();
@@ -913,6 +945,7 @@ var ChalkWhiteboardV1Client = class {
913
945
  #stopPromise = null;
914
946
  #waitingForOperations = false;
915
947
  #reconnectTimer;
948
+ #reconnectAttempt = 0;
916
949
  #heartbeatTimer;
917
950
  #missedHeartbeats = 0;
918
951
  #lastCursorAt = Number.NEGATIVE_INFINITY;
@@ -989,6 +1022,11 @@ var ChalkWhiteboardV1Client = class {
989
1022
  if (this.#snapshots.size === 0 && !this.#waitingForOperations) void this.#requestSnapshot().catch(() => void 0);
990
1023
  return () => this.#listeners.delete(listener);
991
1024
  }
1025
+ subscribeSummary(listener) {
1026
+ this.#summaryListeners.add(listener);
1027
+ this.#notifySummaryListener(listener, this.#summary());
1028
+ return () => this.#summaryListeners.delete(listener);
1029
+ }
992
1030
  submitUpdate(input) {
993
1031
  this.#assertLive("submit_update");
994
1032
  if (!this.#canDraw || !this.#capabilities.includes("drawWhiteboard")) {
@@ -1113,7 +1151,7 @@ var ChalkWhiteboardV1Client = class {
1113
1151
  const cursor = this.#sceneId && this.#revision ? { scene_id: this.#sceneId, revision: this.#revision } : null;
1114
1152
  this.#send(this.#useLegacyHello ? { type: "hello", protocol: "whiteboard-v1", token, cursor } : { type: "hello", protocol: "whiteboard-v1", token, cursor, extensions: [{ name: "presentation_v1" }] });
1115
1153
  } catch {
1116
- socket.close(1008, "whiteboard authentication failed");
1154
+ socket.close(CLIENT_RESTART_CLOSE_CODE, "whiteboard authentication failed");
1117
1155
  }
1118
1156
  }
1119
1157
  async #receive(socket, data) {
@@ -1225,6 +1263,7 @@ var ChalkWhiteboardV1Client = class {
1225
1263
  #welcome(frame) {
1226
1264
  if (this.#phase !== "authenticating") throw new Error("unexpected whiteboard welcome");
1227
1265
  this.#phase = "live";
1266
+ this.#reconnectAttempt = 0;
1228
1267
  this.#participantId = frame.participant_id;
1229
1268
  this.#sceneId = frame.scene_id;
1230
1269
  this.#revision = frame.revision;
@@ -1437,10 +1476,13 @@ var ChalkWhiteboardV1Client = class {
1437
1476
  }
1438
1477
  #scheduleReconnect() {
1439
1478
  this.#clearReconnect();
1479
+ const configuredDelay = this.#options.reconnectDelayMs;
1480
+ const delay = configuredDelay === 0 ? 0 : Math.min(MAX_RECONNECT_DELAY_MS, (configuredDelay ?? DEFAULT_RECONNECT_DELAY_MS) * 2 ** Math.min(this.#reconnectAttempt, 5));
1481
+ this.#reconnectAttempt += 1;
1440
1482
  this.#reconnectTimer = this.#clock().setTimeout(() => {
1441
1483
  this.#reconnectTimer = void 0;
1442
1484
  this.#connect();
1443
- }, this.#options.reconnectDelayMs ?? 250);
1485
+ }, delay);
1444
1486
  }
1445
1487
  #handleLifecycle(event) {
1446
1488
  if (event === "online" || event === "offline") this.#online = event === "online";
@@ -1480,7 +1522,15 @@ var ChalkWhiteboardV1Client = class {
1480
1522
  #publishSummary(status, summaryError) {
1481
1523
  this.#summaryStatus = status;
1482
1524
  this.#summaryError = summaryError;
1483
- const summary = {
1525
+ const summary = this.#summary();
1526
+ try {
1527
+ this.#options.onSummary?.(summary);
1528
+ } catch {
1529
+ }
1530
+ for (const listener of this.#summaryListeners) this.#notifySummaryListener(listener, summary);
1531
+ }
1532
+ #summary() {
1533
+ return {
1484
1534
  status: this.#summaryStatus,
1485
1535
  sceneId: this.#sceneId,
1486
1536
  revision: this.#revision,
@@ -1490,8 +1540,10 @@ var ChalkWhiteboardV1Client = class {
1490
1540
  presenting: this.#presenting,
1491
1541
  error: this.#summaryError
1492
1542
  };
1543
+ }
1544
+ #notifySummaryListener(listener, summary) {
1493
1545
  try {
1494
- this.#options.onSummary?.(summary);
1546
+ listener(summary);
1495
1547
  } catch {
1496
1548
  }
1497
1549
  }
@@ -1739,9 +1791,15 @@ function createMediaClient(apiBaseURL, input) {
1739
1791
  );
1740
1792
  }
1741
1793
  const { subject } = input.access;
1794
+ const replaceMediaConnection = input.replaceMediaConnection;
1742
1795
  const client = new CloudflareSFUClient({
1743
1796
  bootstrap: input.access.media.clientPayload,
1744
1797
  participantId: subject.participantId,
1798
+ replaceMediaConnection: replaceMediaConnection ? async () => {
1799
+ const replacement = await replaceMediaConnection();
1800
+ if (replacement.provider !== "cloudflare_sfu") throw new TypeError("The Cloudflare SFU adapter requires a Cloudflare SFU access grant");
1801
+ return replacement.clientPayload;
1802
+ } : void 0,
1745
1803
  transport: createCloudflareSFUHTTPTransport({
1746
1804
  apiBaseURL,
1747
1805
  credential: input.credential,
@@ -1751,6 +1809,7 @@ function createMediaClient(apiBaseURL, input) {
1751
1809
  participantId: subject.participantId
1752
1810
  }),
1753
1811
  onError: input.onFailure,
1812
+ ...input.recordRtcSummary ? { onRtcSummary: input.recordRtcSummary } : {},
1754
1813
  onScreenEnded: input.onScreenEnded
1755
1814
  });
1756
1815
  return createSFUConnectionMediaClient(client);
@@ -2135,6 +2194,8 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2135
2194
  try: () => platform.createMediaClient({
2136
2195
  access: grant,
2137
2196
  credential: () => toPromise(access.getMediaToken()),
2197
+ replaceMediaConnection: () => toPromise(access.refresh("media_recovery", true)).then((replacement) => replacement.media),
2198
+ ...options.recordRtcSummary ? { recordRtcSummary: options.recordRtcSummary } : {},
2138
2199
  onFailure: () => enqueueBackground(handleMediaFailure()),
2139
2200
  onScreenEnded: () => enqueueBackground(notifyScreenEnded())
2140
2201
  }),
@@ -2327,6 +2388,18 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2327
2388
  yield* publish();
2328
2389
  });
2329
2390
  const handleMediaFailure = () => model.media ? handleMediaSnapshot(model.media, model.media.getSnapshot()) : Effect2.void;
2391
+ const applyAutomaticMediaReplacement = (grant) => Effect2.gen(function* () {
2392
+ const media = model.media;
2393
+ if (!media || !active(model)) return;
2394
+ const restartInput = grant.media.provider === "cloudflare_sfu" ? grant.media.clientPayload : grant.media;
2395
+ const restarted = yield* Effect2.exit(foreign6(() => media.restart(restartInput), "media_start_failed", "The media client could not apply a replacement access grant"));
2396
+ if (restarted._tag === "Failure") {
2397
+ yield* recover("media");
2398
+ return;
2399
+ }
2400
+ model.mediaSnapshot = media.getSnapshot();
2401
+ yield* publish();
2402
+ });
2330
2403
  const notifyScreenEnded = () => Effect2.sync(() => {
2331
2404
  if (!active(model)) return;
2332
2405
  for (const listener of screenEndedListeners) {
@@ -2345,10 +2418,12 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
2345
2418
  yield* Deferred.done(work.deferred, exit);
2346
2419
  }) : Deferred.complete(work.deferred, work.effect).pipe(Effect2.asVoid);
2347
2420
  yield* Effect2.forkScoped(Queue.take(queue).pipe(Effect2.flatMap(process), Effect2.forever));
2421
+ const accessReplacementUnsubscribe = yield* access.subscribeAutomaticMediaReplacement((grant) => enqueueBackground(applyAutomaticMediaReplacement(grant)));
2348
2422
  const foregroundUnsubscribe = platform.subscribeForeground?.(() => enqueueBackground(refreshAccess())) ?? null;
2349
2423
  yield* Effect2.addFinalizer(
2350
2424
  () => Effect2.gen(function* () {
2351
2425
  model.closed = true;
2426
+ accessReplacementUnsubscribe();
2352
2427
  foregroundUnsubscribe?.();
2353
2428
  if (activeJoin) yield* Fiber.interrupt(activeJoin);
2354
2429
  yield* performLeave().pipe(Effect2.ignore);
@@ -4806,7 +4881,7 @@ var makeSpaceClientCoreLayer = (options, platform = {}) => {
4806
4881
  now: baseDependencies.clock.now,
4807
4882
  setTimeout: baseDependencies.clock.setTimeout,
4808
4883
  clearTimeout: baseDependencies.clock.clearTimeout,
4809
- release: { id: "chalk-client@4.1.12" }
4884
+ release: { id: "chalk-client@4.1.14" }
4810
4885
  });
4811
4886
  const diagnosticSyncClients = /* @__PURE__ */ new Set();
4812
4887
  const dependencies = {
@@ -4827,6 +4902,7 @@ var makeSpaceClientCoreLayer = (options, platform = {}) => {
4827
4902
  initialMicrophoneEnabled: platform.initialMicrophoneEnabled,
4828
4903
  initialCameraEnabled: platform.initialCameraEnabled,
4829
4904
  telemetry: platform.telemetry,
4905
+ recordRtcSummary: platform.recordRtcSummary,
4830
4906
  dependencies,
4831
4907
  diagnostics: {
4832
4908
  onEvent: (event) => {
@@ -4870,7 +4946,6 @@ var SpaceClientCore = class {
4870
4946
  #previous;
4871
4947
  #unsubscribeConnection = null;
4872
4948
  #unsubscribeStore = null;
4873
- #unsupportedReported = false;
4874
4949
  #leaveOperation;
4875
4950
  #participantJoinOperation;
4876
4951
  #syncConnectOperation;
@@ -5007,11 +5082,6 @@ var SpaceClientCore = class {
5007
5082
  #publishConnectionDiagnostics() {
5008
5083
  const snapshot = this.#connection.getSnapshot();
5009
5084
  if (!this.#episodeDiagnostics || snapshot.state !== "live") return;
5010
- if (!this.#unsupportedReported) {
5011
- this.#unsupportedReported = true;
5012
- const unsupported = this.#episodeDiagnostics.startOperation("whiteboard.unsupported");
5013
- unsupported?.succeed({ status: "unsupported" });
5014
- }
5015
5085
  if (!this.#participantJoinOperation) {
5016
5086
  this.#participantJoinOperation = this.#episodeDiagnostics.startOperation("participant.join");
5017
5087
  this.#participantJoinOperation?.succeed({ status: "live" });
@@ -5082,7 +5152,7 @@ async function resolveAccessGrant(input) {
5082
5152
  const { operationName: operationName2, reason } = diagnosticAccessOperation(input.request);
5083
5153
  const operationBeforeRotation = input.diagnostics.activeContext() ? input.diagnostics.startOperation(operationName2, { reason }) : void 0;
5084
5154
  try {
5085
- const grant = await requireParsedAccessGrant(await fetchAccessGrant(input));
5155
+ const grant = await requireParsedAccessGrant(await fetchAccessGrant2(input));
5086
5156
  input.diagnostics.rotateCredential(grant.diagnostics ?? null);
5087
5157
  (operationBeforeRotation ?? input.diagnostics.startOperation(operationName2, { reason }))?.succeed({ result: "bound" });
5088
5158
  return grant;
@@ -5095,7 +5165,7 @@ function diagnosticAccessOperation(request) {
5095
5165
  const reason = request?.reason ?? "join";
5096
5166
  return { operationName: reason === "join" ? "access.request" : "access.refresh", reason };
5097
5167
  }
5098
- async function fetchAccessGrant(input) {
5168
+ async function fetchAccessGrant2(input) {
5099
5169
  if (input.platform.connectionAccess) return input.platform.connectionAccess(input.request);
5100
5170
  return input.options.getAccess({ space: input.options.space.trim(), reason: accessReason(input.request?.reason) });
5101
5171
  }
@@ -22,6 +22,8 @@ var TELEMETRY_BUILT_IN_ATTRIBUTE_KEYS = [
22
22
  "connection_state",
23
23
  "ice_connection_state",
24
24
  "signaling_state",
25
+ "dtls_state",
26
+ "selected_candidate_pair_state",
25
27
  "bytes_received",
26
28
  "bytes_sent",
27
29
  "frames_dropped",
@@ -615,7 +617,9 @@ function summarizeRtcStats(stats) {
615
617
  packetsReceived: 0,
616
618
  packetsSent: 0,
617
619
  roundTripTimeSamples: [],
618
- transportEntries: 0
620
+ transportEntries: 0,
621
+ dtlsStates: [],
622
+ selectedCandidatePairStates: []
619
623
  };
620
624
  for (const stat of stats) {
621
625
  statHandlers[stat.type ?? ""]?.(accumulator, stat);
@@ -634,6 +638,8 @@ function rtcSummaryAttributes(connection, stats) {
634
638
  packets_received: summary.packetsReceived,
635
639
  packets_sent: summary.packetsSent,
636
640
  transport_entries: summary.transportEntries,
641
+ ...summary.dtlsState !== void 0 ? { dtls_state: summary.dtlsState } : {},
642
+ ...summary.selectedCandidatePairState !== void 0 ? { selected_candidate_pair_state: summary.selectedCandidatePairState } : {},
637
643
  ...connection.connectionState !== void 0 ? { connection_state: connection.connectionState } : {},
638
644
  ...connection.iceConnectionState !== void 0 ? { ice_connection_state: connection.iceConnectionState } : {},
639
645
  ...summary.jitterMs !== void 0 ? { jitter_ms: summary.jitterMs } : {},
@@ -663,6 +669,13 @@ function accumulateOutbound(accumulator, stat) {
663
669
  function accumulateTransport(accumulator, stat) {
664
670
  accumulator.transportEntries += 1;
665
671
  appendFinite(accumulator.roundTripTimeSamples, stat.roundTripTime);
672
+ appendRtcState(accumulator.dtlsStates, stat.dtlsState, ["new", "connecting", "connected", "closed", "failed"]);
673
+ if (stat.type === "candidate-pair" && (stat.selected === true || stat.nominated === true)) {
674
+ appendRtcState(accumulator.selectedCandidatePairStates, stat.state, ["frozen", "waiting", "in-progress", "failed", "succeeded"]);
675
+ }
676
+ }
677
+ function appendRtcState(states, value, allowed) {
678
+ if (value !== void 0 && allowed.includes(value) && !states.includes(value)) states.push(value);
666
679
  }
667
680
  function appendFinite(samples, value) {
668
681
  if (isFiniteNumber(value)) samples.push(value);
@@ -670,6 +683,8 @@ function appendFinite(samples, value) {
670
683
  function finalizeAccumulator(accumulator) {
671
684
  const jitterMs = averageMilliseconds(accumulator.jitterSamples);
672
685
  const roundTripTimeMs = averageMilliseconds(accumulator.roundTripTimeSamples);
686
+ const dtlsState = lastValue(accumulator.dtlsStates);
687
+ const selectedCandidatePairState = lastValue(accumulator.selectedCandidatePairStates);
673
688
  return {
674
689
  bytesReceived: accumulator.bytesReceived,
675
690
  bytesSent: accumulator.bytesSent,
@@ -680,10 +695,15 @@ function finalizeAccumulator(accumulator) {
680
695
  packetsReceived: accumulator.packetsReceived,
681
696
  packetsSent: accumulator.packetsSent,
682
697
  transportEntries: accumulator.transportEntries,
698
+ ...dtlsState !== void 0 ? { dtlsState } : {},
699
+ ...selectedCandidatePairState !== void 0 ? { selectedCandidatePairState } : {},
683
700
  ...jitterMs !== void 0 ? { jitterMs } : {},
684
701
  ...roundTripTimeMs !== void 0 ? { roundTripTimeMs } : {}
685
702
  };
686
703
  }
704
+ function lastValue(values) {
705
+ return values.at(-1);
706
+ }
687
707
  function averageMilliseconds(samples) {
688
708
  if (samples.length === 0) return void 0;
689
709
  return Math.round(samples.reduce((total, sample) => total + sample, 0) / samples.length * 1e3);
@@ -1916,6 +1916,8 @@ var MAX_LIVE_TARGET_RETRY_DELAY_MS = 1e3;
1916
1916
  var MAX_PROJECTION_EVENT_EVIDENCE = 256;
1917
1917
  var OPERATION_PENDING_POLL_INTERVAL_MS = 1e3;
1918
1918
  var CLIENT_RESTART_CLOSE_CODE = 4e3;
1919
+ var DEFAULT_RECONNECT_DELAY_MS = 250;
1920
+ var MAX_RECONNECT_DELAY_MS = 5e3;
1919
1921
  var MAX_COLLABORATION_REQUESTS_IN_FLIGHT = 64;
1920
1922
  var V1SyncError = class extends Error {
1921
1923
  constructor(message, code) {
@@ -1962,6 +1964,7 @@ var V1SyncClient = class {
1962
1964
  #startupGeneration = 0;
1963
1965
  #connectionGeneration = 0;
1964
1966
  #reconnectTimer;
1967
+ #reconnectAttempt = 0;
1965
1968
  #heartbeatTimer;
1966
1969
  #missedHeartbeats = 0;
1967
1970
  #unsubscribeLifecycle;
@@ -2855,6 +2858,7 @@ var V1SyncClient = class {
2855
2858
  if (!this.#recovery?.controlComplete || !this.#media || !this.#presence) return;
2856
2859
  this.#recovery = null;
2857
2860
  this.#phase = { phase: "live" };
2861
+ this.#reconnectAttempt = 0;
2858
2862
  this.#missedHeartbeats = 0;
2859
2863
  for (const pending of this.#commands.values()) {
2860
2864
  pending.retries = 0;
@@ -2936,10 +2940,13 @@ var V1SyncClient = class {
2936
2940
  this.#phase = { phase: "connecting" };
2937
2941
  this.#emit();
2938
2942
  this.#clearReconnect();
2943
+ const configuredDelay = this.#options.reconnectDelayMs;
2944
+ const delay = configuredDelay === 0 ? 0 : Math.min(MAX_RECONNECT_DELAY_MS, (configuredDelay ?? DEFAULT_RECONNECT_DELAY_MS) * 2 ** Math.min(this.#reconnectAttempt, 5));
2945
+ this.#reconnectAttempt += 1;
2939
2946
  this.#reconnectTimer = this.#clock().setTimeout(() => {
2940
2947
  this.#reconnectTimer = void 0;
2941
2948
  this.#connect();
2942
- }, this.#options.reconnectDelayMs ?? 250);
2949
+ }, delay);
2943
2950
  }
2944
2951
  #recover(reason) {
2945
2952
  const socket = this.#socket;