@bytescale/sdk 3.55.0 → 3.56.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.
@@ -3196,6 +3196,12 @@ function AuthManagerBrowser_awaitIgnored(value, direct) {
3196
3196
  return value && value.then ? value.then(AuthManagerBrowser_empty) : Promise.resolve();
3197
3197
  }
3198
3198
  }
3199
+ function AuthManagerBrowser_invokeIgnored(body) {
3200
+ var result = body();
3201
+ if (result && result.then) {
3202
+ return result.then(AuthManagerBrowser_empty);
3203
+ }
3204
+ }
3199
3205
  function AuthManagerBrowser_await(value, then, direct) {
3200
3206
  if (direct) {
3201
3207
  return then ? then(value) : value;
@@ -3217,12 +3223,6 @@ function AuthManagerBrowser_async(f) {
3217
3223
  }
3218
3224
  };
3219
3225
  }
3220
- function AuthManagerBrowser_invokeIgnored(body) {
3221
- var result = body();
3222
- if (result && result.then) {
3223
- return result.then(AuthManagerBrowser_empty);
3224
- }
3225
- }
3226
3226
  function AuthManagerBrowser_invoke(body, then) {
3227
3227
  var result = body();
3228
3228
  if (result && result.then) {
@@ -3261,8 +3261,14 @@ function AuthManagerBrowser_continueIgnored(value) {
3261
3261
  return value.then(AuthManagerBrowser_empty);
3262
3262
  }
3263
3263
  }
3264
+ function AuthManagerBrowser_continue(value, then) {
3265
+ return value && value.then ? value.then(then) : then(value);
3266
+ }
3264
3267
  function AuthManagerBrowser_typeof(o) { "@babel/helpers - typeof"; return AuthManagerBrowser_typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, AuthManagerBrowser_typeof(o); }
3265
3268
  function AuthManagerBrowser_defineProperty(e, r, t) { return (r = AuthManagerBrowser_toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
3269
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = AuthManagerBrowser_unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
3270
+ function AuthManagerBrowser_unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return AuthManagerBrowser_arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? AuthManagerBrowser_arrayLikeToArray(r, a) : void 0; } }
3271
+ function AuthManagerBrowser_arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
3266
3272
  function AuthManagerBrowser_classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
3267
3273
  function AuthManagerBrowser_defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, AuthManagerBrowser_toPropertyKey(o.key), o); } }
3268
3274
  function AuthManagerBrowser_createClass(e, r, t) { return r && AuthManagerBrowser_defineProperties(e.prototype, r), t && AuthManagerBrowser_defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
@@ -3296,8 +3302,8 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3296
3302
  }, {
3297
3303
  key: "isAuthSessionReady",
3298
3304
  value: function isAuthSessionReady() {
3299
- var _a;
3300
- return ((_a = AuthSessionState.getSession()) === null || _a === void 0 ? void 0 : _a.accessToken) !== undefined;
3305
+ var session = AuthSessionState.getSession();
3306
+ return (session === null || session === void 0 ? void 0 : session.isReady) === true || (session === null || session === void 0 ? void 0 : session.accessToken) !== undefined;
3301
3307
  }
3302
3308
  }, {
3303
3309
  key: "beginAuthSession",
@@ -3309,12 +3315,17 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3309
3315
  if (_this.isAuthSessionActive()) {
3310
3316
  throw new Error("Auth session already active. Please call 'await endAuthSession()' and then call 'await beginAuthSession(...)' to start a new auth session.");
3311
3317
  }
3318
+ var canUseServiceWorkers = _this.serviceWorkerUtils.canUseServiceWorkers();
3319
+ if (_this.isV2(params) && !canUseServiceWorkers) {
3320
+ throw new Error("This auth session requires service workers, but this browser does not support them.");
3321
+ }
3312
3322
  var newSession = {
3313
3323
  accessToken: undefined,
3314
3324
  accessTokenRefreshHandle: undefined,
3315
3325
  params: params,
3316
3326
  isActive: true,
3317
- authServiceWorker: params.serviceWorkerScript !== undefined && _this.serviceWorkerUtils.canUseServiceWorkers() ? {
3327
+ isReady: false,
3328
+ authServiceWorker: params.serviceWorkerScript !== undefined && canUseServiceWorkers ? {
3318
3329
  serviceWorkerScript: params.serviceWorkerScript,
3319
3330
  type: "Uninitialized"
3320
3331
  } : undefined
@@ -3323,7 +3334,13 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3323
3334
  return newSession;
3324
3335
  })), function (session) {
3325
3336
  // IMPORTANT: must be called outside the above, else re-entrant deadlock will occur.
3326
- return AuthManagerBrowser_awaitIgnored(_this.refreshAccessToken(session));
3337
+ return AuthManagerBrowser_invokeIgnored(function () {
3338
+ if (_this.isV2(session.params)) {
3339
+ return AuthManagerBrowser_awaitIgnored(_this.refreshServiceWorkerConfig(session, session.params));
3340
+ } else {
3341
+ return AuthManagerBrowser_awaitIgnored(_this.refreshAccessToken(session, session.params));
3342
+ }
3343
+ });
3327
3344
  });
