@q9labsai/chalk-client 5.3.0 → 5.3.1

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.
@@ -1,3 +1,7 @@
1
+ import {
2
+ sessionBaggage
3
+ } from "./chunk-N4IPKRS6.js";
4
+
1
5
  // src/sync/canonical.ts
2
6
  var encoder = new TextEncoder();
3
7
  function canonicalJson(value) {
@@ -405,11 +409,20 @@ var SyncProtocolMetadata = {
405
409
  pattern: "^[a-z][a-z0-9_*\\-/]{0,255}=[\\x21-\\x2b\\x2d-\\x3c\\x3e-\\x7e]{1,256}(,[a-z][a-z0-9_*\\-/]{0,255}=[\\x21-\\x2b\\x2d-\\x3c\\x3e-\\x7e]{1,256})*$"
406
410
  }
407
411
  },
412
+ helloOnlyFields: {
413
+ baggage: {
414
+ kind: "string",
415
+ minBytes: 47,
416
+ maxBytes: 47,
417
+ format: "w3c-baggage-session-id",
418
+ pattern: "^session\\.id=[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
419
+ }
420
+ },
408
421
  acceptedOn: {
409
422
  hello: "client-to-server",
410
423
  serverFrames: "server-to-client"
411
424
  },
412
- upgradeHeaders: ["x-chalk-journey-id", "traceparent", "tracestate"],
425
+ upgradeHeaders: ["x-chalk-journey-id", "traceparent", "tracestate", "baggage"],
413
426
  rule: "propagate_from_first_observed_layer_to_every_downstream_frame"
414
427
  },
