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