@bytescale/sdk 3.59.0 → 3.60.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.
@@ -3678,14 +3678,14 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3678
3678
  return AuthManagerBrowser_invoke(function () {
3679
3679
  if (session.authConfigs === undefined && typeof session.params.accountId === "string") {
3680
3680
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_catch(function () {
3681
- return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, session.params.accountId));
3681
+ return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, _this3.getCdnUrl(session.params), session.params.accountId));
3682
3682
  }, function (e) {
3683
3683
  cleanupError = e;
3684
3684
  }));
3685
3685
  } else {
3686
3686
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_forOf(cookieConfigs, function (state) {
3687
3687
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_catch(function () {
3688
- return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, state.config.accountId));
3688
+ return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, _this3.getConfigCdnUrl(session.params, state.config), state.config.accountId));
3689
3689
  }, function (e) {
3690
3690
  cleanupError !== null && cleanupError !== void 0 ? cleanupError : cleanupError = e;
3691
3691
  }));
@@ -3736,7 +3736,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3736
3736
  return AuthManagerBrowser_catch(function () {
3737
3737
  return AuthManagerBrowser_await(_this4.getAuthorizationToken(session.params, state.config), function (jwt) {
3738
3738
  var setCookie = _this4.shouldSetCookie(session, state.config);
3739
- return AuthManagerBrowser_await(_this4.setAccessToken(session.params.options, state.config.accountId, jwt, setCookie), function (token) {
3739
+ return AuthManagerBrowser_await(_this4.setAccessToken(session.params.options, _this4.getConfigCdnUrl(session.params, state.config), state.config.accountId, jwt, setCookie), function (token) {
3740
3740
  var expiresAt = Date.now() + token.ttlSeconds * 1000;
3741
3741
  state.accessToken = token.accessToken;
3742
3742
  state.expiresAt = expiresAt;
@@ -3801,7 +3801,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3801
3801
  value: "Bearer ".concat(state.jwt)
3802
3802
  }],
3803
3803
  sourceUrlPrefixes: state.config.sourceUrlPrefixes,
3804
- urlPrefix: "".concat(_this5.getCdnUrl(session.params), "/").concat(state.config.accountId, "/")
3804
+ urlPrefix: "".concat(_this5.getConfigCdnUrl(session.params, state.config), "/").concat(state.config.accountId, "/")
3805
3805
  }];
3806
3806
  });
3807
3807
  return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this5.sendServiceWorkerConfig(session, config, _this5.isV2Params(session.params) ? session.params.urlRewriteRules : undefined)));
@@ -3894,6 +3894,9 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3894
3894
  if (isV2 && !this.isValidAccountId(config.accountId)) {
3895
3895
  throw new Error("Invalid Bytescale account ID: '".concat(String(config.accountId), "'."));
3896
3896
  }
3897
+ if (config.cdnUrl !== undefined && typeof config.cdnUrl !== "string") {
3898
+ throw new Error("The 'cdnUrl' field must be a string when provided.");
3899
+ }
3897
3900
  if (config.enableCookieAuth !== undefined && typeof config.enableCookieAuth !== "boolean" || config.enableServiceWorkerAuth !== undefined && typeof config.enableServiceWorkerAuth !== "boolean") {
3898
3901
  throw new Error("Authentication enablement flags must be booleans when provided.");
3899
3902
  }
@@ -3914,7 +3917,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3914
3917
  cookieConfig = config;
3915
3918
  }
3916
3919
  if (this.isServiceWorkerEnabled(config)) {
3917
- var _prefix = "".concat(this.getCdnUrl(params), "/").concat(config.accountId, "/");
3920
+ var _prefix = "".concat(this.getConfigCdnUrl(params, config), "/").concat(config.accountId, "/");
3918
3921
  if (workerConfigsByPrefix.has(_prefix)) {
3919
3922
  throw new Error("Multiple service-worker auth configurations target the same URL prefix: '".concat(_prefix, "'."));
3920
3923
  }
@@ -3927,7 +3930,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3927
3930
  _iterator2.f();
3928
3931
  }