3328
3345
  } catch (e) {
3329
3346
  return Promise.reject(e);
@@ -3344,14 +3361,15 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3344
3361
  if (session.accessTokenRefreshHandle !== undefined) {
3345
3362
  _this2.scheduler.unschedule(session.accessTokenRefreshHandle);
3346
3363
  }
3347
- return AuthManagerBrowser_await(_this2.deleteAccessToken(session.params), function () {
3364
+ return AuthManagerBrowser_invoke(function () {
3365
+ if (!_this2.isV2(session.params)) {
3366
+ return AuthManagerBrowser_awaitIgnored(_this2.deleteAccessToken(session.params));
3367
+ }
3368
+ }, function () {
3348
3369
  return AuthManagerBrowser_invokeIgnored(function () {
3349
3370
  if (session.authServiceWorker !== undefined) {
3350
3371
  // Prevent service worker from authorizing subsequent requests.
3351
- return AuthManagerBrowser_awaitIgnored(_this2.serviceWorkerUtils.sendMessage({
3352
- type: "SET_BYTESCALE_AUTH_CONFIG",
3353
- config: []
3354
- }, session.authServiceWorker, _this2.serviceWorkerScriptFieldName));
3372
+ return AuthManagerBrowser_awaitIgnored(_this2.sendServiceWorkerConfig(session, []));
3355
3373
  }
3356
3374
  });
3357
3375
  });
@@ -3362,7 +3380,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3362
3380
  }
3363
3381
  }, {
3364
3382
  key: "refreshAccessToken",
3365
- value: function refreshAccessToken(session) {
3383
+ value: function refreshAccessToken(session, params) {
3366
3384
  try {
3367
3385
  var _this3 = this;
3368
3386
  return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this3.authSessionMutex.safe(AuthManagerBrowser_async(function () {
@@ -3375,30 +3393,26 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3375
3393
  var expires = secondsFromNow(_this3.retryAuthAfterErrorSeconds);
3376
3394
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_finallyRethrows(function () {
3377
3395
  return AuthManagerBrowser_catch(function () {
3378
- var _getAccessToken = _this3.getAccessToken,
3379
- _session$params = session.params;
3380
- return AuthManagerBrowser_await(session.params.authHeaders(), function (_session$params$authH) {
3381
- return AuthManagerBrowser_await(_getAccessToken.call(_this3, _session$params, _session$params$authH), function (jwt) {
3396
+ var _getAccessToken = _this3.getAccessToken;
3397
+ return AuthManagerBrowser_await(params.authHeaders(), function (_params$authHeaders) {
3398
+ return AuthManagerBrowser_await(_getAccessToken.call(_this3, params, _params$authHeaders), function (jwt) {
3382
3399
  // We don't use cookie-based auth if the browser supports service worker-based auth, as using both will cause
3383
3400
  // confusion for us in the future (i.e. we may question "do we need to use both together? was there a reason?").
3384
3401
  // Also: if the user has omitted "allowedOrigins" from their JWT, then service worker-based auth is more secure
3385
3402
  // than cookie-based auth, which is another reason to prevent these cookies from being set unless required.
3386
3403
  var setCookie = session.authServiceWorker === undefined;
3387
- return AuthManagerBrowser_await(_this3.setAccessToken(session.params, jwt, setCookie), function (setTokenResult) {
3404
+ return AuthManagerBrowser_await(_this3.setAccessToken(params, jwt, setCookie), function (setTokenResult) {
3388
3405
  return AuthManagerBrowser_invoke(function () {
3389
3406
  if (session.authServiceWorker !== undefined) {
3390
- return AuthManagerBrowser_await(_this3.serviceWorkerUtils.sendMessage({
3391
- type: "SET_BYTESCALE_AUTH_CONFIG",
3392
- config: [{
3393
- headers: [{
3394
- key: "Authorization",
3395
- value: "Bearer ".concat(jwt)
3396
- }],
3397
- expires: secondsFromNow(setTokenResult.ttlSeconds),
3398
- sourceUrlPrefixes: session.params.sourceUrlPrefixes,
3399
- urlPrefix: "".concat(session.params.sourceUrlPrefixes === undefined ? "" : _this3.sourceScopedUrlPrefixMarker).concat(_this3.getCdnUrl(session.params), "/").concat(session.params.accountId, "/")
3400
- }]
3401
- }, session.authServiceWorker, _this3.serviceWorkerScriptFieldName), function () {
3407
+ return AuthManagerBrowser_await(_this3.sendServiceWorkerConfig(session, [{
3408
+ headers: [{
3409
+ key: "Authorization",
3410
+ value: "Bearer ".concat(jwt)
3411
+ }],
3412
+ expires: secondsFromNow(setTokenResult.ttlSeconds),
3413
+ sourceUrlPrefixes: params.sourceUrlPrefixes,
3414
+ urlPrefix: "".concat(_this3.getCdnUrl(params), "/").concat(params.accountId, "/")
3415
+ }]), function () {
3402
3416
  // Allow time for the service worker to receive and process the message. Since this is asynchronous and not
3403
3417
  // synchronized, we need to wait for a sufficient amount of time to ensure the service worker is ready to
3404
3418
  // authenticate requests, so that after 'beginAuthSession' completes, users can start making requests.
@@ -3416,6 +3430,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3416
3430
  expires = secondsFromNow(actualTtl);
3417
3431
  // Set this at the end, as it's also used to signal 'isAuthSessionReady', so must be set after configuring the Service Worker, etc.
3418
3432
  session.accessToken = setTokenResult.accessToken;
3433
+ session.isReady = true;
3419
3434
  });
3420
3435
  });
3421
3436
  });
@@ -3428,7 +3443,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3428
3443
  // 'setTimeout' can be paused (e.g., during hibernation), risking JWT expiration before it triggers. We use a
3429
3444
  // scheduler to check wall-clock time every second and execute the callback at the scheduled time (below).
3430
3445
  session.accessTokenRefreshHandle = _this3.scheduler.schedule(expires, function () {
3431
- _this3.refreshAccessToken(session).then(function () {},
3446
+ _this3.refreshAccessToken(session, params).then(function () {},
3432
3447
  // Should not occur, as this method shouldn't throw errors.
3433
3448
  function (e) {
3434
3449
  return ConsoleUtils.error("Unexpected error when refreshing JWT access token: ".concat(e));
@@ -3441,6 +3456,91 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3441
3456
  return Promise.reject(e);
3442
3457
  }
3443
3458
  }
3459
+ }, {
3460
+ key: "refreshServiceWorkerConfig",
3461
+ value: function refreshServiceWorkerConfig(session, params) {
3462
+ try {
3463
+ var _this4 = this;
3464
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this4.authSessionMutex.safe(AuthManagerBrowser_async(function () {
3465
+ var _exit = false;
3466
+ if (!session.isActive) {
3467
+ return;
3468
+ }
3469
+ var refreshAt;
3470
+ return AuthManagerBrowser_continue(AuthManagerBrowser_catch(function () {
3471
+ return AuthManagerBrowser_await(params.getServiceWorkerConfig(), function (config) {
3472
+ if (!Array.isArray(config)) {
3473
+ throw new Error("The 'getServiceWorkerConfig' callback must return an array.");
3474
+ }
3475
+ return AuthManagerBrowser_await(_this4.sendServiceWorkerConfig(session, config), function () {
3476
+ return AuthManagerBrowser_await(new Promise(function (resolve) {
3477
+ return setTimeout(resolve, 100);
3478
+ }), function () {
3479
+ session.isReady = true;
3480
+ refreshAt = _this4.getServiceWorkerConfigRefreshEpoch(config);
3481
+ });
3482
+ });
3483
+ });
3484
+ }, function (e) {
3485
+ ConsoleUtils.warn("Unable to refresh service worker auth config: ".concat(e));
3486
+ refreshAt = Date.now() + _this4.retryAuthAfterErrorSeconds * 1000;
3487
+ }), function (_result2) {
3488
+ if (_exit) return _result2;
3489
+ if (refreshAt !== undefined) {
3490
+ session.accessTokenRefreshHandle = _this4.scheduler.schedule(refreshAt, function () {
3491
+ _this4.refreshServiceWorkerConfig(session, params).then(function () {}, function (e) {
3492
+ return ConsoleUtils.error("Unexpected error when refreshing service worker auth config: ".concat(e));
3493
+ });
3494
+ });
3495
+ }
3496
+ });
3497
+ }))));
3498
+ } catch (e) {
3499
+ return Promise.reject(e);
3500
+ }
3501
+ }
3502
+ }, {
3503
+ key: "getServiceWorkerConfigRefreshEpoch",
3504
+ value: function getServiceWorkerConfigRefreshEpoch(config) {
3505
+ var earliestExpiry;
3506
+ var _iterator = _createForOfIteratorHelper(config),
3507
+ _step;
3508
+ try {
3509
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
3510
+ var entry = _step.value;
3511
+ if (entry.expires !== undefined && (earliestExpiry === undefined || entry.expires < earliestExpiry)) {
3512
+ earliestExpiry = entry.expires;
3513
+ }
3514
+ }
3515
+ } catch (err) {
3516
+ _iterator.e(err);
3517
+ } finally {
3518
+ _iterator.f();
3519
+ }
3520
+ return earliestExpiry === undefined ? undefined : earliestExpiry - this.refreshBeforeExpirySeconds * 1000;
3521
+ }
3522
+ }, {
3523
+ key: "sendServiceWorkerConfig",
3524
+ value: function sendServiceWorkerConfig(session, config) {
3525
+ try {
3526
+ var _this5 = this;
3527
+ if (session.authServiceWorker === undefined) {
3528
+ throw new Error("Service worker configuration cannot be applied because service workers are unavailable.");
3529
+ }
3530
+ return AuthManagerBrowser_await(_this5.serviceWorkerUtils.sendMessage({
3531
+ type: "SET_BYTESCALE_AUTH_CONFIG",
3532
+ config: config.map(function (entry) {
3533
+ return Object.assign(Object.assign({}, entry), {
3534
+ urlPrefix: "".concat(entry.sourceUrlPrefixes === undefined ? "" : _this5.sourceScopedUrlPrefixMarker).concat(entry.urlPrefix)
3535
+ });
3536
+ })
3537
+ }, session.authServiceWorker, _this5.serviceWorkerScriptFieldName), function (_this5$serviceWorkerU) {
3538
+ session.authServiceWorker = _this5$serviceWorkerU;
3539
+ });
3540
+ } catch (e) {
3541
+ return Promise.reject(e);
3542
+ }
3543
+ }
3444
3544
  }, {
3445
3545
  key: "getAccessTokenUrl",
3446
3546
  value: function getAccessTokenUrl(params, setCookie) {
@@ -3456,9 +3556,9 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3456
3556
  key: "deleteAccessToken",
3457
3557
  value: function deleteAccessToken(params) {
3458
3558
  try {
3459
- var _this4 = this;
3559
+ var _this6 = this;
3460
3560
  var _a;
3461
- return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this4.getAccessTokenUrl(params, true), {
3561
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this6.getAccessTokenUrl(params, true), {
3462
3562
  method: "DELETE",
3463
3563
  credentials: "include",
3464
3564
  headers: {}
@@ -3474,15 +3574,15 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3474
3574
  key: "setAccessToken",
3475
3575
  value: function setAccessToken(params, jwt, setCookie) {
3476
3576
  try {
3477
- var _this5 = this;
3577
+ var _this7 = this;
3478
3578
  var _a;
3479
3579
  var request = {
3480
3580
  accessToken: jwt
3481
3581
  };
3482
- return AuthManagerBrowser_await(BaseAPI.fetch(_this5.getAccessTokenUrl(params, setCookie), {
3582
+ return AuthManagerBrowser_await(BaseAPI.fetch(_this7.getAccessTokenUrl(params, setCookie), {
3483
3583
  method: "PUT",
3484
3584
  credentials: "include",
3485
- headers: AuthManagerBrowser_defineProperty({}, _this5.contentType, _this5.contentTypeJson),
3585
+ headers: AuthManagerBrowser_defineProperty({}, _this7.contentType, _this7.contentTypeJson),
3486
3586
  body: JSON.stringify(request)
3487
3587
  }, {
3488
3588
  isBytescaleApi: true,
@@ -3498,10 +3598,10 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3498
3598
  key: "getAccessToken",
3499
3599
  value: function getAccessToken(params, headers) {
3500
3600
  try {
3501
- var _this6 = this;
3601
+ var _this8 = this;
3502
3602
  var _a, _b;
3503
3603
  var endpointName = "Your auth API endpoint";
3504
- var requiredContentType = _this6.contentTypeText;
3604
+ var requiredContentType = _this8.contentTypeText;
3505
3605
  return AuthManagerBrowser_await(BaseAPI.fetch(params.authUrl, {
3506
3606
  method: "GET",
3507
3607
  headers: headers
@@ -3509,10 +3609,10 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3509
3609
  isBytescaleApi: false,
3510
3610
  fetchApi: (_a = params.options) === null || _a === void 0 ? void 0 : _a.fetchApi
3511
3611
  }), function (result) {
3512
- var actualContentType = (_b = result.headers.get(_this6.contentType)) !== null && _b !== void 0 ? _b : "";
3612
+ var actualContentType = (_b = result.headers.get(_this8.contentType)) !== null && _b !== void 0 ? _b : "";
3513
3613
  // Support content types like "text/plain; charset=utf-8" and "text/plain"
3514
3614
  if (actualContentType.split(";")[0] !== requiredContentType) {
3515
- throw new Error("".concat(endpointName, " returned \"").concat(actualContentType, "\" for the ").concat(_this6.contentType, " response header, but the Bytescale SDK requires \"").concat(requiredContentType, "\"."));
3615
+ throw new Error("".concat(endpointName, " returned \"").concat(actualContentType, "\" for the ").concat(_this8.contentType, " response header, but the Bytescale SDK requires \"").concat(requiredContentType, "\"."));
3516
3616
  }
3517
3617
  return AuthManagerBrowser_await(result.text(), function (jwt) {
3518
3618
  if (jwt.length === 0) {
@@ -3529,6 +3629,11 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3529
3629
  return Promise.reject(e);
3530
3630
  }
3531
3631
  }
3632
+ }, {
3633
+ key: "isV2",
3634
+ value: function isV2(params) {
3635
+ return "getServiceWorkerConfig" in params;
3636
+ }
3532
3637
  }]);
3533
3638
  }();
3534
3639
  /**
@@ -3156,6 +3156,12 @@ function AuthManagerBrowser_awaitIgnored(value, direct) {
3156
3156
  return value && value.then ? value.then(AuthManagerBrowser_empty) : Promise.resolve();
3157
3157
  }
3158
3158
  }
3159
+ function AuthManagerBrowser_invokeIgnored(body) {
3160
+ var result = body();
3161
+ if (result && result.then) {
3162
+ return result.then(AuthManagerBrowser_empty);
3163
+ }
3164
+ }
3159
3165
  function AuthManagerBrowser_await(value, then, direct) {
3160
3166
  if (direct) {
3161
3167
  return then ? then(value) : value;
@@ -3177,12 +3183,6 @@ function AuthManagerBrowser_async(f) {
3177
3183
  }
3178
3184
  };
3179
3185
  }
3180
- function AuthManagerBrowser_invokeIgnored(body) {
3181
- var result = body();
3182
- if (result && result.then) {
3183
- return result.then(AuthManagerBrowser_empty);
3184
- }
3185
- }
3186
3186
  function AuthManagerBrowser_invoke(body, then) {
3187
3187
  var result = body();
3188
3188
  if (result && result.then) {
@@ -3221,8 +3221,14 @@ function AuthManagerBrowser_continueIgnored(value) {
3221
3221
  return value.then(AuthManagerBrowser_empty);
3222
3222
  }
3223
3223
  }
3224
+ function AuthManagerBrowser_continue(value, then) {
3225
+ return value && value.then ? value.then(then) : then(value);
3226
+ }
3224
3227
  function AuthManagerBrowser_typeof(o) { "@babel/helpers - typeof"; return AuthManagerBrowser_typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, AuthManagerBrowser_typeof(o); }
3225
3228
  function AuthManagerBrowser_defineProperty(e, r, t) { return (r = AuthManagerBrowser_toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
3229
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = AuthManagerBrowser_unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
3230
+ function AuthManagerBrowser_unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return AuthManagerBrowser_arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? AuthManagerBrowser_arrayLikeToArray(r, a) : void 0; } }
3231
+ function AuthManagerBrowser_arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
3226
3232
  function AuthManagerBrowser_classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
3227
3233
  function AuthManagerBrowser_defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, AuthManagerBrowser_toPropertyKey(o.key), o); } }
3228
3234
  function AuthManagerBrowser_createClass(e, r, t) { return r && AuthManagerBrowser_defineProperties(e.prototype, r), t && AuthManagerBrowser_defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
@@ -3256,8 +3262,8 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3256
3262
  }, {
3257
3263
  key: "isAuthSessionReady",
3258
3264
  value: function isAuthSessionReady() {
3259
- var _a;
3260
- return ((_a = AuthSessionState.getSession()) === null || _a === void 0 ? void 0 : _a.accessToken) !== undefined;
3265
+ var session = AuthSessionState.getSession();
3266
+ return (session === null || session === void 0 ? void 0 : session.isReady) === true || (session === null || session === void 0 ? void 0 : session.accessToken) !== undefined;
3261
3267
  }
3262
3268
  }, {
3263
3269
  key: "beginAuthSession",
@@ -3269,12 +3275,17 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3269
3275
  if (_this.isAuthSessionActive()) {
3270
3276
  throw new Error("Auth session already active. Please call 'await endAuthSession()' and then call 'await beginAuthSession(...)' to start a new auth session.");
3271
3277
  }
3278
+ var canUseServiceWorkers = _this.serviceWorkerUtils.canUseServiceWorkers();
3279
+ if (_this.isV2(params) && !canUseServiceWorkers) {
3280
+ throw new Error("This auth session requires service workers, but this browser does not support them.");
3281
+ }
3272
3282
  var newSession = {
3273
3283
  accessToken: undefined,
3274
3284
  accessTokenRefreshHandle: undefined,
3275
3285
  params: params,
3276
3286
  isActive: true,
3277
- authServiceWorker: params.serviceWorkerScript !== undefined && _this.serviceWorkerUtils.canUseServiceWorkers() ? {
3287
+ isReady: false,
3288
+ authServiceWorker: params.serviceWorkerScript !== undefined && canUseServiceWorkers ? {
3278
3289
  serviceWorkerScript: params.serviceWorkerScript,
3279
3290
  type: "Uninitialized"
3280
3291
  } : undefined
@@ -3283,7 +3294,13 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3283
3294
  return newSession;
3284
3295
  })), function (session) {
3285
3296
  // IMPORTANT: must be called outside the above, else re-entrant deadlock will occur.
3286
- return AuthManagerBrowser_awaitIgnored(_this.refreshAccessToken(session));
3297
+ return AuthManagerBrowser_invokeIgnored(function () {
3298
+ if (_this.isV2(session.params)) {
3299
+ return AuthManagerBrowser_awaitIgnored(_this.refreshServiceWorkerConfig(session, session.params));
3300
+ } else {
3301
+ return AuthManagerBrowser_awaitIgnored(_this.refreshAccessToken(session, session.params));
3302
+ }
3303
+ });
3287
3304
  });
3288
3305
  } catch (e) {
3289
3306
  return Promise.reject(e);
@@ -3304,14 +3321,15 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3304
3321
  if (session.accessTokenRefreshHandle !== undefined) {
3305
3322
  _this2.scheduler.unschedule(session.accessTokenRefreshHandle);
3306
3323
  }
3307
- return AuthManagerBrowser_await(_this2.deleteAccessToken(session.params), function () {
3324
+ return AuthManagerBrowser_invoke(function () {
3325
+ if (!_this2.isV2(session.params)) {
3326
+ return AuthManagerBrowser_awaitIgnored(_this2.deleteAccessToken(session.params));
3327
+ }
3328
+ }, function () {
3308
3329
  return AuthManagerBrowser_invokeIgnored(function () {
3309
3330
  if (session.authServiceWorker !== undefined) {
3310
3331
  // Prevent service worker from authorizing subsequent requests.
3311
- return AuthManagerBrowser_awaitIgnored(_this2.serviceWorkerUtils.sendMessage({
3312
- type: "SET_BYTESCALE_AUTH_CONFIG",
3313
- config: []
3314
- }, session.authServiceWorker, _this2.serviceWorkerScriptFieldName));
3332
+ return AuthManagerBrowser_awaitIgnored(_this2.sendServiceWorkerConfig(session, []));
3315
3333
  }
3316
3334
  });
3317
3335
  });
@@ -3322,7 +3340,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3322
3340
  }
3323
3341
  }, {
3324
3342
  key: "refreshAccessToken",
3325
- value: function refreshAccessToken(session) {
3343
+ value: function refreshAccessToken(session, params) {
3326
3344
  try {
3327
3345
  var _this3 = this;
3328
3346
  return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this3.authSessionMutex.safe(AuthManagerBrowser_async(function () {
@@ -3335,30 +3353,26 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3335
3353
  var expires = secondsFromNow(_this3.retryAuthAfterErrorSeconds);
3336
3354
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_finallyRethrows(function () {
3337
3355
  return AuthManagerBrowser_catch(function () {
3338
- var _getAccessToken = _this3.getAccessToken,
3339
- _session$params = session.params;
3340
- return AuthManagerBrowser_await(session.params.authHeaders(), function (_session$params$authH) {
3341
- return AuthManagerBrowser_await(_getAccessToken.call(_this3, _session$params, _session$params$authH), function (jwt) {
3356
+ var _getAccessToken = _this3.getAccessToken;
3357
+ return AuthManagerBrowser_await(params.authHeaders(), function (_params$authHeaders) {
3358
+ return AuthManagerBrowser_await(_getAccessToken.call(_this3, params, _params$authHeaders), function (jwt) {
3342
3359
  // We don't use cookie-based auth if the browser supports service worker-based auth, as using both will cause
3343
3360
  // confusion for us in the future (i.e. we may question "do we need to use both together? was there a reason?").
3344
3361
  // Also: if the user has omitted "allowedOrigins" from their JWT, then service worker-based auth is more secure
3345
3362
  // than cookie-based auth, which is another reason to prevent these cookies from being set unless required.
3346
3363
  var setCookie = session.authServiceWorker === undefined;
3347
- return AuthManagerBrowser_await(_this3.setAccessToken(session.params, jwt, setCookie), function (setTokenResult) {
3364
+ return AuthManagerBrowser_await(_this3.setAccessToken(params, jwt, setCookie), function (setTokenResult) {
3348
3365
  return AuthManagerBrowser_invoke(function () {
3349
3366
  if (session.authServiceWorker !== undefined) {
3350
- return AuthManagerBrowser_await(_this3.serviceWorkerUtils.sendMessage({
3351
- type: "SET_BYTESCALE_AUTH_CONFIG",
3352
- config: [{
3353
- headers: [{
3354
- key: "Authorization",
3355
- value: "Bearer ".concat(jwt)
3356
- }],
3357
- expires: secondsFromNow(setTokenResult.ttlSeconds),
3358
- sourceUrlPrefixes: session.params.sourceUrlPrefixes,
3359
- urlPrefix: "".concat(session.params.sourceUrlPrefixes === undefined ? "" : _this3.sourceScopedUrlPrefixMarker).concat(_this3.getCdnUrl(session.params), "/").concat(session.params.accountId, "/")
3360
- }]
3361
- }, session.authServiceWorker, _this3.serviceWorkerScriptFieldName), function () {
3367
+ return AuthManagerBrowser_await(_this3.sendServiceWorkerConfig(session, [{
3368
+ headers: [{
3369
+ key: "Authorization",
3370
+ value: "Bearer ".concat(jwt)
3371
+ }],
3372
+ expires: secondsFromNow(setTokenResult.ttlSeconds),
3373
+ sourceUrlPrefixes: params.sourceUrlPrefixes,
3374
+ urlPrefix: "".concat(_this3.getCdnUrl(params), "/").concat(params.accountId, "/")
3375
+ }]), function () {
3362
3376
  // Allow time for the service worker to receive and process the message. Since this is asynchronous and not
3363
3377
  // synchronized, we need to wait for a sufficient amount of time to ensure the service worker is ready to
3364
3378
  // authenticate requests, so that after 'beginAuthSession' completes, users can start making requests.
@@ -3376,6 +3390,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3376
3390
  expires = secondsFromNow(actualTtl);
3377
3391
  // Set this at the end, as it's also used to signal 'isAuthSessionReady', so must be set after configuring the Service Worker, etc.
3378
3392
  session.accessToken = setTokenResult.accessToken;
3393
+ session.isReady = true;
3379
3394
  });
3380
3395
  });
3381
3396
  });
@@ -3388,7 +3403,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3388
3403
  // 'setTimeout' can be paused (e.g., during hibernation), risking JWT expiration before it triggers. We use a
3389
3404
  // scheduler to check wall-clock time every second and execute the callback at the scheduled time (below).
3390
3405
  session.accessTokenRefreshHandle = _this3.scheduler.schedule(expires, function () {
3391
- _this3.refreshAccessToken(session).then(function () {},
3406
+ _this3.refreshAccessToken(session, params).then(function () {},
3392
3407
  // Should not occur, as this method shouldn't throw errors.
3393
3408
  function (e) {
3394
3409
  return ConsoleUtils.error("Unexpected error when refreshing JWT access token: ".concat(e));
@@ -3401,6 +3416,91 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3401
3416
  return Promise.reject(e);
3402
3417
  }
3403
3418
  }
3419
+ }, {
3420
+ key: "refreshServiceWorkerConfig",
3421
+ value: function refreshServiceWorkerConfig(session, params) {
3422
+ try {
3423
+ var _this4 = this;
3424
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this4.authSessionMutex.safe(AuthManagerBrowser_async(function () {
3425
+ var _exit = false;
3426
+ if (!session.isActive) {
3427
+ return;
3428
+ }
3429
+ var refreshAt;
3430
+ return AuthManagerBrowser_continue(AuthManagerBrowser_catch(function () {
3431
+ return AuthManagerBrowser_await(params.getServiceWorkerConfig(), function (config) {
3432
+ if (!Array.isArray(config)) {
3433
+ throw new Error("The 'getServiceWorkerConfig' callback must return an array.");
3434
+ }
3435
+ return AuthManagerBrowser_await(_this4.sendServiceWorkerConfig(session, config), function () {
3436
+ return AuthManagerBrowser_await(new Promise(function (resolve) {
3437
+ return setTimeout(resolve, 100);
3438
+ }), function () {
3439
+ session.isReady = true;
3440
+ refreshAt = _this4.getServiceWorkerConfigRefreshEpoch(config);
3441
+ });
3442
+ });
3443
+ });
3444
+ }, function (e) {
3445
+ ConsoleUtils.warn("Unable to refresh service worker auth config: ".concat(e));
3446
+ refreshAt = Date.now() + _this4.retryAuthAfterErrorSeconds * 1000;
3447
+ }), function (_result2) {
3448
+ if (_exit) return _result2;
3449
+ if (refreshAt !== undefined) {
3450
+ session.accessTokenRefreshHandle = _this4.scheduler.schedule(refreshAt, function () {
3451
+ _this4.refreshServiceWorkerConfig(session, params).then(function () {}, function (e) {
3452
+ return ConsoleUtils.error("Unexpected error when refreshing service worker auth config: ".concat(e));
3453
+ });
3454
+ });
3455
+ }
3456
+ });
3457
+ }))));
3458
+ } catch (e) {
3459
+ return Promise.reject(e);
3460
+ }
3461
+ }
3462
+ }, {
3463
+ key: "getServiceWorkerConfigRefreshEpoch",
3464
+ value: function getServiceWorkerConfigRefreshEpoch(config) {
3465
+ var earliestExpiry;
3466
+ var _iterator = _createForOfIteratorHelper(config),
3467
+ _step;
3468
+ try {
3469
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
3470
+ var entry = _step.value;
3471
+ if (entry.expires !== undefined && (earliestExpiry === undefined || entry.expires < earliestExpiry)) {
3472
+ earliestExpiry = entry.expires;
3473
+ }
3474
+ }
3475
+ } catch (err) {
3476
+ _iterator.e(err);
3477
+ } finally {
3478
+ _iterator.f();
3479
+ }
3480
+ return earliestExpiry === undefined ? undefined : earliestExpiry - this.refreshBeforeExpirySeconds * 1000;
3481
+ }
3482
+ }, {
3483
+ key: "sendServiceWorkerConfig",
3484
+ value: function sendServiceWorkerConfig(session, config) {
3485
+ try {
3486
+ var _this5 = this;
3487
+ if (session.authServiceWorker === undefined) {
3488
+ throw new Error("Service worker configuration cannot be applied because service workers are unavailable.");
3489
+ }
3490
+ return AuthManagerBrowser_await(_this5.serviceWorkerUtils.sendMessage({
3491
+ type: "SET_BYTESCALE_AUTH_CONFIG",
3492
+ config: config.map(function (entry) {
3493
+ return Object.assign(Object.assign({}, entry), {
3494
+ urlPrefix: "".concat(entry.sourceUrlPrefixes === undefined ? "" : _this5.sourceScopedUrlPrefixMarker).concat(entry.urlPrefix)
3495
+ });
3496
+ })
3497
+ }, session.authServiceWorker, _this5.serviceWorkerScriptFieldName), function (_this5$serviceWorkerU) {
3498
+ session.authServiceWorker = _this5$serviceWorkerU;
3499
+ });
3500
+ } catch (e) {
3501
+ return Promise.reject(e);
3502
+ }
3503
+ }
3404
3504
  }, {
3405
3505
  key: "getAccessTokenUrl",
3406
3506
  value: function getAccessTokenUrl(params, setCookie) {
@@ -3416,9 +3516,9 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3416
3516
  key: "deleteAccessToken",
3417
3517
  value: function deleteAccessToken(params) {
3418
3518
  try {
3419
- var _this4 = this;
3519
+ var _this6 = this;
3420
3520
  var _a;
3421
- return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this4.getAccessTokenUrl(params, true), {
3521
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this6.getAccessTokenUrl(params, true), {
3422
3522
  method: "DELETE",
3423
3523
  credentials: "include",
3424
3524
  headers: {}
@@ -3434,15 +3534,15 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3434
3534
  key: "setAccessToken",
3435
3535
  value: function setAccessToken(params, jwt, setCookie) {
3436
3536
  try {
3437
- var _this5 = this;
3537
+ var _this7 = this;
3438
3538
  var _a;
3439
3539
  var request = {
3440
3540
  accessToken: jwt
3441
3541
  };
3442
- return AuthManagerBrowser_await(BaseAPI.fetch(_this5.getAccessTokenUrl(params, setCookie), {
3542
+ return AuthManagerBrowser_await(BaseAPI.fetch(_this7.getAccessTokenUrl(params, setCookie), {
3443
3543
  method: "PUT",
3444
3544
  credentials: "include",
3445
- headers: AuthManagerBrowser_defineProperty({}, _this5.contentType, _this5.contentTypeJson),
3545
+ headers: AuthManagerBrowser_defineProperty({}, _this7.contentType, _this7.contentTypeJson),
3446
3546
  body: JSON.stringify(request)
3447
3547
  }, {
3448
3548
  isBytescaleApi: true,
@@ -3458,10 +3558,10 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3458
3558
  key: "getAccessToken",
3459
3559
  value: function getAccessToken(params, headers) {
3460
3560
  try {
3461
- var _this6 = this;
3561
+ var _this8 = this;
3462
3562
  var _a, _b;
3463
3563
  var endpointName = "Your auth API endpoint";
3464
- var requiredContentType = _this6.contentTypeText;
3564
+ var requiredContentType = _this8.contentTypeText;
3465
3565
  return AuthManagerBrowser_await(BaseAPI.fetch(params.authUrl, {
3466
3566
  method: "GET",
3467
3567
  headers: headers
@@ -3469,10 +3569,10 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3469
3569
  isBytescaleApi: false,
3470
3570
  fetchApi: (_a = params.options) === null || _a === void 0 ? void 0 : _a.fetchApi
3471
3571
  }), function (result) {
3472
- var actualContentType = (_b = result.headers.get(_this6.contentType)) !== null && _b !== void 0 ? _b : "";
3572
+ var actualContentType = (_b = result.headers.get(_this8.contentType)) !== null && _b !== void 0 ? _b : "";
3473
3573
  // Support content types like "text/plain; charset=utf-8" and "text/plain"
3474
3574
  if (actualContentType.split(";")[0] !== requiredContentType) {
3475
- throw new Error("".concat(endpointName, " returned \"").concat(actualContentType, "\" for the ").concat(_this6.contentType, " response header, but the Bytescale SDK requires \"").concat(requiredContentType, "\"."));
3575
+ throw new Error("".concat(endpointName, " returned \"").concat(actualContentType, "\" for the ").concat(_this8.contentType, " response header, but the Bytescale SDK requires \"").concat(requiredContentType, "\"."));
3476
3576
  }
3477
3577
  return AuthManagerBrowser_await(result.text(), function (jwt) {
3478
3578
  if (jwt.length === 0) {
@@ -3489,6 +3589,11 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3489
3589
  return Promise.reject(e);
3490
3590
  }
3491
3591
  }
3592
+ }, {
3593
+ key: "isV2",
3594
+ value: function isV2(params) {
3595
+ return "getServiceWorkerConfig" in params;
3596
+ }
3492
3597
  }]);
3493
3598
  }();
3494
3599
  /**
@@ -1,7 +1,11 @@
1
1
  import { AuthSwHeaderDto } from "./AuthSwHeaderDto";
2
2
  export interface AuthSwConfigEntryDto {
3
+ /** Epoch time in milliseconds. Omit the value to keep the entry until the config is replaced. */
3
4
  expires: number | undefined;
5
+ /** Headers to add to matching requests. */
4
6
  headers: AuthSwHeaderDto[];
7
+ /** Optional page or iframe URL prefixes. An empty array matches no clients. */
5
8
  sourceUrlPrefixes?: string[];
9
+ /** Request URL prefix. Use the actual URL; AuthManager applies any internal compatibility marker. */
6
10
  urlPrefix: string;
7
11
  }
@@ -1,5 +1,7 @@
1
1
  import { BytescaleApiClientConfig } from "../../public/shared";
2
- export interface BeginAuthSessionParams {
2
+ import { AuthSwConfigEntryDto } from "../dtos/AuthSwConfigEntryDto";
3
+ export type BeginAuthSessionParams = BeginAuthSessionParamsV1 | BeginAuthSessionParamsV2;
4
+ export interface BeginAuthSessionParamsV1 {
3
5
  /**
4
6
  * The account ID to authorize requests for.
5
7
  */
@@ -71,10 +73,29 @@ export interface BeginAuthSessionParams {
71
73
  */
72
74
  sourceUrlPrefixes?: string[];
73
75
  }
76
+ export interface BeginAuthSessionParamsV2 {
77
+ /**
78
+ * Returns the complete replacement service-worker authorization config. Called when the session begins and again
79
+ * 20 seconds before the earliest configured expiry. Entries without an expiry do not trigger a refresh.
80
+ *
81
+ * IMPORTANT: do not call 'AuthManager.beginAuthSession' or 'AuthManager.endAuthSession' inside this callback, as this will cause a deadlock.
82
+ */
83
+ getServiceWorkerConfig: () => Promise<AuthSwConfigEntryDto[]>;
84
+ /**
85
+ * The path to the service worker JavaScript file hosted at the root of your website.
86
+ *
87
+ * Unlike V1 sessions, V2 sessions require service-worker support and do not create an SDK API access token or use
88
+ * CDN cookies as a fallback.
89
+ */
90
+ serviceWorkerScript: string;
91
+ }
74
92
  export interface AuthManagerInterface {
75
93
  /**
76
94
  * Begins a JWT auth session with the Bytescale API and Bytescale CDN.
77
95
  *
96
+ * V1 sessions fetch and refresh a JWT using the supplied auth endpoint. V2 sessions instead refresh the complete
97
+ * service-worker configuration using `getServiceWorkerConfig`.
98
+ *
78
99
  * Specifically, calling this method will cause the SDK to periodically acquire a JWT from your JWT endpoint. The SDK will then automatically include this JWT in all subsequent Bytescale API requests (via the 'authorization-token' request header) and also in all Bytescale CDN download requests (via a session cookie, or an 'authorization' header if service workers are being used).
79
100
  *
80
101
  * You can only call this method if 'isAuthSessionActive() === false', else an error will be returned.
@@ -5,5 +5,6 @@ export interface AuthSession {
5
5
  accessTokenRefreshHandle: number | undefined;
6
6
  authServiceWorker: ServiceWorkerConfig | undefined;
7
7
  isActive: boolean;
8
+ isReady?: boolean;
8
9
  params: BeginAuthSessionParams;
9
10
  }
@@ -1,6 +1,9 @@
1
1
  import { AuthManagerInterface, BeginAuthSessionParams } from "../../private/model/AuthManagerInterface";
2
2
  import { AuthSwSetConfigDto } from "../../private/dtos/AuthSwSetConfigDto";
3
3
  import { ServiceWorkerUtils } from "../../private/ServiceWorkerUtils";
4
+ export type { AuthSwConfigEntryDto } from "../../private/dtos/AuthSwConfigEntryDto";
5
+ export type { AuthSwHeaderDto } from "../../private/dtos/AuthSwHeaderDto";
6
+ export type { BeginAuthSessionParams, BeginAuthSessionParamsV1, BeginAuthSessionParamsV2 } from "../../private/model/AuthManagerInterface";
4
7
  declare class AuthManagerImpl implements AuthManagerInterface {
5
8
  private readonly serviceWorkerUtils;
6
9
  private readonly authSessionMutex;
@@ -19,14 +22,17 @@ declare class AuthManagerImpl implements AuthManagerInterface {
19
22
  beginAuthSession(params: BeginAuthSessionParams): Promise<void>;
20
23
  endAuthSession(): Promise<void>;
21
24
  private refreshAccessToken;
25
+ private refreshServiceWorkerConfig;
26
+ private getServiceWorkerConfigRefreshEpoch;
27
+ private sendServiceWorkerConfig;
22
28
  private getAccessTokenUrl;
23
29
  private getCdnUrl;
24
30
  private deleteAccessToken;
25
31
  private setAccessToken;
26
32
  private getAccessToken;
33
+ private isV2;
27
34
  }
28
35
  /**
29
36
  * Alternative way of implementing a static class (i.e. all methods static). We do this so we can use a interface on the class (interfaces can't define static methods).
30
37
  */
31
38
  export declare const AuthManager: AuthManagerImpl;
32
- export {};
@@ -1,4 +1,7 @@
1
1
  import { AuthManagerInterface, BeginAuthSessionParams } from "../../private/model/AuthManagerInterface";
2
+ export type { AuthSwConfigEntryDto } from "../../private/dtos/AuthSwConfigEntryDto";
3
+ export type { AuthSwHeaderDto } from "../../private/dtos/AuthSwHeaderDto";
4
+ export type { BeginAuthSessionParams, BeginAuthSessionParamsV1, BeginAuthSessionParamsV2 } from "../../private/model/AuthManagerInterface";
2
5
  declare class AuthManagerImpl implements AuthManagerInterface {
3
6
  beginAuthSession(_params: BeginAuthSessionParams): Promise<void>;
4
7
  endAuthSession(): Promise<void>;
@@ -9,4 +12,3 @@ declare class AuthManagerImpl implements AuthManagerInterface {
9
12
  * Alternative way of implementing a static class (i.e. all methods static). We do this so we can use a interface on the class (interfaces can't define static methods).
10
13
  */
11
14
  export declare const AuthManager: AuthManagerImpl;
12
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytescale/sdk",
3
- "version": "3.55.0",
3
+ "version": "3.56.0",
4
4
  "description": "Bytescale JavaScript SDK",
5
5
  "author": "Bytescale <hello@bytescale.com> (https://www.bytescale.com)",
6
6
  "license": "MIT",
@@ -0,0 +1,234 @@
1
+ import { jest } from "@jest/globals";
2
+ import { Response as NodeFetchResponse } from "node-fetch";
3
+ import type { AuthSwConfigEntryDto, BeginAuthSessionParamsV1, BeginAuthSessionParamsV2 } from "../src/index.browser";
4
+
5
+ interface AuthManagerApi {
6
+ beginAuthSession: (params: BeginAuthSessionParamsV1 | BeginAuthSessionParamsV2) => Promise<void>;
7
+ endAuthSession: () => Promise<void>;
8
+ isAuthSessionActive: () => boolean;
9
+ isAuthSessionReady: () => boolean;
10
+ }
11
+
12
+ describe("AuthManager browser service-worker config", () => {
13
+ const originalNavigator = Object.getOwnPropertyDescriptor(globalThis, "navigator");
14
+ const originalFetch = Object.getOwnPropertyDescriptor(globalThis, "fetch");
15
+ const originalWindow = Object.getOwnPropertyDescriptor(globalThis, "window");
16
+ const globalFetch = jest.fn(async (): Promise<Response> => {
17
+ throw new Error("This test must not use the global fetch API.");
18
+ });
19
+ const postMessage = jest.fn((_message: unknown): void => {});
20
+ const activeWorker = { postMessage, state: "activated" } as unknown as ServiceWorker;
21
+ const registration = {
22
+ active: activeWorker,
23
+ installing: null,
24
+ scope: "https://app.example.com/",
25
+ waiting: null
26
+ } as unknown as ServiceWorkerRegistration;
27
+ const serviceWorkerApi = {
28
+ controller: activeWorker,
29
+ getRegistrations: jest.fn(async (): Promise<ServiceWorkerRegistration[]> => [registration]),
30
+ register: jest.fn(async (): Promise<ServiceWorkerRegistration> => registration)
31
+ };
32
+ const navigatorValue: { serviceWorker?: typeof serviceWorkerApi } = { serviceWorker: serviceWorkerApi };
33
+ let AuthManager: AuthManagerApi;
34
+
35
+ beforeAll(async () => {
36
+ Object.defineProperty(globalThis, "fetch", { configurable: true, value: globalFetch });
37
+ Object.defineProperty(globalThis, "window", { configurable: true, value: {} });
38
+ Object.defineProperty(globalThis, "navigator", { configurable: true, value: navigatorValue });
39
+ AuthManager = (await import("../src/public/browser/AuthManagerBrowser")).AuthManager;
40
+ });
41
+
42
+ beforeEach(() => {
43
+ navigatorValue.serviceWorker = serviceWorkerApi;
44
+ globalFetch.mockClear();
45
+ postMessage.mockClear();
46
+ serviceWorkerApi.getRegistrations.mockClear();
47
+ serviceWorkerApi.register.mockClear();
48
+ });
49
+
50
+ afterAll(() => {
51
+ for (const [key, descriptor] of [
52
+ ["fetch", originalFetch],
53
+ ["navigator", originalNavigator],
54
+ ["window", originalWindow]
55
+ ] as const) {
56
+ if (descriptor === undefined) {
57
+ Reflect.deleteProperty(globalThis, key);
58
+ } else {
59
+ Object.defineProperty(globalThis, key, descriptor);
60
+ }
61
+ }
62
+ });
63
+
64
+ afterEach(async () => {
65
+ navigatorValue.serviceWorker = serviceWorkerApi;
66
+ await AuthManager.endAuthSession();
67
+ jest.restoreAllMocks();
68
+ });
69
+
70
+ test("V2 applies multiple entries and refreshes before the earliest expiry", async () => {
71
+ const firstConfig: AuthSwConfigEntryDto[] = [
72
+ {
73
+ expires: Date.now() + 21_000,
74
+ headers: [{ key: "Authorization", value: "Bearer account-a" }],
75
+ sourceUrlPrefixes: ["https://app.example.com/account-a/"],
76
+ urlPrefix: "https://upcdn.io/account-a/"
77
+ },
78
+ {
79
+ expires: Date.now() + 60_000,
80
+ headers: [{ key: "Authorization", value: "Bearer account-b" }],
81
+ urlPrefix: "https://upcdn.io/account-b/"
82
+ }
83
+ ];
84
+ const refreshedConfig: AuthSwConfigEntryDto[] = [
85
+ {
86
+ expires: undefined,
87
+ headers: [{ key: "Authorization", value: "Bearer refreshed" }],
88
+ urlPrefix: "https://upcdn.io/account-a/"
89
+ }
90
+ ];
91
+ const getServiceWorkerConfig = jest
92
+ .fn<BeginAuthSessionParamsV2["getServiceWorkerConfig"]>()
93
+ .mockResolvedValueOnce(firstConfig)
94
+ .mockResolvedValueOnce(refreshedConfig);
95
+
96
+ await AuthManager.beginAuthSession({
97
+ getServiceWorkerConfig,
98
+ serviceWorkerScript: "/bytescale-auth-sw.js"
99
+ });
100
+
101
+ expect(AuthManager.isAuthSessionActive()).toBe(true);
102
+ expect(AuthManager.isAuthSessionReady()).toBe(true);
103
+ expect(postMessage.mock.calls[0][0]).toEqual({
104
+ config: [
105
+ {
106
+ ...firstConfig[0],
107
+ urlPrefix: "!bytescale-source-scoped!https://upcdn.io/account-a/"
108
+ },
109
+ firstConfig[1]
110
+ ],
111
+ type: "SET_BYTESCALE_AUTH_CONFIG"
112
+ });
113
+ expect(firstConfig[0].urlPrefix).toBe("https://upcdn.io/account-a/");
114
+
115
+ await new Promise(resolve => setTimeout(resolve, 1_500));
116
+
117
+ expect(getServiceWorkerConfig).toHaveBeenCalledTimes(2);
118
+ expect(postMessage.mock.calls[1][0]).toEqual({
119
+ config: refreshedConfig,
120
+ type: "SET_BYTESCALE_AUTH_CONFIG"
121
+ });
122
+ });
123
+
124
+ test("V2 clears service-worker config without calling access-token endpoints", async () => {
125
+ await AuthManager.beginAuthSession({
126
+ getServiceWorkerConfig: async () => [
127
+ {
128
+ expires: undefined,
129
+ headers: [{ key: "Authorization", value: "Bearer account-a" }],
130
+ urlPrefix: "https://upcdn.io/account-a/"
131
+ }
132
+ ],
133
+ serviceWorkerScript: "/bytescale-auth-sw.js"
134
+ });
135
+ await AuthManager.endAuthSession();
136
+
137
+ expect(globalFetch).not.toHaveBeenCalled();
138
+ expect(postMessage.mock.calls[1][0]).toEqual({ config: [], type: "SET_BYTESCALE_AUTH_CONFIG" });
139
+ expect(AuthManager.isAuthSessionActive()).toBe(false);
140
+ expect(AuthManager.isAuthSessionReady()).toBe(false);
141
+ });
142
+
143
+ test("V2 rejects instead of falling back to cookies when service workers are unavailable", async () => {
144
+ delete navigatorValue.serviceWorker;
145
+ const getServiceWorkerConfig = jest.fn(async (): Promise<AuthSwConfigEntryDto[]> => []);
146
+
147
+ await expect(
148
+ AuthManager.beginAuthSession({ getServiceWorkerConfig, serviceWorkerScript: "/bytescale-auth-sw.js" })
149
+ ).rejects.toThrow("requires service workers");
150
+
151
+ expect(getServiceWorkerConfig).not.toHaveBeenCalled();
152
+ expect(AuthManager.isAuthSessionActive()).toBe(false);
153
+ });
154
+
155
+ test("V1 retains cookie fallback when service workers are unavailable", async () => {
156
+ delete navigatorValue.serviceWorker;
157
+ const fetchApi = jest.fn(async (_input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
158
+ switch (init?.method) {
159
+ case "GET":
160
+ return new NodeFetchResponse("jwt-a", {
161
+ headers: { "Content-Type": "text/plain" }
162
+ }) as unknown as Response;
163
+ case "PUT":
164
+ return new NodeFetchResponse(
165
+ JSON.stringify({ accessToken: "access-a", ttlSeconds: 3600 })
166
+ ) as unknown as Response;
167
+ case "DELETE":
168
+ return new NodeFetchResponse(null, { status: 204 }) as unknown as Response;
169
+ default:
170
+ throw new Error(`Unexpected method: ${init?.method ?? "undefined"}`);
171
+ }
172
+ });
173
+
174
+ await AuthManager.beginAuthSession({
175
+ accountId: "account-a",
176
+ authHeaders: async (): Promise<Record<string, string>> => ({}),
177
+ authUrl: "https://app.example.com/auth",
178
+ options: { fetchApi },
179
+ serviceWorkerScript: "/bytescale-auth-sw.js"
180
+ });
181
+
182
+ expect((fetchApi.mock.calls[1][0] as string).endsWith("?set-cookie=true")).toBe(true);
183
+ expect(postMessage).not.toHaveBeenCalled();
184
+ expect(AuthManager.isAuthSessionReady()).toBe(true);
185
+ });
186
+
187
+ test("V1 retains the existing JWT, access-token, and single-entry service-worker flow", async () => {
188
+ const fetchApi = jest.fn(async (_input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
189
+ switch (init?.method) {
190
+ case "GET":
191
+ return new NodeFetchResponse("jwt-a", {
192
+ headers: { "Content-Type": "text/plain" }
193
+ }) as unknown as Response;
194
+ case "PUT":
195
+ return new NodeFetchResponse(
196
+ JSON.stringify({ accessToken: "access-a", ttlSeconds: 3600 })
197
+ ) as unknown as Response;
198
+ case "DELETE":
199
+ return new NodeFetchResponse(null, { status: 204 }) as unknown as Response;
200
+ default:
201
+ throw new Error(`Unexpected method: ${init?.method ?? "undefined"}`);
202
+ }
203
+ });
204
+ const params: BeginAuthSessionParamsV1 = {
205
+ accountId: "account-a",
206
+ authHeaders: async (): Promise<Record<string, string>> => ({ "X-App-Authorization": "app-token" }),
207
+ authUrl: "https://app.example.com/auth",
208
+ options: { fetchApi },
209
+ serviceWorkerScript: "/bytescale-auth-sw.js",
210
+ sourceUrlPrefixes: ["https://app.example.com/"]
211
+ };
212
+
213
+ await AuthManager.beginAuthSession(params);
214
+
215
+ expect(AuthManager.isAuthSessionReady()).toBe(true);
216
+ expect(postMessage.mock.calls[0][0]).toEqual({
217
+ config: [
218
+ {
219
+ expires: expect.any(Number),
220
+ headers: [{ key: "Authorization", value: "Bearer jwt-a" }],
221
+ sourceUrlPrefixes: params.sourceUrlPrefixes,
222
+ urlPrefix: "!bytescale-source-scoped!https://upcdn.io/account-a/"
223
+ }
224
+ ],
225
+ type: "SET_BYTESCALE_AUTH_CONFIG"
226
+ });
227
+ expect(fetchApi.mock.calls.map(([, init]) => init?.method)).toEqual(["GET", "PUT"]);
228
+
229
+ await AuthManager.endAuthSession();
230
+
231
+ expect(fetchApi.mock.calls.map(([, init]) => init?.method)).toEqual(["GET", "PUT", "DELETE"]);
232
+ expect(postMessage.mock.calls[1][0]).toEqual({ config: [], type: "SET_BYTESCALE_AUTH_CONFIG" });
233
+ });
234
+ });