415
428
  externalIntents: [
@@ -712,7 +725,9 @@ function strictCommand(value) {
712
725
  function strictCollaborationsHelloExtension(value) {
713
726
  return exactObject(value, ["name", "chat_cursor"]) && ["collaboration_v1", "collaboration_v2"].includes(String(value.name)) && exactObject(value.chat_cursor, ["after_sequence", "retained_floor_sequence"]);
714
727
  }
715
- function strictHello(value) {
728
+ function strictHello(hello) {
729
+ const { baggage, ...value } = hello;
730
+ if (baggage !== void 0 && !(typeof baggage === "string" && boundedCorrelationString(baggage, 47, 47) && /^session\.id=[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(baggage))) return false;
716
731
  const base = exactObjectWithCorrelation(value, ["type", "protocol", "token", "streams"]);
717
732
  const extended = exactObjectWithCorrelation(value, ["type", "protocol", "token", "streams", "extensions"]) && Array.isArray(value.extensions) && value.extensions.length === 1 && strictCollaborationsHelloExtension(value.extensions[0]);
718
733
  if (!base && !extended || !exactObject(value.streams, ["control", "media", "presence", "requests"])) return false;
@@ -957,9 +972,10 @@ var TraceparentSchema = boundedUtf8String(55, 55).check(
957
972
  var TracestateSchema = boundedUtf8String(512, 1).check(
958
973
  Schema.makeFilter((value) => new RegExp("^[a-z][a-z0-9_*\\-/]{0,255}=[\\x21-\\x2b\\x2d-\\x3c\\x3e-\\x7e]{1,256}(,[a-z][a-z0-9_*\\-/]{0,255}=[\\x21-\\x2b\\x2d-\\x3c\\x3e-\\x7e]{1,256})*$", "u").test(value) ? void 0 : { path: [], issue: "must be a valid bounded W3C tracestate" })
959
974
  );
975
+ var BaggageSchema = boundedUtf8String(47, 47).check(Schema.makeFilter((value) => /^session\.id=[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value) ? void 0 : { path: [], issue: "must be the single W3C baggage member session.id=<lowercase UUID>" }));
960
976
  var SyncCorrelationFieldsSchema = Schema.Struct({ journey_id: Schema.optional(JourneyIdSchema), traceparent: Schema.optional(TraceparentSchema), tracestate: Schema.optional(TracestateSchema) });
961
- var HelloCorrelationFields = { journey_id: Schema.optional(JourneyIdSchema), traceparent: Schema.optional(TraceparentSchema), tracestate: Schema.optional(TracestateSchema) };
962
- var ServerCorrelationFields = { ...HelloCorrelationFields };
977
+ var ServerCorrelationFields = { journey_id: Schema.optional(JourneyIdSchema), traceparent: Schema.optional(TraceparentSchema), tracestate: Schema.optional(TracestateSchema) };
978
+ var HelloCorrelationFields = { ...ServerCorrelationFields, baggage: Schema.optional(BaggageSchema) };
963
979
  var StateDigestSchema = Schema.String.check(Schema.makeFilter((value) => /^[0-9a-f]{64}$/u.test(value) ? void 0 : { path: [], issue: "must be a lowercase hexadecimal SHA-256 digest" }));
964
980
  var UnsignedDecimalSchema = Schema.String.check(Schema.makeFilter((value) => /^(?:0|[1-9][0-9]*)$/u.test(value) ? void 0 : { path: [], issue: "must be an unsigned decimal integer" }));
965
981
  var CommandIdSchema = boundedUtf8String(64).check(Schema.makeFilter((value) => textEncoder.encode(value).byteLength >= 16 ? void 0 : { path: [], issue: "must meet the command ID minimum byte length" }));
@@ -2528,6 +2544,11 @@ var V1CommandScheduler = class {
2528
2544
  async acknowledge(frame) {
2529
2545
  const deferred = this.#commands.get(frame.command_id);
2530
2546
  if (!deferred) return;
2547
+ if (isSatisfiedByInactiveParticipant(frame, deferred.frame)) {
2548
+ const head = this.#options.controlHead();
2549
+ await this.#finishCommand(frame.command_id, { type: "ack", command_id: frame.command_id, delivery: "duplicate", outcome: "satisfied", revision: head.revision, state_digest: head.state_digest });
2550
+ return;
2551
+ }
2531
2552
  if (frame.outcome === "rejected" || frame.outcome === "command_id_conflict") {
2532
2553
  await this.#finishCommand(frame.command_id, frame);
2533
2554
  return;
@@ -2566,15 +2587,12 @@ var V1CommandScheduler = class {
2566
2587
  }
2567
2588
  }
2568
2589
  async settleTerminalRecovery(frame) {
2569
- const satisfiedOperations = /* @__PURE__ */ new Set();
2570
- if (frame.reason === "episode_ended") {
2571
- satisfiedOperations.add("end_episode");
2572
- satisfiedOperations.add("participant_leave");
2573
- } else if (frame.reason === "participant_inactive") {
2574
- satisfiedOperations.add("participant_leave");
2575
- }
2590
+ const satisfies = (operation) => {
2591
+ if (frame.reason === "episode_ended") return operation === "end_episode" || operation === "participant_leave";
2592
+ return frame.reason === "participant_inactive" && inactiveParticipantSatisfies(operation);
2593
+ };
2576
2594
  for (const [commandId, deferred] of this.#commands) {
2577
- if (deferred.frame.type !== "operation" || !satisfiedOperations.has(deferred.frame.name)) continue;
2595
+ if (deferred.frame.type !== "operation" || !satisfies(deferred.frame.name)) continue;
2578
2596
  await this.#finishCommand(commandId, {
2579
2597
  type: "ack",
2580
2598
  command_id: commandId,
@@ -2744,6 +2762,12 @@ function targetFrame(commandId, command) {
2744
2762
  return { type: "command", command_id: commandId, name: command.name, payload: command.payload };
2745
2763
  }
2746
2764
  }
2765
+ function inactiveParticipantSatisfies(operation) {
2766
+ return operation === "participant_leave";
2767
+ }
2768
+ function isSatisfiedByInactiveParticipant(result, command) {
2769
+ return result.outcome === "rejected" && result.reason === "participant_inactive" && command.type === "operation" && inactiveParticipantSatisfies(command.name);
2770
+ }
2747
2771
  function isTerminalLifecycleOperation(frame) {
2748
2772
  return frame.type === "operation" && (frame.name === "participant_leave" || frame.name === "end_episode");
2749
2773
  }
@@ -3002,6 +3026,7 @@ var MAX_REPLAY_EVENTS = SyncProtocolLimits.completeReplayMaxEvents;
3002
3026
  var MAX_REPLAY_BYTES = SyncProtocolLimits.completeReplayEncodedBytes;
3003
3027
  var MAX_PROJECTION_EVENT_EVIDENCE = 256;
3004
3028
  var CLIENT_RESTART_CLOSE_CODE = 4e3;
3029
+ var legacyHelloServers = /* @__PURE__ */ new Set();
3005
3030
  var DEFAULT_RECONNECT_DELAY_MS = 250;
3006
3031
  var MAX_RECONNECT_DELAY_MS = 5e3;
3007
3032
  var FAST_RECONNECT_ATTEMPTS = 8;
@@ -3052,6 +3077,8 @@ var V1SyncClient = class {
3052
3077
  return { stage: this.#opened ? "hello" : "upgrade", opened: this.#opened, closeCode: null, closeReason: null, durationMs: Math.max(0, this.#clock().now() - (this.#connectStartedAt ?? this.#clock().now())), attempt: this.#connectAttemptCount };
3053
3078
  }
3054
3079
  #reconnectAttempt = 0;
3080
+ /** The socket whose hello carried baggage and has not been answered by a welcome yet. */
3081
+ #baggageHelloSocket = null;
3055
3082
  #episodeEndTimer;
3056
3083
  #heartbeatTimer;
3057
3084
  #missedHeartbeats = 0;
@@ -3082,7 +3109,11 @@ var V1SyncClient = class {
3082
3109
  isStarted: () => this.#started,
3083
3110
  sendIfLive: (frame) => this.#sendIfLive(frame),
3084
3111
  stateChanged: () => this.#emit(),
3085
- ackHeadIsProven: (ack) => this.#ackHeadIsProven(ack)
3112
+ ackHeadIsProven: (ack) => this.#ackHeadIsProven(ack),
3113
+ controlHead: () => {
3114
+ const control = this.#requireControl();
3115
+ return { revision: control.revision, state_digest: control.stateDigest };
3116
+ }
3086
3117
  });
3087
3118
  this.#liveTargets = new V1LiveTargetCoordinator({
3088
3119
  mediaPlane: options.mediaPlane,
@@ -3373,11 +3404,12 @@ var V1SyncClient = class {
3373
3404
  this.#clock().setTimeout(() => {
3374
3405
  if (connectionGeneration !== this.#connectionGeneration) return;
3375
3406
  this.#inbound = this.#inbound.then(() => {
3407
+ const legacyHello = event.code === 1009 && this.#legacyHelloRejected(socket);
3376
3408
  if (socket === this.#socket && event.code === 1008 && (event.reason === "" || event.reason === "invalid token" || event.reason === "policy violation" || event.reason === "entry timeout")) {
3377
3409
  this.#phase = { phase: "terminal", terminalReason: event.reason === "entry timeout" ? "entry_timeout" : "access_rejected" };
3378
3410
  this.#emit();
3379
3411
  }
3380
- this.#disconnected(socket);
3412
+ this.#disconnected(socket, legacyHello);
3381
3413
  });
3382
3414
  }, 0);
3383
3415
  };
@@ -3394,10 +3426,12 @@ var V1SyncClient = class {
3394
3426
  const token = await this.#options.token();
3395
3427
  if (socket !== this.#socket) return;
3396
3428
  this.#phase = { phase: "recovering" };
3429
+ const baggage = legacyHelloServers.has(this.#options.url) ? void 0 : sessionBaggage();
3397
3430
  const hello = {
3398
3431
  type: "hello",
3399
3432
  protocol: 1,
3400
3433
  ...this.#options.traceparent ? { traceparent: this.#options.traceparent } : {},
3434
+ ...baggage ? { baggage } : {},
3401
3435
  token,
3402
3436
  streams: {
3403
3437
  control: { cursor: this.#control && { revision: this.#control.revision, state_schema_version: this.#control.stateSchemaVersion, state_digest: this.#control.stateDigest } },
@@ -3407,6 +3441,7 @@ var V1SyncClient = class {
3407
3441
  }
3408
3442
  };
3409
3443
  const collaborationExtension = this.#collaborationState.helloExtension;
3444
+ this.#baggageHelloSocket = baggage ? socket : null;
3410
3445
  if (!collaborationExtension) {
3411
3446
  this.#send(hello);
3412
3447
  } else {
@@ -3426,7 +3461,7 @@ var V1SyncClient = class {
3426
3461
  } catch (error) {
3427
3462
  if (this.#snapshotRecoveryRequested) this.#failDigestRecovery();
3428
3463
  else if (error instanceof V1StateDigestMismatchError) this.#recoverDigestMismatch();
3429
- else this.#recover("invalid_frame");
3464
+ else this.#recover("invalid_frame", error instanceof V1ReplicaError && error.message === "invalid_frame" && this.#legacyHelloRejected(socket));
3430
3465
  }
3431
3466
  }
3432
3467
  async #handleFrame(frame) {
@@ -3548,6 +3583,7 @@ var V1SyncClient = class {
3548
3583
  }
3549
3584
  async #welcome(frame) {
3550
3585
  if (this.#phase.phase !== "recovering" || this.#recovery) throw new V1ReplicaError("unexpected welcome");
3586
+ this.#baggageHelloSocket = null;
3551
3587
  this.#collaborationState.acceptWelcome(frame);
3552
3588
  this.#entryNegotiated = frame.entry_confirmation === true;
3553
3589
  this.#entryGeneration = frame.entry_generation ?? this.#entryGeneration;
@@ -3704,7 +3740,7 @@ var V1SyncClient = class {
3704
3740
  if (evidence.get(frame.sequence) !== frameSignature(frame)) throw new V1ReplicaError("conflicting or unprovable duplicate projection event");
3705
3741
  return true;
3706
3742
  }
3707
- #disconnected(socket) {
3743
+ #disconnected(socket, retryNow = false) {
3708
3744
  if (socket !== this.#socket) return;
3709
3745
  this.#socket = null;
3710
3746
  this.#clearDial();
@@ -3723,6 +3759,13 @@ var V1SyncClient = class {
3723
3759
  this.#emit();
3724
3760
  if (!this.#transportAvailable) return;
3725
3761
  this.#clearReconnect();
3762
+ if (retryNow) {
3763
+ this.#reconnectTimer = this.#clock().setTimeout(() => {
3764
+ this.#reconnectTimer = void 0;
3765
+ this.#connect();
3766
+ }, 0);
3767
+ return;
3768
+ }
3726
3769
  const configuredDelay = this.#options.reconnectDelayMs;
3727
3770
  const exponentialAttempt = configuredDelay === void 0 ? Math.max(0, this.#reconnectAttempt - FAST_RECONNECT_ATTEMPTS + 1) : this.#reconnectAttempt;
3728
3771
  const baseDelay = configuredDelay === 0 ? 0 : Math.min(MAX_RECONNECT_DELAY_MS, (configuredDelay ?? DEFAULT_RECONNECT_DELAY_MS) * 2 ** Math.min(exponentialAttempt, 5));
@@ -3733,11 +3776,21 @@ var V1SyncClient = class {
3733
3776
  this.#connect();
3734
3777
  }, delay);
3735
3778
  }
3736
- #recover(reason) {
3779
+ #recover(reason, retryNow = false) {
3737
3780
  const socket = this.#socket;
3738
3781
  if (!socket) return;
3739
3782
  socket.close(CLIENT_RESTART_CLOSE_CODE, reason);
3740
- this.#disconnected(socket);
3783
+ this.#disconnected(socket, retryNow);
3784
+ }
3785
+ /**
3786
+ * A Sync that predates the session ID rejects any hello with an unknown `baggage` field before welcome. The first
3787
+ * rejection marks that server as legacy, so the retry and every later reconnect send the hello without it.
3788
+ */
3789
+ #legacyHelloRejected(socket) {
3790
+ if (this.#baggageHelloSocket !== socket) return false;
3791
+ this.#baggageHelloSocket = null;
3792
+ legacyHelloServers.add(this.#options.url);
3793
+ return true;
3741
3794
  }
3742
3795
  #recoverDigestMismatch() {
3743
3796
  const now = this.#clock().now();
@@ -1,3 +1,7 @@
1
+ import {
2
+ withRequestContext
3
+ } from "./chunk-N4IPKRS6.js";
4
+
1
5
  // src/generated/schemas.ts
2
6
  import { Schema, SchemaGetter } from "effect";
3
7
  var ChalkCodePointLimits = {
@@ -92,6 +96,30 @@ var AdmitEpisodeParticipantRequestSchema = Schema.Struct({
92
96
  role: Schema.String.check(Schema.isMinLength(1))
93
97
  });
94
98
  var AuditLogIdSchema = Schema.String.check(Schema.isMinLength(36), Schema.isMaxLength(36), Schema.isPattern(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).pipe(Schema.brand("AuditLogId"));
99
+ var CallStatsRequestSchema = Schema.Struct({
100
+ samples: Schema.Array(
101
+ Schema.Struct({
102
+ connection_type: Schema.Literals(["udp", "tcp", "turn", "unknown"]),
103
+ elapsed_ms: Schema.Number,
104
+ freeze_count: Schema.optional(Schema.NullOr(Schema.Number)),
105
+ freeze_ms: Schema.optional(Schema.NullOr(Schema.Number)),
106
+ local_candidate_type: Schema.optional(Schema.Literals(["host", "srflx", "prflx", "relay"])),
107
+ recv_bitrate_kbps: Schema.optional(Schema.NullOr(Schema.Number)),
108
+ recv_height: Schema.optional(Schema.NullOr(Schema.Number)),
109
+ recv_jitter_ms: Schema.optional(Schema.NullOr(Schema.Number)),
110
+ recv_loss_pct: Schema.optional(Schema.NullOr(Schema.Number)),
111
+ relay_protocol: Schema.optional(Schema.Literals(["udp", "tcp", "tls"])),
112
+ remote_candidate_type: Schema.optional(Schema.Literals(["host", "srflx", "prflx", "relay"])),
113
+ rtt_ms: Schema.optional(Schema.NullOr(Schema.Number)),
114
+ send_bitrate_kbps: Schema.optional(Schema.NullOr(Schema.Number)),
115
+ send_jitter_ms: Schema.optional(Schema.NullOr(Schema.Number)),
116
+ send_layer: Schema.optional(Schema.Literals(["h", "m", "l"])),
117
+ send_loss_pct: Schema.optional(Schema.NullOr(Schema.Number)),
118
+ window_ms: Schema.Number
119
+ })
120
+ ).check(Schema.isMinLength(1), Schema.isMaxLength(30)),
121
+ version: Schema.Literal(1)
122
+ });
95
123
  var CancelRecordingPreparationRequestSchema = Schema.Struct({
96
124
  expected_revision: Schema.Number
97
125
  });
@@ -136,6 +164,7 @@ var CloudflareSFUCloseTracksAPIResponseSchema = Schema.Struct({
136
164
  var CloudflareSFUCloseTracksRequestSchema = Schema.Struct({
137
165
  connection_id: Schema.String.check(Schema.isMinLength(1)),
138
166
  force: Schema.Boolean,
167
+ receive_only: Schema.optional(Schema.Boolean),
139
168
  session_description: Schema.optional(
140
169
  Schema.NullOr(
141
170
  Schema.Struct({
@@ -173,7 +202,7 @@ var CloudflareSFURenegotiateRequestSchema = Schema.Struct({
173
202
  var CloudflareSFURenegotiateResponseSchema = Schema.Struct({
174
203
  accepted: Schema.Boolean
175
204
  });
176
- var CloudflareSFUTracksAPIResponseSchema = Schema.Struct({
205
+ var CloudflareSFUUpdateTracksAPIResponseSchema = Schema.Struct({
177
206
  requiresImmediateRenegotiation: Schema.optional(Schema.Boolean),
178
207
  sessionDescription: Schema.optional(
179
208
  Schema.NullOr(
@@ -214,6 +243,7 @@ var CloudflareSFUTracksAPIResponseSchema = Schema.Struct({
214
243
  )
215
244
  )
216
245
  });
246
+ var CloudflareSFUTracksAPIResponseSchema = CloudflareSFUUpdateTracksAPIResponseSchema;
217
247
  var CloudflareSFUTracksRequestSchema = Schema.Struct({
218
248
  connection_id: Schema.String.check(Schema.isMinLength(1)),
219
249
  session_description: Schema.optional(
@@ -244,6 +274,28 @@ var CloudflareSFUTracksRequestSchema = Schema.Struct({
244
274
  })
245
275
  )
246
276
  });
277
+ var CloudflareSFUUpdateTracksRequestSchema = Schema.Struct({
278
+ connection_id: Schema.String.check(Schema.isMinLength(1)),
279
+ tracks: Schema.Array(
280
+ Schema.Struct({
281
+ location: Schema.String.check(Schema.isMinLength(1)),
282
+ mid: Schema.optional(Schema.String.check(Schema.isMinLength(1))),
283
+ publication_id: Schema.optional(Schema.String.check(Schema.isMinLength(1))),
284
+ sessionId: Schema.optional(Schema.String.check(Schema.isMinLength(1))),
285
+ simulcast: Schema.optional(
286
+ Schema.NullOr(
287
+ Schema.Struct({
288
+ preferredRid: Schema.String.check(Schema.isMinLength(1)),
289
+ priorityOrdering: Schema.String.check(Schema.isMinLength(1)),
290
+ ridNotAvailable: Schema.String.check(Schema.isMinLength(1))
291
+ })
292
+ )
293
+ ),
294
+ source: Schema.optional(Schema.String.check(Schema.isMinLength(1))),
295
+ trackName: Schema.String.check(Schema.isMinLength(1))
296
+ })
297
+ )
298
+ });
247
299
  var ConnectionReportRequestSchema = Schema.Struct({
248
300
  events: Schema.Array(
249
301
  Schema.Struct({
@@ -1925,6 +1977,19 @@ var TestWebhookEndpoint429ResponseHeadersSchema = Schema.Struct({
1925
1977
  "X-RateLimit-Limit": RateLimitLimitHeaderSchema,
1926
1978
  "X-RateLimit-Remaining": RateLimitRemainingHeaderSchema
1927
1979
  });
1980
+ var UpdateCloudflareSFUTracksPathParamsSchema = Schema.Struct({
1981
+ episode_id: EpisodeIdSchema,
1982
+ participant_id: ParticipantIdSchema,
1983
+ space_id: SpaceIdSchema,
1984
+ tenant_id: TenantIdSchema
1985
+ });
1986
+ var UpdateCloudflareSFUTracksRequestBodySchema = CloudflareSFUUpdateTracksRequestSchema;
1987
+ var UpdateCloudflareSFUTracksResponseSchema = CloudflareSFUUpdateTracksAPIResponseSchema;
1988
+ var UpdateCloudflareSFUTracks429ResponseHeadersSchema = Schema.Struct({
1989
+ "Retry-After": RetryAfterHeaderSchema,
1990
+ "X-RateLimit-Limit": RateLimitLimitHeaderSchema,
1991
+ "X-RateLimit-Remaining": RateLimitRemainingHeaderSchema
1992
+ });
1928
1993
  var UpdateMembershipPathParamsSchema = Schema.Struct({
1929
1994
  membership_id: MembershipIdSchema,
1930
1995
  tenant_id: TenantIdSchema
@@ -1983,6 +2048,15 @@ var UpdateWebhookEndpoint429ResponseHeadersSchema = Schema.Struct({
1983
2048
  "X-RateLimit-Limit": RateLimitLimitHeaderSchema,
1984
2049
  "X-RateLimit-Remaining": RateLimitRemainingHeaderSchema
1985
2050
  });
2051
+ var UploadCallStatsPathParamsSchema = Schema.Struct({
2052
+ tenant_id: TenantIdSchema
2053
+ });
2054
+ var UploadCallStatsRequestBodySchema = CallStatsRequestSchema;
2055
+ var UploadCallStats429ResponseHeadersSchema = Schema.Struct({
2056
+ "Retry-After": RetryAfterHeaderSchema,
2057
+ "X-RateLimit-Limit": RateLimitLimitHeaderSchema,
2058
+ "X-RateLimit-Remaining": RateLimitRemainingHeaderSchema
2059
+ });
1986
2060
  var AccessForbiddenError = class extends Schema.TaggedErrorClass()("AccessForbiddenError", {
1987
2061
  error: Schema.Struct({
1988
2062
  code: Schema.Literal("access.forbidden"),
@@ -5110,6 +5184,22 @@ var TestWebhookEndpointErrorSchema = Schema.Union([
5110
5184
  WebhookInvalidUrlErrorSchema,
5111
5185
  WebhookUnsafeUrlErrorSchema
5112
5186
  ]);
5187
+ var UpdateCloudflareSFUTracksErrorSchema = Schema.Union([
5188
+ AccessForbiddenErrorSchema,
5189
+ AccessUnauthenticatedErrorSchema,
5190
+ EpisodeInvalidIdErrorSchema,
5191
+ EpisodeNotFoundErrorSchema,
5192
+ MediaConnectionNotFoundErrorSchema,
5193
+ MediaUnavailableErrorSchema,
5194
+ ParticipantInvalidIdErrorSchema,
5195
+ RequestInvalidErrorSchema,
5196
+ RequestPayloadTooLargeErrorSchema,
5197
+ RequestRateLimitedErrorSchema,
5198
+ ServiceInternalErrorSchema,
5199
+ ServiceUnavailableErrorSchema,
5200
+ SpaceInvalidIdErrorSchema,
5201
+ TenantInvalidIdErrorSchema
5202
+ ]);
5113
5203
  var UpdateMembershipErrorSchema = Schema.Union([
5114
5204
  AccessForbiddenErrorSchema,
5115
5205
  AccessUnauthenticatedErrorSchema,
@@ -5198,6 +5288,7 @@ var UpdateWebhookEndpointErrorSchema = Schema.Union([
5198
5288
  WebhookInvalidUrlErrorSchema,
5199
5289
  WebhookUnsafeUrlErrorSchema
5200
5290
  ]);
5291
+ var UploadCallStatsErrorSchema = Schema.Union([AccessForbiddenErrorSchema, AccessUnauthenticatedErrorSchema, RequestInvalidErrorSchema, RequestPayloadTooLargeErrorSchema, RequestRateLimitedErrorSchema, ServiceInternalErrorSchema, ServiceUnavailableErrorSchema, TenantInvalidIdErrorSchema]);
5201
5292
  var ChalkOperationPolicies = {
5202
5293
  addCloudflareSFUTracks: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5203
5294
  admitEpisodeParticipant: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
@@ -5259,11 +5350,13 @@ var ChalkOperationPolicies = {
5259
5350
  sendProblemReport: { maxBodyBytes: 37024, rateLimit: { limit: 3, policy: "problem_reports.participant", windowSeconds: 3600 } },
5260
5351
  setEpisodeDeadline: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5261
5352
  testWebhookEndpoint: { rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5353
+ updateCloudflareSFUTracks: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5262
5354
  updateMembership: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5263
5355
  updateSpace: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5264
5356
  updateSpacePublicInvite: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5265
5357
  updateTenant: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5266
- updateWebhookEndpoint: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } }
5358
+ updateWebhookEndpoint: { maxBodyBytes: 1048576, rateLimit: { limit: 60, policy: "v1.authenticated.write", windowSeconds: 60 } },
5359
+ uploadCallStats: { maxBodyBytes: 32768, rateLimit: { limit: 30, policy: "call_stats.participant", windowSeconds: 600 } }
5267
5360
  };
5268
5361
 
5269
5362
  // src/generated/http-api.ts
@@ -5832,6 +5925,28 @@ var defaultGroup = HttpApiGroup.make("default").add(
5832
5925
  ServiceUnavailableErrorSchema.pipe(HttpApiSchema.status(503))
5833
5926
  ]
5834
5927
  })
5928
+ ).add(
5929
+ HttpApiEndpoint.put("updateCloudflareSFUTracks", "/v1/tenants/:tenant_id/spaces/:space_id/episodes/:episode_id/participants/:participant_id/media/sfu/tracks/update", {
5930
+ params: UpdateCloudflareSFUTracksPathParamsSchema,
5931
+ payload: UpdateCloudflareSFUTracksRequestBodySchema,
5932
+ success: UpdateCloudflareSFUTracksResponseSchema.pipe(HttpApiSchema.status(200)),
5933
+ error: [
5934
+ EpisodeInvalidIdErrorSchema.pipe(HttpApiSchema.status(400)),
5935
+ ParticipantInvalidIdErrorSchema.pipe(HttpApiSchema.status(400)),
5936
+ RequestInvalidErrorSchema.pipe(HttpApiSchema.status(400)),
5937
+ SpaceInvalidIdErrorSchema.pipe(HttpApiSchema.status(400)),
5938
+ TenantInvalidIdErrorSchema.pipe(HttpApiSchema.status(400)),
5939
+ AccessUnauthenticatedErrorSchema.pipe(HttpApiSchema.status(401)),
5940
+ AccessForbiddenErrorSchema.pipe(HttpApiSchema.status(403)),
5941
+ EpisodeNotFoundErrorSchema.pipe(HttpApiSchema.status(404)),
5942
+ MediaConnectionNotFoundErrorSchema.pipe(HttpApiSchema.status(404)),
5943
+ RequestPayloadTooLargeErrorSchema.pipe(HttpApiSchema.status(413)),
5944
+ RequestRateLimitedErrorSchema.pipe(HttpApiSchema.status(429)),
5945
+ ServiceInternalErrorSchema.pipe(HttpApiSchema.status(500)),
5946
+ MediaUnavailableErrorSchema.pipe(HttpApiSchema.status(503)),
5947
+ ServiceUnavailableErrorSchema.pipe(HttpApiSchema.status(503))
5948
+ ]
5949
+ })
5835
5950
  ).add(
5836
5951
  HttpApiEndpoint.patch("updateWebhookEndpoint", "/v1/tenants/:tenant_id/webhook-endpoints/:endpoint_id", {
5837
5952
  params: UpdateWebhookEndpointPathParamsSchema,
@@ -5867,6 +5982,22 @@ var defaultGroup = HttpApiGroup.make("default").add(
5867
5982
  ServiceUnavailableErrorSchema.pipe(HttpApiSchema.status(503))
5868
5983
  ]
5869
5984
  })
5985
+ ).add(
5986
+ HttpApiEndpoint.post("uploadCallStats", "/v1/tenants/:tenant_id/call-stats", {
5987
+ params: UploadCallStatsPathParamsSchema,
5988
+ payload: UploadCallStatsRequestBodySchema,
5989
+ success: HttpApiSchema.Empty(204).pipe(HttpApiSchema.status(204)),
5990
+ error: [
5991
+ RequestInvalidErrorSchema.pipe(HttpApiSchema.status(400)),
5992
+ TenantInvalidIdErrorSchema.pipe(HttpApiSchema.status(400)),
5993
+ AccessUnauthenticatedErrorSchema.pipe(HttpApiSchema.status(401)),
5994
+ AccessForbiddenErrorSchema.pipe(HttpApiSchema.status(403)),
5995
+ RequestPayloadTooLargeErrorSchema.pipe(HttpApiSchema.status(413)),
5996
+ RequestRateLimitedErrorSchema.pipe(HttpApiSchema.status(429)),
5997
+ ServiceInternalErrorSchema.pipe(HttpApiSchema.status(500)),
5998
+ ServiceUnavailableErrorSchema.pipe(HttpApiSchema.status(503))
5999
+ ]
6000
+ })
5870
6001
  );
5871
6002
  var episodesGroup = HttpApiGroup.make("episodes").add(
5872
6003
  HttpApiEndpoint.post("admitEpisodeParticipant", "/v1/tenants/:tenant_id/spaces/:space_id/episodes/:episode_id/participants", {
@@ -6864,7 +6995,7 @@ var createChalkEffectClient = (options) => {
6864
6995
  })
6865
6996
  }).pipe(Effect.provide(FetchHttpClient.layer));
6866
6997
  const configuredClient = options.credentials === void 0 ? client : client.pipe(Effect.provideService(FetchHttpClient.RequestInit, { credentials: options.credentials }));
6867
- return options.fetch ? configuredClient.pipe(Effect.provideService(FetchHttpClient.Fetch, options.fetch)) : configuredClient;
6998
+ return configuredClient.pipe(Effect.provideService(FetchHttpClient.Fetch, withRequestContext(options.fetch)));
6868
6999
  };
6869
7000
  function requestHeaders(options) {
6870
7001
  return {
@@ -7015,17 +7146,31 @@ function parseMediaAccess(value) {
7015
7146
  provider: value.provider,
7016
7147
  clientPayload: {
7017
7148
  connectionId: requireNonEmptyString(payload.connectionId),
7018
- stunServer: requireNonEmptyString(payload.stunServer)
7149
+ stunServer: requireNonEmptyString(payload.stunServer),
7150
+ ...payload.iceServers === void 0 || payload.iceServers === null ? {} : { iceServers: parseIceServers(payload.iceServers) }
7019
7151
  }
7020
7152
  };
7021
7153
  default:
7022
7154
  throw new AccessGrantError();
7023
7155
  }
7024
7156
  }
7157
+ function parseIceServers(value) {
7158
+ if (!Array.isArray(value)) throw new AccessGrantError();
7159
+ return value.map((entry) => {
7160
+ if (!isRecord(entry) || !Array.isArray(entry.urls) || entry.urls.length === 0) throw new AccessGrantError();
7161
+ const urls = entry.urls.map(requireNonEmptyString);
7162
+ if (entry.username === void 0 && entry.credential === void 0) return { urls };
7163
+ return { urls, username: requireNonEmptyString(entry.username), credential: requireNonEmptyString(entry.credential) };
7164
+ });
7165
+ }
7025
7166
  function mediaClientPayload(value) {
7026
7167
  switch (value.provider) {
7027
7168
  case "cloudflare_sfu":
7028
- return { connectionId: value.clientPayload.connectionId, stunServer: value.clientPayload.stunServer };
7169
+ return {
7170
+ connectionId: value.clientPayload.connectionId,
7171
+ stunServer: value.clientPayload.stunServer,
7172
+ ...value.clientPayload.iceServers ? { iceServers: value.clientPayload.iceServers } : {}
7173
+ };
7029
7174
  }
7030
7175
  }
7031
7176
  function requireCredential(value, audience) {
@@ -7073,6 +7218,7 @@ export {
7073
7218
  AccessGrantSchema,
7074
7219
  AdmitEpisodeParticipantRequestSchema,
7075
7220
  AuditLogIdSchema,
7221
+ CallStatsRequestSchema,
7076
7222
  CancelRecordingPreparationRequestSchema,
7077
7223
  ChatAttachmentSchema,
7078
7224
  ChatAttachmentDownloadSchema,
@@ -7082,8 +7228,10 @@ export {
7082
7228
  CloudflareSFUPublicationsResponseSchema,
7083
7229
  CloudflareSFURenegotiateRequestSchema,
7084
7230
  CloudflareSFURenegotiateResponseSchema,
7231
+ CloudflareSFUUpdateTracksAPIResponseSchema,
7085
7232
  CloudflareSFUTracksAPIResponseSchema,
7086
7233
  CloudflareSFUTracksRequestSchema,
7234
+ CloudflareSFUUpdateTracksRequestSchema,
7087
7235
  ConnectionReportRequestSchema,
7088
7236
  CreateAPIKeyRequestSchema,
7089
7237
  CreateEpisodeRequestSchema,
@@ -7429,6 +7577,10 @@ export {
7429
7577
  TestWebhookEndpointRequestHeadersSchema,
7430
7578
  TestWebhookEndpointResponseSchema,
7431
7579
  TestWebhookEndpoint429ResponseHeadersSchema,
7580
+ UpdateCloudflareSFUTracksPathParamsSchema,
7581
+ UpdateCloudflareSFUTracksRequestBodySchema,
7582
+ UpdateCloudflareSFUTracksResponseSchema,
7583
+ UpdateCloudflareSFUTracks429ResponseHeadersSchema,
7432
7584
  UpdateMembershipPathParamsSchema,
7433
7585
  UpdateMembershipRequestBodySchema,
7434
7586
  UpdateMembershipResponseSchema,
@@ -7450,6 +7602,9 @@ export {
7450
7602
  UpdateWebhookEndpointRequestBodySchema,
7451
7603
  UpdateWebhookEndpointResponseSchema,
7452
7604
  UpdateWebhookEndpoint429ResponseHeadersSchema,
7605
+ UploadCallStatsPathParamsSchema,
7606
+ UploadCallStatsRequestBodySchema,
7607
+ UploadCallStats429ResponseHeadersSchema,
7453
7608
  AccessForbiddenError,
7454
7609
  AccessForbiddenErrorWireSchema,
7455
7610
  AccessForbiddenErrorSchema,
@@ -7872,11 +8027,13 @@ export {
7872
8027
  SendProblemReportErrorSchema,
7873
8028
  SetEpisodeDeadlineErrorSchema,
7874
8029
  TestWebhookEndpointErrorSchema,
8030
+ UpdateCloudflareSFUTracksErrorSchema,
7875
8031
  UpdateMembershipErrorSchema,
7876
8032
  UpdateSpaceErrorSchema,
7877
8033
  UpdateSpacePublicInviteErrorSchema,
7878
8034
  UpdateTenantErrorSchema,
7879
8035
  UpdateWebhookEndpointErrorSchema,
8036
+ UploadCallStatsErrorSchema,
7880
8037
  ChalkOperationPolicies,
7881
8038
  ChalkApi,
7882
8039
  createChalkEffectClient,
@@ -0,0 +1,76 @@
1
+ // src/session/index.ts
2
+ var SESSION_ID_KEY = "session.id";
3
+ var STORAGE_KEY = "chalk.session.id";
4
+ var TAB_KEY = "chalk.session.tab";
5
+ var TAB_NAME_PREFIX = "chalk.tab:";
6
+ var SESSION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
7
+ var source = browserSession;
8
+ var browserId;
9
+ function setSessionSource(next) {
10
+ source = next;
11
+ }
12
+ function currentSessionId() {
13
+ return source();
14
+ }
15
+ function newSessionId() {
16
+ const random = globalThis.crypto;
17
+ if (typeof random?.randomUUID === "function") return random.randomUUID();
18
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (digit) => (Number(digit) ^ random.getRandomValues(new Uint8Array(1))[0] & 15 >> Number(digit) / 4).toString(16));
19
+ }
20
+ function browserSession() {
21
+ if (browserId) return browserId;
22
+ if (typeof globalThis.document === "undefined") return void 0;
23
+ const tab = tabToken();
24
+ browserId = newSessionId();
25
+ try {
26
+ const storage = globalThis.sessionStorage;
27
+ browserId = storedSession(storage, tab) ?? browserId;
28
+ storage.setItem(STORAGE_KEY, browserId);
29
+ if (tab !== void 0) storage.setItem(TAB_KEY, tab);
30
+ } catch {
31
+ }
32
+ return browserId;
33
+ }
34
+ function storedSession(storage, tab) {
35
+ const stored = storage.getItem(STORAGE_KEY) ?? void 0;
36
+ if (!stored || !SESSION_ID.test(stored)) return void 0;
37
+ return tab === void 0 || storage.getItem(TAB_KEY) === tab ? stored : void 0;
38
+ }
39
+ function tabToken() {
40
+ const win = globalThis.window;
41
+ if (!win) return void 0;
42
+ if (win.name.startsWith(TAB_NAME_PREFIX)) return win.name;
43
+ if (win.name !== "") return void 0;
44
+ win.name = `${TAB_NAME_PREFIX}${newSessionId()}`;
45
+ return win.name;
46
+ }
47
+ function sessionBaggage() {
48
+ const sessionId = currentSessionId();
49
+ return sessionId ? `${SESSION_ID_KEY}=${sessionId}` : void 0;
50
+ }
51
+ function requestContextHeaders() {
52
+ const baggage = sessionBaggage();
53
+ if (!baggage) return {};
54
+ const trace = newSessionId().replaceAll("-", "");
55
+ return { baggage, traceparent: `00-${trace}-${trace.slice(16)}-01` };
56
+ }
57
+ function withRequestContext(fetchImplementation) {
58
+ return (input, init) => {
59
+ const send = fetchImplementation ?? globalThis.fetch;
60
+ const context = requestContextHeaders();
61
+ if (Object.keys(context).length === 0) return send(input, init);
62
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
63
+ for (const [name, value] of Object.entries(context)) if (!headers.has(name)) headers.set(name, value);
64
+ return send(input, { ...init, headers });
65
+ };
66
+ }
67
+
68
+ export {
69
+ SESSION_ID_KEY,
70
+ setSessionSource,
71
+ currentSessionId,
72
+ newSessionId,
73
+ sessionBaggage,
74
+ requestContextHeaders,
75
+ withRequestContext
76
+ };