3929
3932
  if (cookieConfig !== undefined) {
3930
- var prefix = "".concat(this.getCdnUrl(params), "/").concat(cookieConfig.accountId, "/");
3933
+ var prefix = "".concat(this.getConfigCdnUrl(params, cookieConfig), "/").concat(cookieConfig.accountId, "/");
3931
3934
  var workerConfig = workerConfigsByPrefix.get(prefix);
3932
3935
  if (workerConfig !== undefined && workerConfig !== cookieConfig) {
3933
3936
  throw new Error("Cookie and service-worker authentication cannot target the same account from different configs.");
@@ -4006,8 +4009,8 @@ var AuthManagerImpl = /*#__PURE__*/function () {
4006
4009
  }
4007
4010
  }, {
4008
4011
  key: "getAccessTokenUrl",
4009
- value: function getAccessTokenUrl(options, accountId, setCookie) {
4010
- return "".concat(BytescaleApiClientConfigUtils.getCdnUrl(options !== null && options !== void 0 ? options : {}), "/api/v1/access_tokens/").concat(accountId, "?set-cookie=").concat(setCookie ? "true" : "false");
4012
+ value: function getAccessTokenUrl(cdnUrl, accountId, setCookie) {
4013
+ return "".concat(cdnUrl, "/api/v1/access_tokens/").concat(accountId, "?set-cookie=").concat(setCookie ? "true" : "false");
4011
4014
  }
4012
4015
  }, {
4013
4016
  key: "getCdnUrl",
@@ -4015,12 +4018,20 @@ var AuthManagerImpl = /*#__PURE__*/function () {
4015
4018
  var _a;
4016
4019
  return BytescaleApiClientConfigUtils.getCdnUrl((_a = params.options) !== null && _a !== void 0 ? _a : {});
4017
4020
  }
4021
+ }, {
4022
+ key: "getConfigCdnUrl",
4023
+ value: function getConfigCdnUrl(params, config) {
4024
+ var _a, _b;
4025
+ return BytescaleApiClientConfigUtils.getCdnUrl({
4026
+ cdnUrl: (_a = config.cdnUrl) !== null && _a !== void 0 ? _a : (_b = params.options) === null || _b === void 0 ? void 0 : _b.cdnUrl
4027
+ });
4028
+ }
4018
4029
  }, {
4019
4030
  key: "deleteAccessToken",
4020
- value: function deleteAccessToken(options, accountId) {
4031
+ value: function deleteAccessToken(options, cdnUrl, accountId) {
4021
4032
  try {
4022
4033
  var _this8 = this;
4023
- return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this8.getAccessTokenUrl(options, accountId, true), {
4034
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this8.getAccessTokenUrl(cdnUrl, accountId, true), {
4024
4035
  method: "DELETE",
4025
4036
  credentials: "include",
4026
4037
  headers: {}
@@ -4034,13 +4045,13 @@ var AuthManagerImpl = /*#__PURE__*/function () {
4034
4045
  }
4035
4046
  }, {
4036
4047
  key: "setAccessToken",
4037
- value: function setAccessToken(options, accountId, jwt, setCookie) {
4048
+ value: function setAccessToken(options, cdnUrl, accountId, jwt, setCookie) {
4038
4049
  try {
4039
4050
  var _this9 = this;
4040
4051
  var request = {
4041
4052
  accessToken: jwt
4042
4053
  };
4043
- return AuthManagerBrowser_await(BaseAPI.fetch(_this9.getAccessTokenUrl(options, accountId, setCookie), {
4054
+ return AuthManagerBrowser_await(BaseAPI.fetch(_this9.getAccessTokenUrl(cdnUrl, accountId, setCookie), {
4044
4055
  method: "PUT",
4045
4056
  credentials: "include",
4046
4057
  headers: AuthManagerBrowser_defineProperty({}, _this9.contentType, _this9.contentTypeJson),
@@ -3638,14 +3638,14 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3638
3638
  return AuthManagerBrowser_invoke(function () {
3639
3639
  if (session.authConfigs === undefined && typeof session.params.accountId === "string") {
3640
3640
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_catch(function () {
3641
- return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, session.params.accountId));
3641
+ return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, _this3.getCdnUrl(session.params), session.params.accountId));
3642
3642
  }, function (e) {
3643
3643
  cleanupError = e;
3644
3644
  }));
3645
3645
  } else {
3646
3646
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_forOf(cookieConfigs, function (state) {
3647
3647
  return AuthManagerBrowser_continueIgnored(AuthManagerBrowser_catch(function () {
3648
- return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, state.config.accountId));
3648
+ return AuthManagerBrowser_awaitIgnored(_this3.deleteAccessToken(session.params.options, _this3.getConfigCdnUrl(session.params, state.config), state.config.accountId));
3649
3649
  }, function (e) {
3650
3650
  cleanupError !== null && cleanupError !== void 0 ? cleanupError : cleanupError = e;
3651
3651
  }));
@@ -3696,7 +3696,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3696
3696
  return AuthManagerBrowser_catch(function () {
3697
3697
  return AuthManagerBrowser_await(_this4.getAuthorizationToken(session.params, state.config), function (jwt) {
3698
3698
  var setCookie = _this4.shouldSetCookie(session, state.config);
3699
- return AuthManagerBrowser_await(_this4.setAccessToken(session.params.options, state.config.accountId, jwt, setCookie), function (token) {
3699
+ return AuthManagerBrowser_await(_this4.setAccessToken(session.params.options, _this4.getConfigCdnUrl(session.params, state.config), state.config.accountId, jwt, setCookie), function (token) {
3700
3700
  var expiresAt = Date.now() + token.ttlSeconds * 1000;
3701
3701
  state.accessToken = token.accessToken;
3702
3702
  state.expiresAt = expiresAt;
@@ -3761,7 +3761,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3761
3761
  value: "Bearer ".concat(state.jwt)
3762
3762
  }],
3763
3763
  sourceUrlPrefixes: state.config.sourceUrlPrefixes,
3764
- urlPrefix: "".concat(_this5.getCdnUrl(session.params), "/").concat(state.config.accountId, "/")
3764
+ urlPrefix: "".concat(_this5.getConfigCdnUrl(session.params, state.config), "/").concat(state.config.accountId, "/")
3765
3765
  }];
3766
3766
  });
3767
3767
  return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(_this5.sendServiceWorkerConfig(session, config, _this5.isV2Params(session.params) ? session.params.urlRewriteRules : undefined)));
@@ -3854,6 +3854,9 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3854
3854
  if (isV2 && !this.isValidAccountId(config.accountId)) {
3855
3855
  throw new Error("Invalid Bytescale account ID: '".concat(String(config.accountId), "'."));
3856
3856
  }
3857
+ if (config.cdnUrl !== undefined && typeof config.cdnUrl !== "string") {
3858
+ throw new Error("The 'cdnUrl' field must be a string when provided.");
3859
+ }
3857
3860
  if (config.enableCookieAuth !== undefined && typeof config.enableCookieAuth !== "boolean" || config.enableServiceWorkerAuth !== undefined && typeof config.enableServiceWorkerAuth !== "boolean") {
3858
3861
  throw new Error("Authentication enablement flags must be booleans when provided.");
3859
3862
  }
@@ -3874,7 +3877,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3874
3877
  cookieConfig = config;
3875
3878
  }
3876
3879
  if (this.isServiceWorkerEnabled(config)) {
3877
- var _prefix = "".concat(this.getCdnUrl(params), "/").concat(config.accountId, "/");
3880
+ var _prefix = "".concat(this.getConfigCdnUrl(params, config), "/").concat(config.accountId, "/");
3878
3881
  if (workerConfigsByPrefix.has(_prefix)) {
3879
3882
  throw new Error("Multiple service-worker auth configurations target the same URL prefix: '".concat(_prefix, "'."));
3880
3883
  }
@@ -3887,7 +3890,7 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3887
3890
  _iterator2.f();
3888
3891
  }
3889
3892
  if (cookieConfig !== undefined) {
3890
- var prefix = "".concat(this.getCdnUrl(params), "/").concat(cookieConfig.accountId, "/");
3893
+ var prefix = "".concat(this.getConfigCdnUrl(params, cookieConfig), "/").concat(cookieConfig.accountId, "/");
3891
3894
  var workerConfig = workerConfigsByPrefix.get(prefix);
3892
3895
  if (workerConfig !== undefined && workerConfig !== cookieConfig) {
3893
3896
  throw new Error("Cookie and service-worker authentication cannot target the same account from different configs.");
@@ -3966,8 +3969,8 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3966
3969
  }
3967
3970
  }, {
3968
3971
  key: "getAccessTokenUrl",
3969
- value: function getAccessTokenUrl(options, accountId, setCookie) {
3970
- return "".concat(BytescaleApiClientConfigUtils.getCdnUrl(options !== null && options !== void 0 ? options : {}), "/api/v1/access_tokens/").concat(accountId, "?set-cookie=").concat(setCookie ? "true" : "false");
3972
+ value: function getAccessTokenUrl(cdnUrl, accountId, setCookie) {
3973
+ return "".concat(cdnUrl, "/api/v1/access_tokens/").concat(accountId, "?set-cookie=").concat(setCookie ? "true" : "false");
3971
3974
  }
3972
3975
  }, {
3973
3976
  key: "getCdnUrl",
@@ -3975,12 +3978,20 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3975
3978
  var _a;
3976
3979
  return BytescaleApiClientConfigUtils.getCdnUrl((_a = params.options) !== null && _a !== void 0 ? _a : {});
3977
3980
  }
3981
+ }, {
3982
+ key: "getConfigCdnUrl",
3983
+ value: function getConfigCdnUrl(params, config) {
3984
+ var _a, _b;
3985
+ return BytescaleApiClientConfigUtils.getCdnUrl({
3986
+ cdnUrl: (_a = config.cdnUrl) !== null && _a !== void 0 ? _a : (_b = params.options) === null || _b === void 0 ? void 0 : _b.cdnUrl
3987
+ });
3988
+ }
3978
3989
  }, {
3979
3990
  key: "deleteAccessToken",
3980
- value: function deleteAccessToken(options, accountId) {
3991
+ value: function deleteAccessToken(options, cdnUrl, accountId) {
3981
3992
  try {
3982
3993
  var _this8 = this;
3983
- return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this8.getAccessTokenUrl(options, accountId, true), {
3994
+ return AuthManagerBrowser_await(AuthManagerBrowser_awaitIgnored(BaseAPI.fetch(_this8.getAccessTokenUrl(cdnUrl, accountId, true), {
3984
3995
  method: "DELETE",
3985
3996
  credentials: "include",
3986
3997
  headers: {}
@@ -3994,13 +4005,13 @@ var AuthManagerImpl = /*#__PURE__*/function () {
3994
4005
  }
3995
4006
  }, {
3996
4007
  key: "setAccessToken",
3997
- value: function setAccessToken(options, accountId, jwt, setCookie) {
4008
+ value: function setAccessToken(options, cdnUrl, accountId, jwt, setCookie) {
3998
4009
  try {
3999
4010
  var _this9 = this;
4000
4011
  var request = {
4001
4012
  accessToken: jwt
4002
4013
  };
4003
- return AuthManagerBrowser_await(BaseAPI.fetch(_this9.getAccessTokenUrl(options, accountId, setCookie), {
4014
+ return AuthManagerBrowser_await(BaseAPI.fetch(_this9.getAccessTokenUrl(cdnUrl, accountId, setCookie), {
4004
4015
  method: "PUT",
4005
4016
  credentials: "include",
4006
4017
  headers: AuthManagerBrowser_defineProperty({}, _this9.contentType, _this9.contentTypeJson),
@@ -3,6 +3,8 @@ export interface AuthSessionConfigBase {
3
3
  accountId: string;
4
4
  /** A string names this configuration. `undefined` deliberately marks it as the session default. */
5
5
  authConfigId: string | undefined;
6
+ /** Overrides `options.cdnUrl` for this configuration. Excludes the trailing "/". */
7
+ cdnUrl?: string;
6
8
  /** Enables CDN cookie authentication. Defaults to false. */
7
9
  enableCookieAuth?: boolean;
8
10
  /** Enables CDN service-worker authentication. Defaults to true. */
@@ -37,6 +37,7 @@ declare class AuthManagerImpl implements AuthManagerInterface {
37
37
  private waitForServiceWorker;
38
38
  private getAccessTokenUrl;
39
39
  private getCdnUrl;
40
+ private getConfigCdnUrl;
40
41
  private deleteAccessToken;
41
42
  private setAccessToken;
42
43
  private getAuthorizationToken;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytescale/sdk",
3
- "version": "3.59.0",
3
+ "version": "3.60.0",
4
4
  "description": "Bytescale JavaScript SDK",
5
5
  "author": "Bytescale <hello@bytescale.com> (https://www.bytescale.com)",
6
6
  "license": "MIT",
@@ -203,6 +203,60 @@ describe("AuthManager browser multi-configuration sessions", () => {
203
203
  });
204
204
  });
205
205
 
206
+ test("uses each config's effective CDN URL for registration, worker prefixes, cleanup, and collisions", async () => {
207
+ const fetchApi = createFetchApi();
208
+ const defaultCdnUrl = "https://downloads-default.example.com";
209
+ const customCdnUrl = "https://downloads-custom.example.com";
210
+ const cookieCdnUrl = "https://downloads-cookie.example.com";
211
+
212
+ await AuthManager.beginAuthSession({
213
+ authConfigs: async () => [
214
+ {
215
+ ...apiOnlyConfig("custom-worker", accountA, async () => jwtA),
216
+ cdnUrl: customCdnUrl,
217
+ enableServiceWorkerAuth: true
218
+ },
219
+ {
220
+ ...apiOnlyConfig("default-worker", accountA, async () => jwtB),
221
+ enableServiceWorkerAuth: true
222
+ },
223
+ {
224
+ ...apiOnlyConfig("cookie", accountA, async () => jwtC),
225
+ cdnUrl: cookieCdnUrl,
226
+ enableCookieAuth: true
227
+ }
228
+ ],
229
+ options: { cdnUrl: defaultCdnUrl, fetchApi },
230
+ serviceWorkerScript: "/auth-sw.js"
231
+ });
232
+
233
+ expect(fetchApi.mock.calls.filter(([, init]) => init?.method === "PUT").map(([input]) => inputUrl(input))).toEqual([
234
+ `${customCdnUrl}/api/v1/access_tokens/${accountA}?set-cookie=false`,
235
+ `${defaultCdnUrl}/api/v1/access_tokens/${accountA}?set-cookie=false`,
236
+ `${cookieCdnUrl}/api/v1/access_tokens/${accountA}?set-cookie=true`
237
+ ]);
238
+ expect((postMessage.mock.calls.at(-1)?.[0] as any).config).toEqual([
239
+ {
240
+ expires: expect.any(Number),
241
+ headers: [{ key: "Authorization", value: `Bearer ${jwtA}` }],
242
+ sourceUrlPrefixes: undefined,
243
+ urlPrefix: `${customCdnUrl}/${accountA}/`
244
+ },
245
+ {
246
+ expires: expect.any(Number),
247
+ headers: [{ key: "Authorization", value: `Bearer ${jwtB}` }],
248
+ sourceUrlPrefixes: undefined,
249
+ urlPrefix: `${defaultCdnUrl}/${accountA}/`
250
+ }
251
+ ]);
252
+
253
+ await AuthManager.endAuthSession();
254
+
255
+ expect(
256
+ fetchApi.mock.calls.filter(([, init]) => init?.method === "DELETE").map(([input]) => inputUrl(input))
257
+ ).toEqual([`${cookieCdnUrl}/api/v1/access_tokens/${accountA}?set-cookie=true`]);
258
+ });
259
+
206
260
  test("supports a manual cookie-only V2 config without a service worker", async () => {
207
261
  delete navigatorValue.serviceWorker;
208
262
  const fetchApi = createFetchApi();
@@ -340,6 +394,14 @@ describe("AuthManager browser multi-configuration sessions", () => {
340
394
  params: (fetchApi: FetchApi) => v2Params(fetchApi, [apiOnlyConfig("a", "A12/abc", async () => jwtA)]),
341
395
  error: "Invalid Bytescale account ID"
342
396
  },
397
+ {
398
+ name: "invalid config CDN URL",
399
+ params: (fetchApi: FetchApi) =>
400
+ v2Params(fetchApi, [
401
+ { ...apiOnlyConfig("a", accountA, async () => jwtA), cdnUrl: 123 } as unknown as AuthSessionConfig
402
+ ]),
403
+ error: "cdnUrl"
404
+ },
343
405
  {
344
406
  name: "overlapping cookie and worker accounts",
345
407
  params: (fetchApi: FetchApi): BeginAuthSessionParamsV2 => ({
@@ -150,6 +150,15 @@ describe("Auth service-worker URL rewriting", () => {
150
150
  expect(untouched.responded).toBe(false);
151
151
  });
152
152
 
153
+ test("waits for delayed persistent authentication state on a cold start", async () => {
154
+ const harness = new AuthServiceWorkerHarness();
155
+ harness.setPersistentConfig([authConfig("account-a/", "token-a")], 300);
156
+
157
+ const result = await harness.dispatchFetch("https://upcdn.io/account-a/file.pdf");
158
+
159
+ expect(result.outboundRequest?.headers.get("Authorization")).toBe("Bearer token-a");
160
+ });
161
+
153
162
  test("keeps source-page authorization independent across multiple client IDs", async () => {
154
163
  const harness = new AuthServiceWorkerHarness();
155
164
  harness.setWindowClient("client-a", "https://app.example.com/a/");
@@ -221,6 +230,8 @@ type WorkerEventListener = (event: unknown) => void;
221
230
 
222
231
  class AuthServiceWorkerHarness {
223
232
  private readonly clientsById = new Map<string, { type: "window"; url: string }>();
233
+ private readonly cacheEntries = new Map<string, NodeFetchResponse>();
234
+ private cacheReadDelayMilliseconds = 0;
224
235
  private readonly context: {
225
236
  getRewrittenUrl: (url: string, rules: UrlRewriteRule[]) => string | undefined;
226
237
  setConfig: (config: AuthSwConfigEntryDto[], rules?: UrlRewriteRule[]) => Promise<void>;
@@ -231,7 +242,6 @@ class AuthServiceWorkerHarness {
231
242
 
232
243
  constructor(private readonly upstreamResponse: NodeFetchResponse = new NodeFetchResponse("ok")) {
233
244
  const listeners = new Map<string, WorkerEventListener>();
234
- const cacheEntries = new Map<string, NodeFetchResponse>();
235
245
  this.fetchMock = jest.fn(async (_request: TestRequest): Promise<NodeFetchResponse> => this.upstreamResponse);
236
246
 
237
247
  const self = {
@@ -246,9 +256,14 @@ class AuthServiceWorkerHarness {
246
256
  skipWaiting: async (): Promise<void> => {}
247
257
  };
248
258
  const cache = {
249
- match: async (key: string): Promise<NodeFetchResponse | undefined> => cacheEntries.get(key)?.clone(),
259
+ match: async (key: string): Promise<NodeFetchResponse | undefined> => {
260
+ if (this.cacheReadDelayMilliseconds > 0) {
261
+ await new Promise(resolve => setTimeout(resolve, this.cacheReadDelayMilliseconds));
262
+ }
263
+ return this.cacheEntries.get(key)?.clone();
264
+ },
250
265
  put: async (key: string, value: NodeFetchResponse): Promise<void> => {
251
- cacheEntries.set(key, value.clone());
266
+ this.cacheEntries.set(key, value.clone());
252
267
  }
253
268
  };
254
269
  const sandbox = {
@@ -284,6 +299,11 @@ class AuthServiceWorkerHarness {
284
299
  this.clientsById.set(clientId, { type: "window", url });
285
300
  }
286
301
 
302
+ setPersistentConfig(config: AuthSwConfigEntryDto[], cacheReadDelayMilliseconds: number): void {
303
+ this.cacheEntries.set("config", new NodeFetchResponse(JSON.stringify(config)));
304
+ this.cacheReadDelayMilliseconds = cacheReadDelayMilliseconds;
305
+ }
306
+
287
307
  async dispatchFetch(url: string, options: FetchOptions = {}): Promise<FetchResult> {
288
308
  const request = new TestRequest(url, { headers: options.headers as NodeFetchRequestInit["headers"] });
289
309
  if (options.navigation === true) {