@q9labsai/chalk-client 5.3.1 → 5.4.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.
@@ -301,6 +301,73 @@ function scheduleSyncDialDeadline(clock, onTimeout, timeoutMs = SYNC_DIAL_TIMEOU
301
301
  return () => clock.clearTimeout(timer);
302
302
  }
303
303
 
304
+ // src/sync/link-watch.ts
305
+ var LONG_DROP_MS = 6e4;
306
+ var ROUND_TRIP_MS = 5e3;
307
+ var UNANSWERED_LIMIT_MS = 6e4;
308
+ var LinkWatch = class {
309
+ #clock;
310
+ #page;
311
+ #suspends = 0;
312
+ #pings = [];
313
+ #proof = null;
314
+ #suspended = () => {
315
+ this.#suspends += 1;
316
+ };
317
+ constructor(clock, page) {
318
+ this.#clock = clock;
319
+ this.#page = page;
320
+ }
321
+ /** Listens for page suspension. Idempotent, and paired with `close`. */
322
+ open() {
323
+ this.close();
324
+ this.#page?.addEventListener("freeze", this.#suspended);
325
+ this.#page?.addEventListener("resume", this.#suspended);
326
+ }
327
+ close() {
328
+ this.#page?.removeEventListener("freeze", this.#suspended);
329
+ this.#page?.removeEventListener("resume", this.#suspended);
330
+ }
331
+ /** Call when a connection goes live: the previous socket's pings will not be answered. */
332
+ restart() {
333
+ this.#pings = [];
334
+ }
335
+ /**
336
+ * Call as a ping goes out, in the same order as the pings. False means the oldest ping has gone unanswered for over
337
+ * `UNANSWERED_LIMIT_MS`: the socket is dead and this ping must not be sent on it.
338
+ */
339
+ pinged() {
340
+ const now = this.#read();
341
+ const oldest = this.#pings[0];
342
+ if (oldest !== void 0 && (now.wall - oldest.wall > UNANSWERED_LIMIT_MS || now.mono - oldest.mono > UNANSWERED_LIMIT_MS)) return false;
343
+ this.#pings.push(now);
344
+ return true;
345
+ }
346
+ /** Call as a pong is received. It answers the oldest ping in flight, and proves contact only if the round trip was short. */
347
+ ponged() {
348
+ const sent = this.#pings.shift();
349
+ if (sent === void 0) return;
350
+ const now = this.#read();
351
+ const wall = now.wall - sent.wall;
352
+ const mono = now.mono - sent.mono;
353
+ if (wall < 0 || wall > ROUND_TRIP_MS || mono < 0 || mono > ROUND_TRIP_MS || now.suspends !== sent.suspends) return;
354
+ this.#proof = sent;
355
+ }
356
+ proof() {
357
+ return this.#proof;
358
+ }
359
+ longSince(proof) {
360
+ if (proof === null) return true;
361
+ const now = this.#read();
362
+ const wall = now.wall - proof.wall;
363
+ const mono = now.mono - proof.mono;
364
+ return wall < 0 || mono < 0 || wall >= LONG_DROP_MS || mono >= LONG_DROP_MS || now.suspends !== proof.suspends;
365
+ }
366
+ #read() {
367
+ return { wall: this.#clock.wall(), mono: this.#clock.mono(), suspends: this.#suspends + (this.#page?.wasDiscarded === true ? 1 : 0) };
368
+ }
369
+ };
370
+
304
371
  // src/generated/sync.ts
305
372
  import { Schema } from "effect";
306
373
  var SyncProtocolMetadata = {
@@ -874,8 +941,13 @@ function strictEvent(value) {
874
941
  return false;
875
942
  }
876
943
  }
