@eyepop.ai/eyepop 3.25.1 → 3.25.3

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.
@@ -9214,7 +9214,7 @@ var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
9214
9214
  PredictionVersion3[PredictionVersion3["V3"] = 3] = "V3";
9215
9215
  return PredictionVersion3;
9216
9216
  })(PredictionVersion || {});
9217
- var DEFAULT_PREDICTION_VERSION = 2 /* V2 */;
9217
+ var DEFAULT_PREDICTION_VERSION = 3 /* V3 */;
9218
9218
  var VideoMode = /* @__PURE__ */ ((VideoMode2) => {
9219
9219
  VideoMode2["STREAM"] = "stream";
9220
9220
  VideoMode2["BUFFER"] = "buffer";
@@ -10925,14 +10925,6 @@ var _SessionBackend = class _SessionBackend {
10925
10925
  }
10926
10926
  return this.ctx.options.pop || null;
10927
10927
  }
10928
- /**
10929
- * A select forward's results come only at V3. Only a Pop that selects asks
10930
- * for it, so a worker too old to know V3 - which also rejects that Pop - is
10931
- * never sent a version it would not accept.
10932
- */
10933
- predictionVersion() {
10934
- return popSelects(this.pop()) ? 3 /* V3 */ : DEFAULT_PREDICTION_VERSION;
10935
- }
10936
10928
  async changePop(pop) {
10937
10929
  if (this.ctx.options.popId == "transient" /* Transient */) {
10938
10930
  await this.changeTransientPop(pop);
@@ -11033,7 +11025,7 @@ var _SessionBackend = class _SessionBackend {
11033
11025
  return { stream: streamSource.stream, mimeType: streamSource.mimeType };
11034
11026
  })
11035
11027
  );
11036
- return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11028
+ return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11037
11029
  } catch (e) {
11038
11030
  limit.release();
11039
11031
  throw e;
@@ -11052,7 +11044,7 @@ var _SessionBackend = class _SessionBackend {
11052
11044
  stream,
11053
11045
  mimeType: mimeTypes?.[i] ?? null
11054
11046
  }));
