@eyepop.ai/eyepop 3.15.1 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8680,6 +8680,7 @@ __export(index_exports, {
8680
8680
  ForwardOperatorType: () => ForwardOperatorType,
8681
8681
  InferenceType: () => InferenceType,
8682
8682
  LoadFromAssetUuidJob: () => LoadFromAssetUuidJob,
8683
+ LoadFromGroupJob: () => LoadFromGroupJob,
8683
8684
  LoadFromJob: () => LoadFromJob,
8684
8685
  LoadMediaStreamJob: () => LoadMediaStreamJob,
8685
8686
  ModelExportStatus: () => ModelExportStatus,
@@ -8693,12 +8694,12 @@ __export(index_exports, {
8693
8694
  PopComponentType: () => PopComponentType,
8694
8695
  PredictionVersion: () => PredictionVersion,
8695
8696
  Semaphore: () => Semaphore,
8696
- SessionStatus: () => SessionStatus,
8697
8697
  Stream: () => Stream,
8698
8698
  TaskMode: () => TaskMode,
8699
8699
  TaskType: () => TaskType,
8700
8700
  TranscodeMode: () => TranscodeMode,
8701
8701
  TransientPopId: () => TransientPopId,
8702
+ UploadGroupJob: () => UploadGroupJob,
8702
8703
  UploadJob: () => UploadJob,
8703
8704
  UserReview: () => UserReview,
8704
8705
  VideoMode: () => VideoMode,
@@ -8709,11 +8710,211 @@ __export(index_exports, {
8709
8710
  WorkerEndpoint: () => WorkerEndpoint,
8710
8711
  WorkflowPhase: () => WorkflowPhase,
8711
8712
  default: () => index_default,
8713
+ encodeMultipartFormData: () => encodeMultipartFormData,
8712
8714
  getBaseUrl: () => getBaseUrl,
8715
+ randomMultipartBoundary: () => randomMultipartBoundary,
8713
8716
  readableStreamFromString: () => readableStreamFromString
8714
8717
  });
8715
8718
  module.exports = __toCommonJS(index_exports);
8716
8719
 
8720
+ // compute/compute_session.ts
8721
+ var SESSION_DEAD = /* @__PURE__ */ new Set(["error" /* ERROR */, "failed" /* FAILED */, "stopped" /* STOPPED */, "pipeline_error" /* PIPELINE_ERROR */, "unknown" /* UNKNOWN */]);
8722
+ var CHECK_FOR_IS_READY_INTERVAL = 250;
8723
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8724
+ function pipelineIdFromSession(session) {
8725
+ if (session.pipeline_uuid) {
8726
+ return session.pipeline_uuid;
8727
+ }
8728
+ const pipeline = session.pipelines?.[0];
8729
+ return pipeline?.id || pipeline?.pipeline_id || null;
8730
+ }
8731
+ function sessionCreateBody(options) {
8732
+ const body = {};
8733
+ if (options.sessionName) {
8734
+ body.session_name = options.sessionName;
8735
+ }
8736
+ if (options.pipelineImage) {
8737
+ body.pipeline_image = options.pipelineImage;
8738
+ }
8739
+ if (options.pipelineVersion) {
8740
+ body.pipeline_version = options.pipelineVersion;
8741
+ }
8742
+ if (options.pop) {
8743
+ body.pop = options.pop;
8744
+ }
8745
+ return Object.keys(body).length > 0 ? JSON.stringify(body) : void 0;
8746
+ }
8747
+ function sessionHealthReady(body, contentType) {
8748
+ if (contentType.includes("application/json")) {
8749
+ const health = JSON.parse(body);
8750
+ if (health && typeof health == "object") {
8751
+ if (health.message && !health.session_status) {
8752
+ return true;
8753
+ }
8754
+ return health.session_status == "running" /* RUNNING */;
8755
+ }
8756
+ }
8757
+ return contentType.includes("text/plain") && body.includes("I'm fine");
8758
+ }
8759
+ function accessTokenValidUntil(session) {
8760
+ if (session.access_token_expires_at) {
8761
+ const expiresAt = Date.parse(session.access_token_expires_at);
8762
+ return Number.isNaN(expiresAt) ? null : expiresAt;
8763
+ }
8764
+ if (session.access_token_expires_in) {
8765
+ return Date.now() + session.access_token_expires_in * 1e3;
8766
+ }
8767
+ return null;
8768
+ }
8769
+ var ComputeSessionClient = class {
8770
+ constructor(options) {
8771
+ this.options = options;
8772
+ }
8773
+ async resolve() {
8774
+ const session = this.options.sessionUuid ? await this.getPermanentComputeSession(this.options.sessionUuid) : await this.getOnDemandComputeSession();
8775
+ const accessToken = this.sessionAccessToken(session);
8776
+ await this.waitUntilReady(session, accessToken);
8777
+ const pipelineId = pipelineIdFromSession(session);
8778
+ if (this.options.pop && !pipelineId) {
8779
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
8780
+ }
8781
+ return {
8782
+ session,
8783
+ pipelineId,
8784
+ accessToken,
8785
+ accessTokenValidUntil: accessTokenValidUntil(session)
8786
+ };
8787
+ }
8788
+ async getOnDemandComputeSession() {
8789
+ let session = null;
8790
+ const sessionsUrl = `${this.options.computeUrl}/v1/sessions`;
8791
+ const headers = {
8792
+ Authorization: await this.options.authorizationHeader()
8793
+ };
8794
+ let sessions;
8795
+ if (!this.options.pop) {
8796
+ this.options.requestLogger?.debug("fetching sessions from: %s", sessionsUrl);
8797
+ const response = await this.options.httpClient.fetch(sessionsUrl, {
8798
+ headers
8799
+ });
8800
+ if (response.status == 404) {
8801
+ sessions = [];
8802
+ } else if (response.status != 200) {
8803
+ const message = await response.text();
8804
+ throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
8805
+ } else {
8806
+ const response_content = await response.json();
8807
+ sessions = response_content;
8808
+ }
8809
+ if (sessions.length > 0) {
8810
+ this.options.logger?.debug(`User has ${sessions.length} sessions, inspecting for usable session`);
8811
+ for (let s of sessions) {
8812
+ if (!SESSION_DEAD.has(s.session_status) && !s.persistent && (!this.options.sessionName || s.session_name === this.options.sessionName)) {
8813
+ session = s;
8814
+ this.options.logger?.debug(`Use active session ${s.session_uuid} with pipeline ${pipelineIdFromSession(s)}`);
8815
+ break;
8816
+ }
8817
+ }
8818
+ }
8819
+ }
8820
+ if (!session) {
8821
+ this.options.requestLogger?.debug("creating a new session at: %s", sessionsUrl);
8822
+ const createBody = sessionCreateBody(this.options);
8823
+ const createHeaders = {
8824
+ ...headers,
8825
+ ...createBody ? { "Content-Type": "application/json" } : {}
8826
+ };
8827
+ const request = {
8828
+ headers: createHeaders,
8829
+ method: "POST"
8830
+ };
8831
+ if (createBody) {
8832
+ request.body = createBody;
8833
+ }
8834
+ const query = "wait=true";
8835
+ const response = await this.options.httpClient.fetch(`${sessionsUrl}?${query}`, request);
8836
+ if (response.status != 200) {
8837
+ const message = await response.text();
8838
+ throw new Error(`Unexpected status ${response.status} creating a compute session: ${message}`);
8839
+ } else {
8840
+ sessions = await response.json();
8841
+ }
8842
+ if (sessions.length == 0) {
8843
+ throw new Error("Unexpected, no compute session after attempt to create one");
8844
+ }
8845
+ session = sessions[0] || null;
8846
+ }
8847
+ if (!session) {
8848
+ throw new Error("unexpected undefined session");
8849
+ }
8850
+ if (this.options.pop && !pipelineIdFromSession(session)) {
8851
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
8852
+ }
8853
+ return session;
8854
+ }
8855
+ async getPermanentComputeSession(sessionUuid) {
8856
+ let session = null;
8857
+ const sessionUrl = `${this.options.computeUrl}/v1/sessions/${sessionUuid}`;
8858
+ this.options.requestLogger?.debug("fetching session from: %s", sessionUrl);
8859
+ const headers = {
8860
+ Authorization: await this.options.authorizationHeader()
8861
+ };
8862
+ let response = await this.options.httpClient.fetch(sessionUrl, {
8863
+ headers
8864
+ });
8865
+ if (response.status == 404) {
8866
+ throw new Error(`Unexpected status ${response.status} compute sessions ${sessionUuid} not found`);
8867
+ } else if (response.status != 200) {
8868
+ const message = await response.text();
8869
+ throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
8870
+ } else {
8871
+ const response_content = await response.json();
8872
+ session = response_content;
8873
+ }
8874
+ return session;
8875
+ }
8876
+ sessionAccessToken(session) {
8877
+ if (!session.access_token) {
8878
+ throw new Error(`Compute session ${session.session_uuid} did not provide an access token`);
8879
+ }
8880
+ return session.access_token;
8881
+ }
8882
+ async waitUntilReady(session, accessToken) {
8883
+ let isReady = false;
8884
+ const deadline = Date.now() + this.options.readyTimeoutMs;
8885
+ while (!isReady) {
8886
+ const health_url = `${session.session_endpoint}/health`;
8887
+ const response = await this.options.httpClient.fetch(health_url, {
8888
+ headers: {
8889
+ Authorization: `Bearer ${accessToken}`,
8890
+ Accept: "application/json"
8891
+ }
8892
+ });
8893
+ if (response.status == 200) {
8894
+ const contentType = response.headers.get("content-type") || "";
8895
+ const body = await response.text();
8896
+ try {
8897
+ isReady = sessionHealthReady(body, contentType);
8898
+ } catch (error) {
8899
+ this.options.logger?.debug(`session ${session.session_uuid} health check returned invalid JSON, wait and poll for update`);
8900
+ }
8901
+ if (!isReady && Date.now() > deadline) {
8902
+ throw new Error(`Created session ${session.session_uuid} did not become healthy after ${this.options.readyTimeoutMs / 1e3} seconds`);
8903
+ }
8904
+ if (!isReady) {
8905
+ this.options.logger?.debug(`session ${session.session_uuid} health check not ready, wait and poll for update`);
8906
+ await sleep(CHECK_FOR_IS_READY_INTERVAL);
8907
+ }
8908
+ } else if (Date.now() > deadline) {
8909
+ throw new Error(`Created session ${session.session_uuid} did not become healthy after ${this.options.readyTimeoutMs / 1e3} seconds`);
8910
+ } else {
8911
+ this.options.logger?.debug(`session ${session.session_uuid} health check status ${response.status}, wait and poll for update`);
8912
+ await sleep(CHECK_FOR_IS_READY_INTERVAL);
8913
+ }
8914
+ }
8915
+ }
8916
+ };
8917
+
8717
8918
  // types.ts
8718
8919
  var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
8719
8920
  EndpointState2["Idle"] = "Idle";
@@ -8726,6 +8927,7 @@ var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
8726
8927
  })(EndpointState || {});
8727
8928
 
8728
8929
  // streaming.ts
8930
+ var import_uuid = require("uuid");
8729
8931
  var Stream = class _Stream {
8730
8932
  constructor(iterator, controller) {
8731
8933
  this.iterator = iterator;
@@ -8906,6 +9108,76 @@ function readableStreamFromString(s) {
8906
9108
  }
8907
9109
  });
8908
9110
  }
9111
+ function randomMultipartBoundary() {
9112
+ return `----formdata-boundary-${(0, import_uuid.v4)()}`;
9113
+ }
9114
+ function toPartStream(content) {
9115
+ if (content instanceof ReadableStream) {
9116
+ return content;
9117
+ }
9118
+ if (content instanceof Blob) {
9119
+ return content.stream();
9120
+ }
9121
+ const bytes = ArrayBuffer.isView(content) ? new Uint8Array(content.buffer, content.byteOffset, content.byteLength) : new Uint8Array(content);
9122
+ return new ReadableStream({
9123
+ start(controller) {
9124
+ controller.enqueue(bytes);
9125
+ controller.close();
9126
+ }
9127
+ });
9128
+ }
9129
+ async function cancelMultipartContent(content, reason) {
9130
+ if (content instanceof ReadableStream) {
9131
+ await content.cancel(reason);
9132
+ }
9133
+ }
9134
+ function encodeMultipartFormData(parts, boundary) {
9135
+ const encoder = new TextEncoder();
9136
+ let partIndex = 0;
9137
+ let reader = null;
9138
+ return new ReadableStream({
9139
+ async pull(controller) {
9140
+ if (reader) {
9141
+ const { done, value } = await reader.read();
9142
+ if (!done) {
9143
+ controller.enqueue(value);
9144
+ return;
9145
+ }
9146
+ reader = null;
9147
+ controller.enqueue(encoder.encode("\r\n"));
9148
+ partIndex++;
9149
+ }
9150
+ if (partIndex >= parts.length) {
9151
+ controller.enqueue(encoder.encode(`--${boundary}--\r
9152
+ `));
9153
+ controller.close();
9154
+ return;
9155
+ }
9156
+ const part = parts[partIndex];
9157
+ let header = `--${boundary}\r
9158
+ Content-Disposition: form-data; name="${part.name}"\r
9159
+ `;
9160
+ if (part.contentType) {
9161
+ header += `Content-Type: ${part.contentType}\r
9162
+ `;
9163
+ }
9164
+ header += "\r\n";
9165
+ controller.enqueue(encoder.encode(header));
9166
+ if (typeof part.content === "string") {
9167
+ controller.enqueue(encoder.encode(part.content));
9168
+ controller.enqueue(encoder.encode("\r\n"));
9169
+ partIndex++;
9170
+ } else {
9171
+ reader = toPartStream(part.content).getReader();
9172
+ }
9173
+ },
9174
+ async cancel(reason) {
9175
+ await reader?.cancel(reason);
9176
+ const firstUnstartedPart = reader ? partIndex + 1 : partIndex;
9177
+ await Promise.all(parts.slice(firstUnstartedPart).map((part) => cancelMultipartContent(part.content, reason)));
9178
+ }
9179
+ });
9180
+ }
8909
9181
 
8910
9182
  // worker/worker_types.ts
8911
9183
  var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
@@ -8963,7 +9235,7 @@ var ContourType = /* @__PURE__ */ ((ContourType2) => {
8963
9235
  })(ContourType || {});
8964
9236
 
8965
9237
  // worker/webrtc_whip.ts
8966
- var import_uuid = require("uuid");
9238
+ var import_uuid2 = require("uuid");
8967
9239
 
8968
9240
  // worker/webrtc_base.ts
8969
9241
  var import_path = __toESM(require("path"));
@@ -9238,7 +9510,7 @@ var WebrtcBase = class _WebrtcBase {
9238
9510
  // worker/webrtc_whip.ts
9239
9511
  var WebrtcWhip = class extends WebrtcBase {
9240
9512
  constructor(stream, getSession, client, is_pipeline_direct, requestLogger) {
9241
- super(getSession, client, (0, import_uuid.v4)().toString(), "liveIngress/whip", is_pipeline_direct, requestLogger);
9513
+ super(getSession, client, (0, import_uuid2.v4)().toString(), "liveIngress/whip", is_pipeline_direct, requestLogger);
9242
9514
  this._stream = stream;
9243
9515
  }
9244
9516
  async start() {
@@ -9633,6 +9905,111 @@ var LoadFromJob = class extends AbstractJob {
9633
9905
  });
9634
9906
  }
9635
9907
  };
9908
+ var UploadGroupJob = class extends AbstractJob {
9909
+ get [Symbol.toStringTag]() {
9910
+ return "uploadGroupJob";
9911
+ }
9912
+ constructor(sources, params, getSession, client, requestLogger) {
9913
+ super(params, getSession, client, requestLogger);
9914
+ if (sources.length === 0) {
9915
+ throw new Error("upload group requires at least one source");
9916
+ }
9917
+ this._sources = sources;
9918
+ }
9919
+ async startJob() {
9920
+ const session = await this._getSession();
9921
+ if (!session.baseUrl) {
9922
+ return Promise.reject(new Error("session.baseUrl must not be null"));
9923
+ }
9924
+ const queryParams = new URLSearchParams({
9925
+ mode: "queue",
9926
+ processing: "sync"
9927
+ });
9928
+ if (this._version !== void 0) {
9929
+ queryParams.set("version", this._version.toString());
9930
+ }
9931
+ if (this._params.motionDetect) {
9932
+ for (const [key, value] of Object.entries(this._params.motionDetect)) {
9933
+ queryParams.set(key, value);
9934
+ }
9935
+ }
9936
+ const postUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?${queryParams.toString()}`;
9937
+ const parts = [];
9938
+ if (this._params.componentParams) {
9939
+ parts.push({ name: "params", contentType: "application/json", content: JSON.stringify(this._params.componentParams) });
9940
+ }
9941
+ if (this._params.roi) {
9942
+ parts.push({ name: "roi", contentType: "application/json", content: JSON.stringify(this._params.roi) });
9943
+ }
9944
+ if (this._params.fps) {
9945
+ parts.push({ name: "fps", contentType: "application/json", content: JSON.stringify(this._params.fps) });
9946
+ }
9947
+ for (const source of this._sources) {
9948
+ parts.push({ name: "file", contentType: source.mimeType, content: source.stream });
9949
+ }
9950
+ const boundary = randomMultipartBoundary();
9951
+ const headers = {
9952
+ ...session.authenticationHeaders(),
9953
+ Accept: "application/jsonl",
9954
+ "Content-Type": `multipart/form-data; boundary=${boundary}`
9955
+ };
9956
+ return await this._client.fetch(postUrl, {
9957
+ headers,
9958
+ method: "POST",
9959
+ body: encodeMultipartFormData(parts, boundary),
9960
+ signal: this._controller.signal,
9961
+ // @ts-ignore
9962
+ duplex: "half",
9963
+ eyepop: { responseStreaming: true }
9964
+ });
9965
+ }
9966
+ };
9967
+ var LoadFromGroupJob = class extends AbstractJob {
9968
+ get [Symbol.toStringTag]() {
9969
+ return "loadFromGroupJob";
9970
+ }
9971
+ constructor(urls, params, getSession, client, requestLogger) {
9972
+ super(params, getSession, client, requestLogger);
9973
+ if (urls.length === 0) {
9974
+ throw new Error("load from group requires at least one url");
9975
+ }
9976
+ this._urls = urls;
9977
+ }
9978
+ async startJob() {
9979
+ const session = await this._getSession();
9980
+ if (!session.baseUrl) {
9981
+ return Promise.reject(new Error("session.baseUrl must not be null"));
9982
+ }
9983
+ const body = {
9984
+ sourceType: "GROUP",
9985
+ sources: this._urls.map((url) => ({ sourceType: "URL", url })),
9986
+ version: this._version
9987
+ };
9988
+ if (this._params.componentParams) {
9989
+ body["params"] = this._params.componentParams;
9990
+ }
9991
+ if (this._params.roi) {
9992
+ body["roi"] = this._params.roi;
9993
+ }
9994
+ if (this._params.motionDetect) {
9995
+ Object.assign(body, this._params.motionDetect);
9996
+ }
9997
+ const headers = {
9998
+ ...session.authenticationHeaders(),
9999
+ Accept: "application/jsonl",
10000
+ "Content-Type": "application/json"
10001
+ };
10002
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
10003
+ return await this._client.fetch(patchUrl, {
10004
+ headers,
10005
+ method: "PATCH",
10006
+ body: JSON.stringify(body),
10007
+ signal: this._controller.signal,
10008
+ // @ts-ignore
10009
+ eyepop: { responseStreaming: true }
10010
+ });
10011
+ }
10012
+ };
9636
10013
  var LoadFromAssetUuidJob = class extends AbstractJob {
9637
10014
  constructor(assetUuid, params, getSession, client, requestLogger) {
9638
10015
  super(params, getSession, client, requestLogger);
@@ -9799,8 +10176,9 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9799
10176
  };
9800
10177
  } else {
9801
10178
  class NodeHttpClient {
9802
- constructor(agent, logger) {
10179
+ constructor(agent, fetchFn, logger) {
9803
10180
  this._agent = agent;
10181
+ this._fetch = fetchFn;
9804
10182
  this._logger = logger;
9805
10183
  }
9806
10184
  async fetch(input, init) {
@@ -9808,7 +10186,7 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9808
10186
  init.dispatcher = this._agent;
9809
10187
  const logLine = logLineFromRequest(input, init);
9810
10188
  this._logger.debug(`Node fetch() BEFORE ${logLine}`);
9811
- const response = await fetch(input, init);
10189
+ const response = await this._fetch(input, init);
9812
10190
  this._logger.debug(`AFTER ${logLine} -> ${response.status}`);
9813
10191
  return response;
9814
10192
  }
@@ -9828,7 +10206,7 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9828
10206
  pipelining: 0
9829
10207
  });
9830
10208
  logger.debug("Using node fetch with undici agent as http client");
9831
- return new NodeHttpClient(agent, logger);
10209
+ return new NodeHttpClient(agent, undici.fetch, logger);
9832
10210
  };
9833
10211
  }
9834
10212
 
@@ -10233,20 +10611,6 @@ var TransientPopId = /* @__PURE__ */ ((TransientPopId2) => {
10233
10611
  })(TransientPopId || {});
10234
10612
 
10235
10613
  // worker/worker_endpoint.ts
10236
- var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => {
10237
- SessionStatus2["UNKNOWN"] = "unknown";
10238
- SessionStatus2["PENDING"] = "pending";
10239
- SessionStatus2["RUNNING"] = "running";
10240
- SessionStatus2["STOPPED"] = "stopped";
10241
- SessionStatus2["FAILED"] = "failed";
10242
- SessionStatus2["ERROR"] = "error";
10243
- SessionStatus2["PIPELINE_CREATING"] = "pipeline_creating";
10244
- SessionStatus2["PIPELINE_OK"] = "pipeline_ok";
10245
- SessionStatus2["PIPELINE_CHECKING"] = "pipeline_checking";
10246
- SessionStatus2["PIPELINE_ERROR"] = "pipeline_error";
10247
- return SessionStatus2;
10248
- })(SessionStatus || {});
10249
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10250
10614
  var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10251
10615
  constructor(options) {
10252
10616
  super(options);
@@ -10254,7 +10618,10 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10254
10618
  this._baseUrl = null;
10255
10619
  this._pipelineId = null;
10256
10620
  this._popName = null;
10621
+ this._sessionAccessToken = null;
10622
+ this._sessionAccessTokenValidUntil = null;
10257
10623
  this._pipeline = null;
10624
+ this._pipelineCreatePromise = null;
10258
10625
  }
10259
10626
  options() {
10260
10627
  return this._options;
@@ -10281,20 +10648,29 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10281
10648
  this._baseUrl = null;
10282
10649
  }
10283
10650
  async session() {
10284
- const session = await super.session();
10285
- if (this._baseUrl == null || this._pipelineId == null) {
10651
+ let session = null;
10652
+ if (this._baseUrl == null) {
10286
10653
  await this.reconnect();
10287
- if (this._baseUrl == null || this._pipelineId == null) {
10654
+ if (this._baseUrl == null) {
10288
10655
  return Promise.reject("endpoint not connected");
10289
10656
  }
10290
10657
  }
10658
+ if (this._sessionAccessToken) {
10659
+ session = {
10660
+ eyepopUrl: this.options().eyepopUrl,
10661
+ accessToken: this._sessionAccessToken,
10662
+ validUntil: this._sessionAccessTokenValidUntil || Number.MAX_VALUE
10663
+ };
10664
+ } else {
10665
+ session = await super.session();
10666
+ }
10291
10667
  return {
10292
10668
  eyepopUrl: session.eyepopUrl,
10293
10669
  accessToken: session.accessToken,
10294
10670
  validUntil: session.validUntil,
10295
10671
  popId: this.options().popId,
10296
10672
  baseUrl: this._baseUrl,
10297
- pipelineId: this._pipelineId,
10673
+ pipelineId: this._pipelineId || void 0,
10298
10674
  authenticationHeaders: () => this._authenticationHeaders(session)
10299
10675
  };
10300
10676
  }
@@ -10304,11 +10680,14 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10304
10680
  pop() {
10305
10681
  if (this._pipeline) {
10306
10682
  return this._pipeline.pop || null;
10307
- } else {
10308
- return null;
10309
10683
  }
10684
+ return this.options().pop || null;
10310
10685
  }
10311
10686
  async changePop(pop) {
10687
+ if (this.options().popId == "transient" /* Transient */) {
10688
+ await this.changeTransientPop(pop);
10689
+ return;
10690
+ }
10312
10691
  const client = this._client;
10313
10692
  const baseUrl = this._baseUrl;
10314
10693
  if (!baseUrl || !client) {
@@ -10331,6 +10710,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10331
10710
  const message = await response.text();
10332
10711
  throw new Error(`Unexpected status ${response.status}: ${message}`);
10333
10712
  }
10713
+ this.options().pop = pop;
10334
10714
  if (this._pipeline) {
10335
10715
  this._pipeline.pop = pop;
10336
10716
  } else {
@@ -10345,6 +10725,10 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10345
10725
  _authenticationHeaders(session) {
10346
10726
  return this._options.auth.isLocal !== void 0 ? {} : { Authorization: `Bearer ${session.accessToken}` };
10347
10727
  }
10728
+ /**
10729
+ * @deprecated Pop ids are only retained for backwards compatibility.
10730
+ * Configure transient sessions with `WorkerOptions.pop`.
10731
+ */
10348
10732
  popId() {
10349
10733
  return this.options().popId || null;
10350
10734
  }
@@ -10389,7 +10773,116 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10389
10773
  return Promise.reject("unknown source type");
10390
10774
  }
10391
10775
  }
10776
+ async uploadGroup(paths, params = {}) {
10777
+ if (paths.length === 0) {
10778
+ throw new Error("upload group requires at least one path");
10779
+ }
10780
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10781
+ throw new Error("endpoint not connected, use connect()");
10782
+ }
10783
+ await this._limit.acquire();
10784
+ try {
10785
+ this.updateState();
10786
+ const sources = await Promise.all(
10787
+ paths.map(async (path2) => {
10788
+ let streamSource;
10789
+ if (this._options.platformSupport?.resolvePath) {
10790
+ streamSource = await this._options.platformSupport.resolvePath({ path: path2 }, this._logger);
10791
+ } else {
10792
+ streamSource = await resolvePath({ path: path2 }, this._logger);
10793
+ }
10794
+ return { stream: streamSource.stream, mimeType: streamSource.mimeType };
10795
+ })
10796
+ );
10797
+ const job = new UploadGroupJob(
10798
+ sources,
10799
+ params,
10800
+ async () => this.session(),
10801
+ this._client,
10802
+ this._requestLogger
10803
+ );
10804
+ return job.start(
10805
+ () => {
10806
+ this.jobDone(job);
10807
+ },
10808
+ (statusCode) => {
10809
+ this.jobStatus(job, statusCode);
10810
+ }
10811
+ );
10812
+ } catch (e) {
10813
+ this._limit.release();
10814
+ throw e;
10815
+ }
10816
+ }
10817
+ async uploadStreamGroup(streams, mimeTypes, params = {}) {
10818
+ if (streams.length === 0) {
10819
+ throw new Error("upload stream group requires at least one stream");
10820
+ }
10821
+ if (mimeTypes !== void 0 && mimeTypes.length !== streams.length) {
10822
+ throw new Error("mimeTypes must have the same length as streams");
10823
+ }
10824
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10825
+ throw new Error("endpoint not connected, use connect()");
10826
+ }
10827
+ await this._limit.acquire();
10828
+ try {
10829
+ this.updateState();
10830
+ const sources = streams.map((stream, i) => ({
10831
+ stream,
10832
+ mimeType: mimeTypes?.[i] ?? null
10833
+ }));
10834
+ const job = new UploadGroupJob(
10835
+ sources,
10836
+ params,
10837
+ async () => this.session(),
10838
+ this._client,
10839
+ this._requestLogger
10840
+ );
10841
+ return job.start(
10842
+ () => {
10843
+ this.jobDone(job);
10844
+ },
10845
+ (statusCode) => {
10846
+ this.jobStatus(job, statusCode);
10847
+ }
10848
+ );
10849
+ } catch (e) {
10850
+ this._limit.release();
10851
+ throw e;
10852
+ }
10853
+ }
10854
+ async loadFromGroup(urls, params = {}) {
10855
+ if (urls.length === 0) {
10856
+ throw new Error("load from group requires at least one url");
10857
+ }
10858
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10859
+ throw new Error("endpoint not connected, use connect()");
10860
+ }
10861
+ await this._limit.acquire();
10862
+ try {
10863
+ this.updateState();
10864
+ const job = new LoadFromGroupJob(
10865
+ urls,
10866
+ params,
10867
+ async () => this.session(),
10868
+ this._client,
10869
+ this._requestLogger
10870
+ );
10871
+ return job.start(
10872
+ () => {
10873
+ this.jobDone(job);
10874
+ },
10875
+ (statusCode) => {
10876
+ this.jobStatus(job, statusCode);
10877
+ }
10878
+ );
10879
+ } catch (e) {
10880
+ this._limit.release();
10881
+ throw e;
10882
+ }
10883
+ }
10392
10884
  async uploadFile(source, params) {
10885
+ await this.ensureTransientPipelineStarted();
10393
10886
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10394
10887
  return Promise.reject("endpoint not connected, use connect()");
10395
10888
  }
@@ -10422,6 +10915,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10422
10915
  }
10423
10916
  }
10424
10917
  async uploadStream(source, params) {
10918
+ await this.ensureTransientPipelineStarted();
10425
10919
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10426
10920
  return Promise.reject("endpoint not connected, use connect()");
10427
10921
  }
@@ -10454,6 +10948,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10454
10948
  }
10455
10949
  }
10456
10950
  async uploadPath(source, params) {
10951
+ await this.ensureTransientPipelineStarted();
10457
10952
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10458
10953
  throw new Error("endpoint not connected, use connect()");
10459
10954
  }
@@ -10500,6 +10995,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10500
10995
  params
10501
10996
  );
10502
10997
  }
10998
+ await this.ensureTransientPipelineStarted();
10503
10999
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10504
11000
  throw new Error("endpoint not connected, use connect()");
10505
11001
  }
@@ -10529,6 +11025,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10529
11025
  }
10530
11026
  }
10531
11027
  async loadFromAssetUuid(source, params) {
11028
+ await this.ensureTransientPipelineStarted();
10532
11029
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10533
11030
  throw new Error("endpoint not connected, use connect()");
10534
11031
  }
@@ -10558,6 +11055,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10558
11055
  }
10559
11056
  }
10560
11057
  async loadMediaStream(source, params) {
11058
+ await this.ensureTransientPipelineStarted();
10561
11059
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10562
11060
  throw new Error("endpoint not connected, use connect()");
10563
11061
  }
@@ -10600,7 +11098,9 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10600
11098
  async reconnect() {
10601
11099
  if (this.options().popId == "transient" /* Transient */) {
10602
11100
  await this.getComputeSession();
10603
- this._pipelineId = await this.startTransientPipeline();
11101
+ if (this.options().pop) {
11102
+ this.setTransientPipelinePop();
11103
+ }
10604
11104
  } else if (this.options().sessionUuid) {
10605
11105
  await this.getComputeSession();
10606
11106
  } else {
@@ -10697,120 +11197,41 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10697
11197
  }
10698
11198
  }
10699
11199
  }
11200
+ sessionReadyTimeoutMs() {
11201
+ const seconds = this.options().sessionReadyTimeoutSeconds;
11202
+ return typeof seconds == "number" && Number.isFinite(seconds) && seconds > 0 ? seconds * 1e3 : _WorkerEndpoint.WAIT_FOR_IS_READY;
11203
+ }
10700
11204
  async getComputeSession() {
10701
11205
  if (!this._client) {
10702
11206
  throw new Error("endpoint not initialized");
10703
11207
  }
10704
- let session;
10705
- let headers;
10706
- const sessionUuid = this.options().sessionUuid;
10707
- if (sessionUuid) {
10708
- [session, headers] = await this.getPermanentComputeSession(this._client, sessionUuid);
10709
- } else {
10710
- [session, headers] = await this.getOnDemandComputeSession(this._client);
10711
- }
10712
- let isReady = false;
10713
- const deadline = Date.now() + _WorkerEndpoint.WAIT_FOR_IS_READY;
10714
- while (!isReady) {
10715
- const health_url = `${session.session_endpoint}/health`;
10716
- const response = await this._client.fetch(health_url, {
10717
- headers
10718
- });
10719
- if (response.status < 300) {
10720
- isReady = true;
10721
- } else if (Date.now() > deadline) {
10722
- throw new Error(`Created session ${session.session_uuid} did not become healthy after ${_WorkerEndpoint.WAIT_FOR_IS_READY / 1e3} seconds`);
10723
- } else {
10724
- this._logger.debug(`session ${session.session_uuid} health check status ${response.status}, wait and poll for update`);
10725
- await sleep(_WorkerEndpoint.CHECK_FOR_IS_READY_INTERVAL);
10726
- }
10727
- }
10728
- this._baseUrl = session.session_endpoint;
10729
- this._pipeline = null;
10730
- if (session.pipeline_uuid) {
10731
- this._pipelineId = session.pipeline_uuid;
10732
- } else if (session.pipelines) {
10733
- this._pipelineId = session.pipelines[0].pipeline_id;
10734
- } else {
10735
- this._pipelineId = null;
10736
- }
10737
- }
10738
- async getOnDemandComputeSession(httpClient) {
10739
- let session = null;
10740
- const sessionsUrl = `${this.eyepopUrl()}/v1/sessions`;
10741
- this._requestLogger.debug("fetching sessions from: %s", sessionsUrl);
10742
- const headers = {
10743
- Authorization: await this.authorizationHeader()
10744
- };
10745
- let response = await httpClient.fetch(sessionsUrl, {
10746
- headers
10747
- });
10748
- let sessions;
10749
- if (response.status == 404) {
10750
- sessions = [];
10751
- } else if (response.status != 200) {
11208
+ let resolved;
11209
+ try {
11210
+ resolved = await new ComputeSessionClient({
11211
+ computeUrl: this.eyepopUrl(),
11212
+ httpClient: this._client,
11213
+ authorizationHeader: () => this.computeAuthorizationHeader(),
11214
+ sessionUuid: this.options().sessionUuid,
11215
+ sessionName: this.options().sessionName,
11216
+ pipelineImage: this.options().pipelineImage,
11217
+ pipelineVersion: this.options().pipelineVersion,
11218
+ pop: this.options().pop,
11219
+ readyTimeoutMs: this.sessionReadyTimeoutMs(),
11220
+ logger: this._logger,
11221
+ requestLogger: this._requestLogger
11222
+ }).resolve();
11223
+ } catch (error) {
10752
11224
  this.updateState("Error" /* Error */);
10753
- const message = await response.text();
10754
- throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
10755
- } else {
10756
- const response_content = await response.json();
10757
- sessions = response_content;
10758
- }
10759
- if (sessions.length > 0) {
10760
- this._logger.debug(`User has ${sessions.length} sessions, inspecting for usable session`);
10761
- for (let s of sessions) {
10762
- if (!_WorkerEndpoint.SESSION_DEAD.has(s.session_status) && !s.persistent) {
10763
- session = s;
10764
- this._logger.debug(`Use active session ${s.session_uuid} with pipeline ${s.pipeline_uuid}`);
10765
- break;
10766
- }
10767
- }
11225
+ throw error;
10768
11226
  }
10769
- if (!session) {
10770
- this._requestLogger.debug("creating a new session at: %s", sessionsUrl);
10771
- response = await httpClient.fetch(`${sessionsUrl}?wait=true`, {
10772
- headers,
10773
- method: "POST"
10774
- });
10775
- if (response.status != 200) {
10776
- this.updateState("Error" /* Error */);
10777
- const message = await response.text();
10778
- throw new Error(`Unexpected status ${response.status} creating a compute session: ${message}`);
10779
- } else {
10780
- sessions = await response.json();
10781
- }
10782
- if (sessions.length == 0) {
10783
- throw new Error("Unexpected, no compute session after attempt to create one");
10784
- }
10785
- session = sessions[0] || null;
10786
- }
10787
- if (!session) {
10788
- throw new Error("unexpected undefined session");
10789
- }
10790
- return [session, headers];
10791
- }
10792
- async getPermanentComputeSession(httpClient, sessionUuid) {
10793
- let session = null;
10794
- const sessionUrl = `${this.eyepopUrl()}/v1/sessions/${sessionUuid}`;
10795
- this._requestLogger.debug("fetching session from: %s", sessionUrl);
10796
- const headers = {
10797
- Authorization: await this.authorizationHeader()
10798
- };
10799
- let response = await httpClient.fetch(sessionUrl, {
10800
- headers
10801
- });
10802
- if (response.status == 404) {
10803
- this.updateState("Error" /* Error */);
10804
- throw new Error(`Unexpected status ${response.status} compute sessions ${sessionUuid} not found`);
10805
- } else if (response.status != 200) {
10806
- this.updateState("Error" /* Error */);
10807
- const message = await response.text();
10808
- throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
10809
- } else {
10810
- const response_content = await response.json();
10811
- session = response_content;
11227
+ this._sessionAccessToken = resolved.accessToken;
11228
+ this._sessionAccessTokenValidUntil = resolved.accessTokenValidUntil;
11229
+ this._baseUrl = resolved.session.session_endpoint;
11230
+ this._pipeline = null;
11231
+ this._pipelineId = resolved.pipelineId;
11232
+ if (this.options().pop) {
11233
+ this.setTransientPipelinePop();
10812
11234
  }
10813
- return [session, headers];
10814
11235
  }
10815
11236
  async startTransientPipeline() {
10816
11237
  const client = this._client;
@@ -10819,12 +11240,12 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10819
11240
  throw new Error("endpoint not initialized");
10820
11241
  }
10821
11242
  let pop;
10822
- if (this._pipeline && this._pipeline.pop) {
11243
+ if (this.options().pop) {
11244
+ pop = this.options().pop;
11245
+ } else if (this._pipeline && this._pipeline.pop) {
10823
11246
  pop = this._pipeline.pop;
10824
11247
  } else {
10825
- pop = {
10826
- components: []
10827
- };
11248
+ throw new Error("Cannot start a transient pipeline without a pop");
10828
11249
  }
10829
11250
  const body = {
10830
11251
  pop,
@@ -10836,8 +11257,9 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10836
11257
  videoOutput: "no_output"
10837
11258
  };
10838
11259
  const post_url = `${baseUrl.replace(/\/+$/, "")}/pipelines`;
10839
- let response = await this.fetchWithRetry(async () => {
11260
+ const response = await this.fetchWithRetry(async () => {
10840
11261
  let headers = {
11262
+ Accept: "application/json",
10841
11263
  "Content-Type": "application/json",
10842
11264
  Authorization: await this.authorizationHeader()
10843
11265
  };
@@ -10849,15 +11271,116 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10849
11271
  });
10850
11272
  if (response.status != 200) {
10851
11273
  const message = await response.text();
10852
- throw new Error(`Unexpected status ${response.status}: ${message}`);
11274
+ throw new Error(`Unexpected status ${response.status} starting transient pipeline: ${message.slice(0, 300)}`);
10853
11275
  }
10854
11276
  const result = await response.json();
10855
11277
  return result["id"];
10856
11278
  }
11279
+ async ensureTransientPipelineStarted() {
11280
+ if (this.options().popId != "transient" /* Transient */ || this._pipelineId) {
11281
+ return;
11282
+ }
11283
+ if (!this.options().pop) {
11284
+ return;
11285
+ }
11286
+ if (!this._baseUrl) {
11287
+ await this.reconnect();
11288
+ }
11289
+ if (this._pipelineId) {
11290
+ return;
11291
+ }
11292
+ if (this.options().pop) {
11293
+ await this.reconnect();
11294
+ if (!this._pipelineId) {
11295
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
11296
+ }
11297
+ return;
11298
+ }
11299
+ if (!this._pipelineCreatePromise) {
11300
+ this._pipelineCreatePromise = this.startTransientPipeline();
11301
+ }
11302
+ try {
11303
+ this._pipelineId = await this._pipelineCreatePromise;
11304
+ this.setTransientPipelinePop();
11305
+ } finally {
11306
+ this._pipelineCreatePromise = null;
11307
+ }
11308
+ }
11309
+ async changeTransientPop(pop) {
11310
+ if (!this._client) {
11311
+ throw new Error("endpoint not initialized");
11312
+ }
11313
+ const previousPop = this.options().pop;
11314
+ const previousBaseUrl = this._baseUrl;
11315
+ const previousSessionAccessToken = this._sessionAccessToken;
11316
+ const previousSessionAccessTokenValidUntil = this._sessionAccessTokenValidUntil;
11317
+ if (this._pipelineId) {
11318
+ await this.deleteTransientPipeline(this._pipelineId);
11319
+ this._pipelineId = null;
11320
+ }
11321
+ this._pipeline = null;
11322
+ try {
11323
+ this.options().pop = pop;
11324
+ await this.getComputeSession();
11325
+ } catch (error) {
11326
+ this.options().pop = previousPop;
11327
+ this._pipeline = null;
11328
+ this._pipelineId = null;
11329
+ this._baseUrl = previousPop ? null : previousBaseUrl;
11330
+ this._sessionAccessToken = previousSessionAccessToken;
11331
+ this._sessionAccessTokenValidUntil = previousSessionAccessTokenValidUntil;
11332
+ throw error;
11333
+ }
11334
+ if (!this._pipelineId) {
11335
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
11336
+ }
11337
+ this.setTransientPipelinePop();
11338
+ }
11339
+ async deleteTransientPipeline(pipelineId) {
11340
+ if (!this._client || !this._baseUrl) {
11341
+ return;
11342
+ }
11343
+ const deleteUrl = `${this._baseUrl}/pipelines/${pipelineId}`;
11344
+ const headers = {
11345
+ Authorization: await this.authorizationHeader()
11346
+ };
11347
+ let response = await this._client.fetch(deleteUrl, {
11348
+ method: "DELETE",
11349
+ headers
11350
+ });
11351
+ if (response.status >= 300) {
11352
+ const message = await response.text();
11353
+ this._logger.info(`stopping pipeline failed, status ${response.status}: ${message}`);
11354
+ }
11355
+ }
11356
+ async authorizationHeader() {
11357
+ if (this._sessionAccessToken) {
11358
+ return `Bearer ${this._sessionAccessToken}`;
11359
+ }
11360
+ return super.authorizationHeader();
11361
+ }
11362
+ async computeAuthorizationHeader() {
11363
+ if (this.options().auth.isLocal !== void 0) {
11364
+ return "Implicit";
11365
+ }
11366
+ if (this.options().auth.apiKey !== void 0) {
11367
+ return `Bearer ${this.options().auth.apiKey}`;
11368
+ }
11369
+ return super.authorizationHeader();
11370
+ }
11371
+ setTransientPipelinePop() {
11372
+ const pop = this.options().pop;
11373
+ if (pop && this._pipelineId) {
11374
+ this._pipeline = {
11375
+ id: this._pipelineId,
11376
+ state: "idle",
11377
+ startTime: (/* @__PURE__ */ new Date()).toString(),
11378
+ pop
11379
+ };
11380
+ }
11381
+ }
10857
11382
  };
10858
- _WorkerEndpoint.SESSION_DEAD = /* @__PURE__ */ new Set(["error" /* ERROR */, "failed" /* FAILED */, "stopped" /* STOPPED */, "pipeline_error" /* PIPELINE_ERROR */, "unknown" /* UNKNOWN */]);
10859
11383
  _WorkerEndpoint.WAIT_FOR_IS_READY = 60 * 1e3;
10860
- _WorkerEndpoint.CHECK_FOR_IS_READY_INTERVAL = 250;
10861
11384
  var WorkerEndpoint = _WorkerEndpoint;
10862
11385
 
10863
11386
  // shims/websockets.ts
@@ -12231,6 +12754,15 @@ var EyePop;
12231
12754
  if (opts.sessionUuid === void 0) {
12232
12755
  opts.sessionUuid = readEnv("EYEPOP_SESSION_UUID");
12233
12756
  }
12757
+ if (opts.sessionName === void 0) {
12758
+ opts.sessionName = readEnv("EYEPOP_SESSION_NAME");
12759
+ }
12760
+ if (opts.pipelineImage === void 0) {
12761
+ opts.pipelineImage = readEnv("EYEPOP_PIPELINE_IMAGE");
12762
+ }
12763
+ if (opts.pipelineVersion === void 0) {
12764
+ opts.pipelineVersion = readEnv("EYEPOP_PIPELINE_VERSION");
12765
+ }
12234
12766
  if (opts.popId === void 0 && opts.sessionUuid === void 0) {
12235
12767
  opts.popId = readEnv("EYEPOP_POP_ID") || "transient" /* Transient */;
12236
12768
  }
@@ -12340,6 +12872,7 @@ var index_default = EyePop;
12340
12872
  ForwardOperatorType,
12341
12873
  InferenceType,
12342
12874
  LoadFromAssetUuidJob,
12875
+ LoadFromGroupJob,
12343
12876
  LoadFromJob,
12344
12877
  LoadMediaStreamJob,
12345
12878
  ModelExportStatus,
@@ -12353,12 +12886,12 @@ var index_default = EyePop;
12353
12886
  PopComponentType,
12354
12887
  PredictionVersion,
12355
12888
  Semaphore,
12356
- SessionStatus,
12357
12889
  Stream,
12358
12890
  TaskMode,
12359
12891
  TaskType,
12360
12892
  TranscodeMode,
12361
12893
  TransientPopId,
12894
+ UploadGroupJob,
12362
12895
  UploadJob,
12363
12896
  UserReview,
12364
12897
  VideoMode,
@@ -12368,7 +12901,9 @@ var index_default = EyePop;
12368
12901
  WebrtcWhip,
12369
12902
  WorkerEndpoint,
12370
12903
  WorkflowPhase,
12904
+ encodeMultipartFormData,
12371
12905
  getBaseUrl,
12906
+ randomMultipartBoundary,
12372
12907
  readableStreamFromString
12373
12908
  });
12374
12909
  /*! Bundled license information: