@q9labsai/chalk-client 4.1.4 → 4.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access/grant.d.ts +8 -0
- package/dist/access/grant.d.ts.map +1 -1
- package/dist/{chunk-PBCGWE2V.js → chunk-C4BIONCP.js} +442 -7
- package/dist/chunk-DFLTEYWX.js +144 -0
- package/dist/chunk-JPNE4K2L.js +8911 -0
- package/dist/{chunk-2DA665LP.js → chunk-MMK2K3LO.js} +181 -203
- package/dist/client.d.ts +951 -59
- package/dist/client.d.ts.map +1 -1
- package/dist/connection/dependencies.d.ts +4 -4
- package/dist/connection/dependencies.d.ts.map +1 -1
- package/dist/connection/lifecycle.d.ts.map +1 -1
- package/dist/connection/production.d.ts.map +1 -1
- package/dist/connection/snapshot.d.ts +2 -2
- package/dist/connection/snapshot.d.ts.map +1 -1
- package/dist/effect.js +1012 -7052
- package/dist/generated/http-api.d.ts +941 -62
- package/dist/generated/http-api.d.ts.map +1 -1
- package/dist/generated/schemas.d.ts +1353 -31
- package/dist/generated/schemas.d.ts.map +1 -1
- package/dist/generated/whiteboard-v1.d.ts +48 -5
- package/dist/generated/whiteboard-v1.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/media/client.d.ts.map +1 -1
- package/dist/media/index.d.ts +2 -1
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +5 -1
- package/dist/media/observers.d.ts +3 -0
- package/dist/media/observers.d.ts.map +1 -0
- package/dist/media/plane.d.ts +25 -0
- package/dist/media/plane.d.ts.map +1 -1
- package/dist/media/rtk.d.ts +101 -0
- package/dist/media/rtk.d.ts.map +1 -0
- package/dist/media/target.d.ts +10 -0
- package/dist/media/target.d.ts.map +1 -0
- package/dist/public-invites/client.d.ts +4 -0
- package/dist/public-invites/client.d.ts.map +1 -0
- package/dist/public-invites/index.d.ts +5 -0
- package/dist/public-invites/index.d.ts.map +1 -0
- package/dist/public-invites/index.js +18 -0
- package/dist/public-invites/types.d.ts +67 -0
- package/dist/public-invites/types.d.ts.map +1 -0
- package/dist/server/{chunk-A6YMG5FM.js → chunk-C4XITY5U.js} +1 -1
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/index.cjs +72 -19
- package/dist/server/index.d.cts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +72 -19
- package/dist/server/server-only.js +1 -1
- package/dist/server/transport.d.cts +1 -1
- package/dist/server/transport.d.ts +1 -1
- package/dist/server/transport.d.ts.map +1 -1
- package/dist/server/types.d.cts +40 -0
- package/dist/server/types.d.ts +40 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/space-client/controller-parity.test.helpers.d.ts.map +1 -1
- package/dist/space-client/types.d.ts +1 -0
- package/dist/space-client/types.d.ts.map +1 -1
- package/dist/whiteboard/types.d.ts +4 -2
- package/dist/whiteboard/types.d.ts.map +1 -1
- package/dist/whiteboard/v1-client.d.ts +1 -0
- package/dist/whiteboard/v1-client.d.ts.map +1 -1
- package/dist/whiteboard/v1-multipart.d.ts +1 -1
- package/dist/whiteboard/v1-multipart.d.ts.map +1 -1
- package/package.json +20 -2
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CloudflareRTKClient,
|
|
2
3
|
CloudflareSFUClient,
|
|
3
4
|
createCloudflareSFUHTTPTransport
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-C4BIONCP.js";
|
|
6
|
+
import {
|
|
7
|
+
AccessGrantError,
|
|
8
|
+
requireParsedAccessGrant,
|
|
9
|
+
validEpisodeDiagnosticCredential
|
|
10
|
+
} from "./chunk-JPNE4K2L.js";
|
|
5
11
|
import {
|
|
6
12
|
createBrowserSyncLifecycle,
|
|
7
13
|
createBrowserWebSocketFactory,
|
|
@@ -81,7 +87,15 @@ function validateRefresh(previous, next, replaceMediaConnection) {
|
|
|
81
87
|
if (left.tenantId !== right.tenantId || left.spaceId !== right.spaceId || left.episodeId !== right.episodeId || left.participantId !== right.participantId || left.participantGeneration !== right.participantGeneration) {
|
|
82
88
|
throw new TypeError("Access grant refresh changed its subject");
|
|
83
89
|
}
|
|
84
|
-
if (!replaceMediaConnection && previous.media.
|
|
90
|
+
if (!replaceMediaConnection && (previous.media.provider !== next.media.provider || mediaBinding(previous) !== mediaBinding(next))) throw new TypeError("Access grant refresh unexpectedly replaced its media connection");
|
|
91
|
+
}
|
|
92
|
+
function mediaBinding(access) {
|
|
93
|
+
switch (access.media.provider) {
|
|
94
|
+
case "cloudflare_sfu":
|
|
95
|
+
return access.media.clientPayload.connectionId;
|
|
96
|
+
case "cloudflare_rtk":
|
|
97
|
+
return access.media.clientPayload.providerSubject;
|
|
98
|
+
}
|
|
85
99
|
}
|
|
86
100
|
function expiresAt(value) {
|
|
87
101
|
const timestamp = Date.parse(value);
|
|
@@ -358,6 +372,12 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
358
372
|
route: "/v1/whiteboard",
|
|
359
373
|
transport: "websocket-json-text"
|
|
360
374
|
},
|
|
375
|
+
extensions: {
|
|
376
|
+
presentation: {
|
|
377
|
+
name: "presentation_v1",
|
|
378
|
+
exactFields: ["name"]
|
|
379
|
+
}
|
|
380
|
+
},
|
|
361
381
|
identity: {
|
|
362
382
|
credential: "participant-access-token",
|
|
363
383
|
authorityKey: ["tenant_id", "space_id", "episode_id"],
|
|
@@ -372,7 +392,7 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
372
392
|
},
|
|
373
393
|
{
|
|
374
394
|
id: "live",
|
|
375
|
-
accepts: ["submit_update", "submit_update_part", "request_snapshot", "snapshot_ack", "clear", "set_draw_permission", "cursor", "ping"],
|
|
395
|
+
accepts: ["submit_update", "submit_update_part", "request_snapshot", "snapshot_ack", "clear", "set_draw_permission", "set_presentation", "cursor", "ping"],
|
|
376
396
|
heartbeatIntervalMs: 2e4,
|
|
377
397
|
missedDeadlinesBeforeClose: 2
|
|
378
398
|
}
|
|
@@ -424,7 +444,8 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
424
444
|
},
|
|
425
445
|
frames: {
|
|
426
446
|
hello: {
|
|
427
|
-
exactFields: ["type", "protocol", "token", "cursor"]
|
|
447
|
+
exactFields: ["type", "protocol", "token", "cursor"],
|
|
448
|
+
extendedExactFields: ["type", "protocol", "token", "cursor", "extensions"]
|
|
428
449
|
},
|
|
429
450
|
submitUpdate: {
|
|
430
451
|
exactFields: ["type", "operation_id", "scene_id", "sync_all", "elements"]
|
|
@@ -444,12 +465,16 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
444
465
|
setDrawPermission: {
|
|
445
466
|
exactFields: ["type", "operation_id", "participant_id", "can_draw"]
|
|
446
467
|
},
|
|
468
|
+
setPresentation: {
|
|
469
|
+
exactFields: ["type", "operation_id", "presenting"]
|
|
470
|
+
},
|
|
447
471
|
cursor: {
|
|
448
472
|
clientExactFields: ["type", "x", "y"],
|
|
449
473
|
serverExactFields: ["type", "participant_id", "display_name", "x", "y", "occurred_at"]
|
|
450
474
|
},
|
|
451
475
|
welcome: {
|
|
452
|
-
exactFields: ["type", "protocol", "participant_id", "participant_generation", "capabilities", "participant_capabilities", "scene_id", "revision", "can_draw"]
|
|
476
|
+
exactFields: ["type", "protocol", "participant_id", "participant_generation", "capabilities", "participant_capabilities", "scene_id", "revision", "can_draw"],
|
|
477
|
+
extendedExactFields: ["type", "protocol", "participant_id", "participant_generation", "capabilities", "participant_capabilities", "scene_id", "revision", "can_draw", "presenting"]
|
|
453
478
|
},
|
|
454
479
|
snapshotPage: {
|
|
455
480
|
exactFields: ["type", "request_id", "scene_id", "revision", "page", "page_count", "elements", "app_state"]
|
|
@@ -466,6 +491,9 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
466
491
|
permissionUpdated: {
|
|
467
492
|
exactFields: ["type", "participant_id", "can_draw"]
|
|
468
493
|
},
|
|
494
|
+
presentationUpdated: {
|
|
495
|
+
exactFields: ["type", "scene_id", "revision", "presenting"]
|
|
496
|
+
},
|
|
469
497
|
resetRequired: {
|
|
470
498
|
exactFields: ["type", "scene_id", "reason"]
|
|
471
499
|
},
|
|
@@ -473,9 +501,9 @@ var WhiteboardV1ProtocolMetadata = {
|
|
|
473
501
|
exactFields: ["type", "correlation_id", "operation", "code", "recoverable", "message"]
|
|
474
502
|
}
|
|
475
503
|
},
|
|
476
|
-
clientFrames: ["hello", "submit_update", "submit_update_part", "request_snapshot", "snapshot_ack", "clear", "set_draw_permission", "cursor", "ping"],
|
|
477
|
-
serverFrames: ["welcome", "snapshot_page", "update", "update_part", "commit", "cursor", "permission_updated", "reset_required", "operation_error", "pong"],
|
|
478
|
-
receiptOperations: ["submit_update", "clear", "set_draw_permission"],
|
|
504
|
+
clientFrames: ["hello", "submit_update", "submit_update_part", "request_snapshot", "snapshot_ack", "clear", "set_draw_permission", "set_presentation", "cursor", "ping"],
|
|
505
|
+
serverFrames: ["welcome", "snapshot_page", "update", "update_part", "commit", "cursor", "permission_updated", "presentation_updated", "reset_required", "operation_error", "pong"],
|
|
506
|
+
receiptOperations: ["submit_update", "clear", "set_draw_permission", "set_presentation"],
|
|
479
507
|
receiptOutcomes: ["committed", "duplicate"],
|
|
480
508
|
resetReasons: ["scene_changed", "cursor_expired", "gap"],
|
|
481
509
|
errorCodes: ["unavailable", "permission_denied", "invalid_payload", "stale_scene", "cursor_reset_required", "rate_limited", "overloaded", "storage_unavailable"],
|
|
@@ -537,6 +565,9 @@ function isRecord2(value) {
|
|
|
537
565
|
function hasExactKeys(value, keys) {
|
|
538
566
|
return isRecord2(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
539
567
|
}
|
|
568
|
+
function hasExactRecordKeys(value, keys) {
|
|
569
|
+
return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
570
|
+
}
|
|
540
571
|
function isBoundedIdentifier(value, minimum, maximum) {
|
|
541
572
|
return typeof value === "string" && utf8Bytes(value) >= minimum && utf8Bytes(value) <= maximum;
|
|
542
573
|
}
|
|
@@ -581,7 +612,7 @@ function isClientFrame(value) {
|
|
|
581
612
|
if (!isRecord2(value)) return false;
|
|
582
613
|
switch (value.type) {
|
|
583
614
|
case "hello":
|
|
584
|
-
return
|
|
615
|
+
return (hasExactRecordKeys(value, WhiteboardV1ProtocolMetadata.frames.hello.exactFields) || hasExactRecordKeys(value, WhiteboardV1ProtocolMetadata.frames.hello.extendedExactFields) && isPresentationExtensions(value.extensions)) && value.protocol === "whiteboard-v1" && typeof value.token === "string" && utf8Bytes(value.token) > 0 && utf8Bytes(value.token) <= WhiteboardV1ProtocolLimits.tokenBytes && (value.cursor === null || isCursor(value.cursor));
|
|
585
616
|
case "submit_update":
|
|
586
617
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.submitUpdate.exactFields) && isBoundedIdentifier(value.operation_id, WhiteboardV1ProtocolLimits.operationIdMinBytes, WhiteboardV1ProtocolLimits.operationIdMaxBytes) && isUuid(value.scene_id) && typeof value.sync_all === "boolean" && isElementBatch(value.elements, WhiteboardV1ProtocolLimits.elementBatchMaxItems);
|
|
587
618
|
case "submit_update_part":
|
|
@@ -594,6 +625,8 @@ function isClientFrame(value) {
|
|
|
594
625
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.clear.exactFields) && isBoundedIdentifier(value.operation_id, WhiteboardV1ProtocolLimits.operationIdMinBytes, WhiteboardV1ProtocolLimits.operationIdMaxBytes) && isUuid(value.scene_id);
|
|
595
626
|
case "set_draw_permission":
|
|
596
627
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.setDrawPermission.exactFields) && isBoundedIdentifier(value.operation_id, WhiteboardV1ProtocolLimits.operationIdMinBytes, WhiteboardV1ProtocolLimits.operationIdMaxBytes) && isUuid(value.participant_id) && typeof value.can_draw === "boolean";
|
|
628
|
+
case "set_presentation":
|
|
629
|
+
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.setPresentation.exactFields) && isBoundedIdentifier(value.operation_id, WhiteboardV1ProtocolLimits.operationIdMinBytes, WhiteboardV1ProtocolLimits.operationIdMaxBytes) && typeof value.presenting === "boolean";
|
|
597
630
|
case "cursor":
|
|
598
631
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.cursor.clientExactFields) && isFiniteNumber(value.x) && isFiniteNumber(value.y) && encodedWhiteboardV1FrameBytes(value) <= WhiteboardV1ProtocolLimits.cursorFrameBytes;
|
|
599
632
|
case "ping":
|
|
@@ -605,11 +638,18 @@ function isClientFrame(value) {
|
|
|
605
638
|
function isAppState(value) {
|
|
606
639
|
return value === null || hasExactKeys(value, WhiteboardV1ProtocolMetadata.sharedAppState.exactFields) && typeof value.view_background_color === "string" && utf8Bytes(value.view_background_color) <= 64;
|
|
607
640
|
}
|
|
641
|
+
function isPresentationExtensions(value) {
|
|
642
|
+
return Array.isArray(value) && value.length === 1 && hasExactKeys(value[0], WhiteboardV1ProtocolMetadata.extensions.presentation.exactFields) && value[0].name === WhiteboardV1ProtocolMetadata.extensions.presentation.name;
|
|
643
|
+
}
|
|
644
|
+
function isWelcome(value) {
|
|
645
|
+
const exact = hasExactRecordKeys(value, WhiteboardV1ProtocolMetadata.frames.welcome.exactFields) || hasExactRecordKeys(value, WhiteboardV1ProtocolMetadata.frames.welcome.extendedExactFields) && typeof value.presenting === "boolean";
|
|
646
|
+
return exact && value.protocol === "whiteboard-v1" && isUuid(value.participant_id) && Number.isSafeInteger(value.participant_generation) && Number(value.participant_generation) > 0 && isCapabilities(value.capabilities) && isCapabilities(value.participant_capabilities) && isUuid(value.scene_id) && isUnsignedDecimal(value.revision) && typeof value.can_draw === "boolean";
|
|
647
|
+
}
|
|
608
648
|
function isServerFrame(value) {
|
|
609
649
|
if (!isRecord2(value)) return false;
|
|
610
650
|
switch (value.type) {
|
|
611
651
|
case "welcome":
|
|
612
|
-
return
|
|
652
|
+
return isWelcome(value);
|
|
613
653
|
case "snapshot_page":
|
|
614
654
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.snapshotPage.exactFields) && isBoundedIdentifier(value.request_id, WhiteboardV1ProtocolLimits.requestIdMinBytes, WhiteboardV1ProtocolLimits.requestIdMaxBytes) && isUuid(value.scene_id) && isUnsignedDecimal(value.revision) && isNonNegativeInteger(value.page) && isNonNegativeInteger(value.page_count) && value.page_count > 0 && value.page_count <= WhiteboardV1ProtocolLimits.snapshotMaxPages && value.page < value.page_count && isElementBatch(value.elements, WhiteboardV1ProtocolLimits.snapshotPageMaxItems) && isAppState(value.app_state) && encodedWhiteboardV1FrameBytes(value) <= WhiteboardV1ProtocolLimits.snapshotPageEncodedBytes;
|
|
615
655
|
case "update":
|
|
@@ -622,10 +662,12 @@ function isServerFrame(value) {
|
|
|
622
662
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.cursor.serverExactFields) && isUuid(value.participant_id) && typeof value.display_name === "string" && utf8Bytes(value.display_name) > 0 && utf8Bytes(value.display_name) <= WhiteboardV1ProtocolLimits.displayNameMaxBytes && isFiniteNumber(value.x) && isFiniteNumber(value.y) && typeof value.occurred_at === "string" && !Number.isNaN(Date.parse(value.occurred_at)) && encodedWhiteboardV1FrameBytes(value) <= WhiteboardV1ProtocolLimits.cursorFrameBytes;
|
|
623
663
|
case "permission_updated":
|
|
624
664
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.permissionUpdated.exactFields) && isUuid(value.participant_id) && typeof value.can_draw === "boolean";
|
|
665
|
+
case "presentation_updated":
|
|
666
|
+
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.presentationUpdated.exactFields) && isUuid(value.scene_id) && isUnsignedDecimal(value.revision) && typeof value.presenting === "boolean";
|
|
625
667
|
case "reset_required":
|
|
626
668
|
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.resetRequired.exactFields) && isUuid(value.scene_id) && typeof value.reason === "string" && WhiteboardV1ProtocolMetadata.resetReasons.some((reason) => reason === value.reason);
|
|
627
669
|
case "operation_error":
|
|
628
|
-
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.operationError.exactFields) && isBoundedIdentifier(value.correlation_id, WhiteboardV1ProtocolLimits.requestIdMinBytes, WhiteboardV1ProtocolLimits.requestIdMaxBytes) && ["submit_update", "request_snapshot", "clear", "set_draw_permission"].includes(String(value.operation)) && typeof value.code === "string" && WhiteboardV1ProtocolMetadata.errorCodes.some((code) => code === value.code) && typeof value.recoverable === "boolean" && typeof value.message === "string" && utf8Bytes(value.message) <= WhiteboardV1ProtocolLimits.errorMessageMaxBytes;
|
|
670
|
+
return hasExactKeys(value, WhiteboardV1ProtocolMetadata.frames.operationError.exactFields) && isBoundedIdentifier(value.correlation_id, WhiteboardV1ProtocolLimits.requestIdMinBytes, WhiteboardV1ProtocolLimits.requestIdMaxBytes) && ["submit_update", "request_snapshot", "clear", "set_draw_permission", "set_presentation"].includes(String(value.operation)) && typeof value.code === "string" && WhiteboardV1ProtocolMetadata.errorCodes.some((code) => code === value.code) && typeof value.recoverable === "boolean" && typeof value.message === "string" && utf8Bytes(value.message) <= WhiteboardV1ProtocolLimits.errorMessageMaxBytes;
|
|
629
671
|
case "pong":
|
|
630
672
|
return hasExactKeys(value, ["type"]);
|
|
631
673
|
default:
|
|
@@ -858,6 +900,9 @@ var ChalkWhiteboardV1Client = class {
|
|
|
858
900
|
#revision;
|
|
859
901
|
#capabilities = [];
|
|
860
902
|
#canDraw = false;
|
|
903
|
+
#presentationSupported = false;
|
|
904
|
+
#presenting = false;
|
|
905
|
+
#useLegacyHello = false;
|
|
861
906
|
#summaryStatus = "unsubscribed";
|
|
862
907
|
#summaryError = null;
|
|
863
908
|
#latestSnapshot = null;
|
|
@@ -883,6 +928,7 @@ var ChalkWhiteboardV1Client = class {
|
|
|
883
928
|
startSceneSubscription() {
|
|
884
929
|
if (this.#started && this.#startPromise) return this.#startPromise;
|
|
885
930
|
this.#started = true;
|
|
931
|
+
this.#useLegacyHello = false;
|
|
886
932
|
this.#phase = "connecting";
|
|
887
933
|
this.#publishSummary("loading", null);
|
|
888
934
|
const generation = ++this.#startupGeneration;
|
|
@@ -912,6 +958,7 @@ var ChalkWhiteboardV1Client = class {
|
|
|
912
958
|
this.#participantId = null;
|
|
913
959
|
this.#capabilities = [];
|
|
914
960
|
this.#canDraw = false;
|
|
961
|
+
this.#presenting = false;
|
|
915
962
|
this.#publishSummary("unsubscribed", null);
|
|
916
963
|
}
|
|
917
964
|
subscribe(listener) {
|
|
@@ -968,6 +1015,21 @@ var ChalkWhiteboardV1Client = class {
|
|
|
968
1015
|
};
|
|
969
1016
|
await this.#queueOperation(frame);
|
|
970
1017
|
}
|
|
1018
|
+
async setPresentation(presenting) {
|
|
1019
|
+
this.#assertLive("set_presentation");
|
|
1020
|
+
if (!this.#presentationSupported) {
|
|
1021
|
+
throw error(failure("set_presentation", "unavailable", true, "Whiteboard presentation is unavailable on this Sync server."));
|
|
1022
|
+
}
|
|
1023
|
+
if (!this.#canDraw || !this.#capabilities.includes("drawWhiteboard")) {
|
|
1024
|
+
throw error(failure("set_presentation", "permission_denied", false, "Whiteboard draw permission is required."));
|
|
1025
|
+
}
|
|
1026
|
+
const frame = {
|
|
1027
|
+
type: "set_presentation",
|
|
1028
|
+
operation_id: this.#nextId(),
|
|
1029
|
+
presenting
|
|
1030
|
+
};
|
|
1031
|
+
await this.#queueOperation(frame);
|
|
1032
|
+
}
|
|
971
1033
|
async #restoreAndConnect(generation) {
|
|
972
1034
|
try {
|
|
973
1035
|
const stored = await this.#store.load();
|
|
@@ -1024,12 +1086,9 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1024
1086
|
const token = await this.#options.token();
|
|
1025
1087
|
if (socket !== this.#socket) return;
|
|
1026
1088
|
this.#phase = "authenticating";
|
|
1027
|
-
this.#
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
token,
|
|
1031
|
-
cursor: this.#sceneId && this.#revision ? { scene_id: this.#sceneId, revision: this.#revision } : null
|
|
1032
|
-
});
|
|
1089
|
+
this.#presentationSupported = false;
|
|
1090
|
+
const cursor = this.#sceneId && this.#revision ? { scene_id: this.#sceneId, revision: this.#revision } : null;
|
|
1091
|
+
this.#send(this.#useLegacyHello ? { type: "hello", protocol: "whiteboard-v1", token, cursor } : { type: "hello", protocol: "whiteboard-v1", token, cursor, extensions: [{ name: "presentation_v1" }] });
|
|
1033
1092
|
} catch {
|
|
1034
1093
|
socket.close(1008, "whiteboard authentication failed");
|
|
1035
1094
|
}
|
|
@@ -1080,11 +1139,22 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1080
1139
|
if (frame.participant_id === this.#participantId) this.#canDraw = frame.can_draw;
|
|
1081
1140
|
this.#publishSummary("ready", null);
|
|
1082
1141
|
return;
|
|
1142
|
+
case "presentation_updated":
|
|
1143
|
+
this.#requireLive();
|
|
1144
|
+
this.#sceneId = frame.scene_id;
|
|
1145
|
+
this.#revision = frame.revision;
|
|
1146
|
+
this.#presenting = frame.presenting;
|
|
1147
|
+
this.#publishSummary("ready", null);
|
|
1148
|
+
return;
|
|
1083
1149
|
case "reset_required":
|
|
1084
1150
|
this.#requireLive();
|
|
1085
1151
|
this.#sceneId = frame.scene_id;
|
|
1086
1152
|
this.#revision = null;
|
|
1087
1153
|
this.#publishSummary("recovering", failure("request_snapshot", "cursor_reset_required", true, "Whiteboard snapshot recovery is required."));
|
|
1154
|
+
if (this.#presentationSupported) {
|
|
1155
|
+
this.#socket?.close(CLIENT_RESTART_CLOSE_CODE, "whiteboard presentation recovery required");
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1088
1158
|
this.#emit({ type: "reset_required", sceneId: frame.scene_id, reason: frame.reason });
|
|
1089
1159
|
return;
|
|
1090
1160
|
case "operation_error":
|
|
@@ -1137,6 +1207,8 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1137
1207
|
this.#revision = frame.revision;
|
|
1138
1208
|
this.#capabilities = [...frame.capabilities];
|
|
1139
1209
|
this.#canDraw = frame.can_draw;
|
|
1210
|
+
this.#presentationSupported = "presenting" in frame;
|
|
1211
|
+
this.#presenting = "presenting" in frame ? frame.presenting : false;
|
|
1140
1212
|
this.#publishSummary("loading", null);
|
|
1141
1213
|
this.#missedHeartbeats = 0;
|
|
1142
1214
|
for (const entry of [...this.#operations.values()].sort((left, right) => compareChalkWhiteboardV1PendingOperations(left.pending, right.pending))) {
|
|
@@ -1283,6 +1355,24 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1283
1355
|
}
|
|
1284
1356
|
#disconnected(socket, closeCode) {
|
|
1285
1357
|
if (socket !== this.#socket) return;
|
|
1358
|
+
const rejectedPresentationHello = this.#rejectedPresentationHello(closeCode);
|
|
1359
|
+
this.#resetDisconnectedState();
|
|
1360
|
+
if (!this.#started || !this.#transportAvailable) return;
|
|
1361
|
+
if (rejectedPresentationHello) {
|
|
1362
|
+
this.#useLegacyHello = true;
|
|
1363
|
+
this.#scheduleReconnect();
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
if (terminalCloseCode(closeCode)) {
|
|
1367
|
+
this.#failRejectedConnection(closeCode);
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
this.#scheduleReconnect();
|
|
1371
|
+
}
|
|
1372
|
+
#rejectedPresentationHello(closeCode) {
|
|
1373
|
+
return this.#phase === "authenticating" && !this.#useLegacyHello && terminalCloseCode(closeCode);
|
|
1374
|
+
}
|
|
1375
|
+
#resetDisconnectedState() {
|
|
1286
1376
|
this.#socket = null;
|
|
1287
1377
|
this.#clearHeartbeat();
|
|
1288
1378
|
this.#clearUpdateAssemblies();
|
|
@@ -1292,15 +1382,15 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1292
1382
|
this.#canDraw = false;
|
|
1293
1383
|
this.#rejectSnapshots(failure("request_snapshot", "unavailable", true, "Whiteboard connection interrupted."));
|
|
1294
1384
|
this.#publishSummary("recovering", failure("start_scene_subscription", "unavailable", true, "Whiteboard connection interrupted."));
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1385
|
+
}
|
|
1386
|
+
#failRejectedConnection(closeCode) {
|
|
1387
|
+
const terminalFailure = failure("start_scene_subscription", closeCode === 1008 ? "permission_denied" : "invalid_payload", false, "Whiteboard connection was rejected.");
|
|
1388
|
+
this.#started = false;
|
|
1389
|
+
this.#publishSummary("failed", terminalFailure);
|
|
1390
|
+
if (this.#initialSnapshot) rejectDeferred(this.#initialSnapshot, error(terminalFailure));
|
|
1391
|
+
this.#rejectOperationCallers(terminalFailure);
|
|
1392
|
+
}
|
|
1393
|
+
#scheduleReconnect() {
|
|
1304
1394
|
this.#clearReconnect();
|
|
1305
1395
|
this.#reconnectTimer = this.#clock().setTimeout(() => {
|
|
1306
1396
|
this.#reconnectTimer = void 0;
|
|
@@ -1352,6 +1442,7 @@ var ChalkWhiteboardV1Client = class {
|
|
|
1352
1442
|
capabilities: [...this.#capabilities],
|
|
1353
1443
|
canDraw: this.#canDraw,
|
|
1354
1444
|
canClear: this.#capabilities.includes("manageWhiteboard"),
|
|
1445
|
+
presenting: this.#presenting,
|
|
1355
1446
|
error: this.#summaryError
|
|
1356
1447
|
};
|
|
1357
1448
|
try {
|
|
@@ -1473,6 +1564,9 @@ function operationName(frame) {
|
|
|
1473
1564
|
function retryableOperationCode(code) {
|
|
1474
1565
|
return code === "unavailable" || code === "overloaded" || code === "rate_limited" || code === "storage_unavailable";
|
|
1475
1566
|
}
|
|
1567
|
+
function terminalCloseCode(closeCode) {
|
|
1568
|
+
return closeCode === 1008 || closeCode === 1009;
|
|
1569
|
+
}
|
|
1476
1570
|
function mapErrorCode(code) {
|
|
1477
1571
|
return code === "overloaded" || code === "rate_limited" ? "unavailable" : code;
|
|
1478
1572
|
}
|
|
@@ -1589,8 +1683,18 @@ function createChatFileTransport(apiBaseURL, input) {
|
|
|
1589
1683
|
});
|
|
1590
1684
|
}
|
|
1591
1685
|
function createMediaClient(apiBaseURL, input) {
|
|
1686
|
+
if (input.access.media.provider === "cloudflare_rtk") {
|
|
1687
|
+
return createRTKConnectionMediaClient(
|
|
1688
|
+
new CloudflareRTKClient({
|
|
1689
|
+
authToken: input.access.media.clientPayload.token,
|
|
1690
|
+
participantId: input.access.subject.participantId,
|
|
1691
|
+
onError: input.onFailure,
|
|
1692
|
+
onScreenEnded: input.onScreenEnded
|
|
1693
|
+
})
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1592
1696
|
const { subject } = input.access;
|
|
1593
|
-
|
|
1697
|
+
const client = new CloudflareSFUClient({
|
|
1594
1698
|
bootstrap: input.access.media.clientPayload,
|
|
1595
1699
|
participantId: subject.participantId,
|
|
1596
1700
|
transport: createCloudflareSFUHTTPTransport({
|
|
@@ -1604,6 +1708,36 @@ function createMediaClient(apiBaseURL, input) {
|
|
|
1604
1708
|
onError: input.onFailure,
|
|
1605
1709
|
onScreenEnded: input.onScreenEnded
|
|
1606
1710
|
});
|
|
1711
|
+
return createSFUConnectionMediaClient(client);
|
|
1712
|
+
}
|
|
1713
|
+
function createRTKConnectionMediaClient(client) {
|
|
1714
|
+
return bindConnectionMediaClient(client, (input) => {
|
|
1715
|
+
if (!("provider" in input) || input.provider !== "cloudflare_rtk") throw new TypeError("The RealtimeKit adapter requires a Cloudflare RealtimeKit access grant");
|
|
1716
|
+
return client.restart(input);
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
function createSFUConnectionMediaClient(client) {
|
|
1720
|
+
return bindConnectionMediaClient(client, (input) => {
|
|
1721
|
+
if ("provider" in input) {
|
|
1722
|
+
if (input.provider !== "cloudflare_sfu") throw new TypeError("The Cloudflare SFU adapter requires a Cloudflare SFU access grant");
|
|
1723
|
+
return client.restart(input.clientPayload);
|
|
1724
|
+
}
|
|
1725
|
+
return client.restart(input);
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
function bindConnectionMediaClient(client, restart) {
|
|
1729
|
+
return {
|
|
1730
|
+
setLocalPublicationTarget: client.setLocalPublicationTarget.bind(client),
|
|
1731
|
+
observeLocalPublications: client.observeLocalPublications.bind(client),
|
|
1732
|
+
observeRemotePublications: client.observeRemotePublications.bind(client),
|
|
1733
|
+
start: client.start.bind(client),
|
|
1734
|
+
stop: client.stop.bind(client),
|
|
1735
|
+
restart,
|
|
1736
|
+
prepareLocalTrack: client.prepareLocalTrack.bind(client),
|
|
1737
|
+
clearPreparedLocalTrack: client.clearPreparedLocalTrack.bind(client),
|
|
1738
|
+
getSnapshot: client.getSnapshot.bind(client),
|
|
1739
|
+
subscribe: client.subscribe.bind(client)
|
|
1740
|
+
};
|
|
1607
1741
|
}
|
|
1608
1742
|
function createSyncClient(syncURL, input) {
|
|
1609
1743
|
return createV1SyncClient({
|
|
@@ -1645,172 +1779,6 @@ function whiteboardURL(syncURL) {
|
|
|
1645
1779
|
// src/connection/lifecycle.ts
|
|
1646
1780
|
import { Clock as Clock2, Context as Context3, Data as Data2, Deferred, Duration, Effect as Effect2, Exit, Fiber, Layer as Layer3, Queue, Scope, SubscriptionRef } from "effect";
|
|
1647
1781
|
|
|
1648
|
-
// src/access/base64-url.ts
|
|
1649
|
-
function decodeBase64Url(value) {
|
|
1650
|
-
const base64 = value.replaceAll("-", "+").replaceAll("_", "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
1651
|
-
const bytes = Uint8Array.from(globalThis.atob(base64), (character) => character.charCodeAt(0));
|
|
1652
|
-
return new TextDecoder().decode(bytes);
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
// src/access/episode-diagnostic-credential.ts
|
|
1656
|
-
var INTAKE_PATH = "/_internal/episode-diagnostic-events";
|
|
1657
|
-
var RFC3339_ZONED = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
1658
|
-
function parseEpisodeDiagnosticCredential(value) {
|
|
1659
|
-
if (!isRecord3(value)) return null;
|
|
1660
|
-
const token = value.token;
|
|
1661
|
-
const expiresAt2 = value.expires_at;
|
|
1662
|
-
const generation = value.generation;
|
|
1663
|
-
const intakePath = value.intake_path;
|
|
1664
|
-
if (typeof token !== "string" || !validDiagnosticCredential(token)) return null;
|
|
1665
|
-
if (!isRFC3339Zoned(expiresAt2)) return null;
|
|
1666
|
-
if (!Number.isSafeInteger(generation) || generation < 1) return null;
|
|
1667
|
-
if (intakePath !== INTAKE_PATH) return null;
|
|
1668
|
-
return Object.freeze({ token, expiresAt: expiresAt2, generation, intakePath });
|
|
1669
|
-
}
|
|
1670
|
-
function validEpisodeDiagnosticCredential(credential, now) {
|
|
1671
|
-
return credential.intakePath === INTAKE_PATH && Number.isSafeInteger(credential.generation) && credential.generation > 0 && isRFC3339Zoned(credential.expiresAt) && Date.parse(credential.expiresAt) > now && validDiagnosticCredential(credential.token);
|
|
1672
|
-
}
|
|
1673
|
-
function validDiagnosticCredential(token) {
|
|
1674
|
-
const segments = token.split(".");
|
|
1675
|
-
if (segments.length !== 3) return false;
|
|
1676
|
-
try {
|
|
1677
|
-
const payload = JSON.parse(decodeBase64Url(segments[1] ?? ""));
|
|
1678
|
-
return isRecord3(payload) && payload.aud === "chalk-diagnostics";
|
|
1679
|
-
} catch {
|
|
1680
|
-
return false;
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
function isRFC3339Zoned(value) {
|
|
1684
|
-
if (typeof value !== "string") return false;
|
|
1685
|
-
const match = RFC3339_ZONED.exec(value);
|
|
1686
|
-
if (!match) return false;
|
|
1687
|
-
return validTimestampFields(match) && Number.isFinite(Date.parse(value));
|
|
1688
|
-
}
|
|
1689
|
-
function validTimestampFields(match) {
|
|
1690
|
-
const year = Number(match[1]);
|
|
1691
|
-
const month = Number(match[2]);
|
|
1692
|
-
const day = Number(match[3]);
|
|
1693
|
-
const hour = Number(match[4]);
|
|
1694
|
-
const minute = Number(match[5]);
|
|
1695
|
-
const second = Number(match[6]);
|
|
1696
|
-
const zone = match[7] ?? "";
|
|
1697
|
-
return validCalendarDate(year, month, day) && validClockTime(hour, minute, second) && validZoneOffset(zone);
|
|
1698
|
-
}
|
|
1699
|
-
function inRange(value, min, max) {
|
|
1700
|
-
return value >= min && value <= max;
|
|
1701
|
-
}
|
|
1702
|
-
function validCalendarDate(year, month, day) {
|
|
1703
|
-
return inRange(month, 1, 12) && inRange(day, 1, daysInMonth(year, month));
|
|
1704
|
-
}
|
|
1705
|
-
function validClockTime(hour, minute, second) {
|
|
1706
|
-
return inRange(hour, 0, 23) && inRange(minute, 0, 59) && inRange(second, 0, 59);
|
|
1707
|
-
}
|
|
1708
|
-
function validZoneOffset(zone) {
|
|
1709
|
-
if (zone === "Z") return true;
|
|
1710
|
-
return inRange(Number(zone.slice(1, 3)), 0, 23) && inRange(Number(zone.slice(4, 6)), 0, 59);
|
|
1711
|
-
}
|
|
1712
|
-
function daysInMonth(year, month) {
|
|
1713
|
-
if (month === 2) return isLeapYear(year) ? 29 : 28;
|
|
1714
|
-
return [4, 6, 9, 11].includes(month) ? 30 : 31;
|
|
1715
|
-
}
|
|
1716
|
-
function isLeapYear(year) {
|
|
1717
|
-
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
1718
|
-
}
|
|
1719
|
-
function isRecord3(value) {
|
|
1720
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
// src/access/grant.ts
|
|
1724
|
-
var AccessGrantError = class extends TypeError {
|
|
1725
|
-
code = "access.invalid";
|
|
1726
|
-
constructor(message = "Access grant is invalid") {
|
|
1727
|
-
super(message);
|
|
1728
|
-
this.name = "AccessGrantError";
|
|
1729
|
-
}
|
|
1730
|
-
};
|
|
1731
|
-
function parseParsedAccessGrant(value) {
|
|
1732
|
-
if (!isRecord4(value)) throw new AccessGrantError();
|
|
1733
|
-
const subject = parseSubject(value.subject);
|
|
1734
|
-
const diagnostics = parseEpisodeDiagnosticCredential(value.diagnostics);
|
|
1735
|
-
return {
|
|
1736
|
-
subject,
|
|
1737
|
-
sync: parseSyncAccess(value.sync),
|
|
1738
|
-
media: parseMediaAccess(value.media),
|
|
1739
|
-
diagnostics: diagnostics?.generation === subject.participantGeneration ? diagnostics : null
|
|
1740
|
-
};
|
|
1741
|
-
}
|
|
1742
|
-
async function requireParsedAccessGrant(value) {
|
|
1743
|
-
if (typeof Response !== "undefined" && value instanceof Response) {
|
|
1744
|
-
if (!value.ok) throw new AccessGrantError(`Access grant request failed with HTTP ${value.status}`);
|
|
1745
|
-
try {
|
|
1746
|
-
return parseParsedAccessGrant(await value.json());
|
|
1747
|
-
} catch (error2) {
|
|
1748
|
-
if (error2 instanceof AccessGrantError) throw error2;
|
|
1749
|
-
throw new AccessGrantError();
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
return parseParsedAccessGrant(value);
|
|
1753
|
-
}
|
|
1754
|
-
function parseSubject(value) {
|
|
1755
|
-
if (!isRecord4(value)) throw new AccessGrantError();
|
|
1756
|
-
return {
|
|
1757
|
-
tenantId: requireNonEmptyString(value.tenant_id),
|
|
1758
|
-
spaceId: requireNonEmptyString(value.space_id),
|
|
1759
|
-
episodeId: requireNonEmptyString(value.episode_id),
|
|
1760
|
-
participantId: requireNonEmptyString(value.participant_id),
|
|
1761
|
-
participantGeneration: requirePositiveInteger(value.participant_generation)
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
function requirePositiveInteger(value) {
|
|
1765
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) throw new AccessGrantError();
|
|
1766
|
-
return value;
|
|
1767
|
-
}
|
|
1768
|
-
function parseSyncAccess(value) {
|
|
1769
|
-
if (!isRecord4(value)) throw new AccessGrantError();
|
|
1770
|
-
return {
|
|
1771
|
-
token: requireCredential(value.token, "chalk-sync"),
|
|
1772
|
-
expiresAt: requireDateTime(value.expires_at ?? value.expiresAt)
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
function parseMediaAccess(value) {
|
|
1776
|
-
if (!isRecord4(value) || value.provider !== "cloudflare_sfu" || !isRecord4(value.client_payload ?? value.clientPayload)) throw new AccessGrantError();
|
|
1777
|
-
const payload = value.client_payload ?? value.clientPayload;
|
|
1778
|
-
return {
|
|
1779
|
-
token: requireCredential(value.token, "chalk-media"),
|
|
1780
|
-
expiresAt: requireDateTime(value.expires_at ?? value.expiresAt),
|
|
1781
|
-
provider: value.provider,
|
|
1782
|
-
clientPayload: {
|
|
1783
|
-
connectionId: requireNonEmptyString(payload.connectionId),
|
|
1784
|
-
stunServer: requireNonEmptyString(payload.stunServer)
|
|
1785
|
-
}
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
function requireCredential(value, audience) {
|
|
1789
|
-
const token = requireNonEmptyString(value);
|
|
1790
|
-
const segments = token.split(".");
|
|
1791
|
-
if (segments.length !== 3) throw new AccessGrantError();
|
|
1792
|
-
let payload;
|
|
1793
|
-
try {
|
|
1794
|
-
payload = JSON.parse(decodeBase64Url(segments[1] ?? ""));
|
|
1795
|
-
} catch {
|
|
1796
|
-
throw new AccessGrantError();
|
|
1797
|
-
}
|
|
1798
|
-
if (!isRecord4(payload) || payload.aud !== audience) throw new AccessGrantError();
|
|
1799
|
-
return token;
|
|
1800
|
-
}
|
|
1801
|
-
function requireDateTime(value) {
|
|
1802
|
-
const dateTime = requireNonEmptyString(value);
|
|
1803
|
-
if (!Number.isFinite(Date.parse(dateTime))) throw new AccessGrantError();
|
|
1804
|
-
return dateTime;
|
|
1805
|
-
}
|
|
1806
|
-
function requireNonEmptyString(value) {
|
|
1807
|
-
if (typeof value !== "string" || value.trim().length === 0) throw new AccessGrantError();
|
|
1808
|
-
return value;
|
|
1809
|
-
}
|
|
1810
|
-
function isRecord4(value) {
|
|
1811
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
1782
|
// src/connection/diagnostics.ts
|
|
1815
1783
|
var ConnectionDiagnostics = class {
|
|
1816
1784
|
#limit;
|
|
@@ -2266,7 +2234,8 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
|
|
|
2266
2234
|
const sync = model.sync;
|
|
2267
2235
|
if (!media || !sync) return yield* Effect2.fail(lifecycleFailure("invalid_state", false, "Media recovery requires active ports"));
|
|
2268
2236
|
const grant = yield* access.refresh("media_recovery", true).pipe(Effect2.mapError(accessFailure));
|
|
2269
|
-
|
|
2237
|
+
const restartInput = grant.media.provider === "cloudflare_sfu" ? grant.media.clientPayload : grant.media;
|
|
2238
|
+
yield* foreign6(() => media.restart(restartInput));
|
|
2270
2239
|
model.mediaSnapshot = media.getSnapshot();
|
|
2271
2240
|
yield* waitForSyncLive(sync, boundedInteger(options.recovery?.budgetMs, RECOVERY_BUDGET_MS, 1, 6e4));
|
|
2272
2241
|
});
|
|
@@ -2547,7 +2516,7 @@ var EMPTY = empty({
|
|
|
2547
2516
|
}),
|
|
2548
2517
|
chat: empty({ status: "idle", messages: empty([]), pendingSends: empty([]), readReceipts: empty([]), unreadCount: 0, pagination: empty({ cursor: null, hasOlder: false, historyTruncated: false }), lastError: null }),
|
|
2549
2518
|
reactions: empty({ active: empty([]) }),
|
|
2550
|
-
whiteboard: empty({ open: false, engine: empty({ status: "unsubscribed", sceneId: null, revision: null, error: null }) })
|
|
2519
|
+
whiteboard: empty({ open: false, engine: empty({ status: "unsubscribed", sceneId: null, revision: null, presenting: false, error: null }) })
|
|
2551
2520
|
});
|
|
2552
2521
|
var SpaceStore = class _SpaceStore {
|
|
2553
2522
|
#listeners = /* @__PURE__ */ new Set();
|
|
@@ -4031,7 +4000,7 @@ function sameReactions(left, right) {
|
|
|
4031
4000
|
|
|
4032
4001
|
// src/space-client/whiteboard-controller.ts
|
|
4033
4002
|
import { Context as Context9, Effect as Effect8, Layer as Layer9 } from "effect";
|
|
4034
|
-
var EMPTY_SUMMARY = Object.freeze({ status: "unsubscribed", sceneId: null, revision: null, capabilities: Object.freeze([]), canDraw: false, canClear: false, error: null });
|
|
4003
|
+
var EMPTY_SUMMARY = Object.freeze({ status: "unsubscribed", sceneId: null, revision: null, capabilities: Object.freeze([]), canDraw: false, canClear: false, presenting: false, error: null });
|
|
4035
4004
|
var WhiteboardControllerService = class extends Context9.Service()("@chalk/client/WhiteboardController") {
|
|
4036
4005
|
};
|
|
4037
4006
|
var makeWhiteboardController = (connection, store, create) => Effect8.gen(function* () {
|
|
@@ -4067,7 +4036,16 @@ var WhiteboardControllerRuntime = class {
|
|
|
4067
4036
|
}
|
|
4068
4037
|
observe(summary) {
|
|
4069
4038
|
if (!this.#connected && summary.status !== "unsubscribed") return;
|
|
4070
|
-
this.#summary = Object.freeze({
|
|
4039
|
+
this.#summary = Object.freeze({
|
|
4040
|
+
status: summary.status,
|
|
4041
|
+
sceneId: summary.sceneId,
|
|
4042
|
+
revision: summary.revision,
|
|
4043
|
+
capabilities: Object.freeze([...summary.capabilities]),
|
|
4044
|
+
canDraw: summary.canDraw,
|
|
4045
|
+
canClear: summary.canClear,
|
|
4046
|
+
presenting: summary.presenting,
|
|
4047
|
+
error: summary.error ? Object.freeze({ ...summary.error }) : null
|
|
4048
|
+
});
|
|
4071
4049
|
this.#publish();
|
|
4072
4050
|
}
|
|
4073
4051
|
#bind(ports) {
|
|
@@ -4082,14 +4060,14 @@ var WhiteboardControllerRuntime = class {
|
|
|
4082
4060
|
this.#publish();
|
|
4083
4061
|
}
|
|
4084
4062
|
#publish() {
|
|
4085
|
-
const slice = Object.freeze({ open: this.#summary.status !== "unsubscribed", engine: Object.freeze({ status: this.#summary.status, sceneId: this.#summary.sceneId, revision: this.#summary.revision, error: this.#summary.error }) });
|
|
4063
|
+
const slice = Object.freeze({ open: this.#summary.status !== "unsubscribed", engine: Object.freeze({ status: this.#summary.status, sceneId: this.#summary.sceneId, revision: this.#summary.revision, presenting: this.#summary.presenting, error: this.#summary.error }) });
|
|
4086
4064
|
const current = this.#store.getSnapshot().whiteboard;
|
|
4087
4065
|
if (sameWhiteboard(current, slice)) return;
|
|
4088
4066
|
this.#store.updateWhiteboard(slice);
|
|
4089
4067
|
}
|
|
4090
4068
|
};
|
|
4091
4069
|
function sameWhiteboard(left, right) {
|
|
4092
|
-
return left.open === right.open && left.engine.status === right.engine.status && left.engine.sceneId === right.engine.sceneId && left.engine.revision === right.engine.revision && sameError2(left.engine.error, right.engine.error);
|
|
4070
|
+
return left.open === right.open && left.engine.status === right.engine.status && left.engine.sceneId === right.engine.sceneId && left.engine.revision === right.engine.revision && left.engine.presenting === right.engine.presenting && sameError2(left.engine.error, right.engine.error);
|
|
4093
4071
|
}
|
|
4094
4072
|
function sameError2(left, right) {
|
|
4095
4073
|
if (left === right) return true;
|
|
@@ -4668,7 +4646,7 @@ function stripTrailingUrlSlash(value) {
|
|
|
4668
4646
|
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
4669
4647
|
}
|
|
4670
4648
|
function intakeResponse(value) {
|
|
4671
|
-
if (!
|
|
4649
|
+
if (!isRecord3(value) || typeof value.diagnosticReference !== "string" || !Number.isSafeInteger(value.committedCursor)) return null;
|
|
4672
4650
|
if (!Array.isArray(value.accepted) || !Array.isArray(value.duplicates) || !Array.isArray(value.conflicts)) return null;
|
|
4673
4651
|
return { accepted: value.accepted, duplicates: value.duplicates, conflicts: value.conflicts };
|
|
4674
4652
|
}
|
|
@@ -4679,14 +4657,14 @@ function completeDelivery(delivery, expected) {
|
|
|
4679
4657
|
function intakeIds(items, expected, cursorRequired) {
|
|
4680
4658
|
const ids = [];
|
|
4681
4659
|
for (const item of items) {
|
|
4682
|
-
if (!
|
|
4660
|
+
if (!isRecord3(item) || typeof item.eventId !== "string" || !expected.has(item.eventId)) return null;
|
|
4683
4661
|
if (cursorRequired && !Number.isSafeInteger(item.cursor)) return null;
|
|
4684
4662
|
if (!cursorRequired && item.code !== "fingerprint_mismatch") return null;
|
|
4685
4663
|
ids.push(item.eventId);
|
|
4686
4664
|
}
|
|
4687
4665
|
return ids;
|
|
4688
4666
|
}
|
|
4689
|
-
function
|
|
4667
|
+
function isRecord3(value) {
|
|
4690
4668
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4691
4669
|
}
|
|
4692
4670
|
|
|
@@ -4774,7 +4752,7 @@ var makeSpaceClientCoreLayer = (options, platform = {}) => {
|
|
|
4774
4752
|
now: baseDependencies.clock.now,
|
|
4775
4753
|
setTimeout: baseDependencies.clock.setTimeout,
|
|
4776
4754
|
clearTimeout: baseDependencies.clock.clearTimeout,
|
|
4777
|
-
release: { id: "chalk-client@4.1.
|
|
4755
|
+
release: { id: "chalk-client@4.1.7" }
|
|
4778
4756
|
});
|
|
4779
4757
|
const diagnosticSyncClients = /* @__PURE__ */ new Set();
|
|
4780
4758
|
const dependencies = {
|