@eyepop.ai/eyepop 3.22.0 → 3.25.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.
- package/dist/eyepop.index.d.mts +352 -43
- package/dist/eyepop.index.d.ts +352 -43
- package/dist/eyepop.index.js +2353 -1384
- package/dist/eyepop.index.js.map +1 -1
- package/dist/eyepop.index.mjs +2339 -1384
- package/dist/eyepop.index.mjs.map +1 -1
- package/dist/eyepop.min.js +1 -1
- package/package.json +1 -1
package/dist/eyepop.index.js
CHANGED
|
@@ -8666,6 +8666,7 @@ __export(index_exports, {
|
|
|
8666
8666
|
AssetUrlType: () => AssetUrlType,
|
|
8667
8667
|
AutoAnnotateStatus: () => AutoAnnotateStatus,
|
|
8668
8668
|
AutoAnnotateTask: () => AutoAnnotateTask,
|
|
8669
|
+
COMBINED_UPLOAD_MAX_BYTES: () => COMBINED_UPLOAD_MAX_BYTES,
|
|
8669
8670
|
ChangeType: () => ChangeType,
|
|
8670
8671
|
ComputeAccountMismatchError: () => ComputeAccountMismatchError,
|
|
8671
8672
|
ComputeApiError: () => ComputeApiError,
|
|
@@ -8687,6 +8688,7 @@ __export(index_exports, {
|
|
|
8687
8688
|
LoadFromGroupJob: () => LoadFromGroupJob,
|
|
8688
8689
|
LoadFromJob: () => LoadFromJob,
|
|
8689
8690
|
LoadMediaStreamJob: () => LoadMediaStreamJob,
|
|
8691
|
+
MAX_OPEN_SOURCES: () => MAX_OPEN_SOURCES,
|
|
8690
8692
|
ModelExportStatus: () => ModelExportStatus,
|
|
8691
8693
|
ModelFormat: () => ModelFormat,
|
|
8692
8694
|
ModelPrecisionType: () => ModelPrecisionType,
|
|
@@ -8695,11 +8697,21 @@ __export(index_exports, {
|
|
|
8695
8697
|
ModelTrainingStage: () => ModelTrainingStage,
|
|
8696
8698
|
ModelType: () => ModelType,
|
|
8697
8699
|
MotionModel: () => MotionModel,
|
|
8700
|
+
PIPELINE_PATH: () => PIPELINE_PATH,
|
|
8701
|
+
PipelineBackend: () => PipelineBackend,
|
|
8702
|
+
PipelineCommandError: () => PipelineCommandError,
|
|
8703
|
+
PipelineConfigurationError: () => PipelineConfigurationError,
|
|
8704
|
+
PipelineJob: () => PipelineJob,
|
|
8705
|
+
PipelineSocketClosedError: () => PipelineSocketClosedError,
|
|
8706
|
+
PipelineSourceError: () => PipelineSourceError,
|
|
8698
8707
|
PointCloud: () => PointCloud,
|
|
8699
8708
|
PopComponentType: () => PopComponentType,
|
|
8700
8709
|
PredictionVersion: () => PredictionVersion,
|
|
8701
8710
|
QUATERNION_TOLERANCE: () => QUATERNION_TOLERANCE,
|
|
8711
|
+
SUBPROTOCOL: () => SUBPROTOCOL,
|
|
8712
|
+
SelectMode: () => SelectMode,
|
|
8702
8713
|
Semaphore: () => Semaphore,
|
|
8714
|
+
SessionBackend: () => SessionBackend,
|
|
8703
8715
|
Stream: () => Stream,
|
|
8704
8716
|
TaskMode: () => TaskMode,
|
|
8705
8717
|
TaskType: () => TaskType,
|
|
@@ -8725,6 +8737,8 @@ __export(index_exports, {
|
|
|
8725
8737
|
default: () => index_default,
|
|
8726
8738
|
encodeMultipartFormData: () => encodeMultipartFormData,
|
|
8727
8739
|
getBaseUrl: () => getBaseUrl,
|
|
8740
|
+
isSelectOperator: () => isSelectOperator,
|
|
8741
|
+
popSelects: () => popSelects,
|
|
8728
8742
|
randomMultipartBoundary: () => randomMultipartBoundary,
|
|
8729
8743
|
readableStreamFromString: () => readableStreamFromString,
|
|
8730
8744
|
resolveAuth: () => resolveAuth,
|
|
@@ -8767,6 +8781,667 @@ function accessTokenCredential(auth) {
|
|
|
8767
8781
|
return nonEmptyCredential(auth.accessToken);
|
|
8768
8782
|
}
|
|
8769
8783
|
|
|
8784
|
+
// types.ts
|
|
8785
|
+
var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
|
|
8786
|
+
EndpointState2["Idle"] = "Idle";
|
|
8787
|
+
EndpointState2["Busy"] = "Busy";
|
|
8788
|
+
EndpointState2["Authenticating"] = "Authenticating";
|
|
8789
|
+
EndpointState2["FetchConfig"] = "FetchConfig";
|
|
8790
|
+
EndpointState2["Error"] = "Error";
|
|
8791
|
+
EndpointState2["NotAvailable"] = "NotAvailable";
|
|
8792
|
+
return EndpointState2;
|
|
8793
|
+
})(EndpointState || {});
|
|
8794
|
+
|
|
8795
|
+
// shims/http_client.ts
|
|
8796
|
+
var createHttpClient;
|
|
8797
|
+
function logLineFromRequest(input, init) {
|
|
8798
|
+
const url = input instanceof Request ? input.url : input;
|
|
8799
|
+
const method = input instanceof Request ? input.method : init?.method || (init?.body !== void 0 ? "POST" : "GET");
|
|
8800
|
+
return `${method} ${url}`;
|
|
8801
|
+
}
|
|
8802
|
+
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
8803
|
+
class BrowserHttpClient {
|
|
8804
|
+
constructor(logger) {
|
|
8805
|
+
this._logger = logger;
|
|
8806
|
+
}
|
|
8807
|
+
async fetch(input, init) {
|
|
8808
|
+
const logLine = logLineFromRequest(input, init);
|
|
8809
|
+
this._logger.debug(`Browser fetch() BEFORE ${logLine}`);
|
|
8810
|
+
const response = await fetch(input, init);
|
|
8811
|
+
this._logger.debug(`AFTER ${logLine} -> ${response.status}`);
|
|
8812
|
+
return response;
|
|
8813
|
+
}
|
|
8814
|
+
async close() {
|
|
8815
|
+
}
|
|
8816
|
+
isFullDuplex() {
|
|
8817
|
+
return false;
|
|
8818
|
+
}
|
|
8819
|
+
}
|
|
8820
|
+
createHttpClient = async (logger) => {
|
|
8821
|
+
return new BrowserHttpClient(logger);
|
|
8822
|
+
};
|
|
8823
|
+
} else {
|
|
8824
|
+
class NodeHttpClient {
|
|
8825
|
+
constructor(agent, fetchFn, logger) {
|
|
8826
|
+
this._agent = agent;
|
|
8827
|
+
this._fetch = fetchFn;
|
|
8828
|
+
this._logger = logger;
|
|
8829
|
+
}
|
|
8830
|
+
async fetch(input, init) {
|
|
8831
|
+
init || (init = {});
|
|
8832
|
+
init.dispatcher = this._agent;
|
|
8833
|
+
const logLine = logLineFromRequest(input, init);
|
|
8834
|
+
this._logger.debug(`Node fetch() BEFORE ${logLine}`);
|
|
8835
|
+
const response = await this._fetch(input, init);
|
|
8836
|
+
this._logger.debug(`AFTER ${logLine} -> ${response.status}`);
|
|
8837
|
+
return response;
|
|
8838
|
+
}
|
|
8839
|
+
async close() {
|
|
8840
|
+
return this._agent.close();
|
|
8841
|
+
}
|
|
8842
|
+
isFullDuplex() {
|
|
8843
|
+
return false;
|
|
8844
|
+
}
|
|
8845
|
+
}
|
|
8846
|
+
createHttpClient = async (logger) => {
|
|
8847
|
+
const undici = require("undici");
|
|
8848
|
+
const agent = new undici.Agent({
|
|
8849
|
+
keepAliveTimeout: 1e4,
|
|
8850
|
+
connections: 5,
|
|
8851
|
+
// two parallel streaming requests to the same host seem to deadlock with pipelining > 0
|
|
8852
|
+
pipelining: 0
|
|
8853
|
+
});
|
|
8854
|
+
logger.debug("Using node fetch with undici agent as http client");
|
|
8855
|
+
return new NodeHttpClient(agent, undici.fetch, logger);
|
|
8856
|
+
};
|
|
8857
|
+
}
|
|
8858
|
+
|
|
8859
|
+
// semaphore.ts
|
|
8860
|
+
var Semaphore = class {
|
|
8861
|
+
/**
|
|
8862
|
+
* Creates a semaphore.
|
|
8863
|
+
* @param permits The number of permits, i.e. strands of execution being allowed
|
|
8864
|
+
* to run in parallel.
|
|
8865
|
+
* This number can be initialized with a negative integer.
|
|
8866
|
+
*/
|
|
8867
|
+
constructor(permits) {
|
|
8868
|
+
this.promiseResolverQueue = [];
|
|
8869
|
+
this.permits = permits;
|
|
8870
|
+
}
|
|
8871
|
+
/**
|
|
8872
|
+
* Returns the number of available permits.
|
|
8873
|
+
* @returns The number of available permits.
|
|
8874
|
+
*/
|
|
8875
|
+
getPermits() {
|
|
8876
|
+
return this.permits;
|
|
8877
|
+
}
|
|
8878
|
+
/**
|
|
8879
|
+
* Returns a promise used to wait for a permit to become available. This method should be awaited on.
|
|
8880
|
+
* @returns A promise that gets resolved when execution is allowed to proceed.
|
|
8881
|
+
*/
|
|
8882
|
+
async wait() {
|
|
8883
|
+
if (this.permits > 0) {
|
|
8884
|
+
this.permits -= 1;
|
|
8885
|
+
return Promise.resolve(true);
|
|
8886
|
+
}
|
|
8887
|
+
return new Promise((resolver) => this.promiseResolverQueue.push(resolver));
|
|
8888
|
+
}
|
|
8889
|
+
/**
|
|
8890
|
+
* Alias for {@linkcode Semaphore.wait}.
|
|
8891
|
+
* @returns A promise that gets resolved when execution is allowed to proceed.
|
|
8892
|
+
*/
|
|
8893
|
+
async acquire() {
|
|
8894
|
+
return this.wait();
|
|
8895
|
+
}
|
|
8896
|
+
/**
|
|
8897
|
+
* Same as {@linkcode Semaphore.wait} except the promise returned gets resolved with false if no
|
|
8898
|
+
* permit becomes available in time.
|
|
8899
|
+
* @param milliseconds The time spent waiting before the wait is aborted. This is a lower bound,
|
|
8900
|
+
* you shouldn't rely on it being precise.
|
|
8901
|
+
* @returns A promise that gets resolved to true when execution is allowed to proceed or
|
|
8902
|
+
* false if the time given elapses before a permit becomes available.
|
|
8903
|
+
*/
|
|
8904
|
+
async waitFor(milliseconds) {
|
|
8905
|
+
if (this.permits > 0) {
|
|
8906
|
+
this.permits -= 1;
|
|
8907
|
+
return Promise.resolve(true);
|
|
8908
|
+
}
|
|
8909
|
+
let resolver = (_) => void 0;
|
|
8910
|
+
const promise = new Promise((r) => {
|
|
8911
|
+
resolver = r;
|
|
8912
|
+
});
|
|
8913
|
+
this.promiseResolverQueue.push(resolver);
|
|
8914
|
+
setTimeout(() => {
|
|
8915
|
+
const index = this.promiseResolverQueue.indexOf(resolver);
|
|
8916
|
+
if (index !== -1) {
|
|
8917
|
+
this.promiseResolverQueue.splice(index, 1);
|
|
8918
|
+
} else {
|
|
8919
|
+
console.warn(`Semaphore.waitFor couldn't find its promise resolver in the queue`);
|
|
8920
|
+
}
|
|
8921
|
+
resolver(false);
|
|
8922
|
+
}, milliseconds);
|
|
8923
|
+
return promise;
|
|
8924
|
+
}
|
|
8925
|
+
/**
|
|
8926
|
+
* Synchronous function that tries to acquire a permit and returns true if successful, false otherwise.
|
|
8927
|
+
* @returns Whether a permit could be acquired.
|
|
8928
|
+
*/
|
|
8929
|
+
tryAcquire() {
|
|
8930
|
+
if (this.permits > 0) {
|
|
8931
|
+
this.permits -= 1;
|
|
8932
|
+
return true;
|
|
8933
|
+
}
|
|
8934
|
+
return false;
|
|
8935
|
+
}
|
|
8936
|
+
/**
|
|
8937
|
+
* Acquires all permits that are currently available and returns the number of acquired permits.
|
|
8938
|
+
* @returns Number of acquired permits.
|
|
8939
|
+
*/
|
|
8940
|
+
drainPermits() {
|
|
8941
|
+
if (this.permits > 0) {
|
|
8942
|
+
const permitCount = this.permits;
|
|
8943
|
+
this.permits = 0;
|
|
8944
|
+
return permitCount;
|
|
8945
|
+
}
|
|
8946
|
+
return 0;
|
|
8947
|
+
}
|
|
8948
|
+
/**
|
|
8949
|
+
* Increases the number of permits by one. If there are other functions waiting, one of them will
|
|
8950
|
+
* continue to execute in a future iteration of the event loop.
|
|
8951
|
+
*/
|
|
8952
|
+
signal() {
|
|
8953
|
+
this.permits += 1;
|
|
8954
|
+
if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
|
|
8955
|
+
console.warn("Semaphore.permits should never be > 0 when there is someone waiting.");
|
|
8956
|
+
} else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
|
|
8957
|
+
this.permits -= 1;
|
|
8958
|
+
const nextResolver = this.promiseResolverQueue.shift();
|
|
8959
|
+
if (nextResolver) {
|
|
8960
|
+
nextResolver(true);
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8964
|
+
/**
|
|
8965
|
+
* Alias for {@linkcode Semaphore.signal}.
|
|
8966
|
+
*/
|
|
8967
|
+
release() {
|
|
8968
|
+
this.signal();
|
|
8969
|
+
}
|
|
8970
|
+
/**
|
|
8971
|
+
* Schedules func to be called once a permit becomes available.
|
|
8972
|
+
* Returns a promise that resolves to the return value of func.
|
|
8973
|
+
* @typeparam T The return type of func.
|
|
8974
|
+
* @param func The function to be executed.
|
|
8975
|
+
* @return A promise that gets resolved with the return value of the function.
|
|
8976
|
+
*/
|
|
8977
|
+
async execute(func) {
|
|
8978
|
+
await this.wait();
|
|
8979
|
+
try {
|
|
8980
|
+
return await func();
|
|
8981
|
+
} finally {
|
|
8982
|
+
this.signal();
|
|
8983
|
+
}
|
|
8984
|
+
}
|
|
8985
|
+
};
|
|
8986
|
+
|
|
8987
|
+
// endpoint.ts
|
|
8988
|
+
var import_pino = require("pino");
|
|
8989
|
+
|
|
8990
|
+
// shims/browser_session.ts
|
|
8991
|
+
var import_auth0_spa_js = require("@auth0/auth0-spa-js");
|
|
8992
|
+
var authenticateBrowserSession;
|
|
8993
|
+
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
8994
|
+
authenticateBrowserSession = async (auth0, options) => {
|
|
8995
|
+
if (options.eyepopUrl == null) {
|
|
8996
|
+
return Promise.reject("options.eyepopUrl cannot be null");
|
|
8997
|
+
}
|
|
8998
|
+
const auth0ClientOptions = {
|
|
8999
|
+
clientId: auth0.clientId,
|
|
9000
|
+
domain: auth0.domain,
|
|
9001
|
+
authorizationParams: {
|
|
9002
|
+
scope: auth0.scope,
|
|
9003
|
+
audience: auth0.audience
|
|
9004
|
+
}
|
|
9005
|
+
};
|
|
9006
|
+
const auth0Client = await (0, import_auth0_spa_js.createAuth0Client)(auth0ClientOptions);
|
|
9007
|
+
if (!await auth0Client.isAuthenticated()) {
|
|
9008
|
+
await auth0Client.loginWithPopup();
|
|
9009
|
+
}
|
|
9010
|
+
const accessToken = await auth0Client.getTokenSilently();
|
|
9011
|
+
if (!accessToken) {
|
|
9012
|
+
return Promise.reject("auth0 login failed");
|
|
9013
|
+
}
|
|
9014
|
+
let session;
|
|
9015
|
+
const workerOptions = options;
|
|
9016
|
+
if (typeof workerOptions.popId != "undefined") {
|
|
9017
|
+
const workerSession = {
|
|
9018
|
+
eyepopUrl: options.eyepopUrl,
|
|
9019
|
+
accessToken,
|
|
9020
|
+
validUntil: Date.now() + 60 * 60 * 1e3,
|
|
9021
|
+
popId: workerOptions.popId,
|
|
9022
|
+
baseUrl: void 0,
|
|
9023
|
+
pipelineId: void 0,
|
|
9024
|
+
isPipelineMode: workerOptions.isPipelineMode,
|
|
9025
|
+
authenticationHeaders: () => {
|
|
9026
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
9027
|
+
}
|
|
9028
|
+
};
|
|
9029
|
+
session = workerSession;
|
|
9030
|
+
} else {
|
|
9031
|
+
session = {
|
|
9032
|
+
eyepopUrl: options.eyepopUrl,
|
|
9033
|
+
accessToken,
|
|
9034
|
+
validUntil: Date.now() + 60 * 60 * 1e3
|
|
9035
|
+
};
|
|
9036
|
+
}
|
|
9037
|
+
return session;
|
|
9038
|
+
};
|
|
9039
|
+
} else {
|
|
9040
|
+
authenticateBrowserSession = (_auth0, _options) => {
|
|
9041
|
+
return Promise.reject("auth0 login not supported server-side");
|
|
9042
|
+
};
|
|
9043
|
+
}
|
|
9044
|
+
|
|
9045
|
+
// endpoint.ts
|
|
9046
|
+
var Endpoint = class {
|
|
9047
|
+
constructor(options) {
|
|
9048
|
+
this._options = options;
|
|
9049
|
+
this._token = null;
|
|
9050
|
+
this._expire_token_time = null;
|
|
9051
|
+
this._client = null;
|
|
9052
|
+
this._statusRetryHandlers = /* @__PURE__ */ new Map();
|
|
9053
|
+
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9054
|
+
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
9055
|
+
this._state = "Idle" /* Idle */;
|
|
9056
|
+
this._message = null;
|
|
9057
|
+
this._stateChangeHandler = null;
|
|
9058
|
+
let rootLogger;
|
|
9059
|
+
if (options.logger) {
|
|
9060
|
+
rootLogger = options.logger;
|
|
9061
|
+
} else {
|
|
9062
|
+
rootLogger = (0, import_pino.pino)();
|
|
9063
|
+
}
|
|
9064
|
+
this._logger = rootLogger.child({ module: "eyepop" });
|
|
9065
|
+
this._requestLogger = this._logger.child({ module: "requests" });
|
|
9066
|
+
}
|
|
9067
|
+
setStatusRetryHandlers(statusRetryHandlers = void 0) {
|
|
9068
|
+
this._statusRetryHandlers = new Map(statusRetryHandlers);
|
|
9069
|
+
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9070
|
+
}
|
|
9071
|
+
eyepopUrl() {
|
|
9072
|
+
if (this._options.eyepopUrl) {
|
|
9073
|
+
return this._options.eyepopUrl.replace(/\/+$/, "");
|
|
9074
|
+
} else {
|
|
9075
|
+
return "https://compute.eyepop.ai";
|
|
9076
|
+
}
|
|
9077
|
+
}
|
|
9078
|
+
// TODO: deprecated Web API endpoint needed for POP_ID support
|
|
9079
|
+
eyepopWebApiUrl() {
|
|
9080
|
+
if (this._options.eyepopUrl) {
|
|
9081
|
+
return this._options.eyepopUrl.replace(/\/+$/, "").replace(/compute\./, "web-api.");
|
|
9082
|
+
} else {
|
|
9083
|
+
return "https://api.eyepop.ai";
|
|
9084
|
+
}
|
|
9085
|
+
}
|
|
9086
|
+
pendingJobs() {
|
|
9087
|
+
return (this._options.jobQueueLength ?? 1024) - this._limit.getPermits();
|
|
9088
|
+
}
|
|
9089
|
+
state() {
|
|
9090
|
+
return this._state;
|
|
9091
|
+
}
|
|
9092
|
+
lastMessage() {
|
|
9093
|
+
return this._message;
|
|
9094
|
+
}
|
|
9095
|
+
onStateChanged(handler) {
|
|
9096
|
+
this._stateChangeHandler = handler;
|
|
9097
|
+
return this;
|
|
9098
|
+
}
|
|
9099
|
+
updateState(newState = void 0, message = void 0) {
|
|
9100
|
+
const fromState = this._state;
|
|
9101
|
+
if (newState == void 0) {
|
|
9102
|
+
if (this._limit.getPermits() == this._options.jobQueueLength) {
|
|
9103
|
+
newState = "Idle" /* Idle */;
|
|
9104
|
+
} else {
|
|
9105
|
+
newState = "Busy" /* Busy */;
|
|
9106
|
+
}
|
|
9107
|
+
}
|
|
9108
|
+
this._state = newState;
|
|
9109
|
+
this._message = message ? message : null;
|
|
9110
|
+
if (this._stateChangeHandler) {
|
|
9111
|
+
this._stateChangeHandler(fromState, this._state);
|
|
9112
|
+
}
|
|
9113
|
+
}
|
|
9114
|
+
async connect() {
|
|
9115
|
+
if (this._client) {
|
|
9116
|
+
this._logger.warn("endpoint already connected");
|
|
9117
|
+
return this;
|
|
9118
|
+
}
|
|
9119
|
+
if (!this._options.eyepopUrl) {
|
|
9120
|
+
return Promise.reject("option eyepopUrl or environment variable EYEPOP_URL is required");
|
|
9121
|
+
}
|
|
9122
|
+
const auth = resolveAuth(this._options);
|
|
9123
|
+
if (auth === void 0) {
|
|
9124
|
+
return Promise.reject("cannot connect without defined auth option");
|
|
9125
|
+
}
|
|
9126
|
+
if (auth.session !== void 0) {
|
|
9127
|
+
this._token = auth.session.accessToken;
|
|
9128
|
+
this._expire_token_time = auth.session.validUntil / 1e3;
|
|
9129
|
+
} else if (auth.oAuth2 !== void 0) {
|
|
9130
|
+
} else if (accessTokenCredential(auth) !== void 0) {
|
|
9131
|
+
} else if (apiKeyCredential(auth) !== void 0) {
|
|
9132
|
+
} else if (auth.isLocal !== void 0) {
|
|
9133
|
+
} else {
|
|
9134
|
+
return Promise.reject("option apiKey/accessToken or environment variable EYEPOP_API_KEY is required");
|
|
9135
|
+
}
|
|
9136
|
+
if (this._options.platformSupport?.createHttpClient !== void 0) {
|
|
9137
|
+
this._client = await this._options.platformSupport.createHttpClient(this._requestLogger);
|
|
9138
|
+
} else {
|
|
9139
|
+
this._client = await createHttpClient(this._requestLogger);
|
|
9140
|
+
}
|
|
9141
|
+
return await this.reconnect();
|
|
9142
|
+
}
|
|
9143
|
+
async disconnect(wait = true) {
|
|
9144
|
+
if (wait && this._limit) {
|
|
9145
|
+
for (let j = 0; j < this._options.jobQueueLength; j++) {
|
|
9146
|
+
await this._limit.acquire();
|
|
9147
|
+
}
|
|
9148
|
+
}
|
|
9149
|
+
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
9150
|
+
if (this._client) {
|
|
9151
|
+
await this._client.close();
|
|
9152
|
+
this._client = null;
|
|
9153
|
+
}
|
|
9154
|
+
}
|
|
9155
|
+
async session() {
|
|
9156
|
+
await this.currentAccessToken();
|
|
9157
|
+
if (this._token == null || this._expire_token_time == null) {
|
|
9158
|
+
return Promise.reject("endpoint not connected");
|
|
9159
|
+
}
|
|
9160
|
+
return {
|
|
9161
|
+
eyepopUrl: this._options.eyepopUrl,
|
|
9162
|
+
accessToken: this._token,
|
|
9163
|
+
validUntil: this._expire_token_time * 1e3
|
|
9164
|
+
};
|
|
9165
|
+
}
|
|
9166
|
+
statusHandler401(_statusCode) {
|
|
9167
|
+
this._token = null;
|
|
9168
|
+
this._expire_token_time = null;
|
|
9169
|
+
}
|
|
9170
|
+
async fetchWithRetry(fetcher, retries = 1) {
|
|
9171
|
+
while (true) {
|
|
9172
|
+
try {
|
|
9173
|
+
const response = await fetcher();
|
|
9174
|
+
if (response.ok) {
|
|
9175
|
+
return response;
|
|
9176
|
+
} else {
|
|
9177
|
+
const handler = this._statusRetryHandlers.get(response.status);
|
|
9178
|
+
if (handler != null) {
|
|
9179
|
+
handler(response.status);
|
|
9180
|
+
if (retries--) {
|
|
9181
|
+
this._requestLogger.info("received %d response, retry", response.status);
|
|
9182
|
+
} else {
|
|
9183
|
+
return response;
|
|
9184
|
+
}
|
|
9185
|
+
} else {
|
|
9186
|
+
return response;
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
} catch (error) {
|
|
9190
|
+
const handler = this._statusRetryHandlers.get(404);
|
|
9191
|
+
if (handler != null) {
|
|
9192
|
+
handler(error);
|
|
9193
|
+
if (retries--) {
|
|
9194
|
+
this._requestLogger.info("error %s, retry", error);
|
|
9195
|
+
} else {
|
|
9196
|
+
return Promise.reject(`error ${error}`);
|
|
9197
|
+
}
|
|
9198
|
+
} else {
|
|
9199
|
+
return Promise.reject(`error ${error}`);
|
|
9200
|
+
}
|
|
9201
|
+
}
|
|
9202
|
+
}
|
|
9203
|
+
}
|
|
9204
|
+
async reconnect() {
|
|
9205
|
+
throw new Error("reconnect() not implemented");
|
|
9206
|
+
}
|
|
9207
|
+
async authorizationHeader() {
|
|
9208
|
+
if (resolveAuth(this._options)?.isLocal !== void 0) {
|
|
9209
|
+
return `Implicit`;
|
|
9210
|
+
}
|
|
9211
|
+
return `Bearer ${await this.currentAccessToken()}`;
|
|
9212
|
+
}
|
|
9213
|
+
async currentAccessToken() {
|
|
9214
|
+
const now = Date.now() / 1e3;
|
|
9215
|
+
if (!this._token || this._expire_token_time < now) {
|
|
9216
|
+
this.updateState("Authenticating" /* Authenticating */);
|
|
9217
|
+
try {
|
|
9218
|
+
const auth = resolveAuth(this._options);
|
|
9219
|
+
if (!this._client) {
|
|
9220
|
+
return Promise.reject("endpoint not connected");
|
|
9221
|
+
} else if (auth?.isLocal !== void 0) {
|
|
9222
|
+
this._token = "implicit";
|
|
9223
|
+
this._expire_token_time = Number.MAX_VALUE;
|
|
9224
|
+
} else if (auth?.session !== void 0) {
|
|
9225
|
+
return Promise.reject("temporary access token expired");
|
|
9226
|
+
} else if (auth?.oAuth2 !== void 0 && this._options.eyepopUrl) {
|
|
9227
|
+
const session = await authenticateBrowserSession(auth.oAuth2, this._options);
|
|
9228
|
+
this._token = session.accessToken;
|
|
9229
|
+
this._expire_token_time = session.validUntil / 1e3;
|
|
9230
|
+
} else if (accessTokenCredential(auth) !== void 0) {
|
|
9231
|
+
this._token = accessTokenCredential(auth);
|
|
9232
|
+
this._expire_token_time = Number.MAX_VALUE;
|
|
9233
|
+
} else if (this._options.eyepopUrl) {
|
|
9234
|
+
const credential = apiKeyCredential(auth);
|
|
9235
|
+
if (credential === void 0) {
|
|
9236
|
+
return Promise.reject("no valid auth option");
|
|
9237
|
+
}
|
|
9238
|
+
const headers = { Authorization: `Bearer ${credential}` };
|
|
9239
|
+
const post_url = `${this.eyepopUrl()}/v1/auth/authenticate`;
|
|
9240
|
+
const response = await this._client.fetch(post_url, {
|
|
9241
|
+
method: "POST",
|
|
9242
|
+
headers
|
|
9243
|
+
});
|
|
9244
|
+
if (response.status != 200) {
|
|
9245
|
+
const message = await response.text();
|
|
9246
|
+
return Promise.reject(`Unexpected status ${response.status}: ${message}`);
|
|
9247
|
+
}
|
|
9248
|
+
const data = await response.json();
|
|
9249
|
+
const token = data;
|
|
9250
|
+
this._token = token.access_token;
|
|
9251
|
+
this._expire_token_time = now + token.expires_in - 60;
|
|
9252
|
+
} else {
|
|
9253
|
+
return Promise.reject("no valid auth option");
|
|
9254
|
+
}
|
|
9255
|
+
} finally {
|
|
9256
|
+
this.updateState();
|
|
9257
|
+
}
|
|
9258
|
+
}
|
|
9259
|
+
this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
|
|
9260
|
+
return this._token;
|
|
9261
|
+
}
|
|
9262
|
+
};
|
|
9263
|
+
|
|
9264
|
+
// camera.ts
|
|
9265
|
+
var QUATERNION_TOLERANCE = 1e-3;
|
|
9266
|
+
function isFinite_(value) {
|
|
9267
|
+
return value === void 0 || Number.isFinite(value);
|
|
9268
|
+
}
|
|
9269
|
+
function validateCamera(camera) {
|
|
9270
|
+
if (camera.intrinsics !== void 0 && camera.hfovDegrees !== void 0) {
|
|
9271
|
+
throw new Error("camera.intrinsics and camera.hfovDegrees are alternatives, supply one");
|
|
9272
|
+
}
|
|
9273
|
+
if (camera.intrinsics === void 0 && camera.hfovDegrees === void 0) {
|
|
9274
|
+
throw new Error("camera requires either camera.intrinsics or camera.hfovDegrees");
|
|
9275
|
+
}
|
|
9276
|
+
if (camera.hfovDegrees !== void 0) {
|
|
9277
|
+
if (!(camera.hfovDegrees > 0 && camera.hfovDegrees < 180)) {
|
|
9278
|
+
throw new Error("camera.hfovDegrees must be a horizontal field of view in (0, 180) degrees");
|
|
9279
|
+
}
|
|
9280
|
+
} else {
|
|
9281
|
+
const intrinsics = camera.intrinsics;
|
|
9282
|
+
for (const [name, focal] of [
|
|
9283
|
+
["fx", intrinsics.fx],
|
|
9284
|
+
["fy", intrinsics.fy]
|
|
9285
|
+
]) {
|
|
9286
|
+
if (!(focal > 0) || !Number.isFinite(focal)) {
|
|
9287
|
+
throw new Error(`camera.intrinsics.${name} must be a positive, finite focal length`);
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
for (const [name, principal, extent] of [
|
|
9291
|
+
["cx", intrinsics.cx, "width"],
|
|
9292
|
+
["cy", intrinsics.cy, "height"]
|
|
9293
|
+
]) {
|
|
9294
|
+
if (!(principal >= 0 && principal <= 1)) {
|
|
9295
|
+
throw new Error(`camera.intrinsics.${name} must be within [0, 1] of the frame ${extent}`);
|
|
9296
|
+
}
|
|
9297
|
+
}
|
|
9298
|
+
}
|
|
9299
|
+
if (camera.distortion !== void 0) {
|
|
9300
|
+
for (const name of ["k1", "k2", "p1", "p2", "k3"]) {
|
|
9301
|
+
if (!isFinite_(camera.distortion[name])) {
|
|
9302
|
+
throw new Error(`camera.distortion.${name} must be a finite number`);
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
if (camera.extrinsics !== void 0) {
|
|
9307
|
+
const translation = camera.extrinsics.translation;
|
|
9308
|
+
if (translation !== void 0) {
|
|
9309
|
+
for (const axis of ["x", "y", "z"]) {
|
|
9310
|
+
if (!Number.isFinite(translation[axis])) {
|
|
9311
|
+
throw new Error(`camera.extrinsics.translation.${axis} must be a finite number`);
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
}
|
|
9315
|
+
const rotation = camera.extrinsics.rotation;
|
|
9316
|
+
if (rotation !== void 0) {
|
|
9317
|
+
const norm = Math.sqrt(rotation.w ** 2 + rotation.x ** 2 + rotation.y ** 2 + rotation.z ** 2);
|
|
9318
|
+
if (!(Math.abs(norm - 1) <= QUATERNION_TOLERANCE)) {
|
|
9319
|
+
throw new Error("camera.extrinsics.rotation must be a unit quaternion");
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
}
|
|
9324
|
+
|
|
9325
|
+
// worker/worker_types.ts
|
|
9326
|
+
var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
|
|
9327
|
+
PredictionVersion3[PredictionVersion3["V1"] = 1] = "V1";
|
|
9328
|
+
PredictionVersion3[PredictionVersion3["V2"] = 2] = "V2";
|
|
9329
|
+
PredictionVersion3[PredictionVersion3["V3"] = 3] = "V3";
|
|
9330
|
+
return PredictionVersion3;
|
|
9331
|
+
})(PredictionVersion || {});
|
|
9332
|
+
var DEFAULT_PREDICTION_VERSION = 2 /* V2 */;
|
|
9333
|
+
var VideoMode = /* @__PURE__ */ ((VideoMode2) => {
|
|
9334
|
+
VideoMode2["STREAM"] = "stream";
|
|
9335
|
+
VideoMode2["BUFFER"] = "buffer";
|
|
9336
|
+
return VideoMode2;
|
|
9337
|
+
})(VideoMode || {});
|
|
9338
|
+
var PopComponentType = /* @__PURE__ */ ((PopComponentType2) => {
|
|
9339
|
+
PopComponentType2["FORWARD"] = "forward";
|
|
9340
|
+
PopComponentType2["INFERENCE"] = "inference";
|
|
9341
|
+
PopComponentType2["TRACKING"] = "tracking";
|
|
9342
|
+
PopComponentType2["CONTOUR_FINDER"] = "contour_finder";
|
|
9343
|
+
PopComponentType2["COMPONENT_FINDER"] = "component_finder";
|
|
9344
|
+
return PopComponentType2;
|
|
9345
|
+
})(PopComponentType || {});
|
|
9346
|
+
var ForwardOperatorType = /* @__PURE__ */ ((ForwardOperatorType2) => {
|
|
9347
|
+
ForwardOperatorType2["FULL"] = "full";
|
|
9348
|
+
ForwardOperatorType2["CROP"] = "crop";
|
|
9349
|
+
ForwardOperatorType2["CROP_WITH_FULL_FALLBACK"] = "crop_with_full_fallback";
|
|
9350
|
+
ForwardOperatorType2["SELECT_CROP"] = "select_crop";
|
|
9351
|
+
ForwardOperatorType2["SELECT_FULL"] = "select_full";
|
|
9352
|
+
return ForwardOperatorType2;
|
|
9353
|
+
})(ForwardOperatorType || {});
|
|
9354
|
+
var SelectMode = /* @__PURE__ */ ((SelectMode2) => {
|
|
9355
|
+
SelectMode2["MOST_RELEVANT"] = "most_relevant";
|
|
9356
|
+
return SelectMode2;
|
|
9357
|
+
})(SelectMode || {});
|
|
9358
|
+
function isSelectOperator(operator) {
|
|
9359
|
+
return operator?.type === "select_crop" /* SELECT_CROP */ || operator?.type === "select_full" /* SELECT_FULL */;
|
|
9360
|
+
}
|
|
9361
|
+
var InferenceType = /* @__PURE__ */ ((InferenceType2) => {
|
|
9362
|
+
InferenceType2["IMAGE_CLASSIFICATION"] = "image_classification";
|
|
9363
|
+
InferenceType2["OBJECT_DETECTION"] = "object_detection";
|
|
9364
|
+
InferenceType2["KEY_POINTS"] = "key_points";
|
|
9365
|
+
InferenceType2["OCR"] = "ocr";
|
|
9366
|
+
InferenceType2["MESH"] = "mesh";
|
|
9367
|
+
InferenceType2["FEATURE_VECTOR"] = "feature_vector";
|
|
9368
|
+
InferenceType2["SEMANTIC_SEGMENTATION"] = "semantic_segmentation";
|
|
9369
|
+
InferenceType2["SEGMENTATION"] = "segmentation";
|
|
9370
|
+
return InferenceType2;
|
|
9371
|
+
})(InferenceType || {});
|
|
9372
|
+
var MotionModel = /* @__PURE__ */ ((MotionModel2) => {
|
|
9373
|
+
MotionModel2["RANDOM_WALK"] = "random_walk";
|
|
9374
|
+
MotionModel2["CONSTANT_VELOCITY"] = "constant_velocity";
|
|
9375
|
+
MotionModel2["CONSTANT_ACCELERATION"] = "constant_acceleration";
|
|
9376
|
+
return MotionModel2;
|
|
9377
|
+
})(MotionModel || {});
|
|
9378
|
+
var ContourType = /* @__PURE__ */ ((ContourType2) => {
|
|
9379
|
+
ContourType2["ALL_PIXELS"] = "all_pixels";
|
|
9380
|
+
ContourType2["POLYGON"] = "polygon";
|
|
9381
|
+
ContourType2["CONVEX_HULL"] = "convex_hull";
|
|
9382
|
+
ContourType2["HOUGH_CIRCLES"] = "hough_circles";
|
|
9383
|
+
ContourType2["CIRCLE"] = "circle";
|
|
9384
|
+
ContourType2["TRIANGLE"] = "triangle";
|
|
9385
|
+
ContourType2["RECTANGLE"] = "rectangle";
|
|
9386
|
+
return ContourType2;
|
|
9387
|
+
})(ContourType || {});
|
|
9388
|
+
function validatePop(pop) {
|
|
9389
|
+
if (pop.depthMap !== void 0) {
|
|
9390
|
+
if (pop.depthMap.ability === void 0 === (pop.depthMap.abilityUuid === void 0)) {
|
|
9391
|
+
throw new Error("depthMap requires exactly one of ability or abilityUuid");
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
if (pop.defaults?.camera !== void 0) {
|
|
9395
|
+
validateCamera(pop.defaults.camera);
|
|
9396
|
+
}
|
|
9397
|
+
validateForwards(pop.components);
|
|
9398
|
+
}
|
|
9399
|
+
function popSelects(pop) {
|
|
9400
|
+
const selects = (components) => (components ?? []).some((component) => isSelectOperator(component.forward?.operator) || selects(component.forward?.targets));
|
|
9401
|
+
return selects(pop?.components);
|
|
9402
|
+
}
|
|
9403
|
+
function validateForwards(components) {
|
|
9404
|
+
for (const component of components ?? []) {
|
|
9405
|
+
const operator = component.forward?.operator;
|
|
9406
|
+
if (operator !== void 0) {
|
|
9407
|
+
validateSelectOperator(operator);
|
|
9408
|
+
}
|
|
9409
|
+
validateForwards(component.forward?.targets);
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
function validateSelectOperator(operator) {
|
|
9413
|
+
if (!isSelectOperator(operator)) {
|
|
9414
|
+
if (operator.select !== void 0) {
|
|
9415
|
+
throw new Error("select is only valid with the select_crop or select_full operator");
|
|
9416
|
+
}
|
|
9417
|
+
return;
|
|
9418
|
+
}
|
|
9419
|
+
const select = operator.select;
|
|
9420
|
+
if (select === void 0) {
|
|
9421
|
+
throw new Error(`${operator.type} requires a select block`);
|
|
9422
|
+
}
|
|
9423
|
+
if (select.mode !== void 0 && select.mode !== "most_relevant" /* MOST_RELEVANT */) {
|
|
9424
|
+
throw new Error(`select mode ${select.mode} is not supported, the only mode is ${"most_relevant" /* MOST_RELEVANT */}`);
|
|
9425
|
+
}
|
|
9426
|
+
if (select.relevancyModel !== void 0 && select.relevancyModelUuid !== void 0) {
|
|
9427
|
+
throw new Error("select can only have one of relevancyModelUuid or relevancyModel");
|
|
9428
|
+
}
|
|
9429
|
+
if (select.minTrackLengthSeconds !== void 0 && select.minTrackLengthSeconds < 0) {
|
|
9430
|
+
throw new Error("select minTrackLengthSeconds cannot be negative");
|
|
9431
|
+
}
|
|
9432
|
+
if (select.intervalSeconds !== void 0 && select.intervalSeconds <= 0) {
|
|
9433
|
+
throw new Error("select intervalSeconds must be positive");
|
|
9434
|
+
}
|
|
9435
|
+
if (operator.crop !== void 0) {
|
|
9436
|
+
if (operator.type !== "select_crop" /* SELECT_CROP */) {
|
|
9437
|
+
throw new Error(`crop options are only valid with select_crop, not ${operator.type}`);
|
|
9438
|
+
}
|
|
9439
|
+
if (operator.crop.maxItems !== void 0) {
|
|
9440
|
+
throw new Error("select_crop forwards one detection per selection, maxItems does not apply");
|
|
9441
|
+
}
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9444
|
+
|
|
8770
9445
|
// compute/compute_session.ts
|
|
8771
9446
|
var SESSION_DEAD = /* @__PURE__ */ new Set(["error" /* ERROR */, "failed" /* FAILED */, "stopped" /* STOPPED */, "pipeline_error" /* PIPELINE_ERROR */, "unknown" /* UNKNOWN */]);
|
|
8772
9447
|
var CHECK_FOR_IS_READY_INTERVAL = 250;
|
|
@@ -9083,17 +9758,6 @@ var ComputeSessionClient = class {
|
|
|
9083
9758
|
}
|
|
9084
9759
|
};
|
|
9085
9760
|
|
|
9086
|
-
// types.ts
|
|
9087
|
-
var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
|
|
9088
|
-
EndpointState2["Idle"] = "Idle";
|
|
9089
|
-
EndpointState2["Busy"] = "Busy";
|
|
9090
|
-
EndpointState2["Authenticating"] = "Authenticating";
|
|
9091
|
-
EndpointState2["FetchConfig"] = "FetchConfig";
|
|
9092
|
-
EndpointState2["Error"] = "Error";
|
|
9093
|
-
EndpointState2["NotAvailable"] = "NotAvailable";
|
|
9094
|
-
return EndpointState2;
|
|
9095
|
-
})(EndpointState || {});
|
|
9096
|
-
|
|
9097
9761
|
// streaming.ts
|
|
9098
9762
|
var import_uuid = require("uuid");
|
|
9099
9763
|
var Stream = class _Stream {
|
|
@@ -9310,166 +9974,41 @@ function encodeMultipartFormData(parts, boundary) {
|
|
|
9310
9974
|
if (!done) {
|
|
9311
9975
|
controller.enqueue(value);
|
|
9312
9976
|
return;
|
|
9313
|
-
}
|
|
9314
|
-
reader = null;
|
|
9315
|
-
controller.enqueue(encoder.encode("\r\n"));
|
|
9316
|
-
partIndex++;
|
|
9317
|
-
}
|
|
9318
|
-
if (partIndex >= parts.length) {
|
|
9319
|
-
controller.enqueue(encoder.encode(`--${boundary}--\r
|
|
9320
|
-
`));
|
|
9321
|
-
controller.close();
|
|
9322
|
-
return;
|
|
9323
|
-
}
|
|
9324
|
-
const part = parts[partIndex];
|
|
9325
|
-
let header = `--${boundary}\r
|
|
9326
|
-
Content-Disposition: form-data; name="${part.name}"\r
|
|
9327
|
-
`;
|
|
9328
|
-
if (part.contentType) {
|
|
9329
|
-
header += `Content-Type: ${part.contentType}\r
|
|
9330
|
-
`;
|
|
9331
|
-
}
|
|
9332
|
-
header += "\r\n";
|
|
9333
|
-
controller.enqueue(encoder.encode(header));
|
|
9334
|
-
if (typeof part.content === "string") {
|
|
9335
|
-
controller.enqueue(encoder.encode(part.content));
|
|
9336
|
-
controller.enqueue(encoder.encode("\r\n"));
|
|
9337
|
-
partIndex++;
|
|
9338
|
-
} else {
|
|
9339
|
-
reader = toPartStream(part.content).getReader();
|
|
9340
|
-
}
|
|
9341
|
-
},
|
|
9342
|
-
async cancel(reason) {
|
|
9343
|
-
await reader?.cancel(reason);
|
|
9344
|
-
const firstUnstartedPart = reader ? partIndex + 1 : partIndex;
|
|
9345
|
-
await Promise.all(parts.slice(firstUnstartedPart).map((part) => cancelMultipartContent(part.content, reason)));
|
|
9346
|
-
}
|
|
9347
|
-
});
|
|
9348
|
-
}
|
|
9349
|
-
|
|
9350
|
-
// camera.ts
|
|
9351
|
-
var QUATERNION_TOLERANCE = 1e-3;
|
|
9352
|
-
function isFinite_(value) {
|
|
9353
|
-
return value === void 0 || Number.isFinite(value);
|
|
9354
|
-
}
|
|
9355
|
-
function validateCamera(camera) {
|
|
9356
|
-
if (camera.intrinsics !== void 0 && camera.hfovDegrees !== void 0) {
|
|
9357
|
-
throw new Error("camera.intrinsics and camera.hfovDegrees are alternatives, supply one");
|
|
9358
|
-
}
|
|
9359
|
-
if (camera.intrinsics === void 0 && camera.hfovDegrees === void 0) {
|
|
9360
|
-
throw new Error("camera requires either camera.intrinsics or camera.hfovDegrees");
|
|
9361
|
-
}
|
|
9362
|
-
if (camera.hfovDegrees !== void 0) {
|
|
9363
|
-
if (!(camera.hfovDegrees > 0 && camera.hfovDegrees < 180)) {
|
|
9364
|
-
throw new Error("camera.hfovDegrees must be a horizontal field of view in (0, 180) degrees");
|
|
9365
|
-
}
|
|
9366
|
-
} else {
|
|
9367
|
-
const intrinsics = camera.intrinsics;
|
|
9368
|
-
for (const [name, focal] of [
|
|
9369
|
-
["fx", intrinsics.fx],
|
|
9370
|
-
["fy", intrinsics.fy]
|
|
9371
|
-
]) {
|
|
9372
|
-
if (!(focal > 0) || !Number.isFinite(focal)) {
|
|
9373
|
-
throw new Error(`camera.intrinsics.${name} must be a positive, finite focal length`);
|
|
9374
|
-
}
|
|
9375
|
-
}
|
|
9376
|
-
for (const [name, principal, extent] of [
|
|
9377
|
-
["cx", intrinsics.cx, "width"],
|
|
9378
|
-
["cy", intrinsics.cy, "height"]
|
|
9379
|
-
]) {
|
|
9380
|
-
if (!(principal >= 0 && principal <= 1)) {
|
|
9381
|
-
throw new Error(`camera.intrinsics.${name} must be within [0, 1] of the frame ${extent}`);
|
|
9382
|
-
}
|
|
9383
|
-
}
|
|
9384
|
-
}
|
|
9385
|
-
if (camera.distortion !== void 0) {
|
|
9386
|
-
for (const name of ["k1", "k2", "p1", "p2", "k3"]) {
|
|
9387
|
-
if (!isFinite_(camera.distortion[name])) {
|
|
9388
|
-
throw new Error(`camera.distortion.${name} must be a finite number`);
|
|
9389
|
-
}
|
|
9390
|
-
}
|
|
9391
|
-
}
|
|
9392
|
-
if (camera.extrinsics !== void 0) {
|
|
9393
|
-
const translation = camera.extrinsics.translation;
|
|
9394
|
-
if (translation !== void 0) {
|
|
9395
|
-
for (const axis of ["x", "y", "z"]) {
|
|
9396
|
-
if (!Number.isFinite(translation[axis])) {
|
|
9397
|
-
throw new Error(`camera.extrinsics.translation.${axis} must be a finite number`);
|
|
9398
|
-
}
|
|
9399
|
-
}
|
|
9400
|
-
}
|
|
9401
|
-
const rotation = camera.extrinsics.rotation;
|
|
9402
|
-
if (rotation !== void 0) {
|
|
9403
|
-
const norm = Math.sqrt(rotation.w ** 2 + rotation.x ** 2 + rotation.y ** 2 + rotation.z ** 2);
|
|
9404
|
-
if (!(Math.abs(norm - 1) <= QUATERNION_TOLERANCE)) {
|
|
9405
|
-
throw new Error("camera.extrinsics.rotation must be a unit quaternion");
|
|
9406
|
-
}
|
|
9407
|
-
}
|
|
9408
|
-
}
|
|
9409
|
-
}
|
|
9410
|
-
|
|
9411
|
-
// worker/worker_types.ts
|
|
9412
|
-
var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
|
|
9413
|
-
PredictionVersion3[PredictionVersion3["V1"] = 1] = "V1";
|
|
9414
|
-
PredictionVersion3[PredictionVersion3["V2"] = 2] = "V2";
|
|
9415
|
-
return PredictionVersion3;
|
|
9416
|
-
})(PredictionVersion || {});
|
|
9417
|
-
var DEFAULT_PREDICTION_VERSION = 2 /* V2 */;
|
|
9418
|
-
var VideoMode = /* @__PURE__ */ ((VideoMode2) => {
|
|
9419
|
-
VideoMode2["STREAM"] = "stream";
|
|
9420
|
-
VideoMode2["BUFFER"] = "buffer";
|
|
9421
|
-
return VideoMode2;
|
|
9422
|
-
})(VideoMode || {});
|
|
9423
|
-
var PopComponentType = /* @__PURE__ */ ((PopComponentType2) => {
|
|
9424
|
-
PopComponentType2["FORWARD"] = "forward";
|
|
9425
|
-
PopComponentType2["INFERENCE"] = "inference";
|
|
9426
|
-
PopComponentType2["TRACKING"] = "tracking";
|
|
9427
|
-
PopComponentType2["CONTOUR_FINDER"] = "contour_finder";
|
|
9428
|
-
PopComponentType2["COMPONENT_FINDER"] = "component_finder";
|
|
9429
|
-
return PopComponentType2;
|
|
9430
|
-
})(PopComponentType || {});
|
|
9431
|
-
var ForwardOperatorType = /* @__PURE__ */ ((ForwardOperatorType2) => {
|
|
9432
|
-
ForwardOperatorType2["FULL"] = "full";
|
|
9433
|
-
ForwardOperatorType2["CROP"] = "crop";
|
|
9434
|
-
ForwardOperatorType2["CROP_WITH_FULL_FALLBACK"] = "crop_with_full_fallback";
|
|
9435
|
-
return ForwardOperatorType2;
|
|
9436
|
-
})(ForwardOperatorType || {});
|
|
9437
|
-
var InferenceType = /* @__PURE__ */ ((InferenceType2) => {
|
|
9438
|
-
InferenceType2["IMAGE_CLASSIFICATION"] = "image_classification";
|
|
9439
|
-
InferenceType2["OBJECT_DETECTION"] = "object_detection";
|
|
9440
|
-
InferenceType2["KEY_POINTS"] = "key_points";
|
|
9441
|
-
InferenceType2["OCR"] = "ocr";
|
|
9442
|
-
InferenceType2["MESH"] = "mesh";
|
|
9443
|
-
InferenceType2["FEATURE_VECTOR"] = "feature_vector";
|
|
9444
|
-
InferenceType2["SEMANTIC_SEGMENTATION"] = "semantic_segmentation";
|
|
9445
|
-
InferenceType2["SEGMENTATION"] = "segmentation";
|
|
9446
|
-
return InferenceType2;
|
|
9447
|
-
})(InferenceType || {});
|
|
9448
|
-
var MotionModel = /* @__PURE__ */ ((MotionModel2) => {
|
|
9449
|
-
MotionModel2["RANDOM_WALK"] = "random_walk";
|
|
9450
|
-
MotionModel2["CONSTANT_VELOCITY"] = "constant_velocity";
|
|
9451
|
-
MotionModel2["CONSTANT_ACCELERATION"] = "constant_acceleration";
|
|
9452
|
-
return MotionModel2;
|
|
9453
|
-
})(MotionModel || {});
|
|
9454
|
-
var ContourType = /* @__PURE__ */ ((ContourType2) => {
|
|
9455
|
-
ContourType2["ALL_PIXELS"] = "all_pixels";
|
|
9456
|
-
ContourType2["POLYGON"] = "polygon";
|
|
9457
|
-
ContourType2["CONVEX_HULL"] = "convex_hull";
|
|
9458
|
-
ContourType2["HOUGH_CIRCLES"] = "hough_circles";
|
|
9459
|
-
ContourType2["CIRCLE"] = "circle";
|
|
9460
|
-
ContourType2["TRIANGLE"] = "triangle";
|
|
9461
|
-
ContourType2["RECTANGLE"] = "rectangle";
|
|
9462
|
-
return ContourType2;
|
|
9463
|
-
})(ContourType || {});
|
|
9464
|
-
function validatePop(pop) {
|
|
9465
|
-
if (pop.depthMap !== void 0) {
|
|
9466
|
-
if (pop.depthMap.ability === void 0 === (pop.depthMap.abilityUuid === void 0)) {
|
|
9467
|
-
throw new Error("depthMap requires exactly one of ability or abilityUuid");
|
|
9977
|
+
}
|
|
9978
|
+
reader = null;
|
|
9979
|
+
controller.enqueue(encoder.encode("\r\n"));
|
|
9980
|
+
partIndex++;
|
|
9981
|
+
}
|
|
9982
|
+
if (partIndex >= parts.length) {
|
|
9983
|
+
controller.enqueue(encoder.encode(`--${boundary}--\r
|
|
9984
|
+
`));
|
|
9985
|
+
controller.close();
|
|
9986
|
+
return;
|
|
9987
|
+
}
|
|
9988
|
+
const part = parts[partIndex];
|
|
9989
|
+
let header = `--${boundary}\r
|
|
9990
|
+
Content-Disposition: form-data; name="${part.name}"\r
|
|
9991
|
+
`;
|
|
9992
|
+
if (part.contentType) {
|
|
9993
|
+
header += `Content-Type: ${part.contentType}\r
|
|
9994
|
+
`;
|
|
9995
|
+
}
|
|
9996
|
+
header += "\r\n";
|
|
9997
|
+
controller.enqueue(encoder.encode(header));
|
|
9998
|
+
if (typeof part.content === "string") {
|
|
9999
|
+
controller.enqueue(encoder.encode(part.content));
|
|
10000
|
+
controller.enqueue(encoder.encode("\r\n"));
|
|
10001
|
+
partIndex++;
|
|
10002
|
+
} else {
|
|
10003
|
+
reader = toPartStream(part.content).getReader();
|
|
10004
|
+
}
|
|
10005
|
+
},
|
|
10006
|
+
async cancel(reason) {
|
|
10007
|
+
await reader?.cancel(reason);
|
|
10008
|
+
const firstUnstartedPart = reader ? partIndex + 1 : partIndex;
|
|
10009
|
+
await Promise.all(parts.slice(firstUnstartedPart).map((part) => cancelMultipartContent(part.content, reason)));
|
|
9468
10010
|
}
|
|
9469
|
-
}
|
|
9470
|
-
if (pop.defaults?.camera !== void 0) {
|
|
9471
|
-
validateCamera(pop.defaults.camera);
|
|
9472
|
-
}
|
|
10011
|
+
});
|
|
9473
10012
|
}
|
|
9474
10013
|
|
|
9475
10014
|
// worker/webrtc_whip.ts
|
|
@@ -9922,8 +10461,8 @@ var UploadJob = class extends AbstractJob {
|
|
|
9922
10461
|
get [Symbol.toStringTag]() {
|
|
9923
10462
|
return "uploadJob";
|
|
9924
10463
|
}
|
|
9925
|
-
constructor(stream, mimeType, size, videoMode, params, getSession, client, requestLogger, uploadBodyFactory) {
|
|
9926
|
-
super(params, getSession, client, requestLogger);
|
|
10464
|
+
constructor(stream, mimeType, size, videoMode, params, getSession, client, requestLogger, uploadBodyFactory, version = DEFAULT_PREDICTION_VERSION) {
|
|
10465
|
+
super(params, getSession, client, requestLogger, version);
|
|
9927
10466
|
this._uploadStream = stream;
|
|
9928
10467
|
this._uploadBodyFactory = uploadBodyFactory;
|
|
9929
10468
|
this._mimeType = mimeType;
|
|
@@ -10116,8 +10655,8 @@ var UploadJob = class extends AbstractJob {
|
|
|
10116
10655
|
}
|
|
10117
10656
|
};
|
|
10118
10657
|
var LoadFromJob = class extends AbstractJob {
|
|
10119
|
-
constructor(location, params, getSession, client, requestLogger) {
|
|
10120
|
-
super(params, getSession, client, requestLogger);
|
|
10658
|
+
constructor(location, params, getSession, client, requestLogger, version = DEFAULT_PREDICTION_VERSION) {
|
|
10659
|
+
super(params, getSession, client, requestLogger, version);
|
|
10121
10660
|
this._location = location;
|
|
10122
10661
|
}
|
|
10123
10662
|
get [Symbol.toStringTag]() {
|
|
@@ -10163,8 +10702,8 @@ var UploadGroupJob = class extends AbstractJob {
|
|
|
10163
10702
|
get [Symbol.toStringTag]() {
|
|
10164
10703
|
return "uploadGroupJob";
|
|
10165
10704
|
}
|
|
10166
|
-
constructor(sources, params, getSession, client, requestLogger) {
|
|
10167
|
-
super(params, getSession, client, requestLogger);
|
|
10705
|
+
constructor(sources, params, getSession, client, requestLogger, version = DEFAULT_PREDICTION_VERSION) {
|
|
10706
|
+
super(params, getSession, client, requestLogger, version);
|
|
10168
10707
|
if (sources.length === 0) {
|
|
10169
10708
|
throw new Error("upload group requires at least one source");
|
|
10170
10709
|
}
|
|
@@ -10225,8 +10764,8 @@ var LoadFromGroupJob = class extends AbstractJob {
|
|
|
10225
10764
|
get [Symbol.toStringTag]() {
|
|
10226
10765
|
return "loadFromGroupJob";
|
|
10227
10766
|
}
|
|
10228
|
-
constructor(urls, params, getSession, client, requestLogger) {
|
|
10229
|
-
super(params, getSession, client, requestLogger);
|
|
10767
|
+
constructor(urls, params, getSession, client, requestLogger, version = DEFAULT_PREDICTION_VERSION) {
|
|
10768
|
+
super(params, getSession, client, requestLogger, version);
|
|
10230
10769
|
if (urls.length === 0) {
|
|
10231
10770
|
throw new Error("load from group requires at least one url");
|
|
10232
10771
|
}
|
|
@@ -10271,8 +10810,8 @@ var LoadFromGroupJob = class extends AbstractJob {
|
|
|
10271
10810
|
}
|
|
10272
10811
|
};
|
|
10273
10812
|
var LoadFromAssetUuidJob = class extends AbstractJob {
|
|
10274
|
-
constructor(assetUuid, params, getSession, client, requestLogger) {
|
|
10275
|
-
super(params, getSession, client, requestLogger);
|
|
10813
|
+
constructor(assetUuid, params, getSession, client, requestLogger, version = DEFAULT_PREDICTION_VERSION) {
|
|
10814
|
+
super(params, getSession, client, requestLogger, version);
|
|
10276
10815
|
this._assetUuid = assetUuid;
|
|
10277
10816
|
}
|
|
10278
10817
|
get [Symbol.toStringTag]() {
|
|
@@ -10312,1378 +10851,1755 @@ var LoadFromAssetUuidJob = class extends AbstractJob {
|
|
|
10312
10851
|
}
|
|
10313
10852
|
};
|
|
10314
10853
|
var LoadMediaStreamJob = class extends AbstractJob {
|
|
10315
|
-
constructor(mediaStream, params, getSession, client, requestLogger) {
|
|
10316
|
-
super(params, getSession, client, requestLogger);
|
|
10854
|
+
constructor(mediaStream, params, getSession, client, requestLogger, version = DEFAULT_PREDICTION_VERSION) {
|
|
10855
|
+
super(params, getSession, client, requestLogger, version);
|
|
10317
10856
|
this._mediaStream = mediaStream;
|
|
10318
10857
|
}
|
|
10319
10858
|
get [Symbol.toStringTag]() {
|
|
10320
10859
|
return "loadLiveIngressJob";
|
|
10321
10860
|
}
|
|
10322
|
-
async startJob() {
|
|
10323
|
-
const session = await this._getSession();
|
|
10324
|
-
if (!session.baseUrl) {
|
|
10325
|
-
return Promise.reject("session.baseUrl must not ne null");
|
|
10326
|
-
}
|
|
10327
|
-
const whip = new WebrtcWhip(
|
|
10328
|
-
this._mediaStream,
|
|
10329
|
-
async () => {
|
|
10330
|
-
return session;
|
|
10331
|
-
},
|
|
10332
|
-
this._client,
|
|
10333
|
-
true,
|
|
10334
|
-
this._requestLogger
|
|
10335
|
-
);
|
|
10336
|
-
whip.start().then((_) => {
|
|
10337
|
-
this._requestLogger.debug("direct whip for ingressId=%s successful", whip.ingressId());
|
|
10338
|
-
}).catch((reason) => {
|
|
10339
|
-
this._controller.abort(reason);
|
|
10340
|
-
});
|
|
10341
|
-
const body = {
|
|
10342
|
-
sourceType: "WHIP",
|
|
10343
|
-
whipIngressId: whip.ingressId(),
|
|
10344
|
-
params: this._params.componentParams,
|
|
10345
|
-
roi: this._params.roi,
|
|
10346
|
-
fps: this._params.fps,
|
|
10347
|
-
camera: this._params.camera,
|
|
10348
|
-
version: this._version
|
|
10349
|
-
};
|
|
10350
|
-
if (this._params.motionDetect) {
|
|
10351
|
-
Object.assign(body, this._params.motionDetect);
|
|
10352
|
-
}
|
|
10353
|
-
const headers = {
|
|
10354
|
-
...session.authenticationHeaders(),
|
|
10355
|
-
Accept: "application/jsonl",
|
|
10356
|
-
"Content-Type": "application/json"
|
|
10357
|
-
};
|
|
10358
|
-
const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=preempt&processing=sync`;
|
|
10359
|
-
return await this._client.fetch(patchUrl, {
|
|
10360
|
-
headers,
|
|
10361
|
-
method: "PATCH",
|
|
10362
|
-
body: JSON.stringify(body),
|
|
10363
|
-
signal: this._controller.signal,
|
|
10364
|
-
// @ts-ignore
|
|
10365
|
-
eyepop: { responseStreaming: true }
|
|
10366
|
-
});
|
|
10367
|
-
}
|
|
10368
|
-
};
|
|
10369
|
-
|
|
10370
|
-
// shims/local_file.ts
|
|
10371
|
-
var resolvePath;
|
|
10372
|
-
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
10373
|
-
resolvePath = async (_source, _logger) => {
|
|
10374
|
-
throw new DOMException("resolving a path to a file is not supported in browser");
|
|
10375
|
-
};
|
|
10376
|
-
} else {
|
|
10377
|
-
let iteratorToStream = function(iterator) {
|
|
10378
|
-
return new ReadableStream({
|
|
10379
|
-
async pull(controller) {
|
|
10380
|
-
const { value, done } = await iterator.next();
|
|
10381
|
-
if (done) {
|
|
10382
|
-
controller.close();
|
|
10383
|
-
} else {
|
|
10384
|
-
controller.enqueue(new Uint8Array(value));
|
|
10385
|
-
}
|
|
10386
|
-
}
|
|
10387
|
-
});
|
|
10388
|
-
};
|
|
10389
|
-
iteratorToStream2 = iteratorToStream;
|
|
10390
|
-
async function* nodeStreamToIterator(stream) {
|
|
10391
|
-
for await (const chunk of stream) {
|
|
10392
|
-
yield chunk;
|
|
10393
|
-
}
|
|
10394
|
-
}
|
|
10395
|
-
resolvePath = async (source, _) => {
|
|
10396
|
-
const mime = require_mime_types();
|
|
10397
|
-
const filehandle = require("fs/promises");
|
|
10398
|
-
const fd = await filehandle.open(source.path, "r");
|
|
10399
|
-
const stream = fd.createReadStream();
|
|
10400
|
-
const iterator = nodeStreamToIterator(stream);
|
|
10401
|
-
const webStream = iteratorToStream(iterator);
|
|
10402
|
-
const mimeType = source.mimeType ? source.mimeType : mime.lookup(source.path) || "image/*";
|
|
10403
|
-
return {
|
|
10404
|
-
stream: webStream,
|
|
10405
|
-
mimeType
|
|
10406
|
-
};
|
|
10407
|
-
};
|
|
10408
|
-
}
|
|
10409
|
-
var iteratorToStream2;
|
|
10410
|
-
|
|
10411
|
-
// shims/http_client.ts
|
|
10412
|
-
var createHttpClient;
|
|
10413
|
-
function logLineFromRequest(input, init) {
|
|
10414
|
-
const url = input instanceof Request ? input.url : input;
|
|
10415
|
-
const method = input instanceof Request ? input.method : init?.method || (init?.body !== void 0 ? "POST" : "GET");
|
|
10416
|
-
return `${method} ${url}`;
|
|
10417
|
-
}
|
|
10418
|
-
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
10419
|
-
class BrowserHttpClient {
|
|
10420
|
-
constructor(logger) {
|
|
10421
|
-
this._logger = logger;
|
|
10422
|
-
}
|
|
10423
|
-
async fetch(input, init) {
|
|
10424
|
-
const logLine = logLineFromRequest(input, init);
|
|
10425
|
-
this._logger.debug(`Browser fetch() BEFORE ${logLine}`);
|
|
10426
|
-
const response = await fetch(input, init);
|
|
10427
|
-
this._logger.debug(`AFTER ${logLine} -> ${response.status}`);
|
|
10428
|
-
return response;
|
|
10429
|
-
}
|
|
10430
|
-
async close() {
|
|
10431
|
-
}
|
|
10432
|
-
isFullDuplex() {
|
|
10433
|
-
return false;
|
|
10434
|
-
}
|
|
10435
|
-
}
|
|
10436
|
-
createHttpClient = async (logger) => {
|
|
10437
|
-
return new BrowserHttpClient(logger);
|
|
10438
|
-
};
|
|
10439
|
-
} else {
|
|
10440
|
-
class NodeHttpClient {
|
|
10441
|
-
constructor(agent, fetchFn, logger) {
|
|
10442
|
-
this._agent = agent;
|
|
10443
|
-
this._fetch = fetchFn;
|
|
10444
|
-
this._logger = logger;
|
|
10445
|
-
}
|
|
10446
|
-
async fetch(input, init) {
|
|
10447
|
-
init || (init = {});
|
|
10448
|
-
init.dispatcher = this._agent;
|
|
10449
|
-
const logLine = logLineFromRequest(input, init);
|
|
10450
|
-
this._logger.debug(`Node fetch() BEFORE ${logLine}`);
|
|
10451
|
-
const response = await this._fetch(input, init);
|
|
10452
|
-
this._logger.debug(`AFTER ${logLine} -> ${response.status}`);
|
|
10453
|
-
return response;
|
|
10454
|
-
}
|
|
10455
|
-
async close() {
|
|
10456
|
-
return this._agent.close();
|
|
10457
|
-
}
|
|
10458
|
-
isFullDuplex() {
|
|
10459
|
-
return false;
|
|
10460
|
-
}
|
|
10461
|
-
}
|
|
10462
|
-
createHttpClient = async (logger) => {
|
|
10463
|
-
const undici = require("undici");
|
|
10464
|
-
const agent = new undici.Agent({
|
|
10465
|
-
keepAliveTimeout: 1e4,
|
|
10466
|
-
connections: 5,
|
|
10467
|
-
// two parallel streaming requests to the same host seem to deadlock with pipelining > 0
|
|
10468
|
-
pipelining: 0
|
|
10469
|
-
});
|
|
10470
|
-
logger.debug("Using node fetch with undici agent as http client");
|
|
10471
|
-
return new NodeHttpClient(agent, undici.fetch, logger);
|
|
10472
|
-
};
|
|
10473
|
-
}
|
|
10474
|
-
|
|
10475
|
-
// semaphore.ts
|
|
10476
|
-
var Semaphore = class {
|
|
10477
|
-
/**
|
|
10478
|
-
* Creates a semaphore.
|
|
10479
|
-
* @param permits The number of permits, i.e. strands of execution being allowed
|
|
10480
|
-
* to run in parallel.
|
|
10481
|
-
* This number can be initialized with a negative integer.
|
|
10482
|
-
*/
|
|
10483
|
-
constructor(permits) {
|
|
10484
|
-
this.promiseResolverQueue = [];
|
|
10485
|
-
this.permits = permits;
|
|
10486
|
-
}
|
|
10487
|
-
/**
|
|
10488
|
-
* Returns the number of available permits.
|
|
10489
|
-
* @returns The number of available permits.
|
|
10490
|
-
*/
|
|
10491
|
-
getPermits() {
|
|
10492
|
-
return this.permits;
|
|
10493
|
-
}
|
|
10494
|
-
/**
|
|
10495
|
-
* Returns a promise used to wait for a permit to become available. This method should be awaited on.
|
|
10496
|
-
* @returns A promise that gets resolved when execution is allowed to proceed.
|
|
10497
|
-
*/
|
|
10498
|
-
async wait() {
|
|
10499
|
-
if (this.permits > 0) {
|
|
10500
|
-
this.permits -= 1;
|
|
10501
|
-
return Promise.resolve(true);
|
|
10502
|
-
}
|
|
10503
|
-
return new Promise((resolver) => this.promiseResolverQueue.push(resolver));
|
|
10504
|
-
}
|
|
10505
|
-
/**
|
|
10506
|
-
* Alias for {@linkcode Semaphore.wait}.
|
|
10507
|
-
* @returns A promise that gets resolved when execution is allowed to proceed.
|
|
10508
|
-
*/
|
|
10509
|
-
async acquire() {
|
|
10510
|
-
return this.wait();
|
|
10511
|
-
}
|
|
10512
|
-
/**
|
|
10513
|
-
* Same as {@linkcode Semaphore.wait} except the promise returned gets resolved with false if no
|
|
10514
|
-
* permit becomes available in time.
|
|
10515
|
-
* @param milliseconds The time spent waiting before the wait is aborted. This is a lower bound,
|
|
10516
|
-
* you shouldn't rely on it being precise.
|
|
10517
|
-
* @returns A promise that gets resolved to true when execution is allowed to proceed or
|
|
10518
|
-
* false if the time given elapses before a permit becomes available.
|
|
10519
|
-
*/
|
|
10520
|
-
async waitFor(milliseconds) {
|
|
10521
|
-
if (this.permits > 0) {
|
|
10522
|
-
this.permits -= 1;
|
|
10523
|
-
return Promise.resolve(true);
|
|
10524
|
-
}
|
|
10525
|
-
let resolver = (_) => void 0;
|
|
10526
|
-
const promise = new Promise((r) => {
|
|
10527
|
-
resolver = r;
|
|
10528
|
-
});
|
|
10529
|
-
this.promiseResolverQueue.push(resolver);
|
|
10530
|
-
setTimeout(() => {
|
|
10531
|
-
const index = this.promiseResolverQueue.indexOf(resolver);
|
|
10532
|
-
if (index !== -1) {
|
|
10533
|
-
this.promiseResolverQueue.splice(index, 1);
|
|
10534
|
-
} else {
|
|
10535
|
-
console.warn(`Semaphore.waitFor couldn't find its promise resolver in the queue`);
|
|
10536
|
-
}
|
|
10537
|
-
resolver(false);
|
|
10538
|
-
}, milliseconds);
|
|
10539
|
-
return promise;
|
|
10540
|
-
}
|
|
10541
|
-
/**
|
|
10542
|
-
* Synchronous function that tries to acquire a permit and returns true if successful, false otherwise.
|
|
10543
|
-
* @returns Whether a permit could be acquired.
|
|
10544
|
-
*/
|
|
10545
|
-
tryAcquire() {
|
|
10546
|
-
if (this.permits > 0) {
|
|
10547
|
-
this.permits -= 1;
|
|
10548
|
-
return true;
|
|
10861
|
+
async startJob() {
|
|
10862
|
+
const session = await this._getSession();
|
|
10863
|
+
if (!session.baseUrl) {
|
|
10864
|
+
return Promise.reject("session.baseUrl must not ne null");
|
|
10549
10865
|
}
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10866
|
+
const whip = new WebrtcWhip(
|
|
10867
|
+
this._mediaStream,
|
|
10868
|
+
async () => {
|
|
10869
|
+
return session;
|
|
10870
|
+
},
|
|
10871
|
+
this._client,
|
|
10872
|
+
true,
|
|
10873
|
+
this._requestLogger
|
|
10874
|
+
);
|
|
10875
|
+
whip.start().then((_) => {
|
|
10876
|
+
this._requestLogger.debug("direct whip for ingressId=%s successful", whip.ingressId());
|
|
10877
|
+
}).catch((reason) => {
|
|
10878
|
+
this._controller.abort(reason);
|
|
10879
|
+
});
|
|
10880
|
+
const body = {
|
|
10881
|
+
sourceType: "WHIP",
|
|
10882
|
+
whipIngressId: whip.ingressId(),
|
|
10883
|
+
params: this._params.componentParams,
|
|
10884
|
+
roi: this._params.roi,
|
|
10885
|
+
fps: this._params.fps,
|
|
10886
|
+
camera: this._params.camera,
|
|
10887
|
+
version: this._version
|
|
10888
|
+
};
|
|
10889
|
+
if (this._params.motionDetect) {
|
|
10890
|
+
Object.assign(body, this._params.motionDetect);
|
|
10561
10891
|
}
|
|
10562
|
-
|
|
10892
|
+
const headers = {
|
|
10893
|
+
...session.authenticationHeaders(),
|
|
10894
|
+
Accept: "application/jsonl",
|
|
10895
|
+
"Content-Type": "application/json"
|
|
10896
|
+
};
|
|
10897
|
+
const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=preempt&processing=sync`;
|
|
10898
|
+
return await this._client.fetch(patchUrl, {
|
|
10899
|
+
headers,
|
|
10900
|
+
method: "PATCH",
|
|
10901
|
+
body: JSON.stringify(body),
|
|
10902
|
+
signal: this._controller.signal,
|
|
10903
|
+
// @ts-ignore
|
|
10904
|
+
eyepop: { responseStreaming: true }
|
|
10905
|
+
});
|
|
10563
10906
|
}
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10907
|
+
};
|
|
10908
|
+
|
|
10909
|
+
// shims/local_file.ts
|
|
10910
|
+
var resolvePath;
|
|
10911
|
+
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
10912
|
+
resolvePath = async (_source, _logger) => {
|
|
10913
|
+
throw new DOMException("resolving a path to a file is not supported in browser");
|
|
10914
|
+
};
|
|
10915
|
+
} else {
|
|
10916
|
+
let iteratorToStream = function(iterator) {
|
|
10917
|
+
return new ReadableStream({
|
|
10918
|
+
async pull(controller) {
|
|
10919
|
+
const { value, done } = await iterator.next();
|
|
10920
|
+
if (done) {
|
|
10921
|
+
controller.close();
|
|
10922
|
+
} else {
|
|
10923
|
+
controller.enqueue(new Uint8Array(value));
|
|
10924
|
+
}
|
|
10577
10925
|
}
|
|
10926
|
+
});
|
|
10927
|
+
};
|
|
10928
|
+
iteratorToStream2 = iteratorToStream;
|
|
10929
|
+
async function* nodeStreamToIterator(stream) {
|
|
10930
|
+
for await (const chunk of stream) {
|
|
10931
|
+
yield chunk;
|
|
10578
10932
|
}
|
|
10579
10933
|
}
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10934
|
+
resolvePath = async (source, _) => {
|
|
10935
|
+
const mime = require_mime_types();
|
|
10936
|
+
const filehandle = require("fs/promises");
|
|
10937
|
+
const fd = await filehandle.open(source.path, "r");
|
|
10938
|
+
const size = (await fd.stat()).size;
|
|
10939
|
+
const stream = fd.createReadStream();
|
|
10940
|
+
const iterator = nodeStreamToIterator(stream);
|
|
10941
|
+
const webStream = iteratorToStream(iterator);
|
|
10942
|
+
const mimeType = source.mimeType ? source.mimeType : mime.lookup(source.path) || "image/*";
|
|
10943
|
+
return {
|
|
10944
|
+
stream: webStream,
|
|
10945
|
+
mimeType,
|
|
10946
|
+
size
|
|
10947
|
+
};
|
|
10948
|
+
};
|
|
10949
|
+
}
|
|
10950
|
+
var iteratorToStream2;
|
|
10951
|
+
|
|
10952
|
+
// worker/worker_options.ts
|
|
10953
|
+
var TransientPopId = /* @__PURE__ */ ((TransientPopId2) => {
|
|
10954
|
+
TransientPopId2["Transient"] = "transient";
|
|
10955
|
+
return TransientPopId2;
|
|
10956
|
+
})(TransientPopId || {});
|
|
10957
|
+
|
|
10958
|
+
// worker/session_backend.ts
|
|
10959
|
+
var _SessionBackend = class _SessionBackend {
|
|
10960
|
+
constructor(ctx) {
|
|
10961
|
+
this.ctx = ctx;
|
|
10962
|
+
this._baseUrl = null;
|
|
10963
|
+
this._pipelineId = null;
|
|
10964
|
+
this._popName = null;
|
|
10965
|
+
this._sessionAccessToken = null;
|
|
10966
|
+
this._sessionAccessTokenValidUntil = null;
|
|
10967
|
+
// The account the connected compute session runs under, as compute-api reports it.
|
|
10968
|
+
this._accountId = void 0;
|
|
10969
|
+
this._pipeline = null;
|
|
10970
|
+
this._pipelineCreatePromise = null;
|
|
10585
10971
|
}
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10972
|
+
async disconnect(closeEndpoint) {
|
|
10973
|
+
await this.deleteOwnPipeline();
|
|
10974
|
+
await closeEndpoint();
|
|
10975
|
+
}
|
|
10976
|
+
async deleteOwnPipeline() {
|
|
10977
|
+
const client = this.ctx.client();
|
|
10978
|
+
if (this.ctx.options.popId == "transient" /* Transient */ && this._pipelineId && client) {
|
|
10979
|
+
const deleteUrl = `${this._baseUrl}/pipelines/${this._pipelineId}`;
|
|
10980
|
+
const headers = {
|
|
10981
|
+
Authorization: await this.authorizationHeader()
|
|
10982
|
+
};
|
|
10983
|
+
let response = await client.fetch(deleteUrl, {
|
|
10984
|
+
method: "DELETE",
|
|
10985
|
+
headers
|
|
10986
|
+
});
|
|
10987
|
+
if (response?.status != 204) {
|
|
10988
|
+
const message = await response?.text();
|
|
10989
|
+
this.ctx.logger.info(`stopping pipeline failed, status ${response?.status}: ${message}`);
|
|
10990
|
+
}
|
|
10599
10991
|
}
|
|
10600
10992
|
}
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
// shims/browser_session.ts
|
|
10607
|
-
var import_auth0_spa_js = require("@auth0/auth0-spa-js");
|
|
10608
|
-
var authenticateBrowserSession;
|
|
10609
|
-
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
10610
|
-
authenticateBrowserSession = async (auth0, options) => {
|
|
10611
|
-
if (options.eyepopUrl == null) {
|
|
10612
|
-
return Promise.reject("options.eyepopUrl cannot be null");
|
|
10993
|
+
onStatus(statusCode) {
|
|
10994
|
+
if (statusCode == 404) {
|
|
10995
|
+
this._pipelineId = null;
|
|
10996
|
+
this._baseUrl = null;
|
|
10613
10997
|
}
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10998
|
+
}
|
|
10999
|
+
async session() {
|
|
11000
|
+
let session = null;
|
|
11001
|
+
if (this._baseUrl == null) {
|
|
11002
|
+
await this.reconnect();
|
|
11003
|
+
if (this._baseUrl == null) {
|
|
11004
|
+
return Promise.reject("endpoint not connected");
|
|
10620
11005
|
}
|
|
10621
|
-
};
|
|
10622
|
-
const auth0Client = await (0, import_auth0_spa_js.createAuth0Client)(auth0ClientOptions);
|
|
10623
|
-
if (!await auth0Client.isAuthenticated()) {
|
|
10624
|
-
await auth0Client.loginWithPopup();
|
|
10625
|
-
}
|
|
10626
|
-
const accessToken = await auth0Client.getTokenSilently();
|
|
10627
|
-
if (!accessToken) {
|
|
10628
|
-
return Promise.reject("auth0 login failed");
|
|
10629
11006
|
}
|
|
10630
|
-
|
|
10631
|
-
const workerOptions = options;
|
|
10632
|
-
if (typeof workerOptions.popId != "undefined") {
|
|
10633
|
-
const workerSession = {
|
|
10634
|
-
eyepopUrl: options.eyepopUrl,
|
|
10635
|
-
accessToken,
|
|
10636
|
-
validUntil: Date.now() + 60 * 60 * 1e3,
|
|
10637
|
-
popId: workerOptions.popId,
|
|
10638
|
-
baseUrl: void 0,
|
|
10639
|
-
pipelineId: void 0,
|
|
10640
|
-
authenticationHeaders: () => {
|
|
10641
|
-
return { Authorization: `Bearer ${accessToken}` };
|
|
10642
|
-
}
|
|
10643
|
-
};
|
|
10644
|
-
session = workerSession;
|
|
10645
|
-
} else {
|
|
11007
|
+
if (this._sessionAccessToken) {
|
|
10646
11008
|
session = {
|
|
10647
|
-
eyepopUrl: options.eyepopUrl,
|
|
10648
|
-
accessToken,
|
|
10649
|
-
validUntil:
|
|
11009
|
+
eyepopUrl: this.ctx.options.eyepopUrl,
|
|
11010
|
+
accessToken: this._sessionAccessToken,
|
|
11011
|
+
validUntil: this._sessionAccessTokenValidUntil || Number.MAX_VALUE
|
|
10650
11012
|
};
|
|
10651
|
-
}
|
|
10652
|
-
return session;
|
|
10653
|
-
};
|
|
10654
|
-
} else {
|
|
10655
|
-
authenticateBrowserSession = (_auth0, _options) => {
|
|
10656
|
-
return Promise.reject("auth0 login not supported server-side");
|
|
10657
|
-
};
|
|
10658
|
-
}
|
|
10659
|
-
|
|
10660
|
-
// endpoint.ts
|
|
10661
|
-
var Endpoint = class {
|
|
10662
|
-
constructor(options) {
|
|
10663
|
-
this._options = options;
|
|
10664
|
-
this._token = null;
|
|
10665
|
-
this._expire_token_time = null;
|
|
10666
|
-
this._client = null;
|
|
10667
|
-
this._statusRetryHandlers = /* @__PURE__ */ new Map();
|
|
10668
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
10669
|
-
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
10670
|
-
this._state = "Idle" /* Idle */;
|
|
10671
|
-
this._message = null;
|
|
10672
|
-
this._stateChangeHandler = null;
|
|
10673
|
-
let rootLogger;
|
|
10674
|
-
if (options.logger) {
|
|
10675
|
-
rootLogger = options.logger;
|
|
10676
11013
|
} else {
|
|
10677
|
-
|
|
11014
|
+
session = await this.ctx.credentialSession();
|
|
11015
|
+
}
|
|
11016
|
+
return {
|
|
11017
|
+
eyepopUrl: session.eyepopUrl,
|
|
11018
|
+
accessToken: session.accessToken,
|
|
11019
|
+
validUntil: session.validUntil,
|
|
11020
|
+
popId: this.ctx.options.popId,
|
|
11021
|
+
accountId: this._accountId ?? this.ctx.options.accountId,
|
|
11022
|
+
baseUrl: this._baseUrl,
|
|
11023
|
+
pipelineId: this._pipelineId || void 0,
|
|
11024
|
+
isPipelineMode: false,
|
|
11025
|
+
authenticationHeaders: () => this._authenticationHeaders(session)
|
|
11026
|
+
};
|
|
11027
|
+
}
|
|
11028
|
+
popName() {
|
|
11029
|
+
return this._popName;
|
|
11030
|
+
}
|
|
11031
|
+
pop() {
|
|
11032
|
+
if (this._pipeline) {
|
|
11033
|
+
return this._pipeline.pop || null;
|
|
10678
11034
|
}
|
|
10679
|
-
this.
|
|
10680
|
-
this._requestLogger = this._logger.child({ module: "requests" });
|
|
11035
|
+
return this.ctx.options.pop || null;
|
|
10681
11036
|
}
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
11037
|
+
/**
|
|
11038
|
+
* A select forward's results come only at V3. Only a Pop that selects asks
|
|
11039
|
+
* for it, so a worker too old to know V3 - which also rejects that Pop - is
|
|
11040
|
+
* never sent a version it would not accept.
|
|
11041
|
+
*/
|
|
11042
|
+
predictionVersion() {
|
|
11043
|
+
return popSelects(this.pop()) ? 3 /* V3 */ : DEFAULT_PREDICTION_VERSION;
|
|
10685
11044
|
}
|
|
10686
|
-
|
|
10687
|
-
if (this.
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
return "https://compute.eyepop.ai";
|
|
11045
|
+
async changePop(pop) {
|
|
11046
|
+
if (this.ctx.options.popId == "transient" /* Transient */) {
|
|
11047
|
+
await this.changeTransientPop(pop);
|
|
11048
|
+
return;
|
|
10691
11049
|
}
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
11050
|
+
const client = this.ctx.client();
|
|
11051
|
+
const baseUrl = this._baseUrl;
|
|
11052
|
+
if (!baseUrl || !client) {
|
|
11053
|
+
return Promise.reject("endpoint not connected, use connect()");
|
|
11054
|
+
}
|
|
11055
|
+
let response = await this.ctx.fetchWithRetry(async () => {
|
|
11056
|
+
const session = await this.session();
|
|
11057
|
+
let headers = {
|
|
11058
|
+
"Content-Type": "application/json",
|
|
11059
|
+
...this._authenticationHeaders(session)
|
|
11060
|
+
};
|
|
11061
|
+
const patch_url = `${session.baseUrl}/pipelines/${session.pipelineId}/pop`;
|
|
11062
|
+
return await client.fetch(patch_url, {
|
|
11063
|
+
method: "PATCH",
|
|
11064
|
+
body: JSON.stringify(pop),
|
|
11065
|
+
headers
|
|
11066
|
+
});
|
|
11067
|
+
});
|
|
11068
|
+
if (response.status != 204) {
|
|
11069
|
+
const message = await response.text();
|
|
11070
|
+
throw new Error(`Unexpected status ${response.status}: ${message}`);
|
|
11071
|
+
}
|
|
11072
|
+
this.ctx.options.pop = pop;
|
|
11073
|
+
if (this._pipeline) {
|
|
11074
|
+
this._pipeline.pop = pop;
|
|
10697
11075
|
} else {
|
|
10698
|
-
|
|
11076
|
+
this._pipeline = {
|
|
11077
|
+
id: this._pipelineId || "unknown",
|
|
11078
|
+
state: "idle",
|
|
11079
|
+
startTime: (/* @__PURE__ */ new Date()).toString(),
|
|
11080
|
+
pop
|
|
11081
|
+
};
|
|
10699
11082
|
}
|
|
10700
11083
|
}
|
|
10701
|
-
|
|
10702
|
-
return (this.
|
|
10703
|
-
}
|
|
10704
|
-
state() {
|
|
10705
|
-
return this._state;
|
|
11084
|
+
_authenticationHeaders(session) {
|
|
11085
|
+
return resolveAuth(this.ctx.options)?.isLocal !== void 0 ? {} : { Authorization: `Bearer ${session.accessToken}` };
|
|
10706
11086
|
}
|
|
10707
|
-
|
|
10708
|
-
|
|
11087
|
+
async submit(work) {
|
|
11088
|
+
switch (work.kind) {
|
|
11089
|
+
case "source":
|
|
11090
|
+
return await this.process(work.source, work.params);
|
|
11091
|
+
case "uploadGroup":
|
|
11092
|
+
return await this.uploadGroup(work.paths, work.params);
|
|
11093
|
+
case "uploadStreamGroup":
|
|
11094
|
+
return await this.uploadStreamGroup(work.streams, work.mimeTypes, work.params);
|
|
11095
|
+
case "loadFromGroup":
|
|
11096
|
+
return await this.loadFromGroup(work.urls, work.params);
|
|
11097
|
+
}
|
|
10709
11098
|
}
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
11099
|
+
async process(source, params) {
|
|
11100
|
+
if (source.file !== void 0) {
|
|
11101
|
+
return this.uploadFile(source, params);
|
|
11102
|
+
} else if (source.stream !== void 0) {
|
|
11103
|
+
return this.uploadStream(source, params);
|
|
11104
|
+
} else if (source.path !== void 0) {
|
|
11105
|
+
return this.uploadPath(source, params);
|
|
11106
|
+
} else if (source.url !== void 0) {
|
|
11107
|
+
return this.loadFrom(source, params);
|
|
11108
|
+
} else if (source.assetUuid !== void 0) {
|
|
11109
|
+
return this.loadFromAssetUuid(source, params);
|
|
11110
|
+
} else if (source.mediaStream !== void 0) {
|
|
11111
|
+
return this.loadMediaStream(source, params);
|
|
11112
|
+
} else {
|
|
11113
|
+
return Promise.reject("unknown source type");
|
|
11114
|
+
}
|
|
10713
11115
|
}
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
11116
|
+
start(job) {
|
|
11117
|
+
return job.start(
|
|
11118
|
+
() => {
|
|
11119
|
+
this.ctx.jobDone();
|
|
11120
|
+
},
|
|
11121
|
+
(statusCode) => {
|
|
11122
|
+
if (statusCode == 404) {
|
|
11123
|
+
this.onStatus(statusCode);
|
|
11124
|
+
} else if (statusCode == 401) {
|
|
11125
|
+
this.ctx.forgetCredential();
|
|
11126
|
+
}
|
|
10721
11127
|
}
|
|
11128
|
+
);
|
|
11129
|
+
}
|
|
11130
|
+
async uploadGroup(paths, params) {
|
|
11131
|
+
const client = this.ctx.client();
|
|
11132
|
+
const limit = this.ctx.limit();
|
|
11133
|
+
if (!this._baseUrl || !this._pipelineId || !client) {
|
|
11134
|
+
throw new Error("endpoint not connected, use connect()");
|
|
10722
11135
|
}
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
11136
|
+
await limit.acquire();
|
|
11137
|
+
try {
|
|
11138
|
+
this.ctx.updateState();
|
|
11139
|
+
const sources = await Promise.all(
|
|
11140
|
+
paths.map(async (path2) => {
|
|
11141
|
+
const streamSource = await this.resolvePath({ path: path2 });
|
|
11142
|
+
return { stream: streamSource.stream, mimeType: streamSource.mimeType };
|
|
11143
|
+
})
|
|
11144
|
+
);
|
|
11145
|
+
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11146
|
+
} catch (e) {
|
|
11147
|
+
limit.release();
|
|
11148
|
+
throw e;
|
|
10727
11149
|
}
|
|
10728
11150
|
}
|
|
10729
|
-
async
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
11151
|
+
async uploadStreamGroup(streams, mimeTypes, params) {
|
|
11152
|
+
const client = this.ctx.client();
|
|
11153
|
+
const limit = this.ctx.limit();
|
|
11154
|
+
if (!this._baseUrl || !this._pipelineId || !client) {
|
|
11155
|
+
throw new Error("endpoint not connected, use connect()");
|
|
10733
11156
|
}
|
|
10734
|
-
|
|
10735
|
-
|
|
11157
|
+
await limit.acquire();
|
|
11158
|
+
try {
|
|
11159
|
+
this.ctx.updateState();
|
|
11160
|
+
const sources = streams.map((stream, i) => ({
|
|
11161
|
+
stream,
|
|
11162
|
+
mimeType: mimeTypes?.[i] ?? null
|
|
11163
|
+
}));
|
|
11164
|
+
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11165
|
+
} catch (e) {
|
|
11166
|
+
limit.release();
|
|
11167
|
+
throw e;
|
|
10736
11168
|
}
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
11169
|
+
}
|
|
11170
|
+
async loadFromGroup(urls, params) {
|
|
11171
|
+
const client = this.ctx.client();
|
|
11172
|
+
const limit = this.ctx.limit();
|
|
11173
|
+
if (!this._baseUrl || !this._pipelineId || !client) {
|
|
11174
|
+
throw new Error("endpoint not connected, use connect()");
|
|
10740
11175
|
}
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
this.
|
|
10744
|
-
|
|
10745
|
-
}
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
} else {
|
|
10749
|
-
return Promise.reject("option apiKey/accessToken or environment variable EYEPOP_API_KEY is required");
|
|
11176
|
+
await limit.acquire();
|
|
11177
|
+
try {
|
|
11178
|
+
this.ctx.updateState();
|
|
11179
|
+
return this.start(new LoadFromGroupJob(urls, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11180
|
+
} catch (e) {
|
|
11181
|
+
limit.release();
|
|
11182
|
+
throw e;
|
|
10750
11183
|
}
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
11184
|
+
}
|
|
11185
|
+
async resolvePath(source) {
|
|
11186
|
+
if (this.ctx.options.platformSupport?.resolvePath) {
|
|
11187
|
+
return await this.ctx.options.platformSupport.resolvePath(source, this.ctx.logger);
|
|
10755
11188
|
}
|
|
10756
|
-
return await this.
|
|
11189
|
+
return await resolvePath(source, this.ctx.logger);
|
|
10757
11190
|
}
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
11191
|
+
/** Start a job once the transient pipeline runs and a queue permit is held; the permit is released if it cannot start. */
|
|
11192
|
+
async startConnected(makeJob, rejectsWithText = false) {
|
|
11193
|
+
await this.ensureTransientPipelineStarted();
|
|
11194
|
+
const client = this.ctx.client();
|
|
11195
|
+
const limit = this.ctx.limit();
|
|
11196
|
+
if (!this._baseUrl || !this._pipelineId || !client) {
|
|
11197
|
+
if (rejectsWithText) {
|
|
11198
|
+
return Promise.reject("endpoint not connected, use connect()");
|
|
10762
11199
|
}
|
|
11200
|
+
throw new Error("endpoint not connected, use connect()");
|
|
10763
11201
|
}
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
10767
|
-
this.
|
|
11202
|
+
await limit.acquire();
|
|
11203
|
+
try {
|
|
11204
|
+
this.ctx.updateState();
|
|
11205
|
+
return this.start(await makeJob(client));
|
|
11206
|
+
} catch (e) {
|
|
11207
|
+
limit.release();
|
|
11208
|
+
throw e;
|
|
10768
11209
|
}
|
|
10769
11210
|
}
|
|
10770
|
-
async
|
|
10771
|
-
await this.
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
11211
|
+
async uploadFile(source, params) {
|
|
11212
|
+
return await this.startConnected(
|
|
11213
|
+
async (client) => new UploadJob(
|
|
11214
|
+
source.file,
|
|
11215
|
+
source.file.type,
|
|
11216
|
+
source.file.size,
|
|
11217
|
+
source.videoMode,
|
|
11218
|
+
params,
|
|
11219
|
+
async () => this.session(),
|
|
11220
|
+
client,
|
|
11221
|
+
this.ctx.requestLogger,
|
|
11222
|
+
void 0,
|
|
11223
|
+
this.predictionVersion()
|
|
11224
|
+
),
|
|
11225
|
+
true
|
|
11226
|
+
);
|
|
10780
11227
|
}
|
|
10781
|
-
|
|
10782
|
-
this.
|
|
10783
|
-
|
|
11228
|
+
async uploadStream(source, params) {
|
|
11229
|
+
return await this.startConnected(
|
|
11230
|
+
async (client) => new UploadJob(source.stream, source.mimeType, source.size, source.videoMode, params, async () => this.session(), client, this.ctx.requestLogger, void 0, this.predictionVersion()),
|
|
11231
|
+
true
|
|
11232
|
+
);
|
|
10784
11233
|
}
|
|
10785
|
-
async
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
if (handler != null) {
|
|
10794
|
-
handler(response.status);
|
|
10795
|
-
if (retries--) {
|
|
10796
|
-
this._requestLogger.info("received %d response, retry", response.status);
|
|
10797
|
-
} else {
|
|
10798
|
-
return response;
|
|
10799
|
-
}
|
|
10800
|
-
} else {
|
|
10801
|
-
return response;
|
|
10802
|
-
}
|
|
11234
|
+
async uploadPath(source, params) {
|
|
11235
|
+
return await this.startConnected(async (client) => {
|
|
11236
|
+
const streamSource = await this.resolvePath(source);
|
|
11237
|
+
let firstStreamPending = true;
|
|
11238
|
+
const uploadBodyFactory = async () => {
|
|
11239
|
+
if (firstStreamPending) {
|
|
11240
|
+
firstStreamPending = false;
|
|
11241
|
+
return streamSource.stream;
|
|
10803
11242
|
}
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
11243
|
+
return (await this.resolvePath(source)).stream;
|
|
11244
|
+
};
|
|
11245
|
+
return new UploadJob(
|
|
11246
|
+
streamSource.stream,
|
|
11247
|
+
streamSource.mimeType,
|
|
11248
|
+
streamSource.size,
|
|
11249
|
+
source.videoMode,
|
|
11250
|
+
params,
|
|
11251
|
+
async () => this.session(),
|
|
11252
|
+
client,
|
|
11253
|
+
this.ctx.requestLogger,
|
|
11254
|
+
uploadBodyFactory,
|
|
11255
|
+
this.predictionVersion()
|
|
11256
|
+
);
|
|
11257
|
+
});
|
|
11258
|
+
}
|
|
11259
|
+
async loadFrom(source, params) {
|
|
11260
|
+
if (source.url.startsWith("file://")) {
|
|
11261
|
+
return await this.uploadPath({ path: source.url.substring("file://".length) }, params);
|
|
11262
|
+
}
|
|
11263
|
+
return await this.startConnected(async (client) => new LoadFromJob(source.url, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11264
|
+
}
|
|
11265
|
+
async loadFromAssetUuid(source, params) {
|
|
11266
|
+
return await this.startConnected(async (client) => new LoadFromAssetUuidJob(source.assetUuid, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11267
|
+
}
|
|
11268
|
+
async loadMediaStream(source, params) {
|
|
11269
|
+
return await this.startConnected(async (client) => new LoadMediaStreamJob(source.mediaStream, params, async () => this.session(), client, this.ctx.requestLogger, this.predictionVersion()));
|
|
11270
|
+
}
|
|
11271
|
+
async reconnect() {
|
|
11272
|
+
if (this.ctx.options.popId == "transient" /* Transient */) {
|
|
11273
|
+
await this.getComputeSession();
|
|
11274
|
+
if (this.ctx.options.pop) {
|
|
11275
|
+
this.setTransientPipelinePop();
|
|
11276
|
+
}
|
|
11277
|
+
} else if (this.ctx.options.sessionUuid) {
|
|
11278
|
+
await this.getComputeSession();
|
|
11279
|
+
} else {
|
|
11280
|
+
await this.startWebApiSessionWithPopId();
|
|
11281
|
+
}
|
|
11282
|
+
this.ctx.updateState();
|
|
11283
|
+
}
|
|
11284
|
+
async startWebApiSessionWithPopId() {
|
|
11285
|
+
const client = this.ctx.client();
|
|
11286
|
+
if (!client) {
|
|
11287
|
+
throw new Error("endpoint not initialized");
|
|
11288
|
+
}
|
|
11289
|
+
const config_url = `${this.ctx.eyepopWebApiUrl()}/pops/${this.ctx.options.popId}/config?auto_start=false`;
|
|
11290
|
+
const headers = {
|
|
11291
|
+
Authorization: await this.authorizationHeader()
|
|
11292
|
+
};
|
|
11293
|
+
this.ctx.updateState("FetchConfig" /* FetchConfig */);
|
|
11294
|
+
let response = await client.fetch(config_url, { headers });
|
|
11295
|
+
if (response.status == 401) {
|
|
11296
|
+
this.ctx.requestLogger.debug("GET %s: 401, about to retry with fresh access token", config_url);
|
|
11297
|
+
this.ctx.forgetCredential();
|
|
11298
|
+
const headers2 = {
|
|
11299
|
+
Authorization: await this.authorizationHeader()
|
|
11300
|
+
};
|
|
11301
|
+
this.ctx.updateState("FetchConfig" /* FetchConfig */);
|
|
11302
|
+
response = await client.fetch(config_url, {
|
|
11303
|
+
headers: headers2
|
|
11304
|
+
});
|
|
11305
|
+
}
|
|
11306
|
+
if (response.status != 200) {
|
|
11307
|
+
const message = await response.text();
|
|
11308
|
+
if (response.status == 429) {
|
|
11309
|
+
this.ctx.updateState("NotAvailable" /* NotAvailable */, message);
|
|
11310
|
+
} else {
|
|
11311
|
+
this.ctx.updateState("Error" /* Error */, message);
|
|
11312
|
+
}
|
|
11313
|
+
throw new Error(`Unexpected status ${response.status}: ${message}`);
|
|
11314
|
+
}
|
|
11315
|
+
let config = await response.json();
|
|
11316
|
+
if (!config.base_url && this.ctx.options.autoStart) {
|
|
11317
|
+
const auto_start_config_url = `${this.ctx.eyepopWebApiUrl()}/pops/${this.ctx.options.popId}/config?auto_start=true`;
|
|
11318
|
+
this.ctx.requestLogger.debug("pop was not running, trying to autostart with: %s", auto_start_config_url);
|
|
11319
|
+
const headers2 = {
|
|
11320
|
+
Authorization: await this.authorizationHeader()
|
|
11321
|
+
};
|
|
11322
|
+
response = await client.fetch(auto_start_config_url, {
|
|
11323
|
+
headers: headers2
|
|
11324
|
+
});
|
|
11325
|
+
if (response.status != 200) {
|
|
11326
|
+
this.ctx.updateState("Error" /* Error */);
|
|
11327
|
+
const message = await response.text();
|
|
11328
|
+
throw new Error(`Unexpected status ${response.status}: ${message}`);
|
|
11329
|
+
}
|
|
11330
|
+
config = await response.json();
|
|
11331
|
+
}
|
|
11332
|
+
const baseUrl = new URL(config.base_url, this.ctx.eyepopUrl());
|
|
11333
|
+
this._baseUrl = baseUrl.toString();
|
|
11334
|
+
if (this._baseUrl) {
|
|
11335
|
+
this._baseUrl = this._baseUrl.replace(/\/+$/, "");
|
|
11336
|
+
this._pipelineId = config.pipeline_id;
|
|
11337
|
+
this._popName = config.name;
|
|
11338
|
+
if (!this._pipelineId || !this._baseUrl) {
|
|
11339
|
+
throw new Error("Pop not started");
|
|
11340
|
+
}
|
|
11341
|
+
const get_url = `${this._baseUrl}/pipelines/${this._pipelineId}`;
|
|
11342
|
+
const headers2 = {
|
|
11343
|
+
Authorization: await this.authorizationHeader()
|
|
11344
|
+
};
|
|
11345
|
+
let response2 = await client.fetch(get_url, {
|
|
11346
|
+
method: "GET",
|
|
11347
|
+
headers: headers2
|
|
11348
|
+
});
|
|
11349
|
+
if (response2.status > 200) {
|
|
11350
|
+
const message = await response2.text();
|
|
11351
|
+
throw new Error(`Unexpected status ${response2.status}: ${message}`);
|
|
11352
|
+
}
|
|
11353
|
+
this._pipeline = await response2.json();
|
|
11354
|
+
if (this.ctx.options.stopJobs) {
|
|
11355
|
+
const body = { sourceType: "NONE" };
|
|
11356
|
+
const headers3 = {
|
|
11357
|
+
Authorization: await this.authorizationHeader(),
|
|
11358
|
+
"Content-Type": "application/json"
|
|
11359
|
+
};
|
|
11360
|
+
const stop_url = `${this._baseUrl}/pipelines/${this._pipeline.id}/source?mode=preempt&processing=sync`;
|
|
11361
|
+
let response3 = await client.fetch(stop_url, {
|
|
11362
|
+
method: "PATCH",
|
|
11363
|
+
headers: headers3,
|
|
11364
|
+
body: JSON.stringify(body)
|
|
11365
|
+
});
|
|
11366
|
+
if (response3.status >= 300) {
|
|
11367
|
+
const message = await response3.text();
|
|
11368
|
+
throw new Error(`Unexpected status ${response3.status}: ${message}`);
|
|
10815
11369
|
}
|
|
10816
11370
|
}
|
|
10817
11371
|
}
|
|
10818
11372
|
}
|
|
10819
|
-
|
|
10820
|
-
|
|
11373
|
+
sessionReadyTimeoutMs() {
|
|
11374
|
+
const seconds = this.ctx.options.sessionReadyTimeoutSeconds;
|
|
11375
|
+
return typeof seconds == "number" && Number.isFinite(seconds) && seconds > 0 ? seconds * 1e3 : _SessionBackend.WAIT_FOR_IS_READY;
|
|
10821
11376
|
}
|
|
10822
|
-
async
|
|
10823
|
-
|
|
10824
|
-
|
|
11377
|
+
async getComputeSession() {
|
|
11378
|
+
const client = this.ctx.client();
|
|
11379
|
+
if (!client) {
|
|
11380
|
+
throw new Error("endpoint not initialized");
|
|
10825
11381
|
}
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
} else if (accessTokenCredential(auth) !== void 0) {
|
|
10846
|
-
this._token = accessTokenCredential(auth);
|
|
10847
|
-
this._expire_token_time = Number.MAX_VALUE;
|
|
10848
|
-
} else if (this._options.eyepopUrl) {
|
|
10849
|
-
const credential = apiKeyCredential(auth);
|
|
10850
|
-
if (credential === void 0) {
|
|
10851
|
-
return Promise.reject("no valid auth option");
|
|
10852
|
-
}
|
|
10853
|
-
const headers = { Authorization: `Bearer ${credential}` };
|
|
10854
|
-
const post_url = `${this.eyepopUrl()}/v1/auth/authenticate`;
|
|
10855
|
-
const response = await this._client.fetch(post_url, {
|
|
10856
|
-
method: "POST",
|
|
10857
|
-
headers
|
|
10858
|
-
});
|
|
10859
|
-
if (response.status != 200) {
|
|
10860
|
-
const message = await response.text();
|
|
10861
|
-
return Promise.reject(`Unexpected status ${response.status}: ${message}`);
|
|
10862
|
-
}
|
|
10863
|
-
const data = await response.json();
|
|
10864
|
-
const token = data;
|
|
10865
|
-
this._token = token.access_token;
|
|
10866
|
-
this._expire_token_time = now + token.expires_in - 60;
|
|
10867
|
-
} else {
|
|
10868
|
-
return Promise.reject("no valid auth option");
|
|
10869
|
-
}
|
|
10870
|
-
} finally {
|
|
10871
|
-
this.updateState();
|
|
10872
|
-
}
|
|
11382
|
+
let resolved;
|
|
11383
|
+
try {
|
|
11384
|
+
resolved = await new ComputeSessionClient({
|
|
11385
|
+
computeUrl: this.ctx.eyepopUrl(),
|
|
11386
|
+
httpClient: client,
|
|
11387
|
+
authorizationHeader: () => this.computeAuthorizationHeader(),
|
|
11388
|
+
sessionUuid: this.ctx.options.sessionUuid,
|
|
11389
|
+
sessionName: this.ctx.options.sessionName,
|
|
11390
|
+
accountId: this.ctx.options.accountId,
|
|
11391
|
+
pipelineImage: this.ctx.options.pipelineImage,
|
|
11392
|
+
pipelineVersion: this.ctx.options.pipelineVersion,
|
|
11393
|
+
pop: this.ctx.options.pop,
|
|
11394
|
+
readyTimeoutMs: this.sessionReadyTimeoutMs(),
|
|
11395
|
+
logger: this.ctx.logger,
|
|
11396
|
+
requestLogger: this.ctx.requestLogger
|
|
11397
|
+
}).resolve();
|
|
11398
|
+
} catch (error) {
|
|
11399
|
+
this.ctx.updateState("Error" /* Error */);
|
|
11400
|
+
throw error;
|
|
10873
11401
|
}
|
|
10874
|
-
this.
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
// worker/worker_options.ts
|
|
10880
|
-
var TransientPopId = /* @__PURE__ */ ((TransientPopId2) => {
|
|
10881
|
-
TransientPopId2["Transient"] = "transient";
|
|
10882
|
-
return TransientPopId2;
|
|
10883
|
-
})(TransientPopId || {});
|
|
10884
|
-
|
|
10885
|
-
// worker/worker_endpoint.ts
|
|
10886
|
-
var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
10887
|
-
constructor(options) {
|
|
10888
|
-
super(options);
|
|
10889
|
-
this.setStatusRetryHandlers(/* @__PURE__ */ new Map([[404, (statusCode) => this.statusHandler404(statusCode)]]));
|
|
10890
|
-
this._baseUrl = null;
|
|
10891
|
-
this._pipelineId = null;
|
|
10892
|
-
this._popName = null;
|
|
10893
|
-
this._sessionAccessToken = null;
|
|
10894
|
-
this._sessionAccessTokenValidUntil = null;
|
|
10895
|
-
this._accountId = void 0;
|
|
11402
|
+
this._sessionAccessToken = resolved.accessToken;
|
|
11403
|
+
this._sessionAccessTokenValidUntil = resolved.accessTokenValidUntil;
|
|
11404
|
+
this._baseUrl = resolved.session.session_endpoint;
|
|
11405
|
+
this._accountId = resolved.session.account_uuid || void 0;
|
|
10896
11406
|
this._pipeline = null;
|
|
10897
|
-
this.
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
11407
|
+
this._pipelineId = resolved.pipelineId;
|
|
11408
|
+
if (this.ctx.options.pop) {
|
|
11409
|
+
this.setTransientPipelinePop();
|
|
11410
|
+
}
|
|
10901
11411
|
}
|
|
10902
|
-
async
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
11412
|
+
async startTransientPipeline() {
|
|
11413
|
+
const client = this.ctx.client();
|
|
11414
|
+
const baseUrl = this._baseUrl;
|
|
11415
|
+
if (client == null || baseUrl == null) {
|
|
11416
|
+
throw new Error("endpoint not initialized");
|
|
11417
|
+
}
|
|
11418
|
+
let pop;
|
|
11419
|
+
if (this.ctx.options.pop) {
|
|
11420
|
+
pop = this.ctx.options.pop;
|
|
11421
|
+
} else if (this._pipeline && this._pipeline.pop) {
|
|
11422
|
+
pop = this._pipeline.pop;
|
|
11423
|
+
} else {
|
|
11424
|
+
throw new Error("Cannot start a transient pipeline without a pop");
|
|
11425
|
+
}
|
|
11426
|
+
const body = {
|
|
11427
|
+
pop,
|
|
11428
|
+
source: {
|
|
11429
|
+
sourceType: "NONE"
|
|
11430
|
+
},
|
|
11431
|
+
idleTimeoutSeconds: 30,
|
|
11432
|
+
logging: ["out_meta"],
|
|
11433
|
+
videoOutput: "no_output"
|
|
11434
|
+
};
|
|
11435
|
+
const post_url = `${baseUrl.replace(/\/+$/, "")}/pipelines`;
|
|
11436
|
+
const response = await this.ctx.fetchWithRetry(async () => {
|
|
11437
|
+
let headers = {
|
|
11438
|
+
Accept: "application/json",
|
|
11439
|
+
"Content-Type": "application/json",
|
|
10906
11440
|
Authorization: await this.authorizationHeader()
|
|
10907
11441
|
};
|
|
10908
|
-
|
|
10909
|
-
method: "
|
|
11442
|
+
return client.fetch(post_url, {
|
|
11443
|
+
method: "POST",
|
|
11444
|
+
body: JSON.stringify(body),
|
|
10910
11445
|
headers
|
|
10911
11446
|
});
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
}
|
|
11447
|
+
});
|
|
11448
|
+
if (response.status != 200) {
|
|
11449
|
+
const message = await response.text();
|
|
11450
|
+
throw new Error(`Unexpected status ${response.status} starting transient pipeline: ${message.slice(0, 300)}`);
|
|
10916
11451
|
}
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
statusHandler404(_) {
|
|
10920
|
-
this._pipelineId = null;
|
|
10921
|
-
this._baseUrl = null;
|
|
11452
|
+
const result = await response.json();
|
|
11453
|
+
return result["id"];
|
|
10922
11454
|
}
|
|
10923
|
-
async
|
|
10924
|
-
|
|
10925
|
-
|
|
11455
|
+
async ensureTransientPipelineStarted() {
|
|
11456
|
+
if (this.ctx.options.popId != "transient" /* Transient */ || this._pipelineId) {
|
|
11457
|
+
return;
|
|
11458
|
+
}
|
|
11459
|
+
if (!this.ctx.options.pop) {
|
|
11460
|
+
return;
|
|
11461
|
+
}
|
|
11462
|
+
if (!this._baseUrl) {
|
|
10926
11463
|
await this.reconnect();
|
|
10927
|
-
|
|
10928
|
-
|
|
11464
|
+
}
|
|
11465
|
+
if (this._pipelineId) {
|
|
11466
|
+
return;
|
|
11467
|
+
}
|
|
11468
|
+
if (this.ctx.options.pop) {
|
|
11469
|
+
await this.reconnect();
|
|
11470
|
+
if (!this._pipelineId) {
|
|
11471
|
+
throw new Error("Compute session did not provide a pipeline for the requested pop");
|
|
10929
11472
|
}
|
|
11473
|
+
return;
|
|
10930
11474
|
}
|
|
10931
|
-
if (this.
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
}
|
|
10938
|
-
|
|
11475
|
+
if (!this._pipelineCreatePromise) {
|
|
11476
|
+
this._pipelineCreatePromise = this.startTransientPipeline();
|
|
11477
|
+
}
|
|
11478
|
+
try {
|
|
11479
|
+
this._pipelineId = await this._pipelineCreatePromise;
|
|
11480
|
+
this.setTransientPipelinePop();
|
|
11481
|
+
} finally {
|
|
11482
|
+
this._pipelineCreatePromise = null;
|
|
10939
11483
|
}
|
|
10940
|
-
return {
|
|
10941
|
-
eyepopUrl: session.eyepopUrl,
|
|
10942
|
-
accessToken: session.accessToken,
|
|
10943
|
-
validUntil: session.validUntil,
|
|
10944
|
-
popId: this.options().popId,
|
|
10945
|
-
accountId: this._accountId ?? this.options().accountId,
|
|
10946
|
-
baseUrl: this._baseUrl,
|
|
10947
|
-
pipelineId: this._pipelineId || void 0,
|
|
10948
|
-
authenticationHeaders: () => this._authenticationHeaders(session)
|
|
10949
|
-
};
|
|
10950
11484
|
}
|
|
10951
|
-
|
|
10952
|
-
|
|
11485
|
+
async changeTransientPop(pop) {
|
|
11486
|
+
if (!this.ctx.client()) {
|
|
11487
|
+
throw new Error("endpoint not initialized");
|
|
11488
|
+
}
|
|
11489
|
+
const previousPop = this.ctx.options.pop;
|
|
11490
|
+
const previousBaseUrl = this._baseUrl;
|
|
11491
|
+
const previousSessionAccessToken = this._sessionAccessToken;
|
|
11492
|
+
const previousSessionAccessTokenValidUntil = this._sessionAccessTokenValidUntil;
|
|
11493
|
+
if (this._pipelineId) {
|
|
11494
|
+
await this.deleteTransientPipeline(this._pipelineId);
|
|
11495
|
+
this._pipelineId = null;
|
|
11496
|
+
}
|
|
11497
|
+
this._pipeline = null;
|
|
11498
|
+
try {
|
|
11499
|
+
this.ctx.options.pop = pop;
|
|
11500
|
+
await this.getComputeSession();
|
|
11501
|
+
} catch (error) {
|
|
11502
|
+
this.ctx.options.pop = previousPop;
|
|
11503
|
+
this._pipeline = null;
|
|
11504
|
+
this._pipelineId = null;
|
|
11505
|
+
this._baseUrl = previousPop ? null : previousBaseUrl;
|
|
11506
|
+
this._sessionAccessToken = previousSessionAccessToken;
|
|
11507
|
+
this._sessionAccessTokenValidUntil = previousSessionAccessTokenValidUntil;
|
|
11508
|
+
throw error;
|
|
11509
|
+
}
|
|
11510
|
+
if (!this._pipelineId) {
|
|
11511
|
+
throw new Error("Compute session did not provide a pipeline for the requested pop");
|
|
11512
|
+
}
|
|
11513
|
+
this.setTransientPipelinePop();
|
|
10953
11514
|
}
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
11515
|
+
async deleteTransientPipeline(pipelineId) {
|
|
11516
|
+
const client = this.ctx.client();
|
|
11517
|
+
if (!client || !this._baseUrl) {
|
|
11518
|
+
return;
|
|
11519
|
+
}
|
|
11520
|
+
const deleteUrl = `${this._baseUrl}/pipelines/${pipelineId}`;
|
|
11521
|
+
const headers = {
|
|
11522
|
+
Authorization: await this.authorizationHeader()
|
|
11523
|
+
};
|
|
11524
|
+
let response = await client.fetch(deleteUrl, {
|
|
11525
|
+
method: "DELETE",
|
|
11526
|
+
headers
|
|
11527
|
+
});
|
|
11528
|
+
if (response.status >= 300) {
|
|
11529
|
+
const message = await response.text();
|
|
11530
|
+
this.ctx.logger.info(`stopping pipeline failed, status ${response.status}: ${message}`);
|
|
10957
11531
|
}
|
|
10958
|
-
return this.options().pop || null;
|
|
10959
11532
|
}
|
|
10960
|
-
async
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
await this.changeTransientPop(pop);
|
|
10964
|
-
return;
|
|
11533
|
+
async authorizationHeader() {
|
|
11534
|
+
if (this._sessionAccessToken) {
|
|
11535
|
+
return `Bearer ${this._sessionAccessToken}`;
|
|
10965
11536
|
}
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
11537
|
+
return this.ctx.credentialHeader();
|
|
11538
|
+
}
|
|
11539
|
+
async computeAuthorizationHeader() {
|
|
11540
|
+
const auth = resolveAuth(this.ctx.options);
|
|
11541
|
+
if (auth?.isLocal !== void 0) {
|
|
11542
|
+
return "Implicit";
|
|
10970
11543
|
}
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
"Content-Type": "application/json",
|
|
10975
|
-
...this._authenticationHeaders(session)
|
|
10976
|
-
};
|
|
10977
|
-
const patch_url = `${session.baseUrl}/pipelines/${session.pipelineId}/pop`;
|
|
10978
|
-
return await client.fetch(patch_url, {
|
|
10979
|
-
method: "PATCH",
|
|
10980
|
-
body: JSON.stringify(pop),
|
|
10981
|
-
headers
|
|
10982
|
-
});
|
|
10983
|
-
});
|
|
10984
|
-
if (response.status != 204) {
|
|
10985
|
-
const message = await response.text();
|
|
10986
|
-
throw new Error(`Unexpected status ${response.status}: ${message}`);
|
|
11544
|
+
const credential = apiKeyCredential(auth) ?? accessTokenCredential(auth);
|
|
11545
|
+
if (credential !== void 0) {
|
|
11546
|
+
return `Bearer ${credential}`;
|
|
10987
11547
|
}
|
|
10988
|
-
this.
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
11548
|
+
return this.ctx.credentialHeader();
|
|
11549
|
+
}
|
|
11550
|
+
setTransientPipelinePop() {
|
|
11551
|
+
const pop = this.ctx.options.pop;
|
|
11552
|
+
if (pop && this._pipelineId) {
|
|
10992
11553
|
this._pipeline = {
|
|
10993
|
-
id: this._pipelineId
|
|
11554
|
+
id: this._pipelineId,
|
|
10994
11555
|
state: "idle",
|
|
10995
11556
|
startTime: (/* @__PURE__ */ new Date()).toString(),
|
|
10996
11557
|
pop
|
|
10997
11558
|
};
|
|
10998
11559
|
}
|
|
10999
11560
|
}
|
|
11000
|
-
|
|
11001
|
-
|
|
11561
|
+
};
|
|
11562
|
+
_SessionBackend.WAIT_FOR_IS_READY = 60 * 1e3;
|
|
11563
|
+
var SessionBackend = _SessionBackend;
|
|
11564
|
+
|
|
11565
|
+
// shims/websockets.ts
|
|
11566
|
+
var createWebSocket;
|
|
11567
|
+
var UpgradeError = class extends Error {
|
|
11568
|
+
constructor(message, status, retryAfter) {
|
|
11569
|
+
super(message);
|
|
11570
|
+
this.name = "UpgradeError";
|
|
11571
|
+
this.status = status;
|
|
11572
|
+
this.retryAfter = retryAfter;
|
|
11002
11573
|
}
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
return
|
|
11574
|
+
};
|
|
11575
|
+
var openAuthorizedWebSocket;
|
|
11576
|
+
var isBrowser = "document" in globalThis && "implementation" in globalThis.document;
|
|
11577
|
+
if (isBrowser) {
|
|
11578
|
+
createWebSocket = (url, protocols) => {
|
|
11579
|
+
return new WebSocket(url, protocols);
|
|
11580
|
+
};
|
|
11581
|
+
openAuthorizedWebSocket = (url, protocol, token) => {
|
|
11582
|
+
const protocols = token === void 0 ? [protocol] : [protocol, `eyepop.bearer.${token}`];
|
|
11583
|
+
const ws = new WebSocket(url, protocols);
|
|
11584
|
+
ws.binaryType = "arraybuffer";
|
|
11585
|
+
return new Promise((resolve, reject) => {
|
|
11586
|
+
ws.onopen = () => resolve(ws);
|
|
11587
|
+
ws.onerror = () => reject(new UpgradeError(`could not open ${url}`));
|
|
11588
|
+
});
|
|
11589
|
+
};
|
|
11590
|
+
} else {
|
|
11591
|
+
createWebSocket = (url, protocols) => {
|
|
11592
|
+
const ws = require("ws");
|
|
11593
|
+
return new ws.WebSocket(url, protocols);
|
|
11594
|
+
};
|
|
11595
|
+
openAuthorizedWebSocket = (url, protocol, token) => {
|
|
11596
|
+
const ws = require("ws");
|
|
11597
|
+
const headers = token === void 0 ? {} : { Authorization: `Bearer ${token}` };
|
|
11598
|
+
const socket = new ws.WebSocket(url, [protocol], { headers, maxPayload: 0 });
|
|
11599
|
+
socket.binaryType = "arraybuffer";
|
|
11600
|
+
return new Promise((resolve, reject) => {
|
|
11601
|
+
socket.once("open", () => resolve(socket));
|
|
11602
|
+
socket.once("unexpected-response", (_request, response) => {
|
|
11603
|
+
const retryAfter = Number.parseFloat(response.headers?.["retry-after"]);
|
|
11604
|
+
reject(new UpgradeError(`the pipeline API answered the upgrade ${response.statusCode}`, response.statusCode, Number.isFinite(retryAfter) ? retryAfter : void 0));
|
|
11605
|
+
response.resume();
|
|
11606
|
+
socket.terminate();
|
|
11607
|
+
});
|
|
11608
|
+
socket.once("error", (error) => reject(new UpgradeError(`could not open ${url}: ${error.message}`)));
|
|
11609
|
+
});
|
|
11610
|
+
};
|
|
11611
|
+
}
|
|
11612
|
+
|
|
11613
|
+
// worker/pipeline_backend.ts
|
|
11614
|
+
var PIPELINE_PATH = "/api/v1/pipeline";
|
|
11615
|
+
var SUBPROTOCOL = "eyepop.v1";
|
|
11616
|
+
var MAX_OPEN_SOURCES = 256;
|
|
11617
|
+
var COMBINED_UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
11618
|
+
var CHUNK = 1;
|
|
11619
|
+
var COMBINED = 2;
|
|
11620
|
+
var CHUNK_BYTES = 64 * 1024;
|
|
11621
|
+
var HEARTBEAT_MS = 20 * 1e3;
|
|
11622
|
+
var MAX_RETRY_MS = 30 * 1e3;
|
|
11623
|
+
var RECONNECT_AT_ONCE = /* @__PURE__ */ new Set(["1008 credential_expired", "1011 overloaded", "1001 shutting_down"]);
|
|
11624
|
+
var MAX_RECONNECT_DELAY_MS = 30 * 1e3;
|
|
11625
|
+
var PipelineConfigurationError = class extends Error {
|
|
11626
|
+
constructor(problem, message, options = []) {
|
|
11627
|
+
super(message);
|
|
11628
|
+
this.name = "PipelineConfigurationError";
|
|
11629
|
+
this.problem = problem;
|
|
11630
|
+
this.options = options;
|
|
11009
11631
|
}
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11632
|
+
};
|
|
11633
|
+
var PipelineSocketClosedError = class extends Error {
|
|
11634
|
+
constructor(code, reason) {
|
|
11635
|
+
super(`the pipeline API closed the socket: ${code ?? ""} ${reason}`.trim());
|
|
11636
|
+
this.name = "PipelineSocketClosedError";
|
|
11637
|
+
this.code = code;
|
|
11638
|
+
this.reason = reason;
|
|
11016
11639
|
}
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
} else if (source.url !== void 0) {
|
|
11025
|
-
return this.loadFrom(source, { componentParams: params });
|
|
11026
|
-
} else if (source.assetUuid !== void 0) {
|
|
11027
|
-
return this.loadFromAssetUuid(source, { componentParams: params });
|
|
11028
|
-
} else if (source.mediaStream !== void 0) {
|
|
11029
|
-
return this.loadMediaStream(source, { componentParams: params });
|
|
11030
|
-
} else {
|
|
11031
|
-
return Promise.reject("unknown source type");
|
|
11032
|
-
}
|
|
11640
|
+
};
|
|
11641
|
+
var PipelineCommandError = class extends Error {
|
|
11642
|
+
constructor(error, detail = "") {
|
|
11643
|
+
super(`the pipeline API refused the command: ${error} ${detail}`.trim());
|
|
11644
|
+
this.name = "PipelineCommandError";
|
|
11645
|
+
this.error = error;
|
|
11646
|
+
this.detail = detail;
|
|
11033
11647
|
}
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
*/
|
|
11043
|
-
validateProcessParams(params) {
|
|
11044
|
-
if (params.camera !== void 0) {
|
|
11045
|
-
validateCamera(params.camera);
|
|
11046
|
-
}
|
|
11648
|
+
};
|
|
11649
|
+
var PipelineSourceError = class extends Error {
|
|
11650
|
+
constructor(sourceId, reason, detail = "") {
|
|
11651
|
+
super(`Error in source ${sourceId}: ${reason} ${detail}`.trim());
|
|
11652
|
+
this.name = "PipelineSourceError";
|
|
11653
|
+
this.sourceId = sourceId;
|
|
11654
|
+
this.reason = reason;
|
|
11655
|
+
this.detail = detail;
|
|
11047
11656
|
}
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
} else if (request.source.mediaStream !== void 0) {
|
|
11061
|
-
return this.loadMediaStream(request.source, request);
|
|
11657
|
+
};
|
|
11658
|
+
var CANCELLED = /* @__PURE__ */ Symbol("cancelled");
|
|
11659
|
+
var Inbox = class {
|
|
11660
|
+
constructor() {
|
|
11661
|
+
this._items = [];
|
|
11662
|
+
this._waiter = null;
|
|
11663
|
+
}
|
|
11664
|
+
push(item) {
|
|
11665
|
+
const waiter = this._waiter;
|
|
11666
|
+
if (waiter) {
|
|
11667
|
+
this._waiter = null;
|
|
11668
|
+
waiter(item);
|
|
11062
11669
|
} else {
|
|
11063
|
-
|
|
11670
|
+
this._items.push(item);
|
|
11064
11671
|
}
|
|
11065
11672
|
}
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11673
|
+
next() {
|
|
11674
|
+
const item = this._items.shift();
|
|
11675
|
+
if (item !== void 0) {
|
|
11676
|
+
return Promise.resolve(item);
|
|
11069
11677
|
}
|
|
11070
|
-
this.
|
|
11071
|
-
|
|
11072
|
-
|
|
11678
|
+
return new Promise((resolve) => this._waiter = resolve);
|
|
11679
|
+
}
|
|
11680
|
+
};
|
|
11681
|
+
var UploadEnded = class extends Error {
|
|
11682
|
+
};
|
|
11683
|
+
var Credit = class {
|
|
11684
|
+
constructor() {
|
|
11685
|
+
this._credit = 0;
|
|
11686
|
+
this._ended = false;
|
|
11687
|
+
this._waiter = null;
|
|
11688
|
+
}
|
|
11689
|
+
grant(n) {
|
|
11690
|
+
this._credit += n;
|
|
11691
|
+
this.wake();
|
|
11692
|
+
}
|
|
11693
|
+
end() {
|
|
11694
|
+
this._ended = true;
|
|
11695
|
+
this.wake();
|
|
11696
|
+
}
|
|
11697
|
+
async take(most) {
|
|
11698
|
+
while (this._credit <= 0 && !this._ended) {
|
|
11699
|
+
await new Promise((resolve) => this._waiter = resolve);
|
|
11073
11700
|
}
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
this.updateState();
|
|
11077
|
-
const sources = await Promise.all(
|
|
11078
|
-
paths.map(async (path2) => {
|
|
11079
|
-
let streamSource;
|
|
11080
|
-
if (this._options.platformSupport?.resolvePath) {
|
|
11081
|
-
streamSource = await this._options.platformSupport.resolvePath({ path: path2 }, this._logger);
|
|
11082
|
-
} else {
|
|
11083
|
-
streamSource = await resolvePath({ path: path2 }, this._logger);
|
|
11084
|
-
}
|
|
11085
|
-
return { stream: streamSource.stream, mimeType: streamSource.mimeType };
|
|
11086
|
-
})
|
|
11087
|
-
);
|
|
11088
|
-
const job = new UploadGroupJob(sources, params, async () => this.session(), this._client, this._requestLogger);
|
|
11089
|
-
return job.start(
|
|
11090
|
-
() => {
|
|
11091
|
-
this.jobDone(job);
|
|
11092
|
-
},
|
|
11093
|
-
(statusCode) => {
|
|
11094
|
-
this.jobStatus(job, statusCode);
|
|
11095
|
-
}
|
|
11096
|
-
);
|
|
11097
|
-
} catch (e) {
|
|
11098
|
-
this._limit.release();
|
|
11099
|
-
throw e;
|
|
11701
|
+
if (this._ended) {
|
|
11702
|
+
throw new UploadEnded();
|
|
11100
11703
|
}
|
|
11704
|
+
const n = Math.min(most, this._credit);
|
|
11705
|
+
this._credit -= n;
|
|
11706
|
+
return n;
|
|
11101
11707
|
}
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11708
|
+
wake() {
|
|
11709
|
+
const waiter = this._waiter;
|
|
11710
|
+
this._waiter = null;
|
|
11711
|
+
waiter?.();
|
|
11712
|
+
}
|
|
11713
|
+
};
|
|
11714
|
+
var OpenSource = class {
|
|
11715
|
+
constructor(id) {
|
|
11716
|
+
this.id = id;
|
|
11717
|
+
this.inbox = new Inbox();
|
|
11718
|
+
this.uploads = /* @__PURE__ */ new Map();
|
|
11719
|
+
}
|
|
11720
|
+
close(error) {
|
|
11721
|
+
this.uploads.forEach((upload) => upload.end());
|
|
11722
|
+
this.inbox.push(error);
|
|
11723
|
+
}
|
|
11724
|
+
};
|
|
11725
|
+
var PipelineSocket = class {
|
|
11726
|
+
constructor(ws) {
|
|
11727
|
+
this.ws = ws;
|
|
11728
|
+
this.sources = /* @__PURE__ */ new Map();
|
|
11729
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
11730
|
+
this._closed = false;
|
|
11731
|
+
this.closed = new Promise((resolve) => this._markClosed = resolve);
|
|
11732
|
+
}
|
|
11733
|
+
get open() {
|
|
11734
|
+
return !this._closed && this.ws.readyState === 1;
|
|
11735
|
+
}
|
|
11736
|
+
markClosed() {
|
|
11737
|
+
this._closed = true;
|
|
11738
|
+
this._markClosed();
|
|
11739
|
+
}
|
|
11740
|
+
sendJson(message) {
|
|
11741
|
+
this.ws.send(JSON.stringify(message));
|
|
11742
|
+
}
|
|
11743
|
+
sendBytes(data) {
|
|
11744
|
+
this.ws.send(data);
|
|
11745
|
+
}
|
|
11746
|
+
};
|
|
11747
|
+
var PipelineJob = class {
|
|
11748
|
+
constructor(source, media, live, params, mediaStream = void 0) {
|
|
11749
|
+
this.source = source;
|
|
11750
|
+
this.media = media;
|
|
11751
|
+
this.live = live;
|
|
11752
|
+
this.params = params;
|
|
11753
|
+
this.mediaStream = mediaStream;
|
|
11754
|
+
this._results = new Inbox();
|
|
11755
|
+
this._consumed = false;
|
|
11756
|
+
this._cancelled = false;
|
|
11757
|
+
this.openSource = null;
|
|
11758
|
+
// Stops the job's source on the pipeline, once it was sent.
|
|
11759
|
+
this.stopOnPipeline = null;
|
|
11760
|
+
}
|
|
11761
|
+
get cancelled() {
|
|
11762
|
+
return this._cancelled;
|
|
11763
|
+
}
|
|
11764
|
+
command(id) {
|
|
11765
|
+
const command = { type: "setSource", id, ...this.source };
|
|
11766
|
+
if (this.params.componentParams !== void 0) {
|
|
11767
|
+
command["params"] = this.params.componentParams;
|
|
11105
11768
|
}
|
|
11106
|
-
if (
|
|
11107
|
-
|
|
11769
|
+
if (this.params.roi !== void 0) {
|
|
11770
|
+
command["roi"] = this.params.roi;
|
|
11108
11771
|
}
|
|
11109
|
-
this.
|
|
11110
|
-
|
|
11111
|
-
throw new Error("endpoint not connected, use connect()");
|
|
11772
|
+
if (this.params.camera !== void 0) {
|
|
11773
|
+
command["camera"] = this.params.camera;
|
|
11112
11774
|
}
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
this.updateState();
|
|
11116
|
-
const sources = streams.map((stream, i) => ({
|
|
11117
|
-
stream,
|
|
11118
|
-
mimeType: mimeTypes?.[i] ?? null
|
|
11119
|
-
}));
|
|
11120
|
-
const job = new UploadGroupJob(sources, params, async () => this.session(), this._client, this._requestLogger);
|
|
11121
|
-
return job.start(
|
|
11122
|
-
() => {
|
|
11123
|
-
this.jobDone(job);
|
|
11124
|
-
},
|
|
11125
|
-
(statusCode) => {
|
|
11126
|
-
this.jobStatus(job, statusCode);
|
|
11127
|
-
}
|
|
11128
|
-
);
|
|
11129
|
-
} catch (e) {
|
|
11130
|
-
this._limit.release();
|
|
11131
|
-
throw e;
|
|
11775
|
+
if (this.params.fps !== void 0) {
|
|
11776
|
+
command["fps"] = this.params.fps;
|
|
11132
11777
|
}
|
|
11778
|
+
return command;
|
|
11133
11779
|
}
|
|
11134
|
-
|
|
11135
|
-
if (
|
|
11136
|
-
|
|
11137
|
-
}
|
|
11138
|
-
this.validateProcessParams(params);
|
|
11139
|
-
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
11140
|
-
throw new Error("endpoint not connected, use connect()");
|
|
11141
|
-
}
|
|
11142
|
-
await this._limit.acquire();
|
|
11143
|
-
try {
|
|
11144
|
-
this.updateState();
|
|
11145
|
-
const job = new LoadFromGroupJob(urls, params, async () => this.session(), this._client, this._requestLogger);
|
|
11146
|
-
return job.start(
|
|
11147
|
-
() => {
|
|
11148
|
-
this.jobDone(job);
|
|
11149
|
-
},
|
|
11150
|
-
(statusCode) => {
|
|
11151
|
-
this.jobStatus(job, statusCode);
|
|
11152
|
-
}
|
|
11153
|
-
);
|
|
11154
|
-
} catch (e) {
|
|
11155
|
-
this._limit.release();
|
|
11156
|
-
throw e;
|
|
11780
|
+
predicted(prediction) {
|
|
11781
|
+
if (!this._cancelled) {
|
|
11782
|
+
this._results.push(prediction);
|
|
11157
11783
|
}
|
|
11158
11784
|
}
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
return Promise.reject("endpoint not connected, use connect()");
|
|
11163
|
-
}
|
|
11164
|
-
await this._limit.acquire();
|
|
11165
|
-
try {
|
|
11166
|
-
this.updateState();
|
|
11167
|
-
const job = new UploadJob(
|
|
11168
|
-
source.file,
|
|
11169
|
-
source.file.type,
|
|
11170
|
-
source.file.size,
|
|
11171
|
-
source.videoMode,
|
|
11172
|
-
params,
|
|
11173
|
-
async () => {
|
|
11174
|
-
return this.session();
|
|
11175
|
-
},
|
|
11176
|
-
this._client,
|
|
11177
|
-
this._requestLogger
|
|
11178
|
-
);
|
|
11179
|
-
return job.start(
|
|
11180
|
-
() => {
|
|
11181
|
-
this.jobDone(job);
|
|
11182
|
-
},
|
|
11183
|
-
(statusCode) => {
|
|
11184
|
-
this.jobStatus(job, statusCode);
|
|
11185
|
-
}
|
|
11186
|
-
);
|
|
11187
|
-
} catch (e) {
|
|
11188
|
-
this._limit.release();
|
|
11189
|
-
throw e;
|
|
11785
|
+
finish(error = null) {
|
|
11786
|
+
if (error !== null) {
|
|
11787
|
+
this._results.push(error);
|
|
11190
11788
|
}
|
|
11789
|
+
this._results.push(null);
|
|
11191
11790
|
}
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
return Promise.reject("endpoint not connected, use connect()");
|
|
11196
|
-
}
|
|
11197
|
-
await this._limit.acquire();
|
|
11198
|
-
try {
|
|
11199
|
-
this.updateState();
|
|
11200
|
-
const job = new UploadJob(
|
|
11201
|
-
source.stream,
|
|
11202
|
-
source.mimeType,
|
|
11203
|
-
source.size,
|
|
11204
|
-
source.videoMode,
|
|
11205
|
-
params,
|
|
11206
|
-
async () => {
|
|
11207
|
-
return this.session();
|
|
11208
|
-
},
|
|
11209
|
-
this._client,
|
|
11210
|
-
this._requestLogger
|
|
11211
|
-
);
|
|
11212
|
-
return job.start(
|
|
11213
|
-
() => {
|
|
11214
|
-
this.jobDone(job);
|
|
11215
|
-
},
|
|
11216
|
-
(statusCode) => {
|
|
11217
|
-
this.jobStatus(job, statusCode);
|
|
11218
|
-
}
|
|
11219
|
-
);
|
|
11220
|
-
} catch (e) {
|
|
11221
|
-
this._limit.release();
|
|
11222
|
-
throw e;
|
|
11791
|
+
cancel() {
|
|
11792
|
+
if (this._cancelled) {
|
|
11793
|
+
return;
|
|
11223
11794
|
}
|
|
11795
|
+
this._cancelled = true;
|
|
11796
|
+
this._results.push(null);
|
|
11797
|
+
this.openSource?.inbox.push(CANCELLED);
|
|
11798
|
+
this.stopOnPipeline?.();
|
|
11224
11799
|
}
|
|
11225
|
-
async
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
throw new Error("endpoint not connected, use connect()");
|
|
11800
|
+
async *[Symbol.asyncIterator]() {
|
|
11801
|
+
if (this._consumed) {
|
|
11802
|
+
throw new Error("Cannot iterate over a consumed stream");
|
|
11229
11803
|
}
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
this.
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
}
|
|
11239
|
-
|
|
11240
|
-
let firstStreamPending = true;
|
|
11241
|
-
const uploadBodyFactory = async () => {
|
|
11242
|
-
if (firstStreamPending) {
|
|
11243
|
-
firstStreamPending = false;
|
|
11244
|
-
return streamSource.stream;
|
|
11245
|
-
}
|
|
11246
|
-
return (await resolveStreamSource()).stream;
|
|
11247
|
-
};
|
|
11248
|
-
const job = new UploadJob(
|
|
11249
|
-
streamSource.stream,
|
|
11250
|
-
streamSource.mimeType,
|
|
11251
|
-
streamSource.size,
|
|
11252
|
-
source.videoMode,
|
|
11253
|
-
params,
|
|
11254
|
-
async () => {
|
|
11255
|
-
return this.session();
|
|
11256
|
-
},
|
|
11257
|
-
this._client,
|
|
11258
|
-
this._requestLogger,
|
|
11259
|
-
uploadBodyFactory
|
|
11260
|
-
);
|
|
11261
|
-
return job.start(
|
|
11262
|
-
() => {
|
|
11263
|
-
this.jobDone(job);
|
|
11264
|
-
},
|
|
11265
|
-
(statusCode) => {
|
|
11266
|
-
this.jobStatus(job, statusCode);
|
|
11267
|
-
}
|
|
11268
|
-
);
|
|
11269
|
-
} catch (e) {
|
|
11270
|
-
this._limit.release();
|
|
11271
|
-
throw e;
|
|
11804
|
+
this._consumed = true;
|
|
11805
|
+
while (true) {
|
|
11806
|
+
const item = await this._results.next();
|
|
11807
|
+
if (item === null) {
|
|
11808
|
+
return;
|
|
11809
|
+
}
|
|
11810
|
+
if (item instanceof Error) {
|
|
11811
|
+
throw item;
|
|
11812
|
+
}
|
|
11813
|
+
yield item;
|
|
11272
11814
|
}
|
|
11273
11815
|
}
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11816
|
+
};
|
|
11817
|
+
var PipelineBackend = class {
|
|
11818
|
+
constructor(ctx) {
|
|
11819
|
+
this.ctx = ctx;
|
|
11820
|
+
this._pipelineApiUrl = null;
|
|
11821
|
+
this._socket = null;
|
|
11822
|
+
this._connecting = null;
|
|
11823
|
+
// Every job's setSource and every changePop goes out through this chain, in the order submitted.
|
|
11824
|
+
this._sendChain = Promise.resolve();
|
|
11825
|
+
this._disconnecting = false;
|
|
11826
|
+
this._nextId = 0;
|
|
11827
|
+
this._warned = /* @__PURE__ */ new Set();
|
|
11828
|
+
this._heartbeat = null;
|
|
11829
|
+
// Reconnects at once since a source last played.
|
|
11830
|
+
this._reconnects = 0;
|
|
11831
|
+
this._currentPop = ctx.options.pop ?? null;
|
|
11832
|
+
}
|
|
11833
|
+
// Connection
|
|
11834
|
+
async reconnect() {
|
|
11835
|
+
this._disconnecting = false;
|
|
11836
|
+
await this.inOrder(() => this.ensureSocket());
|
|
11837
|
+
this.ctx.updateState();
|
|
11838
|
+
}
|
|
11839
|
+
async disconnect(closeEndpoint) {
|
|
11840
|
+
await closeEndpoint();
|
|
11841
|
+
this._disconnecting = true;
|
|
11842
|
+
if (this._heartbeat !== null) {
|
|
11843
|
+
clearInterval(this._heartbeat);
|
|
11844
|
+
this._heartbeat = null;
|
|
11282
11845
|
}
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11846
|
+
const socket = this._socket;
|
|
11847
|
+
this._socket = null;
|
|
11848
|
+
if (socket !== null) {
|
|
11849
|
+
socket.ws.close(1e3);
|
|
11850
|
+
await socket.closed;
|
|
11286
11851
|
}
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
}
|
|
11306
|
-
);
|
|
11307
|
-
} catch (e) {
|
|
11308
|
-
this._limit.release();
|
|
11309
|
-
throw e;
|
|
11852
|
+
}
|
|
11853
|
+
onStatus(_statusCode) {
|
|
11854
|
+
}
|
|
11855
|
+
popName() {
|
|
11856
|
+
return null;
|
|
11857
|
+
}
|
|
11858
|
+
async authorizationHeader() {
|
|
11859
|
+
return this.ctx.credentialHeader();
|
|
11860
|
+
}
|
|
11861
|
+
inOrder(work) {
|
|
11862
|
+
const result = this._sendChain.then(work);
|
|
11863
|
+
this._sendChain = result.catch(() => void 0);
|
|
11864
|
+
return result;
|
|
11865
|
+
}
|
|
11866
|
+
/** The open socket, or a new one with the last pop set on it. */
|
|
11867
|
+
async ensureSocket() {
|
|
11868
|
+
if (this._socket !== null && this._socket.open) {
|
|
11869
|
+
return this._socket;
|
|
11310
11870
|
}
|
|
11871
|
+
if (this._connecting === null) {
|
|
11872
|
+
this._connecting = this.openSocket().finally(() => this._connecting = null);
|
|
11873
|
+
}
|
|
11874
|
+
return await this._connecting;
|
|
11311
11875
|
}
|
|
11312
|
-
async
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
throw new Error("endpoint not connected, use connect()");
|
|
11876
|
+
async openSocket() {
|
|
11877
|
+
if (this.ctx.client() === null) {
|
|
11878
|
+
throw new PipelineSocketClosedError(void 0, "the endpoint is not connected");
|
|
11316
11879
|
}
|
|
11317
|
-
await this.
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
},
|
|
11326
|
-
this._client,
|
|
11327
|
-
this._requestLogger
|
|
11328
|
-
);
|
|
11329
|
-
return job.start(
|
|
11330
|
-
() => {
|
|
11331
|
-
this.jobDone(job);
|
|
11332
|
-
},
|
|
11333
|
-
(statusCode) => {
|
|
11334
|
-
this.jobStatus(job, statusCode);
|
|
11880
|
+
const ws = await this.upgrade(await this.socketUrl());
|
|
11881
|
+
const socket = new PipelineSocket(ws);
|
|
11882
|
+
ws.onmessage = (event) => {
|
|
11883
|
+
if (typeof event.data === "string") {
|
|
11884
|
+
try {
|
|
11885
|
+
this.dispatch(socket, JSON.parse(event.data));
|
|
11886
|
+
} catch (error) {
|
|
11887
|
+
this.ctx.logger.warn("unreadable message from the pipeline API: %s", error);
|
|
11335
11888
|
}
|
|
11336
|
-
|
|
11337
|
-
}
|
|
11338
|
-
|
|
11339
|
-
|
|
11889
|
+
}
|
|
11890
|
+
};
|
|
11891
|
+
ws.onerror = () => {
|
|
11892
|
+
};
|
|
11893
|
+
ws.onclose = (event) => this.closed(socket, event.code, event.reason);
|
|
11894
|
+
this._socket = socket;
|
|
11895
|
+
this.startHeartbeat(ws);
|
|
11896
|
+
if (this._currentPop !== null) {
|
|
11897
|
+
try {
|
|
11898
|
+
await this.setPopOn(socket, this._currentPop);
|
|
11899
|
+
} catch (error) {
|
|
11900
|
+
this._socket = null;
|
|
11901
|
+
ws.close(1e3);
|
|
11902
|
+
throw error;
|
|
11903
|
+
}
|
|
11340
11904
|
}
|
|
11905
|
+
return socket;
|
|
11341
11906
|
}
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
throw new Error("endpoint not connected, use connect()");
|
|
11907
|
+
startHeartbeat(ws) {
|
|
11908
|
+
if (this._heartbeat !== null) {
|
|
11909
|
+
clearInterval(this._heartbeat);
|
|
11346
11910
|
}
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
this.
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
params,
|
|
11353
|
-
async () => {
|
|
11354
|
-
return this.session();
|
|
11355
|
-
},
|
|
11356
|
-
this._client,
|
|
11357
|
-
this._requestLogger
|
|
11358
|
-
);
|
|
11359
|
-
return job.start(
|
|
11360
|
-
() => {
|
|
11361
|
-
this.jobDone(job);
|
|
11362
|
-
},
|
|
11363
|
-
(statusCode) => {
|
|
11364
|
-
this.jobStatus(job, statusCode);
|
|
11911
|
+
const ping = ws.ping;
|
|
11912
|
+
if (typeof ping === "function") {
|
|
11913
|
+
this._heartbeat = setInterval(() => {
|
|
11914
|
+
if (ws.readyState === 1) {
|
|
11915
|
+
ping.call(ws);
|
|
11365
11916
|
}
|
|
11366
|
-
);
|
|
11367
|
-
|
|
11368
|
-
this._limit.release();
|
|
11369
|
-
throw e;
|
|
11917
|
+
}, HEARTBEAT_MS);
|
|
11918
|
+
this._heartbeat.unref?.();
|
|
11370
11919
|
}
|
|
11371
11920
|
}
|
|
11372
|
-
|
|
11373
|
-
this.
|
|
11374
|
-
|
|
11921
|
+
async socketUrl() {
|
|
11922
|
+
if (this._pipelineApiUrl === null) {
|
|
11923
|
+
const base = this.ctx.options.isLocalMode ? this.ctx.eyepopUrl() : new URL(await this.configuredPipelineApiUrl(), this.ctx.eyepopUrl() + "/").toString();
|
|
11924
|
+
this._pipelineApiUrl = checkedUrl(base);
|
|
11925
|
+
}
|
|
11926
|
+
return "ws" + this._pipelineApiUrl.slice("http".length) + PIPELINE_PATH;
|
|
11375
11927
|
}
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11928
|
+
async configuredPipelineApiUrl() {
|
|
11929
|
+
const client = this.ctx.client();
|
|
11930
|
+
if (client === null) {
|
|
11931
|
+
throw new Error("endpoint not connected");
|
|
11932
|
+
}
|
|
11933
|
+
let configUrl = `${this.ctx.eyepopUrl()}/v1/configs`;
|
|
11934
|
+
const accountId = this.ctx.options.accountId;
|
|
11935
|
+
if (accountId) {
|
|
11936
|
+
configUrl += `?account_uuid=${encodeURIComponent(accountId)}`;
|
|
11937
|
+
}
|
|
11938
|
+
const response = await this.ctx.fetchWithRetry(async () => client.fetch(configUrl, { headers: { Authorization: await this.ctx.credentialHeader() } }));
|
|
11939
|
+
if (!response.ok) {
|
|
11940
|
+
throw new Error(`Unexpected status ${response.status} for GET ${configUrl}: ${await response.text()}`);
|
|
11941
|
+
}
|
|
11942
|
+
const config = await response.json();
|
|
11943
|
+
const url = config?.["pipeline_api_url"];
|
|
11944
|
+
if (!url) {
|
|
11945
|
+
throw new PipelineConfigurationError("no_pipeline_api_url", `${configUrl} has no pipeline_api_url: this compute API serves no pipeline API`);
|
|
11946
|
+
}
|
|
11947
|
+
return url;
|
|
11948
|
+
}
|
|
11949
|
+
async token() {
|
|
11950
|
+
if (resolveAuth(this.ctx.options)?.isLocal !== void 0) {
|
|
11951
|
+
return void 0;
|
|
11381
11952
|
}
|
|
11953
|
+
return (await this.ctx.credentialHeader()).replace(/^Bearer /, "");
|
|
11382
11954
|
}
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11955
|
+
/** Open the socket, waiting through 503 with Retry-After and exchanging the API key again after a 401. */
|
|
11956
|
+
async upgrade(url) {
|
|
11957
|
+
const deadline = Date.now() + MAX_RETRY_MS;
|
|
11958
|
+
let rejected = false;
|
|
11959
|
+
while (true) {
|
|
11960
|
+
try {
|
|
11961
|
+
this.ctx.requestLogger.debug("before upgrade %s", url);
|
|
11962
|
+
return await openAuthorizedWebSocket(url, SUBPROTOCOL, await this.token());
|
|
11963
|
+
} catch (error) {
|
|
11964
|
+
if (!(error instanceof UpgradeError)) {
|
|
11965
|
+
throw error;
|
|
11966
|
+
}
|
|
11967
|
+
if (error.status === 401 && !rejected) {
|
|
11968
|
+
this.ctx.requestLogger.debug("the pipeline API answered 401, authenticating again");
|
|
11969
|
+
rejected = true;
|
|
11970
|
+
this.ctx.forgetCredential();
|
|
11971
|
+
continue;
|
|
11972
|
+
}
|
|
11973
|
+
const wait = error.status === 503 && error.retryAfter !== void 0 ? Math.max(error.retryAfter, 1) * 1e3 : void 0;
|
|
11974
|
+
if (wait === void 0 || Date.now() + wait > deadline) {
|
|
11975
|
+
throw error;
|
|
11976
|
+
}
|
|
11977
|
+
this.ctx.requestLogger.info("the pipeline API answered 503, retrying in %d ms", wait);
|
|
11978
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
11388
11979
|
}
|
|
11389
|
-
} else if (this.options().sessionUuid) {
|
|
11390
|
-
await this.getComputeSession();
|
|
11391
|
-
} else {
|
|
11392
|
-
await this.startWebApiSessionWithPopId();
|
|
11393
11980
|
}
|
|
11394
|
-
this.updateState();
|
|
11395
|
-
return Promise.resolve(this);
|
|
11396
11981
|
}
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11982
|
+
closed(socket, code, reason) {
|
|
11983
|
+
socket.markClosed();
|
|
11984
|
+
if (this._socket === socket) {
|
|
11985
|
+
this._socket = null;
|
|
11400
11986
|
}
|
|
11401
|
-
const
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
this.
|
|
11406
|
-
|
|
11407
|
-
if (response.status == 401) {
|
|
11408
|
-
this._requestLogger.debug("GET %s: 401, about to retry with fresh access token", config_url);
|
|
11409
|
-
this.statusHandler401(response.status);
|
|
11410
|
-
const headers2 = {
|
|
11411
|
-
Authorization: await this.authorizationHeader()
|
|
11412
|
-
};
|
|
11413
|
-
this.updateState("FetchConfig" /* FetchConfig */);
|
|
11414
|
-
response = await this._client.fetch(config_url, {
|
|
11415
|
-
headers: headers2
|
|
11416
|
-
});
|
|
11987
|
+
const error = new PipelineSocketClosedError(code, reason);
|
|
11988
|
+
socket.sources.forEach((source) => source.close(error));
|
|
11989
|
+
socket.commands.forEach((command) => command.reject(error));
|
|
11990
|
+
socket.commands.clear();
|
|
11991
|
+
if (this._disconnecting) {
|
|
11992
|
+
return;
|
|
11417
11993
|
}
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
if (
|
|
11421
|
-
this.
|
|
11422
|
-
} else {
|
|
11423
|
-
this.updateState("Error" /* Error */, message);
|
|
11994
|
+
this.ctx.requestLogger.debug("the pipeline API closed the socket: %s %s", code, reason);
|
|
11995
|
+
if (RECONNECT_AT_ONCE.has(`${code} ${reason}`)) {
|
|
11996
|
+
if (reason === "credential_expired") {
|
|
11997
|
+
this.ctx.forgetCredential();
|
|
11424
11998
|
}
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
const auto_start_config_url = `${this.eyepopWebApiUrl()}/pops/${this.options().popId}/config?auto_start=true`;
|
|
11430
|
-
this._requestLogger.debug("pop was not running, trying to autostart with: %s", auto_start_config_url);
|
|
11431
|
-
const headers2 = {
|
|
11432
|
-
Authorization: await this.authorizationHeader()
|
|
11433
|
-
};
|
|
11434
|
-
response = await this._client.fetch(auto_start_config_url, {
|
|
11435
|
-
headers: headers2
|
|
11999
|
+
const delay = this._reconnects === 0 ? 0 : Math.min(1e3 * 2 ** (this._reconnects - 1), MAX_RECONNECT_DELAY_MS);
|
|
12000
|
+
this._reconnects += 1;
|
|
12001
|
+
new Promise((resolve) => setTimeout(resolve, delay)).then(() => this._disconnecting ? void 0 : this.inOrder(() => this.ensureSocket())).catch((error2) => {
|
|
12002
|
+
this.ctx.requestLogger.info("could not reopen the pipeline API socket: %s", error2);
|
|
11436
12003
|
});
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
12004
|
+
}
|
|
12005
|
+
}
|
|
12006
|
+
dispatch(socket, message) {
|
|
12007
|
+
const type = message["type"];
|
|
12008
|
+
const id = message["id"];
|
|
12009
|
+
const source = typeof id === "string" ? socket.sources.get(id) : void 0;
|
|
12010
|
+
if (type === "send" || type === "credit") {
|
|
12011
|
+
const upload = source?.uploads.get(message["member"] ?? null);
|
|
12012
|
+
upload?.grant(Number(type === "send" ? message["credit"] : message["bytes"]));
|
|
12013
|
+
return;
|
|
12014
|
+
}
|
|
12015
|
+
const command = typeof id === "string" ? socket.commands.get(id) : void 0;
|
|
12016
|
+
if (command !== void 0) {
|
|
12017
|
+
socket.commands.delete(id);
|
|
12018
|
+
if (type === "error") {
|
|
12019
|
+
command.reject(new PipelineCommandError(message["error"] ?? "", message["message"] ?? ""));
|
|
12020
|
+
} else {
|
|
12021
|
+
command.resolve(message);
|
|
11441
12022
|
}
|
|
11442
|
-
|
|
12023
|
+
return;
|
|
11443
12024
|
}
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
this._baseUrl = this._baseUrl.replace(/\/+$/, "");
|
|
11448
|
-
this._pipelineId = config.pipeline_id;
|
|
11449
|
-
this._popName = config.name;
|
|
11450
|
-
if (!this._pipelineId || !this._baseUrl) {
|
|
11451
|
-
throw new Error("Pop not started");
|
|
12025
|
+
if (source !== void 0) {
|
|
12026
|
+
if (type === "start") {
|
|
12027
|
+
this._reconnects = 0;
|
|
11452
12028
|
}
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
Authorization: await this.authorizationHeader()
|
|
11456
|
-
};
|
|
11457
|
-
let response2 = await this._client.fetch(get_url, {
|
|
11458
|
-
method: "GET",
|
|
11459
|
-
headers: headers2
|
|
11460
|
-
});
|
|
11461
|
-
if (response2.status > 200) {
|
|
11462
|
-
const message = await response2.text();
|
|
11463
|
-
throw new Error(`Unexpected status ${response2.status}: ${message}`);
|
|
12029
|
+
if (type === "end") {
|
|
12030
|
+
source.uploads.forEach((upload) => upload.end());
|
|
11464
12031
|
}
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
};
|
|
11472
|
-
const stop_url = `${this._baseUrl}/pipelines/${this._pipeline.id}/source?mode=preempt&processing=sync`;
|
|
11473
|
-
let response3 = await this._client.fetch(stop_url, {
|
|
11474
|
-
method: "PATCH",
|
|
11475
|
-
headers: headers3,
|
|
11476
|
-
body: JSON.stringify(body)
|
|
11477
|
-
});
|
|
11478
|
-
if (response3.status >= 300) {
|
|
11479
|
-
const message = await response3.text();
|
|
11480
|
-
throw new Error(`Unexpected status ${response3.status}: ${message}`);
|
|
11481
|
-
}
|
|
12032
|
+
source.inbox.push(message);
|
|
12033
|
+
} else if (type === "error") {
|
|
12034
|
+
if (id === void 0) {
|
|
12035
|
+
this.ctx.logger.warn("the pipeline API refused a message: %s %s", message["error"], message["message"]);
|
|
12036
|
+
} else {
|
|
12037
|
+
this.ctx.requestLogger.debug("the pipeline API refused a command for %s: %s %s", id, message["error"], message["message"]);
|
|
11482
12038
|
}
|
|
11483
12039
|
}
|
|
11484
12040
|
}
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
return
|
|
12041
|
+
newId(prefix) {
|
|
12042
|
+
this._nextId += 1;
|
|
12043
|
+
return `${prefix}-${this._nextId}`;
|
|
11488
12044
|
}
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
12045
|
+
/** Send a command that is answered by `result` or `error` under its id. */
|
|
12046
|
+
command(socket, message) {
|
|
12047
|
+
const id = this.newId(message["type"]);
|
|
12048
|
+
return new Promise((resolve, reject) => {
|
|
12049
|
+
socket.commands.set(id, { resolve, reject });
|
|
12050
|
+
socket.sendJson({ ...message, id });
|
|
12051
|
+
});
|
|
12052
|
+
}
|
|
12053
|
+
// Pop
|
|
12054
|
+
pop() {
|
|
12055
|
+
return this._currentPop;
|
|
12056
|
+
}
|
|
12057
|
+
/** Set the pop on the socket's pipeline; a pop it refuses rejects, and the last pop it took stays. */
|
|
12058
|
+
async changePop(pop) {
|
|
12059
|
+
if (this.ctx.client() === null) {
|
|
12060
|
+
this._currentPop = pop;
|
|
12061
|
+
return;
|
|
11492
12062
|
}
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
12063
|
+
await this.inOrder(async () => {
|
|
12064
|
+
if (this._socket !== null && this._socket.open) {
|
|
12065
|
+
await this.setPopOn(this._socket, pop);
|
|
12066
|
+
this._currentPop = pop;
|
|
12067
|
+
return;
|
|
12068
|
+
}
|
|
12069
|
+
const previous = this._currentPop;
|
|
12070
|
+
this._currentPop = pop;
|
|
12071
|
+
try {
|
|
12072
|
+
await this.ensureSocket();
|
|
12073
|
+
} catch (error) {
|
|
12074
|
+
this._currentPop = previous;
|
|
12075
|
+
throw error;
|
|
12076
|
+
}
|
|
12077
|
+
});
|
|
12078
|
+
}
|
|
12079
|
+
async setPopOn(socket, pop) {
|
|
12080
|
+
await this.command(socket, { type: "setPop", pop });
|
|
12081
|
+
}
|
|
12082
|
+
// Jobs
|
|
12083
|
+
async submit(work) {
|
|
12084
|
+
if (this.ctx.client() === null) {
|
|
12085
|
+
throw new Error("endpoint not connected, use connect()");
|
|
11512
12086
|
}
|
|
11513
|
-
|
|
11514
|
-
this.
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
this.
|
|
11519
|
-
|
|
11520
|
-
|
|
12087
|
+
const sourceOptions = work.kind === "source" ? work.source : void 0;
|
|
12088
|
+
this.notSent({ motionDetect: work.params.motionDetect, videoMode: sourceOptions?.videoMode, rtspForceNonCompliantUrl: work.params.rtspForceNonCompliantUrl });
|
|
12089
|
+
const job = await this.jobOf(work);
|
|
12090
|
+
const limit = this.ctx.limit();
|
|
12091
|
+
await limit.acquire();
|
|
12092
|
+
this.ctx.updateState();
|
|
12093
|
+
this.run(job).then(
|
|
12094
|
+
() => job.finish(),
|
|
12095
|
+
(error) => job.finish(error instanceof Error ? error : new Error(String(error)))
|
|
12096
|
+
).finally(() => this.ctx.jobDone());
|
|
12097
|
+
return job;
|
|
12098
|
+
}
|
|
12099
|
+
notSent(options) {
|
|
12100
|
+
for (const [name, value] of Object.entries(options)) {
|
|
12101
|
+
if (value === void 0 || this._warned.has(name)) {
|
|
12102
|
+
continue;
|
|
12103
|
+
}
|
|
12104
|
+
this._warned.add(name);
|
|
12105
|
+
const ticket = name === "rtspForceNonCompliantUrl" ? "" : " (AWSU-317)";
|
|
12106
|
+
this.ctx.logger.warn(`${name} is not sent to the pipeline API and has no effect in pipeline mode${ticket}`);
|
|
12107
|
+
}
|
|
12108
|
+
}
|
|
12109
|
+
async jobOf(work) {
|
|
12110
|
+
switch (work.kind) {
|
|
12111
|
+
case "source":
|
|
12112
|
+
return await this.sourceJob(work.source, work.params);
|
|
12113
|
+
case "uploadGroup": {
|
|
12114
|
+
const media = await Promise.all(work.paths.map((path2) => this.pathMedia({ path: path2 })));
|
|
12115
|
+
return new PipelineJob(uploadGroup(media), media, false, work.params);
|
|
12116
|
+
}
|
|
12117
|
+
case "uploadStreamGroup": {
|
|
12118
|
+
const media = work.streams.map((stream, i) => bodyMedia(stream, work.mimeTypes?.[i] ?? "application/octet-stream", void 0));
|
|
12119
|
+
return new PipelineJob(uploadGroup(media), media, false, work.params);
|
|
12120
|
+
}
|
|
12121
|
+
case "loadFromGroup":
|
|
12122
|
+
return new PipelineJob({ group: work.urls.map((url) => ({ url })) }, [], false, work.params);
|
|
11521
12123
|
}
|
|
11522
12124
|
}
|
|
11523
|
-
async
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
}
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
} else if (
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
12125
|
+
async sourceJob(source, params) {
|
|
12126
|
+
if (source.file !== void 0) {
|
|
12127
|
+
const file = source.file;
|
|
12128
|
+
const media = bodyMedia(file, file.type || "application/octet-stream", file.size);
|
|
12129
|
+
return new PipelineJob({ upload: { contentType: media.contentType } }, [media], false, params);
|
|
12130
|
+
} else if (source.stream !== void 0) {
|
|
12131
|
+
const stream = source;
|
|
12132
|
+
const media = bodyMedia(stream.stream, stream.mimeType, stream.size ?? knownSize(stream.stream));
|
|
12133
|
+
return new PipelineJob({ upload: { contentType: media.contentType } }, [media], false, params);
|
|
12134
|
+
} else if (source.path !== void 0) {
|
|
12135
|
+
const media = await this.pathMedia(source);
|
|
12136
|
+
return new PipelineJob({ upload: { contentType: media.contentType } }, [media], false, params);
|
|
12137
|
+
} else if (source.url !== void 0) {
|
|
12138
|
+
const url = source.url;
|
|
12139
|
+
if (url.startsWith("file://")) {
|
|
12140
|
+
return await this.sourceJob({ path: url.substring("file://".length) }, params);
|
|
12141
|
+
}
|
|
12142
|
+
return new PipelineJob({ url }, [], /^(rtsp|rtmp):\/\//.test(url), params);
|
|
12143
|
+
} else if (source.assetUuid !== void 0) {
|
|
12144
|
+
return new PipelineJob({ assetUuid: source.assetUuid }, [], false, params);
|
|
12145
|
+
} else if (source.mediaStream !== void 0) {
|
|
12146
|
+
const ingressId = this.newId("whip");
|
|
12147
|
+
return new PipelineJob({ whip: { ingressId } }, [], true, params, source.mediaStream);
|
|
11536
12148
|
}
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
const
|
|
11561
|
-
|
|
12149
|
+
throw new Error("unknown source type");
|
|
12150
|
+
}
|
|
12151
|
+
async pathMedia(source) {
|
|
12152
|
+
const options = this.ctx.options;
|
|
12153
|
+
const resolve = async () => options.platformSupport?.resolvePath ? await options.platformSupport.resolvePath(source, this.ctx.logger) : await resolvePath(source, this.ctx.logger);
|
|
12154
|
+
const first = await resolve();
|
|
12155
|
+
let firstPending = true;
|
|
12156
|
+
return {
|
|
12157
|
+
contentType: first.mimeType,
|
|
12158
|
+
size: first.size,
|
|
12159
|
+
open: async () => {
|
|
12160
|
+
if (firstPending) {
|
|
12161
|
+
firstPending = false;
|
|
12162
|
+
return first.stream;
|
|
12163
|
+
}
|
|
12164
|
+
return (await resolve()).stream;
|
|
12165
|
+
}
|
|
12166
|
+
};
|
|
12167
|
+
}
|
|
12168
|
+
/** Run the job's source to its end; a live source refused overloaded goes once more on a new socket. */
|
|
12169
|
+
async run(job) {
|
|
12170
|
+
let retried = false;
|
|
12171
|
+
while (true) {
|
|
12172
|
+
const sent = await this.inOrder(() => this.setSource(job));
|
|
12173
|
+
if (sent === null) {
|
|
12174
|
+
return;
|
|
12175
|
+
}
|
|
12176
|
+
const { socket, source } = sent;
|
|
12177
|
+
const uploading = source.uploads.size > 0 ? this.upload(socket, source, job) : Promise.resolve();
|
|
12178
|
+
let whip = null;
|
|
12179
|
+
let overloaded;
|
|
12180
|
+
try {
|
|
12181
|
+
overloaded = await this.follow(job, source, () => {
|
|
12182
|
+
if (job.mediaStream !== void 0) {
|
|
12183
|
+
const fail = (error) => {
|
|
12184
|
+
job.stopOnPipeline?.();
|
|
12185
|
+
source.inbox.push(error instanceof Error ? error : new Error(String(error)));
|
|
12186
|
+
};
|
|
12187
|
+
whip = new SocketWhip(job.mediaStream, job.source["whip"]["ingressId"], (message) => this.command(socket, message), fail, this.ctx.requestLogger);
|
|
12188
|
+
whip.start().catch(fail);
|
|
12189
|
+
}
|
|
12190
|
+
});
|
|
12191
|
+
} finally {
|
|
12192
|
+
job.openSource = null;
|
|
12193
|
+
job.stopOnPipeline = null;
|
|
12194
|
+
socket.sources.delete(source.id);
|
|
12195
|
+
source.uploads.forEach((upload) => upload.end());
|
|
12196
|
+
await uploading;
|
|
12197
|
+
await whip?.close();
|
|
12198
|
+
}
|
|
12199
|
+
if (!overloaded) {
|
|
12200
|
+
return;
|
|
12201
|
+
}
|
|
12202
|
+
if (retried || !job.live) {
|
|
12203
|
+
throw new PipelineCommandError("overloaded", "the pipeline API has no capacity for another live source");
|
|
12204
|
+
}
|
|
12205
|
+
retried = true;
|
|
12206
|
+
await socket.closed;
|
|
11562
12207
|
}
|
|
11563
|
-
const result = await response.json();
|
|
11564
|
-
return result["id"];
|
|
11565
12208
|
}
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
12209
|
+
/** Send the job's setSource; the caller runs it in submission order. */
|
|
12210
|
+
async setSource(job) {
|
|
12211
|
+
if (job.cancelled) {
|
|
12212
|
+
return null;
|
|
11569
12213
|
}
|
|
11570
|
-
|
|
11571
|
-
|
|
12214
|
+
const combined = await combinedMedia(job);
|
|
12215
|
+
const socket = await this.ensureSocket();
|
|
12216
|
+
const source = new OpenSource(this.newId("source"));
|
|
12217
|
+
if (combined === null) {
|
|
12218
|
+
uploadKeys(job).forEach((key) => source.uploads.set(key, new Credit()));
|
|
12219
|
+
}
|
|
12220
|
+
socket.sources.set(source.id, source);
|
|
12221
|
+
job.openSource = source;
|
|
12222
|
+
job.stopOnPipeline = () => {
|
|
12223
|
+
if (socket.open) {
|
|
12224
|
+
socket.sendJson({ type: "stopSource", id: source.id });
|
|
12225
|
+
}
|
|
12226
|
+
};
|
|
12227
|
+
const command = job.command(source.id);
|
|
12228
|
+
if (combined === null) {
|
|
12229
|
+
socket.sendJson(command);
|
|
12230
|
+
} else {
|
|
12231
|
+
socket.sendBytes(combinedMessage(command, combined));
|
|
11572
12232
|
}
|
|
11573
|
-
|
|
11574
|
-
|
|
12233
|
+
return { socket, source };
|
|
12234
|
+
}
|
|
12235
|
+
/** Hand the source's predictions to the job until it ends; true when the pipeline API was overloaded. */
|
|
12236
|
+
async follow(job, source, onResult) {
|
|
12237
|
+
while (true) {
|
|
12238
|
+
if (job.cancelled) {
|
|
12239
|
+
return false;
|
|
12240
|
+
}
|
|
12241
|
+
const message = await source.inbox.next();
|
|
12242
|
+
if (message === CANCELLED) {
|
|
12243
|
+
return false;
|
|
12244
|
+
}
|
|
12245
|
+
if (message instanceof Error) {
|
|
12246
|
+
throw message;
|
|
12247
|
+
}
|
|
12248
|
+
switch (message["type"]) {
|
|
12249
|
+
case "result":
|
|
12250
|
+
onResult();
|
|
12251
|
+
break;
|
|
12252
|
+
case "prediction":
|
|
12253
|
+
job.predicted(message["prediction"]);
|
|
12254
|
+
break;
|
|
12255
|
+
case "end":
|
|
12256
|
+
if (message["reason"] === "eos") {
|
|
12257
|
+
return false;
|
|
12258
|
+
}
|
|
12259
|
+
throw new PipelineSourceError(source.id, message["reason"] ?? "", message["message"] ?? "");
|
|
12260
|
+
case "error":
|
|
12261
|
+
if (message["error"] === "overloaded") {
|
|
12262
|
+
return true;
|
|
12263
|
+
}
|
|
12264
|
+
throw new PipelineCommandError(message["error"] ?? "", message["message"] ?? "");
|
|
12265
|
+
case "dropped":
|
|
12266
|
+
this.ctx.requestLogger.debug("the pipeline API dropped %s predictions of a live source", message["count"]);
|
|
12267
|
+
break;
|
|
12268
|
+
}
|
|
11575
12269
|
}
|
|
11576
|
-
|
|
11577
|
-
|
|
12270
|
+
}
|
|
12271
|
+
/** Send each upload member's bytes in chunks within its credit, then its uploaded. */
|
|
12272
|
+
async upload(socket, source, job) {
|
|
12273
|
+
const keys = uploadKeys(job);
|
|
12274
|
+
try {
|
|
12275
|
+
for (let i = 0; i < keys.length; i++) {
|
|
12276
|
+
const key = keys[i];
|
|
12277
|
+
const credit = source.uploads.get(key);
|
|
12278
|
+
for await (let piece of pieces(await job.media[i].open())) {
|
|
12279
|
+
while (piece.length > 0) {
|
|
12280
|
+
const n = await credit.take(Math.min(piece.length, CHUNK_BYTES));
|
|
12281
|
+
const chunk = new Uint8Array(n + 1);
|
|
12282
|
+
chunk[0] = CHUNK;
|
|
12283
|
+
chunk.set(piece.subarray(0, n), 1);
|
|
12284
|
+
socket.sendBytes(chunk);
|
|
12285
|
+
piece = piece.subarray(n);
|
|
12286
|
+
}
|
|
12287
|
+
}
|
|
12288
|
+
socket.sendJson(key === null ? { type: "uploaded", id: source.id } : { type: "uploaded", id: source.id, member: key });
|
|
12289
|
+
}
|
|
12290
|
+
} catch (error) {
|
|
12291
|
+
if (!(error instanceof UploadEnded)) {
|
|
12292
|
+
if (socket.open) {
|
|
12293
|
+
source.inbox.push(error instanceof Error ? error : new Error(String(error)));
|
|
12294
|
+
}
|
|
12295
|
+
}
|
|
11578
12296
|
}
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
12297
|
+
}
|
|
12298
|
+
async session() {
|
|
12299
|
+
const token = await this.token();
|
|
12300
|
+
const validUntil = token === void 0 ? Number.MAX_VALUE : Math.min((await this.ctx.credentialSession()).validUntil, Number.MAX_VALUE);
|
|
12301
|
+
return {
|
|
12302
|
+
eyepopUrl: this.ctx.eyepopUrl(),
|
|
12303
|
+
accessToken: token ?? "",
|
|
12304
|
+
validUntil,
|
|
12305
|
+
popId: "transient",
|
|
12306
|
+
accountId: this.ctx.options.accountId,
|
|
12307
|
+
baseUrl: this._pipelineApiUrl ?? void 0,
|
|
12308
|
+
pipelineId: void 0,
|
|
12309
|
+
isPipelineMode: true,
|
|
12310
|
+
authenticationHeaders: () => token === void 0 ? {} : { Authorization: `Bearer ${token}` }
|
|
12311
|
+
};
|
|
12312
|
+
}
|
|
12313
|
+
};
|
|
12314
|
+
var SocketWhip = class {
|
|
12315
|
+
constructor(_stream, _ingressId, _command, _fail, _logger) {
|
|
12316
|
+
this._stream = _stream;
|
|
12317
|
+
this._ingressId = _ingressId;
|
|
12318
|
+
this._command = _command;
|
|
12319
|
+
this._fail = _fail;
|
|
12320
|
+
this._logger = _logger;
|
|
12321
|
+
this._pc = null;
|
|
12322
|
+
this._answered = false;
|
|
12323
|
+
this._queued = [];
|
|
12324
|
+
this._offerData = null;
|
|
12325
|
+
}
|
|
12326
|
+
async start() {
|
|
12327
|
+
const servers = await this._command({ type: "whipIceServers", ingressId: this._ingressId });
|
|
12328
|
+
const iceServers = servers["iceServers"] ?? [];
|
|
12329
|
+
this._logger.debug("whip %s: ice servers %j", this._ingressId, iceServers.map((server) => server.split(";")[0]));
|
|
12330
|
+
const pc = new RTCPeerConnection({ iceServers: WebrtcBase.linkToIceServers(iceServers.join(", ") || null) });
|
|
12331
|
+
this._pc = pc;
|
|
12332
|
+
pc.oniceconnectionstatechange = () => {
|
|
12333
|
+
this._logger.debug("whip %s: ice connection %s", this._ingressId, pc.iceConnectionState);
|
|
12334
|
+
if (pc.iceConnectionState === "failed" && this._pc === pc) {
|
|
12335
|
+
this._fail(
|
|
12336
|
+
new Error(
|
|
12337
|
+
`the WebRTC connection of whip ingress ${this._ingressId} failed: no candidate pair connected, so the pipeline API is likely reachable through no STUN or TURN server (ice servers: ${iceServers.length})`
|
|
12338
|
+
)
|
|
12339
|
+
);
|
|
12340
|
+
}
|
|
12341
|
+
};
|
|
12342
|
+
pc.onicecandidate = (evt) => {
|
|
12343
|
+
if (evt.candidate !== null) {
|
|
12344
|
+
this.candidate(evt.candidate).catch((error) => this._logger.debug("whipIce failed: %s", error));
|
|
11583
12345
|
}
|
|
12346
|
+
};
|
|
12347
|
+
this._stream.getTracks().forEach((track) => pc.addTrack(track, this._stream));
|
|
12348
|
+
const offer = await pc.createOffer();
|
|
12349
|
+
if (!offer.sdp) {
|
|
12350
|
+
throw new Error("the local offer has no sdp");
|
|
12351
|
+
}
|
|
12352
|
+
this._offerData = WebrtcBase.parseOffer(offer.sdp);
|
|
12353
|
+
await pc.setLocalDescription(offer);
|
|
12354
|
+
const answer = await this._command({ type: "whipOffer", ingressId: this._ingressId, sdp: offer.sdp });
|
|
12355
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer["sdp"] });
|
|
12356
|
+
this._answered = true;
|
|
12357
|
+
const queued = this._queued;
|
|
12358
|
+
this._queued = [];
|
|
12359
|
+
if (queued.length > 0) {
|
|
12360
|
+
await this.sendCandidates(queued);
|
|
12361
|
+
}
|
|
12362
|
+
}
|
|
12363
|
+
async candidate(candidate) {
|
|
12364
|
+
if (!this._answered) {
|
|
12365
|
+
this._queued.push(candidate);
|
|
11584
12366
|
return;
|
|
11585
12367
|
}
|
|
11586
|
-
|
|
11587
|
-
|
|
12368
|
+
await this.sendCandidates([candidate]);
|
|
12369
|
+
}
|
|
12370
|
+
async sendCandidates(candidates) {
|
|
12371
|
+
if (this._offerData === null) {
|
|
12372
|
+
return;
|
|
11588
12373
|
}
|
|
12374
|
+
await this._command({ type: "whipIce", ingressId: this._ingressId, fragment: WebrtcBase.generateSdpFragment(this._offerData, candidates) });
|
|
12375
|
+
}
|
|
12376
|
+
async close() {
|
|
12377
|
+
this._stream.getTracks().forEach((track) => track.stop());
|
|
12378
|
+
const pc = this._pc;
|
|
12379
|
+
this._pc = null;
|
|
12380
|
+
pc?.close();
|
|
12381
|
+
}
|
|
12382
|
+
};
|
|
12383
|
+
function checkedUrl(url) {
|
|
12384
|
+
const parsed = new URL(url);
|
|
12385
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
12386
|
+
throw new PipelineConfigurationError("insecure_url", `the pipeline API url ${url} must be an http or https url`);
|
|
12387
|
+
}
|
|
12388
|
+
if (parsed.protocol === "http:" && !isLoopback(parsed.hostname)) {
|
|
12389
|
+
throw new PipelineConfigurationError("insecure_url", `the pipeline API url ${url} must use https unless its host is a loopback address`);
|
|
12390
|
+
}
|
|
12391
|
+
return url.replace(/\/+$/, "");
|
|
12392
|
+
}
|
|
12393
|
+
function isLoopback(host) {
|
|
12394
|
+
return host === "localhost" || host === "[::1]" || host === "::1" || /^127\.\d+\.\d+\.\d+$/.test(host);
|
|
12395
|
+
}
|
|
12396
|
+
function knownSize(body) {
|
|
12397
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) {
|
|
12398
|
+
return body.size;
|
|
12399
|
+
}
|
|
12400
|
+
if (ArrayBuffer.isView(body) || body instanceof ArrayBuffer) {
|
|
12401
|
+
return body.byteLength;
|
|
12402
|
+
}
|
|
12403
|
+
return void 0;
|
|
12404
|
+
}
|
|
12405
|
+
function bodyMedia(body, contentType, size) {
|
|
12406
|
+
return { open: async () => body, contentType, size: size ?? knownSize(body) };
|
|
12407
|
+
}
|
|
12408
|
+
function uploadGroup(media) {
|
|
12409
|
+
return { group: media.map((m) => ({ upload: { contentType: m.contentType } })) };
|
|
12410
|
+
}
|
|
12411
|
+
function uploadKeys(job) {
|
|
12412
|
+
if (job.source["upload"] !== void 0) {
|
|
12413
|
+
return [null];
|
|
12414
|
+
}
|
|
12415
|
+
const group = job.source["group"] ?? [];
|
|
12416
|
+
return group.flatMap((member, i) => member["upload"] !== void 0 ? [i] : []);
|
|
12417
|
+
}
|
|
12418
|
+
async function combinedMedia(job) {
|
|
12419
|
+
if (job.media.length === 0 || job.live || job.media.some((m) => m.size === void 0)) {
|
|
12420
|
+
return null;
|
|
12421
|
+
}
|
|
12422
|
+
if (job.media.reduce((total, m) => total + (m.size ?? 0), 0) > COMBINED_UPLOAD_MAX_BYTES) {
|
|
12423
|
+
return null;
|
|
12424
|
+
}
|
|
12425
|
+
const bytes = [];
|
|
12426
|
+
for (const media of job.media) {
|
|
12427
|
+
bytes.push(await readAll(await media.open()));
|
|
12428
|
+
}
|
|
12429
|
+
return bytes;
|
|
12430
|
+
}
|
|
12431
|
+
async function readAll(body) {
|
|
12432
|
+
const parts = [];
|
|
12433
|
+
for await (const piece of pieces(body)) {
|
|
12434
|
+
parts.push(piece);
|
|
12435
|
+
}
|
|
12436
|
+
return concat(parts);
|
|
12437
|
+
}
|
|
12438
|
+
function concat(parts) {
|
|
12439
|
+
const all = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
|
|
12440
|
+
let offset = 0;
|
|
12441
|
+
for (const part of parts) {
|
|
12442
|
+
all.set(part, offset);
|
|
12443
|
+
offset += part.length;
|
|
12444
|
+
}
|
|
12445
|
+
return all;
|
|
12446
|
+
}
|
|
12447
|
+
function combinedMessage(command, media) {
|
|
12448
|
+
if (command["group"] !== void 0) {
|
|
12449
|
+
let i = 0;
|
|
12450
|
+
command = {
|
|
12451
|
+
...command,
|
|
12452
|
+
group: command["group"].map((member) => member["upload"] !== void 0 ? { upload: { ...member["upload"], bytes: media[i++].length } } : member)
|
|
12453
|
+
};
|
|
12454
|
+
}
|
|
12455
|
+
const header = new TextEncoder().encode(JSON.stringify(command));
|
|
12456
|
+
const prefix = new Uint8Array(5);
|
|
12457
|
+
prefix[0] = COMBINED;
|
|
12458
|
+
new DataView(prefix.buffer).setUint32(1, header.length, false);
|
|
12459
|
+
return concat([prefix, header, ...media]);
|
|
12460
|
+
}
|
|
12461
|
+
async function* pieces(body) {
|
|
12462
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) {
|
|
12463
|
+
yield* pieces(body.stream());
|
|
12464
|
+
} else if (body instanceof ArrayBuffer) {
|
|
12465
|
+
yield new Uint8Array(body);
|
|
12466
|
+
} else if (ArrayBuffer.isView(body)) {
|
|
12467
|
+
yield new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
12468
|
+
} else {
|
|
12469
|
+
const reader = body.getReader();
|
|
11589
12470
|
try {
|
|
11590
|
-
|
|
11591
|
-
|
|
12471
|
+
while (true) {
|
|
12472
|
+
const { value, done } = await reader.read();
|
|
12473
|
+
if (done) {
|
|
12474
|
+
return;
|
|
12475
|
+
}
|
|
12476
|
+
if (value !== void 0 && value.length > 0) {
|
|
12477
|
+
yield value;
|
|
12478
|
+
}
|
|
12479
|
+
}
|
|
11592
12480
|
} finally {
|
|
11593
|
-
|
|
12481
|
+
reader.releaseLock();
|
|
11594
12482
|
}
|
|
11595
12483
|
}
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
this._pipelineId = null;
|
|
11607
|
-
}
|
|
11608
|
-
this._pipeline = null;
|
|
11609
|
-
try {
|
|
11610
|
-
this.options().pop = pop;
|
|
11611
|
-
await this.getComputeSession();
|
|
11612
|
-
} catch (error) {
|
|
11613
|
-
this.options().pop = previousPop;
|
|
11614
|
-
this._pipeline = null;
|
|
11615
|
-
this._pipelineId = null;
|
|
11616
|
-
this._baseUrl = previousPop ? null : previousBaseUrl;
|
|
11617
|
-
this._sessionAccessToken = previousSessionAccessToken;
|
|
11618
|
-
this._sessionAccessTokenValidUntil = previousSessionAccessTokenValidUntil;
|
|
11619
|
-
throw error;
|
|
11620
|
-
}
|
|
11621
|
-
if (!this._pipelineId) {
|
|
11622
|
-
throw new Error("Compute session did not provide a pipeline for the requested pop");
|
|
12484
|
+
}
|
|
12485
|
+
|
|
12486
|
+
// worker/worker_endpoint.ts
|
|
12487
|
+
var WorkerEndpoint = class extends Endpoint {
|
|
12488
|
+
constructor(options) {
|
|
12489
|
+
if (options.isPipelineMode) {
|
|
12490
|
+
options = { ...options, jobQueueLength: options.jobQueueLength || MAX_OPEN_SOURCES };
|
|
12491
|
+
if (options.jobQueueLength > MAX_OPEN_SOURCES) {
|
|
12492
|
+
throw new PipelineConfigurationError("job_queue_length", `jobQueueLength ${options.jobQueueLength} exceeds ${MAX_OPEN_SOURCES}, the open sources a pipeline API socket takes`);
|
|
12493
|
+
}
|
|
11623
12494
|
}
|
|
11624
|
-
|
|
12495
|
+
super(options);
|
|
12496
|
+
const context = {
|
|
12497
|
+
options,
|
|
12498
|
+
logger: this._logger,
|
|
12499
|
+
requestLogger: this._requestLogger,
|
|
12500
|
+
client: () => this._client,
|
|
12501
|
+
limit: () => this._limit,
|
|
12502
|
+
jobDone: () => {
|
|
12503
|
+
this._limit.release();
|
|
12504
|
+
this.updateState();
|
|
12505
|
+
},
|
|
12506
|
+
updateState: (state, message) => this.updateState(state, message),
|
|
12507
|
+
eyepopUrl: () => this.eyepopUrl(),
|
|
12508
|
+
eyepopWebApiUrl: () => this.eyepopWebApiUrl(),
|
|
12509
|
+
credentialHeader: () => super.authorizationHeader(),
|
|
12510
|
+
forgetCredential: () => this.statusHandler401(401),
|
|
12511
|
+
fetchWithRetry: (fetcher, retries) => this.fetchWithRetry(fetcher, retries),
|
|
12512
|
+
credentialSession: () => super.session()
|
|
12513
|
+
};
|
|
12514
|
+
this._backend = options.isPipelineMode ? new PipelineBackend(context) : new SessionBackend(context);
|
|
12515
|
+
this.setStatusRetryHandlers(/* @__PURE__ */ new Map([[404, (statusCode) => this._backend.onStatus(statusCode)]]));
|
|
11625
12516
|
}
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
12517
|
+
options() {
|
|
12518
|
+
return this._options;
|
|
12519
|
+
}
|
|
12520
|
+
/** What runs this endpoint's jobs. */
|
|
12521
|
+
backend() {
|
|
12522
|
+
return this._backend;
|
|
12523
|
+
}
|
|
12524
|
+
async disconnect(wait = true) {
|
|
12525
|
+
await this._backend.disconnect(() => super.disconnect(wait));
|
|
12526
|
+
}
|
|
12527
|
+
async session() {
|
|
12528
|
+
return await this._backend.session();
|
|
12529
|
+
}
|
|
12530
|
+
popName() {
|
|
12531
|
+
return this._backend.popName();
|
|
12532
|
+
}
|
|
12533
|
+
pop() {
|
|
12534
|
+
return this._backend.pop();
|
|
12535
|
+
}
|
|
12536
|
+
async changePop(pop) {
|
|
12537
|
+
validatePop(pop);
|
|
12538
|
+
await this._backend.changePop(pop);
|
|
12539
|
+
}
|
|
12540
|
+
/**
|
|
12541
|
+
* @deprecated Pop ids are only retained for backwards compatibility.
|
|
12542
|
+
* Configure transient sessions with `WorkerOptions.pop`.
|
|
12543
|
+
*/
|
|
12544
|
+
popId() {
|
|
12545
|
+
return this.options().popId || null;
|
|
12546
|
+
}
|
|
12547
|
+
async process(param1, param2) {
|
|
12548
|
+
if (param1.source !== void 0) {
|
|
12549
|
+
const request = param1;
|
|
12550
|
+
this.validateProcessParams(request);
|
|
12551
|
+
return await this.submit({ kind: "source", source: request.source, params: request });
|
|
12552
|
+
} else {
|
|
12553
|
+
return await this.submit({ kind: "source", source: param1, params: { componentParams: param2 } });
|
|
11629
12554
|
}
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
12555
|
+
}
|
|
12556
|
+
/**
|
|
12557
|
+
* Reject a source's calibration before any work is started for it.
|
|
12558
|
+
*
|
|
12559
|
+
* Called by every entry point that takes ProcessParams, before the
|
|
12560
|
+
* concurrency limit is acquired, so a request that can never be sent
|
|
12561
|
+
* does not hold a slot.
|
|
12562
|
+
*/
|
|
12563
|
+
validateProcessParams(params) {
|
|
12564
|
+
if (params.camera !== void 0) {
|
|
12565
|
+
validateCamera(params.camera);
|
|
11641
12566
|
}
|
|
11642
12567
|
}
|
|
11643
|
-
async
|
|
11644
|
-
if (
|
|
11645
|
-
|
|
12568
|
+
async uploadGroup(paths, params = {}) {
|
|
12569
|
+
if (paths.length === 0) {
|
|
12570
|
+
throw new Error("upload group requires at least one path");
|
|
11646
12571
|
}
|
|
11647
|
-
|
|
12572
|
+
this.validateProcessParams(params);
|
|
12573
|
+
return await this.submit({ kind: "uploadGroup", paths, params });
|
|
11648
12574
|
}
|
|
11649
|
-
async
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
return "Implicit";
|
|
12575
|
+
async uploadStreamGroup(streams, mimeTypes, params = {}) {
|
|
12576
|
+
if (streams.length === 0) {
|
|
12577
|
+
throw new Error("upload stream group requires at least one stream");
|
|
11653
12578
|
}
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
return `Bearer ${credential}`;
|
|
12579
|
+
if (mimeTypes !== void 0 && mimeTypes.length !== streams.length) {
|
|
12580
|
+
throw new Error("mimeTypes must have the same length as streams");
|
|
11657
12581
|
}
|
|
11658
|
-
|
|
12582
|
+
this.validateProcessParams(params);
|
|
12583
|
+
return await this.submit({ kind: "uploadStreamGroup", streams, mimeTypes, params });
|
|
11659
12584
|
}
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
this._pipeline = {
|
|
11664
|
-
id: this._pipelineId,
|
|
11665
|
-
state: "idle",
|
|
11666
|
-
startTime: (/* @__PURE__ */ new Date()).toString(),
|
|
11667
|
-
pop
|
|
11668
|
-
};
|
|
12585
|
+
async loadFromGroup(urls, params = {}) {
|
|
12586
|
+
if (urls.length === 0) {
|
|
12587
|
+
throw new Error("load from group requires at least one url");
|
|
11669
12588
|
}
|
|
12589
|
+
this.validateProcessParams(params);
|
|
12590
|
+
return await this.submit({ kind: "loadFromGroup", urls, params });
|
|
12591
|
+
}
|
|
12592
|
+
async submit(work) {
|
|
12593
|
+
return await this._backend.submit(work);
|
|
12594
|
+
}
|
|
12595
|
+
async reconnect() {
|
|
12596
|
+
await this._backend.reconnect();
|
|
12597
|
+
return this;
|
|
12598
|
+
}
|
|
12599
|
+
async authorizationHeader() {
|
|
12600
|
+
return await this._backend.authorizationHeader();
|
|
11670
12601
|
}
|
|
11671
12602
|
};
|
|
11672
|
-
_WorkerEndpoint.WAIT_FOR_IS_READY = 60 * 1e3;
|
|
11673
|
-
var WorkerEndpoint = _WorkerEndpoint;
|
|
11674
|
-
|
|
11675
|
-
// shims/websockets.ts
|
|
11676
|
-
var createWebSocket;
|
|
11677
|
-
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
11678
|
-
createWebSocket = (url, protocols) => {
|
|
11679
|
-
return new WebSocket(url, protocols);
|
|
11680
|
-
};
|
|
11681
|
-
} else {
|
|
11682
|
-
createWebSocket = (url, protocols) => {
|
|
11683
|
-
const ws = require("ws");
|
|
11684
|
-
return new ws.WebSocket(url, protocols);
|
|
11685
|
-
};
|
|
11686
|
-
}
|
|
11687
12603
|
|
|
11688
12604
|
// data/data_endpoint.ts
|
|
11689
12605
|
var import_url_join = __toESM(require("url-join"));
|
|
@@ -13351,6 +14267,13 @@ var EyePop;
|
|
|
13351
14267
|
if (opts.isLocalMode === void 0) {
|
|
13352
14268
|
opts.isLocalMode = stringToBooleanSafe(readEnv("EYEPOP_LOCAL_MODE"));
|
|
13353
14269
|
}
|
|
14270
|
+
if (opts.isPipelineMode === void 0) {
|
|
14271
|
+
const shared = resolveAuth(opts)?.session;
|
|
14272
|
+
opts.isPipelineMode = shared?.isPipelineMode ?? stringToBooleanSafe(readEnv("EYEPOP_PIPELINE_MODE"));
|
|
14273
|
+
}
|
|
14274
|
+
if (opts.isPipelineMode) {
|
|
14275
|
+
return pipelineEndpoint(opts);
|
|
14276
|
+
}
|
|
13354
14277
|
if (opts.isLocalMode) {
|
|
13355
14278
|
opts.eyepopUrl = "http://127.0.0.1:8080";
|
|
13356
14279
|
}
|
|
@@ -13402,6 +14325,38 @@ var EyePop;
|
|
|
13402
14325
|
return new WorkerEndpoint(opts);
|
|
13403
14326
|
}
|
|
13404
14327
|
EyePop2.workerEndpoint = workerEndpoint;
|
|
14328
|
+
function pipelineEndpoint(opts) {
|
|
14329
|
+
const popId = opts.popId ?? readEnv("EYEPOP_POP_ID");
|
|
14330
|
+
const sessionOnly = {
|
|
14331
|
+
popId: popId === "transient" /* Transient */ ? void 0 : popId,
|
|
14332
|
+
sessionUuid: opts.sessionUuid ?? readEnv("EYEPOP_SESSION_UUID"),
|
|
14333
|
+
sessionName: opts.sessionName,
|
|
14334
|
+
pipelineImage: opts.pipelineImage,
|
|
14335
|
+
pipelineVersion: opts.pipelineVersion
|
|
14336
|
+
};
|
|
14337
|
+
const named = Object.entries(sessionOnly).filter(([, value]) => value).map(([name]) => name);
|
|
14338
|
+
if (named.length > 0) {
|
|
14339
|
+
throw new PipelineConfigurationError("session_options", `pipeline mode runs no worker session; it takes no ${named.join(", ")}`, named);
|
|
14340
|
+
}
|
|
14341
|
+
if (opts.isLocalMode && !opts.eyepopUrl) {
|
|
14342
|
+
opts.eyepopUrl = readEnv("EYEPOP_URL") || "http://127.0.0.1:9002";
|
|
14343
|
+
}
|
|
14344
|
+
if (!opts.jobQueueLength) {
|
|
14345
|
+
opts.jobQueueLength = MAX_OPEN_SOURCES;
|
|
14346
|
+
}
|
|
14347
|
+
_fill_default_options(opts);
|
|
14348
|
+
if (resolveAuth(opts) === void 0) {
|
|
14349
|
+
if (opts.isLocalMode) {
|
|
14350
|
+
opts.auth = { isLocal: true };
|
|
14351
|
+
} else {
|
|
14352
|
+
throw new Error("apiKey/accessToken/session/oAuth2 option or EYEPOP_API_KEY environment variable is required");
|
|
14353
|
+
}
|
|
14354
|
+
}
|
|
14355
|
+
if (opts.accountId === void 0) {
|
|
14356
|
+
opts.accountId = resolveAuth(opts)?.session?.accountId ?? readAccountUuidEnv();
|
|
14357
|
+
}
|
|
14358
|
+
return new WorkerEndpoint(opts);
|
|
14359
|
+
}
|
|
13405
14360
|
function dataEndpoint(opts = {}) {
|
|
13406
14361
|
_fill_default_options(opts);
|
|
13407
14362
|
const auth = resolveAuth(opts);
|
|
@@ -13479,6 +14434,7 @@ var index_default = EyePop;
|
|
|
13479
14434
|
AssetUrlType,
|
|
13480
14435
|
AutoAnnotateStatus,
|
|
13481
14436
|
AutoAnnotateTask,
|
|
14437
|
+
COMBINED_UPLOAD_MAX_BYTES,
|
|
13482
14438
|
ChangeType,
|
|
13483
14439
|
ComputeAccountMismatchError,
|
|
13484
14440
|
ComputeApiError,
|
|
@@ -13500,6 +14456,7 @@ var index_default = EyePop;
|
|
|
13500
14456
|
LoadFromGroupJob,
|
|
13501
14457
|
LoadFromJob,
|
|
13502
14458
|
LoadMediaStreamJob,
|
|
14459
|
+
MAX_OPEN_SOURCES,
|
|
13503
14460
|
ModelExportStatus,
|
|
13504
14461
|
ModelFormat,
|
|
13505
14462
|
ModelPrecisionType,
|
|
@@ -13508,11 +14465,21 @@ var index_default = EyePop;
|
|
|
13508
14465
|
ModelTrainingStage,
|
|
13509
14466
|
ModelType,
|
|
13510
14467
|
MotionModel,
|
|
14468
|
+
PIPELINE_PATH,
|
|
14469
|
+
PipelineBackend,
|
|
14470
|
+
PipelineCommandError,
|
|
14471
|
+
PipelineConfigurationError,
|
|
14472
|
+
PipelineJob,
|
|
14473
|
+
PipelineSocketClosedError,
|
|
14474
|
+
PipelineSourceError,
|
|
13511
14475
|
PointCloud,
|
|
13512
14476
|
PopComponentType,
|
|
13513
14477
|
PredictionVersion,
|
|
13514
14478
|
QUATERNION_TOLERANCE,
|
|
14479
|
+
SUBPROTOCOL,
|
|
14480
|
+
SelectMode,
|
|
13515
14481
|
Semaphore,
|
|
14482
|
+
SessionBackend,
|
|
13516
14483
|
Stream,
|
|
13517
14484
|
TaskMode,
|
|
13518
14485
|
TaskType,
|
|
@@ -13537,6 +14504,8 @@ var index_default = EyePop;
|
|
|
13537
14504
|
decodePointCloud,
|
|
13538
14505
|
encodeMultipartFormData,
|
|
13539
14506
|
getBaseUrl,
|
|
14507
|
+
isSelectOperator,
|
|
14508
|
+
popSelects,
|
|
13540
14509
|
randomMultipartBoundary,
|
|
13541
14510
|
readableStreamFromString,
|
|
13542
14511
|
resolveAuth,
|