@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.
- package/dist/access/manager.d.ts +2 -0
- package/dist/access/manager.d.ts.map +1 -1
- package/dist/{chunk-DFLTEYWX.js → chunk-2NUTY2VP.js} +3 -3
- package/dist/{chunk-JPNE4K2L.js → chunk-HE6CLPZM.js} +51 -2
- package/dist/{chunk-D6W4ON4A.js → chunk-JCNUF3DP.js} +116 -46
- package/dist/{chunk-QVHR3RMB.js → chunk-NKOKY2AE.js} +21 -1
- package/dist/{chunk-5VXGLI3T.js → chunk-PWUU6T3R.js} +8 -1
- package/dist/{chunk-C4BIONCP.js → chunk-S5OUHFGC.js} +174 -13
- package/dist/client.d.ts +12 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/connection/dependencies.d.ts +3 -1
- package/dist/connection/dependencies.d.ts.map +1 -1
- package/dist/connection/index.d.ts +2 -0
- package/dist/connection/index.d.ts.map +1 -1
- package/dist/connection/lifecycle.d.ts.map +1 -1
- package/dist/effect.js +17 -5
- package/dist/generated/http-api.d.ts +12 -1
- package/dist/generated/http-api.d.ts.map +1 -1
- package/dist/generated/schemas.d.ts +53 -1
- package/dist/generated/schemas.d.ts.map +1 -1
- package/dist/index.js +26 -26
- package/dist/media/client.d.ts.map +1 -1
- package/dist/media/index.js +1 -1
- package/dist/media/transport.d.ts.map +1 -1
- package/dist/media/types.d.ts +12 -1
- package/dist/media/types.d.ts.map +1 -1
- package/dist/public-invites/index.js +3 -3
- package/dist/public-invites/types.d.ts +1 -0
- package/dist/public-invites/types.d.ts.map +1 -1
- package/dist/space-client/core.d.ts +2 -0
- package/dist/space-client/core.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/v1-client.d.ts.map +1 -1
- package/dist/telemetry/attributes.d.ts +1 -1
- package/dist/telemetry/attributes.d.ts.map +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/rtc.d.ts +6 -0
- package/dist/telemetry/rtc.d.ts.map +1 -1
- package/dist/whiteboard/types.d.ts +1 -0
- package/dist/whiteboard/types.d.ts.map +1 -1
- package/dist/whiteboard/v1-client.d.ts +2 -1
- package/dist/whiteboard/v1-client.d.ts.map +1 -1
- package/dist/whiteboard/v1-codec.d.ts +1 -2
- package/dist/whiteboard/v1-codec.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/episode-diagnostic-runtime.test.helpers.d.ts +0 -3
- package/dist/episode-diagnostic-runtime.test.helpers.d.ts.map +0 -1
- package/dist/space-client/controller-parity.test.helpers.d.ts +0 -151
- package/dist/space-client/controller-parity.test.helpers.d.ts.map +0 -1
- package/dist/space-client/media-controller.test.helpers.d.ts +0 -105
- package/dist/space-client/media-controller.test.helpers.d.ts.map +0 -1
package/dist/access/manager.d.ts
CHANGED
|
@@ -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;
|
|
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-
|
|
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-
|
|
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-
|
|
5
|
+
} from "./chunk-S5OUHFGC.js";
|
|
6
6
|
import {
|
|
7
7
|
AccessGrantError,
|
|
8
8
|
requireParsedAccessGrant,
|
|
9
9
|
validEpisodeDiagnosticCredential
|
|
10
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
33
|
-
|
|
34
|
-
(
|
|
35
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
},
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
},
|
|
2949
|
+
}, delay);
|
|
2943
2950
|
}
|
|
2944
2951
|
#recover(reason) {
|
|
2945
2952
|
const socket = this.#socket;
|