@eyepop.ai/eyepop 0.9.2 → 0.10.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/index.mjs CHANGED
@@ -1,9 +1,4 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
1
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
2
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
3
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
4
  }) : x)(function(x) {
@@ -14,22 +9,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
14
9
  var __commonJS = (cb, mod) => function __require2() {
15
10
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
11
  };
17
- var __copyProps = (to, from, except, desc) => {
18
- if (from && typeof from === "object" || typeof from === "function") {
19
- for (let key of __getOwnPropNames(from))
20
- if (!__hasOwnProp.call(to, key) && key !== except)
21
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
- }
23
- return to;
24
- };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
- // If the importer is in node compatibility mode or this is not an ESM
27
- // file that has been converted to a CommonJS file using a Babel-
28
- // compatible transform (i.e. "__esModule" has not been set), then set
29
- // "default" to the CommonJS "module.exports" for node compatibility.
30
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
- mod
32
- ));
33
12
 
34
13
  // node_modules/mime-db/db.json
35
14
  var require_db = __commonJS({
@@ -8655,6 +8634,17 @@ var require_mime_types = __commonJS({
8655
8634
  }
8656
8635
  });
8657
8636
 
8637
+ // src/types.ts
8638
+ var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
8639
+ EndpointState2["Idle"] = "Idle";
8640
+ EndpointState2["Busy"] = "Busy";
8641
+ EndpointState2["Authenticating"] = "Authenticating";
8642
+ EndpointState2["FetchConfig"] = "FetchConfig";
8643
+ EndpointState2["StartingPop"] = "StartingPop";
8644
+ EndpointState2["Error"] = "Error";
8645
+ return EndpointState2;
8646
+ })(EndpointState || {});
8647
+
8658
8648
  // src/streaming.ts