944
+ function validMediaOffReason(item) {
945
+ if (!Object.hasOwn(item, "off_reason")) return !Object.hasOwn(item, "off_revision");
946
+ if (item.off_reason === "host") return Number.isInteger(item.off_revision) && item.off_revision > 0;
947
+ return item.enabled === false && (item.off_reason === "self" || item.off_reason === "dead") && !Object.hasOwn(item, "off_revision");
948
+ }
877
949
  function strictProjectionItem(value, stream) {
878
- if (stream === "media") return exactObject(value, ["participant_id", "source", "enabled", "publication_id"]);
950
+ if (stream === "media") return isObject(value) && exactObject(value, [...["participant_id", "source", "enabled", "publication_id"], ...Object.hasOwn(value, "off_reason") ? ["off_reason"] : [], ...Object.hasOwn(value, "off_revision") ? ["off_revision"] : []]) && validMediaOffReason(value);
879
951
  if (stream === "presence")
880
952
  return isObject(value) && exactObject(value, [...["participant_id", "state", "speaking", "active_speaker"], ...Object.hasOwn(value, "identity_id") ? ["identity_id"] : [], ...Object.hasOwn(value, "device_type") ? ["device_type"] : [], ...Object.hasOwn(value, "admission_revision") ? ["admission_revision"] : []]);
881
953
  return false;
@@ -889,10 +961,12 @@ function strictWelcome(value) {
889
961
  const extension = Array.isArray(value.extensions) && value.extensions.length === 1 ? value.extensions[0] : void 0;
890
962
  const extensionValid = isObject(extension) && ["collaboration_v1", "collaboration_v2"].includes(String(extension.name)) && exactObject(extension, ["name", "capabilities", "participant_capabilities", "chat_head_sequence", "retained_floor_sequence", "read_receipts"]) && Array.isArray(extension.read_receipts);
891
963
  if (Object.hasOwn(value, "entry_confirmation") && value.entry_confirmation !== true) return false;
964
+ if (Object.hasOwn(value, "media_off_reason") && value.media_off_reason !== true) return false;
892
965
  const fields = [
893
966
  ...base,
894
967
  ...extensionValid ? ["extensions"] : [],
895
968
  ...Object.hasOwn(value, "entry_confirmation") ? ["entry_confirmation"] : [],
969
+ ...Object.hasOwn(value, "media_off_reason") ? ["media_off_reason"] : [],
896
970
  ...Object.hasOwn(value, "entry_generation") ? ["entry_generation"] : [],
897
971
  ...Object.hasOwn(value, "participant_identity_id") ? ["participant_identity_id"] : []
898
972
  ];
@@ -1506,6 +1580,7 @@ var WelcomeBase = {
1506
1580
  recovery_id: UuidSchema,
1507
1581
  head: ControlCursorSchema,
1508
1582
  entry_confirmation: Schema.optionalKey(Schema.Literal(true)),
1583
+ media_off_reason: Schema.optionalKey(Schema.Literal(true)),
1509
1584
  entry_generation: Schema.optionalKey(PositiveIntegerSchema.check(Schema.isLessThanOrEqualTo(9007199254740991)))
1510
1585
  };
1511
1586
  var ExtendedWelcomeBase = { ...WelcomeBase, extensions: Schema.Tuple([CollaborationWelcomeExtensionSchema]) };
@@ -1548,9 +1623,14 @@ var CommittedAckFrameSchema = Schema.Struct({ type: Schema.Literal("ack"), comma
1548
1623
  var SatisfiedAckFrameSchema = Schema.Struct({ type: Schema.Literal("ack"), command_id: CommandIdSchema, delivery: AckDeliverySchema, outcome: Schema.Literal("satisfied"), revision: NonNegativeIntegerSchema, state_digest: StateDigestSchema, ...ServerCorrelationFields });
1549
1624
  var RejectedAckFrameSchema = Schema.Struct({ type: Schema.Literal("ack"), command_id: CommandIdSchema, delivery: AckDeliverySchema, outcome: Schema.Literal("rejected"), reason: RejectionReasonSchema, ...ServerCorrelationFields });
1550
1625
  var CommandIdConflictAckFrameSchema = Schema.Struct({ type: Schema.Literal("ack"), command_id: CommandIdSchema, delivery: AckDeliverySchema, outcome: Schema.Literal("command_id_conflict"), reason: Schema.Literal("command_id_conflict"), ...ServerCorrelationFields });
1551
- var MediaProjectionItemSchema = Schema.Struct({ participant_id: UuidSchema, source: Schema.Union([Schema.Literal("microphone"), Schema.Literal("camera"), Schema.Literal("screen")]), enabled: Schema.Boolean, publication_id: Schema.NullOr(boundedUtf8String(256, 1)) }).check(
1552
- Schema.makeFilter((item) => !item.enabled || item.publication_id !== null ? void 0 : { path: [], issue: "enabled requires a publication_id" })
1553
- );
1626
+ var MediaProjectionItemSchema = Schema.Struct({
1627
+ participant_id: UuidSchema,
1628
+ source: Schema.Union([Schema.Literal("microphone"), Schema.Literal("camera"), Schema.Literal("screen")]),
1629
+ enabled: Schema.Boolean,
1630
+ publication_id: Schema.NullOr(boundedUtf8String(256, 1)),
1631
+ off_reason: Schema.optional(Schema.Literals(["host", "self", "dead"])),
1632
+ off_revision: Schema.optional(PositiveIntegerSchema)
1633
+ }).check(Schema.makeFilter((item) => !item.enabled || item.publication_id !== null ? void 0 : { path: [], issue: "enabled requires a publication_id" }));
1554
1634
  var PresenceProjectionItemSchema = Schema.Struct({
1555
1635
  participant_id: UuidSchema,
1556
1636
  state: Schema.Union([Schema.Literal("connected"), Schema.Literal("disconnected")]),
@@ -3071,6 +3151,11 @@ var V1SyncClient = class {
3071
3151
  #socketCloses = [];
3072
3152
  #omittedSocketCloses = 0;
3073
3153
  #lastHeartbeatReplyAt = null;
3154
+ /** Whether this connection's welcome advertised `media_off_reason`; reset for each connection. */
3155
+ #offReasonNegotiated = false;
3156
+ #watch;
3157
+ /** Tells a recovery whether the drop was proven short. */
3158
+ link;
3074
3159
  #opened = false;
3075
3160
  #connectFailure;
3076
3161
  #connectDiagnostic() {
@@ -3097,6 +3182,8 @@ var V1SyncClient = class {
3097
3182
  constructor(options) {
3098
3183
  assertV1Url(options.url);
3099
3184
  this.#options = options;
3185
+ this.#watch = new LinkWatch({ wall: () => this.#now(), mono: options.monotonic ?? (() => performance.now()) }, options.page === void 0 ? typeof document === "object" ? document : null : options.page);
3186
+ this.link = this.#watch;
3100
3187
  this.#commandScheduler = new V1CommandScheduler({
3101
3188
  store: options.pendingStore ?? new InMemoryV1PendingTargetStore(),
3102
3189
  ids: options.ids,
@@ -3143,6 +3230,7 @@ var V1SyncClient = class {
3143
3230
  async start() {
3144
3231
  if (this.#started) return;
3145
3232
  this.#started = true;
3233
+ this.#watch.open();
3146
3234
  this.#connectAttemptCount = 0;
3147
3235
  this.#socketCloses = [];
3148
3236
  this.#omittedSocketCloses = 0;
@@ -3155,6 +3243,7 @@ var V1SyncClient = class {
3155
3243
  } catch (error) {
3156
3244
  if (startupGeneration === this.#startupGeneration) {
3157
3245
  this.#started = false;
3246
+ this.#watch.close();
3158
3247
  }
3159
3248
  throw error;
3160
3249
  }
@@ -3166,6 +3255,7 @@ var V1SyncClient = class {
3166
3255
  }
3167
3256
  stop() {
3168
3257
  this.#started = false;
3258
+ this.#watch.close();
3169
3259
  this.#entryPending = false;
3170
3260
  this.#entryGeneration = null;
3171
3261
  this.#startupGeneration += 1;
@@ -3197,6 +3287,10 @@ var V1SyncClient = class {
3197
3287
  if (!this.#entryNegotiated) return;
3198
3288
  this.#send({ type: "entered" });
3199
3289
  }
3290
+ /** True when the connected Sync advertised `media_off_reason` in its welcome, so media items may carry `off_reason`. */
3291
+ offReasonNegotiated() {
3292
+ return this.#offReasonNegotiated;
3293
+ }
3200
3294
  getSnapshot() {
3201
3295
  const pendingCommands = this.#commandScheduler.pendingCommands;
3202
3296
  const optimisticControl = this.#control && this.#participantId ? optimisticV1Control(this.#control, this.#participantId, pendingCommands) : this.#control;
@@ -3357,6 +3451,7 @@ var V1SyncClient = class {
3357
3451
  if (this.#connectFailure?.opened) this.#connectFailure = void 0;
3358
3452
  this.#emit();
3359
3453
  this.#entryNegotiated = false;
3454
+ this.#offReasonNegotiated = false;
3360
3455
  let socket;
3361
3456
  try {
3362
3457
  socket = this.#options.webSocket.connect(entryUrl(this.#options.url, this.#entryGeneration, this.#options.entryConfirmation === true));
@@ -3572,6 +3667,7 @@ var V1SyncClient = class {
3572
3667
  return;
3573
3668
  case "pong": {
3574
3669
  this.#requireLive();
3670
+ this.#watch.ponged();
3575
3671
  this.#missedHeartbeats = 0;
3576
3672
  this.#lastHeartbeatReplyAt = this.#now();
3577
3673
  const previous = this.#health.health;
@@ -3586,6 +3682,7 @@ var V1SyncClient = class {
3586
3682
  this.#baggageHelloSocket = null;
3587
3683
  this.#collaborationState.acceptWelcome(frame);
3588
3684
  this.#entryNegotiated = frame.entry_confirmation === true;
3685
+ this.#offReasonNegotiated = frame.media_off_reason === true;
3589
3686
  this.#entryGeneration = frame.entry_generation ?? this.#entryGeneration;
3590
3687
  this.#participantId = frame.participant_id;
3591
3688
  this.#participantGeneration = frame.participant_generation;
@@ -3667,6 +3764,9 @@ var V1SyncClient = class {
3667
3764
  this.#media = updateProjection(this.#media, frame.projection_id, frame.sequence, item, mediaKey);
3668
3765
  rememberBoundedEvidence(this.#mediaEventEvidence, frame.sequence, frameSignature(frame), MAX_PROJECTION_EVENT_EVIDENCE);
3669
3766
  this.#notifyRemotePublicationResumed(previous, item);
3767
+ if (!this.#mediaEnded() && item.participantId === this.#participantId && previous?.publicationId && previous.publicationId !== item.publicationId) {
3768
+ this.#options.mediaPlane?.localPublicationWithdrawn?.(previous.publicationId);
3769
+ }
3670
3770
  if (!this.#mediaEnded() && item.participantId !== this.#participantId && (previous?.enabled !== item.enabled || previous?.publicationId !== item.publicationId)) {
3671
3771
  this.#options.mediaPlane?.remotePublicationsChanged?.(this.#media?.items ?? []);
3672
3772
  }
@@ -3925,7 +4025,7 @@ var V1SyncClient = class {
3925
4025
  this.#socket?.close(CLIENT_RESTART_CLOSE_CODE, "heartbeat timeout");
3926
4026
  return;
3927
4027
  }
3928
- this.#send({ type: "ping" });
4028
+ if (!this.#sendPing()) return;
3929
4029
  this.#startHeartbeat();
3930
4030
  }, 2e4);
3931
4031
  }
@@ -3936,7 +4036,8 @@ var V1SyncClient = class {
3936
4036
  }
3937
4037
  #startNoticeProbe() {
3938
4038
  this.#clearNoticeProbe();
3939
- this.#send({ type: "ping" });
4039
+ this.#watch.restart();
4040
+ this.#sendPing();
3940
4041
  this.#health = new V1SyncHealth(this.#now());
3941
4042
  const tick = () => {
3942
4043
  this.#noticeProbeTimer = void 0;
@@ -3944,7 +4045,7 @@ var V1SyncClient = class {
3944
4045
  const previous = this.#health.health;
3945
4046
  this.#health.check(this.#now());
3946
4047
  if (previous !== this.#health.health) this.#emit();
3947
- this.#send({ type: "ping" });
4048
+ if (!this.#sendPing()) return;
3948
4049
  this.#noticeProbeTimer = this.#clock().setTimeout(tick, SYNC_HEALTH_BUDGET.probeIntervalMs);
3949
4050
  };
3950
4051
  this.#noticeProbeTimer = this.#clock().setTimeout(tick, SYNC_HEALTH_BUDGET.probeIntervalMs);
@@ -3954,6 +4055,16 @@ var V1SyncClient = class {
3954
4055
  this.#emit();
3955
4056
  }, SYNC_HEALTH_BUDGET.degradedAfterMs);
3956
4057
  }
4058
+ // The link watch times each ping, so every ping goes through here. When the oldest ping has gone unanswered for over 60 s, the
4059
+ // old heartbeat timeout, the socket is dead: it is replaced through the normal reconnect and this ping is not sent.
4060
+ #sendPing() {
4061
+ if (!this.#watch.pinged()) {
4062
+ this.#recover("ping unanswered for 60 s");
4063
+ return false;
4064
+ }
4065
+ this.#send({ type: "ping" });
4066
+ return true;
4067
+ }
3957
4068
  #clearNoticeProbe() {
3958
4069
  if (this.#noticeTimer !== void 0) this.#clock().clearTimeout(this.#noticeTimer);
3959
4070
  if (this.#noticeProbeTimer !== void 0) this.#clock().clearTimeout(this.#noticeProbeTimer);
@@ -3972,7 +4083,14 @@ function updateProjection(projection, projectionId, sequence, item, key) {
3972
4083
  return { projectionId, sequence, items: [...projection.items.filter((candidate) => key(candidate) !== itemKey), item] };
3973
4084
  }
3974
4085
  function mediaItem(item) {
3975
- return { participantId: item.participant_id, source: item.source, enabled: item.enabled, publicationId: item.publication_id };
4086
+ return {
4087
+ participantId: item.participant_id,
4088
+ source: item.source,
4089
+ enabled: item.enabled,
4090
+ publicationId: item.publication_id,
4091
+ ...item.off_reason === void 0 ? {} : { offReason: item.off_reason },
4092
+ ...item.off_revision === void 0 ? {} : { offRevision: item.off_revision }
4093
+ };
3976
4094
  }
3977
4095
  function presenceItem(item) {
3978
4096
  return { participantId: item.participant_id, admissionRevision: item.admission_revision, identityId: item.identity_id, deviceType: item.device_type, state: item.state, speaking: item.speaking, activeSpeaker: item.active_speaker };
@@ -4005,6 +4123,7 @@ function entryUrl(value, generation, confirmation) {
4005
4123
  const url = new URL(value);
4006
4124
  url.searchParams.set("participant_devices", "1");
4007
4125
  url.searchParams.set("participant_device_admissions", "2");
4126
+ url.searchParams.set("media_off_reason", "1");
4008
4127
  url.searchParams.set("entry_generation", generation === null ? "new" : String(generation));
4009
4128
  if (confirmation) url.searchParams.set("entry_confirmation", "1");
4010
4129
  return url.toString();
@@ -3,6 +3,7 @@ import type { ConnectionReporter, ConnectionReportTransportEvent } from "./repor
3
3
  import type { ClientMediaPlane, CloudflareSFUBootstrap, ConnectionMediaSnapshot } from "../media";
4
4
  import type { ChalkChatFileTransport } from "../chat-files";
5
5
  import type { V1AdmissionTarget, V1AssignableRole, V1CommandResult, V1EpisodeSnapshot, V1SelfMediaTargetResult } from "../sync";
6
+ import type { LinkJudge } from "../sync/link-watch";
6
7
  import type { V1CollaborationClient, V1DirectedRequest, V1DirectedRequestResult, V1MediaPause } from "../sync/v1-types";
7
8
  import type { ChalkWhiteboardSummary, ChalkWhiteboardV1ClientOptions, ChalkWhiteboardV1Transport } from "../whiteboard/types";
8
9
  import type { ParsedAccessGrant, ParticipantMediaAccess, ParticipantMediaCredential } from "../access/grant";
@@ -40,6 +41,10 @@ export type ConnectionSyncClient = V1CollaborationClient & {
40
41
  readonly restartTransport?: () => void;
41
42
  readonly stop: () => void;
42
43
  readonly getSnapshot: () => V1EpisodeSnapshot;
44
+ /** Judges whether a drop was proven short. Absent means unknown, which is treated as long. */
45
+ readonly link?: LinkJudge;
46
+ /** Whether the connected Sync advertised `media_off_reason`. Absent means no. */
47
+ readonly offReasonNegotiated?: () => boolean;
43
48
  readonly subscribe: (listener: (snapshot: V1EpisodeSnapshot) => void) => () => void;
44
49
  readonly leave: () => Promise<V1CommandResult>;
45
50
  readonly setMicrophoneEnabled: (enabled: boolean) => Promise<V1SelfMediaTargetResult>;
@@ -10,6 +10,6 @@ export declare const makeProductionConnectionPlatformLayer: (options: {
10
10
  readonly syncURL: string;
11
11
  readonly whiteboardURL?: string | null;
12
12
  }) => import("effect/Layer").Layer<import("./dependencies").ConnectionPlatformService, never, never>;
13
- type BoundMediaClient = Pick<ConnectionMediaClient, "setLocalPublicationTarget" | "pauseLocalCapture" | "setLocalSourceIntent" | "closeForcedLocalPublication" | "observeLocalPublications" | "observeRemotePublications" | "remotePublicationsChanged" | "remoteParticipantsChanged" | "remotePublicationResumed" | "start" | "stop" | "prepareLocalTrack" | "clearPreparedLocalTrack" | "getSnapshot" | "subscribe">;
13
+ type BoundMediaClient = Pick<ConnectionMediaClient, "setLocalPublicationTarget" | "pauseLocalCapture" | "setLocalSourceIntent" | "closeForcedLocalPublication" | "observeLocalPublications" | "observeRemotePublications" | "remotePublicationsChanged" | "remoteParticipantsChanged" | "remotePublicationResumed" | "localPublicationWithdrawn" | "start" | "stop" | "prepareLocalTrack" | "clearPreparedLocalTrack" | "getSnapshot" | "subscribe">;
14
14
  export declare function bindConnectionMediaClient(client: BoundMediaClient, restart: ConnectionMediaClient["restart"]): ConnectionMediaClient;
15
15
  export {};
package/dist/effect.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  makeSpaceClientCoreLayer,
18
18
  makeSpaceClientCoreLayerFromServices,
19
19
  makeSpaceStoreLayer
20
- } from "./chunk-2CEMOTZF.js";
20
+ } from "./chunk-TA2BJXI7.js";
21
21
  import {
22
22
  APIKeyListSchema,
23
23
  APIKeyWithSecretSchema,
@@ -853,8 +853,8 @@ import {
853
853
  WhiteboardStorageUnavailableErrorWireSchema,
854
854
  createChalkEffectClient
855
855
  } from "./chunk-MHJ4UPM3.js";
856
- import "./chunk-O5JB5XVX.js";
857
- import "./chunk-FD7ZSDTM.js";
856
+ import "./chunk-UVWRTXNO.js";
857
+ import "./chunk-XF567GPH.js";
858
858
  import "./chunk-N4IPKRS6.js";
859
859
 
860
860
  // src/space-client/effect.ts
@@ -1333,6 +1333,7 @@ export declare const WelcomeSnapshotFrameSchema: Schema.Struct<{
1333
1333
  readonly state_digest: Schema.String;
1334
1334
  }>;
1335
1335
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1336
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1336
1337
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1337
1338
  readonly mode: Schema.Literal<"snapshot">;
1338
1339
  readonly snapshot: Schema.Struct<{
@@ -1382,6 +1383,7 @@ export declare const WelcomeReplayFrameSchema: Schema.Struct<{
1382
1383
  readonly state_digest: Schema.String;
1383
1384
  }>;
1384
1385
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1386
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1385
1387
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1386
1388
  readonly mode: Schema.Literal<"replay">;
1387
1389
  }>;
@@ -1401,6 +1403,7 @@ export declare const WelcomeUpToDateFrameSchema: Schema.Struct<{
1401
1403
  readonly state_digest: Schema.String;
1402
1404
  }>;
1403
1405
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1406
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1404
1407
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1405
1408
  readonly mode: Schema.Literal<"up_to_date">;
1406
1409
  }>;
@@ -1420,6 +1423,7 @@ export declare const WelcomeTerminalFrameSchema: Schema.Struct<{
1420
1423
  readonly state_digest: Schema.String;
1421
1424
  }>;
1422
1425
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1426
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1423
1427
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1424
1428
  readonly mode: Schema.Literal<"terminal">;
1425
1429
  readonly reason: Schema.Union<readonly [Schema.Literal<"episode_ended">, Schema.Literal<"participant_inactive">, Schema.Literal<"stale_participant_generation">]>;
@@ -1440,6 +1444,7 @@ export declare const ExtendedWelcomeSnapshotFrameSchema: Schema.Struct<{
1440
1444
  readonly state_digest: Schema.String;
1441
1445
  }>;
1442
1446
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1447
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1443
1448
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1444
1449
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1445
1450
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1502,6 +1507,7 @@ export declare const ExtendedWelcomeReplayFrameSchema: Schema.Struct<{
1502
1507
  readonly state_digest: Schema.String;
1503
1508
  }>;
1504
1509
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1510
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1505
1511
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1506
1512
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1507
1513
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1534,6 +1540,7 @@ export declare const ExtendedWelcomeUpToDateFrameSchema: Schema.Struct<{
1534
1540
  readonly state_digest: Schema.String;
1535
1541
  }>;
1536
1542
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1543
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1537
1544
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1538
1545
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1539
1546
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1566,6 +1573,7 @@ export declare const ExtendedWelcomeTerminalFrameSchema: Schema.Struct<{
1566
1573
  readonly state_digest: Schema.String;
1567
1574
  }>;
1568
1575
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1576
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1569
1577
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1570
1578
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1571
1579
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1599,6 +1607,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1599
1607
  readonly state_digest: Schema.String;
1600
1608
  }>;
1601
1609
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1610
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1602
1611
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1603
1612
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1604
1613
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1660,6 +1669,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1660
1669
  readonly state_digest: Schema.String;
1661
1670
  }>;
1662
1671
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1672
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1663
1673
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1664
1674
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1665
1675
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1691,6 +1701,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1691
1701
  readonly state_digest: Schema.String;
1692
1702
  }>;
1693
1703
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1704
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1694
1705
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1695
1706
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1696
1707
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1722,6 +1733,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1722
1733
  readonly state_digest: Schema.String;
1723
1734
  }>;
1724
1735
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1736
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1725
1737
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1726
1738
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
1727
1739
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -1754,6 +1766,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1754
1766
  readonly state_digest: Schema.String;
1755
1767
  }>;
1756
1768
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1769
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1757
1770
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1758
1771
  readonly mode: Schema.Literal<"snapshot">;
1759
1772
  readonly snapshot: Schema.Struct<{
@@ -1802,6 +1815,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1802
1815
  readonly state_digest: Schema.String;
1803
1816
  }>;
1804
1817
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1818
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1805
1819
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1806
1820
  readonly mode: Schema.Literal<"replay">;
1807
1821
  }>, Schema.Struct<{
@@ -1820,6 +1834,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1820
1834
  readonly state_digest: Schema.String;
1821
1835
  }>;
1822
1836
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1837
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1823
1838
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1824
1839
  readonly mode: Schema.Literal<"up_to_date">;
1825
1840
  }>, Schema.Struct<{
@@ -1838,6 +1853,7 @@ export declare const WelcomeFrameSchema: Schema.Union<readonly [Schema.Struct<{
1838
1853
  readonly state_digest: Schema.String;
1839
1854
  }>;
1840
1855
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
1856
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
1841
1857
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
1842
1858
  readonly mode: Schema.Literal<"terminal">;
1843
1859
  readonly reason: Schema.Union<readonly [Schema.Literal<"episode_ended">, Schema.Literal<"participant_inactive">, Schema.Literal<"stale_participant_generation">]>;
@@ -2198,6 +2214,8 @@ export declare const MediaProjectionItemSchema: Schema.Struct<{
2198
2214
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
2199
2215
  readonly enabled: Schema.Boolean;
2200
2216
  readonly publication_id: Schema.NullOr<Schema.String>;
2217
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
2218
+ readonly off_revision: Schema.optional<Schema.Number>;
2201
2219
  }>;
2202
2220
  export declare const PresenceProjectionItemSchema: Schema.Struct<{
2203
2221
  readonly participant_id: Schema.String;
@@ -2221,6 +2239,8 @@ export declare const MediaProjectionSnapshotFrameSchema: Schema.Struct<{
2221
2239
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
2222
2240
  readonly enabled: Schema.Boolean;
2223
2241
  readonly publication_id: Schema.NullOr<Schema.String>;
2242
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
2243
+ readonly off_revision: Schema.optional<Schema.Number>;
2224
2244
  }>>;
2225
2245
  }>;
2226
2246
  export declare const PresenceProjectionSnapshotFrameSchema: Schema.Struct<{
@@ -2254,6 +2274,8 @@ export declare const MediaProjectionEventFrameSchema: Schema.Struct<{
2254
2274
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
2255
2275
  readonly enabled: Schema.Boolean;
2256
2276
  readonly publication_id: Schema.NullOr<Schema.String>;
2277
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
2278
+ readonly off_revision: Schema.optional<Schema.Number>;
2257
2279
  }>;
2258
2280
  }>;
2259
2281
  export declare const PresenceProjectionEventFrameSchema: Schema.Struct<{
@@ -2287,6 +2309,8 @@ export declare const ProjectionFrameSchema: Schema.Union<readonly [Schema.Struct
2287
2309
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
2288
2310
  readonly enabled: Schema.Boolean;
2289
2311
  readonly publication_id: Schema.NullOr<Schema.String>;
2312
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
2313
+ readonly off_revision: Schema.optional<Schema.Number>;
2290
2314
  }>>;
2291
2315
  }>, Schema.Struct<{
2292
2316
  readonly journey_id: Schema.optional<Schema.String>;
@@ -2318,6 +2342,8 @@ export declare const ProjectionFrameSchema: Schema.Union<readonly [Schema.Struct
2318
2342
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
2319
2343
  readonly enabled: Schema.Boolean;
2320
2344
  readonly publication_id: Schema.NullOr<Schema.String>;
2345
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
2346
+ readonly off_revision: Schema.optional<Schema.Number>;
2321
2347
  }>;
2322
2348
  }>, Schema.Struct<{
2323
2349
  readonly journey_id: Schema.optional<Schema.String>;
@@ -3037,6 +3063,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3037
3063
  readonly state_digest: Schema.String;
3038
3064
  }>;
3039
3065
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3066
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3040
3067
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3041
3068
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
3042
3069
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -3098,6 +3125,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3098
3125
  readonly state_digest: Schema.String;
3099
3126
  }>;
3100
3127
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3128
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3101
3129
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3102
3130
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
3103
3131
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -3129,6 +3157,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3129
3157
  readonly state_digest: Schema.String;
3130
3158
  }>;
3131
3159
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3160
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3132
3161
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3133
3162
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
3134
3163
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -3160,6 +3189,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3160
3189
  readonly state_digest: Schema.String;
3161
3190
  }>;
3162
3191
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3192
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3163
3193
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3164
3194
  readonly extensions: Schema.Tuple<readonly [Schema.Struct<{
3165
3195
  readonly name: Schema.Literals<readonly ["collaboration_v1", "collaboration_v2"]>;
@@ -3192,6 +3222,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3192
3222
  readonly state_digest: Schema.String;
3193
3223
  }>;
3194
3224
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3225
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3195
3226
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3196
3227
  readonly mode: Schema.Literal<"snapshot">;
3197
3228
  readonly snapshot: Schema.Struct<{
@@ -3240,6 +3271,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3240
3271
  readonly state_digest: Schema.String;
3241
3272
  }>;
3242
3273
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3274
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3243
3275
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3244
3276
  readonly mode: Schema.Literal<"replay">;
3245
3277
  }>, Schema.Struct<{
@@ -3258,6 +3290,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3258
3290
  readonly state_digest: Schema.String;
3259
3291
  }>;
3260
3292
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3293
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3261
3294
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3262
3295
  readonly mode: Schema.Literal<"up_to_date">;
3263
3296
  }>, Schema.Struct<{
@@ -3276,6 +3309,7 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3276
3309
  readonly state_digest: Schema.String;
3277
3310
  }>;
3278
3311
  readonly entry_confirmation: Schema.optionalKey<Schema.Literal<true>>;
3312
+ readonly media_off_reason: Schema.optionalKey<Schema.Literal<true>>;
3279
3313
  readonly entry_generation: Schema.optionalKey<Schema.Number>;
3280
3314
  readonly mode: Schema.Literal<"terminal">;
3281
3315
  readonly reason: Schema.Union<readonly [Schema.Literal<"episode_ended">, Schema.Literal<"participant_inactive">, Schema.Literal<"stale_participant_generation">]>;
@@ -3921,6 +3955,8 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3921
3955
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
3922
3956
  readonly enabled: Schema.Boolean;
3923
3957
  readonly publication_id: Schema.NullOr<Schema.String>;
3958
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
3959
+ readonly off_revision: Schema.optional<Schema.Number>;
3924
3960
  }>>;
3925
3961
  }>, Schema.Struct<{
3926
3962
  readonly journey_id: Schema.optional<Schema.String>;
@@ -3952,6 +3988,8 @@ export declare const SyncV1ServerFrameSchema: Schema.decodeTo<Schema.Union<reado
3952
3988
  readonly source: Schema.Union<readonly [Schema.Literal<"microphone">, Schema.Literal<"camera">, Schema.Literal<"screen">]>;
3953
3989
  readonly enabled: Schema.Boolean;
3954
3990
  readonly publication_id: Schema.NullOr<Schema.String>;
3991
+ readonly off_reason: Schema.optional<Schema.Literals<readonly ["host", "self", "dead"]>>;
3992
+ readonly off_revision: Schema.optional<Schema.Number>;
3955
3993
  }>;
3956
3994
  }>, Schema.Struct<{
3957
3995
  readonly journey_id: Schema.optional<Schema.String>;
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  createSpaceClient,
16
16
  decodeWhiteboardV1ServerFrame,
17
17
  encodeWhiteboardV1ClientFrame
18
- } from "./chunk-2CEMOTZF.js";
18
+ } from "./chunk-TA2BJXI7.js";
19
19
  import {
20
20
  createChalkPublicClient,
21
21
  createChalkPublicInviteClient,
@@ -41,7 +41,7 @@ import {
41
41
  setVideoRenderSize,
42
42
  validatePublicationSnapshot,
43
43
  waitFor
44
- } from "./chunk-O5JB5XVX.js";
44
+ } from "./chunk-UVWRTXNO.js";
45
45
  import {
46
46
  AsyncStorageV1PendingTargetStore,
47
47
  InMemoryV1PendingTargetStore,
@@ -70,7 +70,7 @@ import {
70
70
  optimisticV1Control,
71
71
  reactionFromFrame,
72
72
  restoreV1Snapshot
73
- } from "./chunk-FD7ZSDTM.js";
73
+ } from "./chunk-XF567GPH.js";
74
74
  import {
75
75
  SESSION_ID_KEY,
76
76
  currentSessionId,
@@ -6,6 +6,7 @@ export declare class CloudflareSFUClient implements ClientMediaPlane {
6
6
  getSnapshot(): CloudflareSFUSnapshot;
7
7
  subscribe(listener: () => void): () => void;
8
8
  remotePublicationResumed(publicationId: string): void;
9
+ localPublicationWithdrawn(publicationId: string): void;
9
10
  remoteParticipantsChanged(participantIds: readonly string[]): void;
10
11
  remotePublicationsChanged(publications?: readonly MediaPublication[], projectionState?: "available" | "unavailable"): void;
11
12
  prepareLocalTrack(source: MediaPublicationSource, track: MediaStreamTrack): void;
@@ -11,7 +11,7 @@ import {
11
11
  setVideoRenderSize,
12
12
  validatePublicationSnapshot,
13
13
  waitFor
14
- } from "../chunk-O5JB5XVX.js";
14
+ } from "../chunk-UVWRTXNO.js";
15
15
  import "../chunk-N4IPKRS6.js";
16
16
  export {
17
17
  CloudflareSFUClient,
@@ -67,4 +67,5 @@ export type ClientMediaPlane = {
67
67
  readonly remotePublicationsChanged?: (publications?: readonly MediaPublication[], projectionState?: "available" | "unavailable") => void;
68
68
  readonly remoteParticipantsChanged?: (participantIds: readonly string[]) => void;
69
69
  readonly remotePublicationResumed?: (publicationId: string) => void;
70
+ readonly localPublicationWithdrawn?: (publicationId: string) => void;
70
71
  };
@@ -22,7 +22,7 @@ import {
22
22
  encodeV1ClientFrame,
23
23
  optimisticV1Control,
24
24
  restoreV1Snapshot
25
- } from "../chunk-FD7ZSDTM.js";
25
+ } from "../chunk-XF567GPH.js";
26
26
  import "../chunk-N4IPKRS6.js";
27
27
  export {
28
28
  AsyncStorageV1PendingTargetStore,