11055
- return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11047
+ return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11056
11048
  } catch (e) {
11057
11049
  limit.release();
11058
11050
  throw e;
@@ -11067,7 +11059,7 @@ var _SessionBackend = class _SessionBackend {
11067
11059
  await limit.acquire();
11068
11060
  try {
11069
11061
  this.ctx.updateState();
11070
- return this.start(new LoadFromGroupJob(urls, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11062
+ return this.start(new LoadFromGroupJob(urls, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11071
11063
  } catch (e) {
11072
11064
  limit.release();
11073
11065
  throw e;
@@ -11111,14 +11103,14 @@ var _SessionBackend = class _SessionBackend {
11111
11103
  client,
11112
11104
  this.ctx.requestLogger,
11113
11105
  void 0,
11114
- this.predictionVersion()
11106
+ DEFAULT_PREDICTION_VERSION
11115
11107
  ),
11116
11108
  true
11117
11109
  );
11118
11110
  }
11119
11111
  async uploadStream(source, params) {
11120
11112
  return await this.startConnected(
11121
- async (client) => new UploadJob(source.stream, source.mimeType, source.size, source.videoMode, params, async () => this.session(), client, this.ctx.requestLogger, void 0, this.predictionVersion()),
11113
+ async (client) => new UploadJob(source.stream, source.mimeType, source.size, source.videoMode, params, async () => this.session(), client, this.ctx.requestLogger, void 0, DEFAULT_PREDICTION_VERSION),
11122
11114
  true
11123
11115
  );
11124
11116
  }
@@ -11143,7 +11135,7 @@ var _SessionBackend = class _SessionBackend {
11143
11135
  client,
11144
11136
  this.ctx.requestLogger,
11145
11137
  uploadBodyFactory,
11146
- this.predictionVersion()
11138
+ DEFAULT_PREDICTION_VERSION
11147
11139
  );
11148
11140
  });
11149
11141
  }
@@ -11151,13 +11143,13 @@ var _SessionBackend = class _SessionBackend {
11151
11143
  if (source.url.startsWith("file://")) {
11152
11144
  return await this.uploadPath({ path: source.url.substring("file://".length) }, params);
11153
11145
  }
11154
- return await this.startConnected(async (client) => new LoadFromJob(source.url, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11146
+ return await this.startConnected(async (client) => new LoadFromJob(source.url, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11155
11147
  }
11156
11148
  async loadFromAssetUuid(source, params) {
11157
- return await this.startConnected(async (client) => new LoadFromAssetUuidJob(source.assetUuid, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11149
+ return await this.startConnected(async (client) => new LoadFromAssetUuidJob(source.assetUuid, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11158
11150
  }
11159
11151
  async loadMediaStream(source, params) {
11160
- return await this.startConnected(async (client) => new LoadMediaStreamJob(source.mediaStream, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
11152
+ return await this.startConnected(async (client) => new LoadMediaStreamJob(source.mediaStream, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
11161
11153
  }
11162
11154
  async reconnect() {
11163
11155
  if (this.ctx.options.popId == "transient" /* Transient */) {
@@ -11607,6 +11599,8 @@ var OpenSource = class {
11607
11599
  this.id = id;
11608
11600
  this.inbox = new Inbox();
11609
11601
  this.uploads = /* @__PURE__ */ new Map();
11602
+ // The socket's ended count when the setSource went out.
11603
+ this.endedBefore = 0;
11610
11604
  }
11611
11605
  close(error) {
11612
11606
  this.uploads.forEach((upload) => upload.end());
@@ -11619,14 +11613,53 @@ var PipelineSocket = class {
11619
11613
  this.sources = /* @__PURE__ */ new Map();
11620
11614
  this.commands = /* @__PURE__ */ new Map();
11621
11615
  this._closed = false;
11616
+ // The ids of sources sent on this socket that have neither ended nor been refused, as the
11617
+ // pipeline API counts them, cancelled ones included.
11618
+ this.held = /* @__PURE__ */ new Set();
11619
+ // The held sources the pipeline API took: only these end, so only these make room.
11620
+ this.accepted = /* @__PURE__ */ new Set();
11621
+ // The ends on this socket so far.
11622
+ this.ended = 0;
11623
+ // How many sources the socket had taken when it last refused queue_full: while as many are held,
11624
+ // the next send waits. Null once none is held.
11625
+ this.cap = null;
11626
+ this._roomWaiters = [];
11622
11627
  this.closed = new Promise((resolve) => this._markClosed = resolve);
11623
11628
  }
11629
+ /** A source ended or the socket closed: wake the send waiting for room. */
11630
+ freed() {
11631
+ this.ended += 1;
11632
+ this.wake();
11633
+ }
11634
+ /** A held source ended or was refused: wake the send waiting for room. */
11635
+ wake() {
11636
+ if (this.held.size === 0) {
11637
+ this.cap = null;
11638
+ }
11639
+ const waiters = this._roomWaiters;
11640
+ this._roomWaiters = [];
11641
+ waiters.forEach((wake) => wake());
11642
+ }
11643
+ get hasRoom() {
11644
+ return this.cap === null || this.held.size < this.cap;
11645
+ }
11646
+ /**
11647
+ * Wait, while the socket is full, until a source ends, it closes or the job is cancelled. The
11648
+ * caller sends in order, so one send waits here and each end lets one go, instead of every
11649
+ * refused source sending its media again at once.
11650
+ */
11651
+ async room(job) {
11652
+ while (!this.hasRoom && this.open && !job.cancelled) {
11653
+ await Promise.race([new Promise((resolve) => this._roomWaiters.push(resolve)), job.whenCancelled]);
11654
+ }
11655
+ }
11624
11656
  get open() {
11625
11657
  return !this._closed && this.ws.readyState === 1;
11626
11658
  }
11627
11659
  markClosed() {
11628
11660
  this._closed = true;
11629
11661
  this._markClosed();
11662
+ this.freed();
11630
11663
  }
11631
11664
  sendJson(message) {
11632
11665
  this.ws.send(JSON.stringify(message));
@@ -11645,6 +11678,8 @@ var PipelineJob = class {
11645
11678
  this._results = new Inbox();
11646
11679
  this._consumed = false;
11647
11680
  this._cancelled = false;
11681
+ // Settles when the job is cancelled, so that a send waiting for room ends at once.
11682
+ this.whenCancelled = new Promise((resolve) => this._markCancelled = resolve);
11648
11683
  this.openSource = null;
11649
11684
  // Stops the job's source on the pipeline, once it was sent.
11650
11685
  this.stopOnPipeline = null;
@@ -11684,6 +11719,7 @@ var PipelineJob = class {
11684
11719
  return;
11685
11720
  }
11686
11721
  this._cancelled = true;
11722
+ this._markCancelled();
11687
11723
  this._results.push(null);
11688
11724
  this.openSource?.inbox.push(CANCELLED);
11689
11725
  this.stopOnPipeline?.();
@@ -11898,6 +11934,16 @@ var PipelineBackend = class {
11898
11934
  const type = message["type"];
11899
11935
  const id = message["id"];
11900
11936
  const source = typeof id === "string" ? socket.sources.get(id) : void 0;
11937
+ if (type === "end") {
11938
+ socket.held.delete(id);
11939
+ socket.accepted.delete(id);
11940
+ socket.freed();
11941
+ } else if (type === "result" && socket.held.has(id)) {
11942
+ socket.accepted.add(id);
11943
+ } else if (type === "error" && message["error"] !== "unknown_command") {
11944
+ socket.held.delete(id);
11945
+ socket.wake();
11946
+ }
11901
11947
  if (type === "send" || type === "credit") {
11902
11948
  const upload = source?.uploads.get(message["member"] ?? null);
11903
11949
  upload?.grant(Number(type === "send" ? message["credit"] : message["bytes"]));
@@ -12056,7 +12102,12 @@ var PipelineBackend = class {
12056
12102
  }
12057
12103
  };
12058
12104
  }
12059
- /** Run the job's source to its end; a live source refused overloaded goes once more on a new socket. */
12105
+ /**
12106
+ * Run the job's source to its end. A live source refused overloaded goes once more on a new socket.
12107
+ * A source refused queue_full goes again once another source on the socket ended, so it may go after
12108
+ * jobs submitted later; with none of the endpoint's other sources open there, or with an upload from
12109
+ * the caller's own stream, which cannot be read twice, the refusal stands.
12110
+ */
12060
12111
  async run(job) {
12061
12112
  let retried = false;
12062
12113
  while (true) {
@@ -12067,9 +12118,9 @@ var PipelineBackend = class {
12067
12118
  const { socket, source } = sent;
12068
12119
  const uploading = source.uploads.size > 0 ? this.upload(socket, source, job) : Promise.resolve();
12069
12120
  let whip = null;
12070
- let overloaded;
12121
+ let refused;
12071
12122
  try {
12072
- overloaded = await this.follow(job, source, () => {
12123
+ refused = await this.follow(job, source, () => {
12073
12124
  if (job.mediaStream !== void 0) {
12074
12125
  const fail = (error) => {
12075
12126
  job.stopOnPipeline?.();
@@ -12087,9 +12138,19 @@ var PipelineBackend = class {
12087
12138
  await uploading;
12088
12139
  await whip?.close();
12089
12140
  }
12090
- if (!overloaded) {
12141
+ if (refused === null) {
12091
12142
  return;
12092
12143
  }
12144
+ if (refused === "queue_full") {
12145
+ if (socket.accepted.size === 0 && socket.ended === source.endedBefore) {
12146
+ throw new PipelineCommandError("queue_full", "the socket refused the source with none of its own open");
12147
+ }
12148
+ if (job.media.some((media) => media.once)) {
12149
+ throw new PipelineCommandError("queue_full", "the socket refused an upload from a stream, which cannot be sent again");
12150
+ }
12151
+ socket.cap = Math.min(socket.accepted.size, socket.cap ?? socket.accepted.size) || null;
12152
+ continue;
12153
+ }
12093
12154
  if (retried || !job.live) {
12094
12155
  throw new PipelineCommandError("overloaded", "the pipeline API has no capacity for another live source");
12095
12156
  }
@@ -12099,16 +12160,23 @@ var PipelineBackend = class {
12099
12160
  }
12100
12161
  /** Send the job's setSource; the caller runs it in submission order. */
12101
12162
  async setSource(job) {
12163
+ if (job.cancelled) {
12164
+ return null;
12165
+ }
12166
+ let socket = await this.ensureSocket();
12167
+ await socket.room(job);
12102
12168
  if (job.cancelled) {
12103
12169
  return null;
12104
12170
  }
12105
12171
  const combined = await combinedMedia(job);
12106
- const socket = await this.ensureSocket();
12172
+ socket = await this.ensureSocket();
12107
12173
  const source = new OpenSource(this.newId("source"));
12108
12174
  if (combined === null) {
12109
12175
  uploadKeys(job).forEach((key) => source.uploads.set(key, new Credit()));
12110
12176
  }
12111
12177
  socket.sources.set(source.id, source);
12178
+ socket.held.add(source.id);
12179
+ source.endedBefore = socket.ended;
12112
12180
  job.openSource = source;
12113
12181
  job.stopOnPipeline = () => {
12114
12182
  if (socket.open) {
@@ -12123,15 +12191,15 @@ var PipelineBackend = class {
12123
12191
  }
12124
12192
  return { socket, source };
12125
12193
  }
12126
- /** Hand the source's predictions to the job until it ends; true when the pipeline API was overloaded. */
12194
+ /** Hand the source's predictions to the job until it ends; the refusal to retry on, overloaded or queue_full. */
12127
12195
  async follow(job, source, onResult) {
12128
12196
  while (true) {
12129
12197
  if (job.cancelled) {
12130
- return false;
12198
+ return null;
12131
12199
  }
12132
12200
  const message = await source.inbox.next();
12133
12201
  if (message === CANCELLED) {
12134
- return false;
12202
+ return null;
12135
12203
  }
12136
12204
  if (message instanceof Error) {
12137
12205
  throw message;
@@ -12145,12 +12213,12 @@ var PipelineBackend = class {
12145
12213
  break;
12146
12214
  case "end":
12147
12215
  if (message["reason"] === "eos") {
12148
- return false;
12216
+ return null;
12149
12217
  }
12150
12218
  throw new PipelineSourceError(source.id, message["reason"] ?? "", message["message"] ?? "");
12151
12219
  case "error":
12152
- if (message["error"] === "overloaded") {
12153
- return true;
12220
+ if (message["error"] === "overloaded" || message["error"] === "queue_full") {
12221
+ return message["error"];
12154
12222
  }
12155
12223
  throw new PipelineCommandError(message["error"] ?? "", message["message"] ?? "");
12156
12224
  case "dropped":
@@ -12294,7 +12362,8 @@ function knownSize(body) {
12294
12362
  return void 0;
12295
12363
  }
12296
12364
  function bodyMedia(body, contentType, size) {
12297
- return { open: async () => body, contentType, size: size ?? knownSize(body) };
12365
+ const once = !(typeof Blob !== "undefined" && body instanceof Blob) && !(body instanceof ArrayBuffer) && !ArrayBuffer.isView(body);
12366
+ return { open: async () => body, contentType, size: size ?? knownSize(body), once };
12298
12367
  }
12299
12368
  function uploadGroup(media) {
12300
12369
  return { group: media.map((m) => ({ upload: { contentType: m.contentType } })) };