8659
8649
  var Stream = class _Stream {
8660
8650
  constructor(iterator, controller) {
@@ -8907,16 +8897,13 @@ function readableStreamAsyncIterable(stream) {
8907
8897
  }
8908
8898
 
8909
8899
  // src/jobs.ts
8910
- var _AbstractJob = class _AbstractJob {
8911
- constructor(baseUrl, pipelineId, authorizationHeader, client, requestLogger) {
8900
+ var AbstractJob = class {
8901
+ constructor(getSession, client, requestLogger) {
8912
8902
  this._responseStream = null;
8913
8903
  this._controller = new AbortController();
8914
- this._baseUrl = baseUrl.replace(/\/+$/, "");
8915
- this._pipelineId = pipelineId;
8916
- this._authorizationHeader = authorizationHeader;
8904
+ this._getSession = getSession;
8917
8905
  this._client = client;
8918
8906
  this._requestLogger = requestLogger;
8919
- this.i = _AbstractJob.n++;
8920
8907
  }
8921
8908
  [Symbol.asyncIterator]() {
8922
8909
  if (!this._responseStream) {
@@ -8927,22 +8914,55 @@ var _AbstractJob = class _AbstractJob {
8927
8914
  async startJob() {
8928
8915
  return Promise.reject("abstract class");
8929
8916
  }
8930
- start(done) {
8931
- this._responseStream = new Promise((resolve2, reject) => {
8932
- const request = this.startJob();
8933
- request.then((response) => {
8934
- if (response.status != 200) {
8935
- reject(`upload error ${response.status}: ${response.text()}`);
8936
- } else if (!response.body) {
8937
- reject(`response body is empty`);
8938
- } else {
8939
- resolve2(response.body);
8917
+ start(done, status) {
8918
+ this._responseStream = new Promise(async (resolve2, reject) => {
8919
+ try {
8920
+ let retries = 1;
8921
+ while (true) {
8922
+ try {
8923
+ const response = await this.startJob();
8924
+ if (response.status == 401) {
8925
+ status(response.status);
8926
+ if (retries--) {
8927
+ this._requestLogger.info("received 401 response, attempt to reauthorize once");
8928
+ } else {
8929
+ reject(`response ${response.status}: ${response.statusText}`);
8930
+ break;
8931
+ }
8932
+ } else if (response.status == 404) {
8933
+ status(response.status);
8934
+ if (retries--) {
8935
+ this._requestLogger.info("received 404 response, attempt to restart pop");
8936
+ } else {
8937
+ reject(`response ${response.status}: ${response.statusText}`);
8938
+ break;
8939
+ }
8940
+ } else if (response.status != 200) {
8941
+ reject(`upload error ${response.status}: ${await response.text()}`);
8942
+ break;
8943
+ } else if (!response.body) {
8944
+ reject(`response body is empty`);
8945
+ break;
8946
+ } else {
8947
+ resolve2(response.body);
8948
+ break;
8949
+ }
8950
+ } catch (error) {
8951
+ if (retries--) {
8952
+ this._requestLogger.info("unknown, attempt to restart pop (as if we received 404)", error);
8953
+ status(404);
8954
+ } else {
8955
+ status(500);
8956
+ reject(error);
8957
+ break;
8958
+ }
8959
+ }
8940
8960
  }
8941
- }).catch((reason) => {
8961
+ } catch (reason) {
8942
8962
  reject(reason);
8943
- }).finally(() => {
8963
+ } finally {
8944
8964
  done();
8945
- });
8965
+ }
8946
8966
  });
8947
8967
  return this;
8948
8968
  }
@@ -8950,24 +8970,24 @@ var _AbstractJob = class _AbstractJob {
8950
8970
  this._controller.abort("cancelled");
8951
8971
  }
8952
8972
  };
8953
- _AbstractJob.n = 0;
8954
- var AbstractJob = _AbstractJob;
8973
+ AbstractJob.n = 0;
8955
8974
  var UploadJob = class extends AbstractJob {
8956
8975
  get [Symbol.toStringTag]() {
8957
8976
  return "uploadJob";
8958
8977
  }
8959
- constructor(stream, mimeType, baseUrl, pipelineId, authorizationHeader, client, requestLogger) {
8960
- super(baseUrl, pipelineId, authorizationHeader, client, requestLogger);
8978
+ constructor(stream, mimeType, getSession, client, requestLogger) {
8979
+ super(getSession, client, requestLogger);
8961
8980
  this._uploadStream = stream;
8962
8981
  this._mimeType = mimeType;
8963
8982
  }
8964
8983
  async startJob() {
8984
+ const session = await this._getSession();
8965
8985
  const headers = {
8966
- "Authorization": this._authorizationHeader,
8986
+ "Authorization": `Bearer ${session.accessToken}`,
8967
8987
  "Accept": "application/jsonl",
8968
8988
  "Content-Type": this._mimeType
8969
8989
  };
8970
- const postUrl = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=queue&processing=sync`;
8990
+ const postUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
8971
8991
  this._requestLogger.debug("before POST %s with stream as body");
8972
8992
  const response = await this._client.fetch(postUrl, {
8973
8993
  headers,
@@ -8983,26 +9003,27 @@ var UploadJob = class extends AbstractJob {
8983
9003
  }
8984
9004
  };
8985
9005
  var LoadFromJob = class extends AbstractJob {
8986
- constructor(location, baseUrl, pipelineId, authorizationHeader, client, requestLogger) {
8987
- super(baseUrl, pipelineId, authorizationHeader, client, requestLogger);
9006
+ constructor(location, getSession, client, requestLogger) {
9007
+ super(getSession, client, requestLogger);
8988
9008
  this._location = location;
8989
9009
  }
8990
9010
  get [Symbol.toStringTag]() {
8991
9011
  return "loadFromJob";
8992
9012
  }
8993
9013
  async startJob() {
8994
- const headers = {
8995
- "Authorization": this._authorizationHeader,
8996
- "Accept": "application/jsonl",
8997
- "Content-Type": "application/json"
8998
- };
8999
9014
  const body = {
9000
9015
  "sourceType": "URL",
9001
9016
  "url": this._location
9002
9017
  };
9003
- const postUrl = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=queue&processing=sync`;
9004
- this._requestLogger.debug("before PATCH %s with url %s as source", postUrl, this._location);
9005
- const response = await this._client.fetch(postUrl, {
9018
+ const session = await this._getSession();
9019
+ const headers = {
9020
+ "Authorization": `Bearer ${session.accessToken}`,
9021
+ "Accept": "application/jsonl",
9022
+ "Content-Type": "application/json"
9023
+ };
9024
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9025
+ this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._location);
9026
+ const response = await this._client.fetch(patchUrl, {
9006
9027
  headers,
9007
9028
  method: "PATCH",
9008
9029
  body: JSON.stringify(body),
@@ -9010,7 +9031,7 @@ var LoadFromJob = class extends AbstractJob {
9010
9031
  // @ts-ignore
9011
9032
  dispatcher: this._dispatcher
9012
9033
  });
9013
- this._requestLogger.debug("after PATCH %s with url %s as source", postUrl, this._location);
9034
+ this._requestLogger.debug("after PATCH %s with url %s as source", patchUrl, this._location);
9014
9035
  return response;
9015
9036
  }
9016
9037
  };
@@ -9045,7 +9066,7 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9045
9066
  }
9046
9067
  }
9047
9068
  createHttpClient = async () => {
9048
- const undici = await import("undici");
9069
+ const undici = __require("undici");
9049
9070
  const agent = new undici.Agent({
9050
9071
  keepAliveTimeout: 1e4,
9051
9072
  connections: 5
@@ -9203,8 +9224,8 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9203
9224
  if (params.file) {
9204
9225
  return params;
9205
9226
  }
9206
- const mime = await Promise.resolve().then(() => __toESM(require_mime_types()));
9207
- const fs = await import("fs");
9227
+ const mime = require_mime_types();
9228
+ const fs = __require("fs");
9208
9229
  let mimeType;
9209
9230
  let stream;
9210
9231
  if (params.filePath) {
@@ -9235,7 +9256,9 @@ var Endpoint = class {
9235
9256
  this._token = null;
9236
9257
  this._expire_token_time = null;
9237
9258
  this._client = null;
9238
- this._limit = null;
9259
+ this._state = "Idle" /* Idle */;
9260
+ this._stateChangeHandler = null;
9261
+ this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9239
9262
  let rootLogger;
9240
9263
  if (options.logger) {
9241
9264
  rootLogger = options.logger;
@@ -9245,19 +9268,41 @@ var Endpoint = class {
9245
9268
  this._logger = rootLogger.child({ module: "eyepop" });
9246
9269
  this._requestLogger = this._logger.child({ module: "requests" });
9247
9270
  }
9271
+ onStateChanged(handler) {
9272
+ this._stateChangeHandler = handler;
9273
+ return this;
9274
+ }
9275
+ state() {
9276
+ return this._state;
9277
+ }
9278
+ pendingJobs() {
9279
+ return (this._options.jobQueueLength ?? 1024) - this._limit.getPermits();
9280
+ }
9248
9281
  popName() {
9249
9282
  return this._popName;
9250
9283
  }
9284
+ popId() {
9285
+ return this._options.popId || null;
9286
+ }
9251
9287
  async session() {
9252
9288
  await this.currentAccessToken();
9253
9289
  if (this._token == null || this._expire_token_time == null) {
9254
9290
  return Promise.reject("endpoint not connected");
9255
9291
  }
9256
- const token = {
9292
+ if (this._baseUrl == null || this._pipelineId == null) {
9293
+ await this.reconnect();
9294
+ if (this._baseUrl == null || this._pipelineId == null) {
9295
+ return Promise.reject("endpoint not connected");
9296
+ }
9297
+ }
9298
+ return {
9299
+ eyepopUrl: this._options.eyepopUrl,
9300
+ popId: this._options.popId,
9257
9301
  accessToken: this._token,
9258
- validUntil: new Date(this._expire_token_time * 1e3)
9302
+ validUntil: this._expire_token_time * 1e3,
9303
+ baseUrl: this._baseUrl,
9304
+ pipelineId: this._pipelineId
9259
9305
  };
9260
- return token;
9261
9306
  }
9262
9307
  async upload(params) {
9263
9308
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
@@ -9268,17 +9313,14 @@ var Endpoint = class {
9268
9313
  return Promise.reject("upload for streams requires a mimeType");
9269
9314
  }
9270
9315
  await this._limit.acquire();
9271
- const job = new UploadJob(
9272
- params.file,
9273
- params.mimeType,
9274
- this._baseUrl,
9275
- this._pipelineId,
9276
- await this.authorizationHeader(),
9277
- this._client,
9278
- this._requestLogger
9279
- );
9316
+ this.updateState();
9317
+ const job = new UploadJob(params.file, params.mimeType, async () => {
9318
+ return this.session();
9319
+ }, this._client, this._requestLogger);
9280
9320
  return job.start(() => {
9281
- this._limit?.release();
9321
+ this.jobDone(job);
9322
+ }, (statusCode) => {
9323
+ this.jobStatus(job, statusCode);
9282
9324
  });
9283
9325
  }
9284
9326
  async loadFrom(location) {
@@ -9286,16 +9328,14 @@ var Endpoint = class {
9286
9328
  throw new Error("endpoint not connected, use open() before loadFrom()");
9287
9329
  }
9288
9330
  await this._limit.acquire();
9289
- const job = new LoadFromJob(
9290
- location,
9291
- this._baseUrl,
9292
- this._pipelineId,
9293
- await this.authorizationHeader(),
9294
- this._client,
9295
- this._requestLogger
9296
- );
9331
+ this.updateState();
9332
+ const job = new LoadFromJob(location, async () => {
9333
+ return this.session();
9334
+ }, this._client, this._requestLogger);
9297
9335
  return job.start(() => {
9298
- this._limit?.release();
9336
+ this.jobDone(job);
9337
+ }, (statusCode) => {
9338
+ this.jobStatus(job, statusCode);
9299
9339
  });
9300
9340
  }
9301
9341
  // noinspection TypeScriptValidateTypes
@@ -9310,20 +9350,69 @@ var Endpoint = class {
9310
9350
  if (!this._options.popId) {
9311
9351
  return Promise.reject("option popId or environment variable EYEPOP_POP_ID is required");
9312
9352
  }
9313
- if (this._options.session) {
9314
- this._token = this._options.session.accessToken;
9315
- this._expire_token_time = this._options.session.validUntil.getTime() / 1e3;
9353
+ if (this._options.auth && this._options.auth.session) {
9354
+ this._token = this._options.auth.session.accessToken;
9355
+ this._expire_token_time = this._options.auth.session.validUntil / 1e3;
9316
9356
  } else {
9317
- if (!this._options.secretKey) {
9357
+ if (!this._options.auth || !this._options.auth.secretKey) {
9318
9358
  return Promise.reject("option secretKey or environment variable EYEPOP_SECRET_KEY is required");
9319
9359
  }
9320
9360
  }
9321
9361
  this._client = await createHttpClient();
9322
- this._limit = new Semaphore(this._options.jobQueueLength);
9323
- const config_url = `${this.eyepopUrl()}/pops/${this._options.popId}/config?auto_start=${this._options.autoStart}`;
9362
+ let result = await this.reconnect();
9363
+ if (this._baseUrl && this._options.stopJobs) {
9364
+ const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9365
+ const body = { "sourceType": "NONE" };
9366
+ const headers = {
9367
+ "Authorization": await this.authorizationHeader(),
9368
+ "Content-Type": "application/json"
9369
+ };
9370
+ this._requestLogger.debug("before PATCH %s", stop_url);
9371
+ let response = await this._client.fetch(stop_url, { method: "PATCH", headers, body: JSON.stringify(body) });
9372
+ if (response.status >= 300) {
9373
+ const message = await response.text();
9374
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9375
+ }
9376
+ this._requestLogger.debug("after PATCH %s", stop_url);
9377
+ }
9378
+ return result;
9379
+ }
9380
+ jobDone(job) {
9381
+ this._limit?.release();
9382
+ this.updateState();
9383
+ }
9384
+ updateState(newState = void 0) {
9385
+ const fromState = this._state;
9386
+ if (newState == void 0) {
9387
+ if (this._limit.getPermits() == this._options.jobQueueLength) {
9388
+ newState = "Idle" /* Idle */;
9389
+ } else {
9390
+ newState = "Busy" /* Busy */;
9391
+ }
9392
+ }
9393
+ this._state = newState;
9394
+ if (this._stateChangeHandler) {
9395
+ this._stateChangeHandler(fromState, this._state);
9396
+ }
9397
+ }
9398
+ jobStatus(job, statusCode) {
9399
+ if (statusCode == 404) {
9400
+ this._pipelineId = null;
9401
+ this._baseUrl = null;
9402
+ } else if (statusCode == 401) {
9403
+ this._token = null;
9404
+ this._expire_token_time = null;
9405
+ }
9406
+ }
9407
+ async reconnect() {
9408
+ if (!this._client) {
9409
+ return Promise.reject("endpoint not initialized");
9410
+ }
9411
+ const config_url = `${this.eyepopUrl()}/pops/${this._options.popId}/config?auto_start=false`;
9324
9412
  const headers = {
9325
9413
  "Authorization": await this.authorizationHeader()
9326
9414
  };
9415
+ this.updateState("FetchConfig" /* FetchConfig */);
9327
9416
  this._requestLogger.debug("before GET %s", config_url);
9328
9417
  let response = await this._client.fetch(config_url, { headers });
9329
9418
  if (response.status == 401) {
@@ -9332,41 +9421,47 @@ var Endpoint = class {
9332
9421
  const headers2 = {
9333
9422
  "Authorization": await this.authorizationHeader()
9334
9423
  };
9424
+ this.updateState("FetchConfig" /* FetchConfig */);
9335
9425
  response = await this._client.fetch(config_url, {
9336
9426
  headers: headers2
9337
9427
  // @ts-ignore
9338
9428
  });
9339
9429
  }
9340
9430
  if (response.status != 200) {
9431
+ this.updateState("Error" /* Error */);
9341
9432
  const message = await response.text();
9342
9433
  return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9343
- }
9344
- const data = await response.json();
9345
- const config = data;
9346
- this._baseUrl = config.base_url;
9347
- this._pipelineId = config.pipeline_id;
9348
- this._popName = config.name;
9349
- this._requestLogger.debug("after GET %s: %s / %s", config_url, this._baseUrl, this._pipelineId);
9350
- if (!this._pipelineId || !this._baseUrl) {
9351
- return Promise.reject(`Pop not started`);
9352
- }
9353
- this._baseUrl = this._baseUrl.replace(/\/+$/, "");
9354
- if (this._options.stopJobs) {
9355
- const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9356
- const body = { "sourceType": "NONE" };
9357
- const headers2 = {
9358
- "Authorization": await this.authorizationHeader(),
9359
- "Content-Type": "application/json"
9360
- };
9361
- this._requestLogger.debug("before PATCH %s", stop_url);
9362
- let response2 = await this._client.fetch(stop_url, { method: "PATCH", headers: headers2, body: JSON.stringify(body) });
9363
- if (response2.status >= 300) {
9364
- const message = await response2.text();
9365
- return Promise.reject(`Unexpected status ${response2.status}: ${message}`);
9434
+ } else {
9435
+ let config = await response.json();
9436
+ if (!config.base_url && this._options.autoStart) {
9437
+ const auto_start_config_url = `${this.eyepopUrl()}/pops/${this._options.popId}/config?auto_start=true`;
9438
+ this._requestLogger.debug("pop was not running, trying to autostart with: %s", auto_start_config_url);
9439
+ const headers2 = {
9440
+ "Authorization": await this.authorizationHeader()
9441
+ };
9442
+ this.updateState("StartingPop" /* StartingPop */);
9443
+ response = await this._client.fetch(auto_start_config_url, {
9444
+ headers: headers2
9445
+ // @ts-ignore
9446
+ });
9447
+ if (response.status != 200) {
9448
+ this.updateState("Error" /* Error */);
9449
+ const message = await response.text();
9450
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9451
+ }
9452
+ config = await response.json();
9366
9453
  }
9367
- this._requestLogger.debug("after PATCH %s", stop_url);
9454
+ this._baseUrl = config.base_url;
9455
+ this._pipelineId = config.pipeline_id;
9456
+ this._popName = config.name;
9457
+ this._requestLogger.debug("after GET %s: %s / %s", config_url, this._baseUrl, this._pipelineId);
9458
+ if (!this._pipelineId || !this._baseUrl) {
9459
+ return Promise.reject(`Pop not started`);
9460
+ }
9461
+ this._baseUrl = this._baseUrl.replace(/\/+$/, "");
9462
+ this.updateState();
9463
+ return Promise.resolve(this);
9368
9464
  }
9369
- return this;
9370
9465
  }
9371
9466
  async disconnect(wait = true) {
9372
9467
  if (wait && this._limit) {
@@ -9374,13 +9469,13 @@ var Endpoint = class {
9374
9469
  await this._limit.acquire();
9375
9470
  }
9376
9471
  }
9472
+ this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9377
9473
  if (this._client) {
9378
9474
  await this._client.close();
9379
9475
  this._client = null;
9380
9476
  }
9381
9477
  this._baseUrl = null;
9382
9478
  this._pipelineId = null;
9383
- this._limit = null;
9384
9479
  }
9385
9480
  async authorizationHeader() {
9386
9481
  return `Bearer ${await this.currentAccessToken()}`;
@@ -9388,12 +9483,13 @@ var Endpoint = class {
9388
9483
  async currentAccessToken() {
9389
9484
  const now = Date.now() / 1e3;
9390
9485
  if (!this._token || this._expire_token_time < now) {
9486
+ this.updateState("Authenticating" /* Authenticating */);
9391
9487
  if (!this._client) {
9392
9488
  return Promise.reject("endpoint not connected");
9393
- } else if (!this._options.secretKey) {
9489
+ } else if (!this._options.auth || !this._options.auth.secretKey) {
9394
9490
  return Promise.reject("temporary access token expired");
9395
9491
  }
9396
- const body = { "secret_key": this._options.secretKey };
9492
+ const body = { "secret_key": this._options.auth.secretKey };
9397
9493
  const headers = { "Content-Type": "application/json" };
9398
9494
  const post_url = `${this.eyepopUrl()}/authentication/token`;
9399
9495
  this._requestLogger.debug("before POST %s", post_url);
@@ -9411,6 +9507,7 @@ var Endpoint = class {
9411
9507
  const token = data;
9412
9508
  this._token = token.access_token;
9413
9509
  this._expire_token_time = now + token.expires_in - 60;
9510
+ this.updateState();
9414
9511
  }
9415
9512
  this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
9416
9513
  return this._token;
@@ -9433,17 +9530,19 @@ var _EyePopPlot = class _EyePopPlot {
9433
9530
  }
9434
9531
  prediction(p) {
9435
9532
  if (p && p.objects) {
9533
+ const x_scale = this._context.canvas.width / p.source_width;
9534
+ const y_scale = this._context.canvas.height / p.source_height;
9436
9535
  for (let i = 0; i < p.objects.length; i++) {
9437
- this.object(p.objects[i]);
9536
+ this.object(p.objects[i], x_scale, y_scale);
9438
9537
  }
9439
9538
  }
9440
9539
  }
9441
- object(obj) {
9540
+ object(obj, x_scale, y_scale) {
9442
9541
  const context = this._context;
9443
- const x = obj.x;
9444
- const y = obj.y;
9445
- const w = obj.width;
9446
- const h = obj.height;
9542
+ const x = obj.x * x_scale;
9543
+ const y = obj.y * y_scale;
9544
+ const w = obj.width * x_scale;
9545
+ const h = obj.height * y_scale;
9447
9546
  context.beginPath();
9448
9547
  context.rect(obj.x, obj.y, obj.width, obj.height);
9449
9548
  context.lineWidth = 1;
@@ -9535,11 +9634,50 @@ _EyePopPlot.colors = {
9535
9634
  };
9536
9635
  var EyePopPlot = _EyePopPlot;
9537
9636
 
9637
+ // src/shims/browser_session.ts
9638
+ var authenticateBrowserSession;
9639
+ if ("document" in globalThis && "implementation" in globalThis.document) {
9640
+ authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
9641
+ const queryString = window.location.search;
9642
+ const urlParams = new URLSearchParams(queryString);
9643
+ let token = urlParams.get("pop_token");
9644
+ if (token) {
9645
+ urlParams.delete("pop_token");
9646
+ const url = `${window.location.pathname}?${urlParams}`;
9647
+ window.history.replaceState({}, document.title, url);
9648
+ return {
9649
+ eyepopUrl,
9650
+ popId,
9651
+ accessToken: token,
9652
+ validUntil: Date.now() + 60 * 60 * 1e3
9653
+ };
9654
+ } else {
9655
+ const url = `${oauthUrl}?url=${encodeURIComponent(window.location.href)}`;
9656
+ window.location.replace(url);
9657
+ return {
9658
+ eyepopUrl,
9659
+ popId,
9660
+ accessToken: "in-progress",
9661
+ validUntil: 0
9662
+ };
9663
+ }
9664
+ };
9665
+ } else {
9666
+ authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
9667
+ return null;
9668
+ };
9669
+ }
9670
+
9538
9671
  // src/index.ts
9672
+ var readEnv = (env) => {
9673
+ if (typeof process !== "undefined") {
9674
+ return process.env?.[env] ?? void 0;
9675
+ }
9676
+ return void 0;
9677
+ };
9539
9678
  var EyePopSdk = class {
9540
9679
  static endpoint({
9541
- secretKey = readEnv("EYEPOP_SECRET_KEY"),
9542
- session = void 0,
9680
+ auth = this.defaultAuth,
9543
9681
  popId = readEnv("EYEPOP_POP_ID"),
9544
9682
  eyepopUrl = readEnv("EYEPOP_URL"),
9545
9683
  autoStart = true,
@@ -9548,11 +9686,17 @@ var EyePopSdk = class {
9548
9686
  logger,
9549
9687
  ...opts
9550
9688
  } = {}) {
9689
+ if (auth.oAuth && popId) {
9690
+ let oauthUrl = "https://dashboard.eyepop.ai/sdkauth";
9691
+ if (typeof auth.oAuth == "string") {
9692
+ oauthUrl = auth.oAuth;
9693
+ }
9694
+ auth.session = authenticateBrowserSession(oauthUrl, eyepopUrl ?? "https://api.eyepop.ai", popId) ?? void 0;
9695
+ }
9551
9696
  const options = {
9552
- secretKey,
9553
- session,
9554
- popId,
9555
- eyepopUrl: eyepopUrl ?? "https://api.eyepop.ai",
9697
+ auth,
9698
+ popId: auth.session ? auth.session.popId : popId,
9699
+ eyepopUrl: auth.session ? auth.session.eyepopUrl : eyepopUrl ?? "https://api.eyepop.ai",
9556
9700
  autoStart,
9557
9701
  stopJobs,
9558
9702
  jobQueueLength,
@@ -9565,14 +9709,14 @@ var EyePopSdk = class {
9565
9709
  return new EyePopPlot(context);
9566
9710
  }
9567
9711
  };
9568
- var readEnv = (env) => {
9569
- if (typeof process !== "undefined") {
9570
- return process.env?.[env] ?? void 0;
9571
- }
9572
- return void 0;
9712
+ EyePopSdk.defaultAuth = {
9713
+ secretKey: readEnv("EYEPOP_SECRET_KEY")
9573
9714
  };
9715
+ var src_default = EyePopSdk;
9574
9716
  export {
9575
- EyePopSdk
9717
+ EndpointState,
9718
+ EyePopSdk,
9719
+ src_default as default
9576
9720
  };
9577
9721
  /*! Bundled license information:
9578
9722