@ghostly-solutions/auth 0.2.1 → 0.2.3

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/extension.js CHANGED
@@ -2,11 +2,19 @@
2
2
  var authApiPrefix = "/oauth";
3
3
  var authEndpoints = {
4
4
  authorize: `${authApiPrefix}/authorize`,
5
+ extensionToken: `${authApiPrefix}/extension/token`,
5
6
  session: `${authApiPrefix}/session`,
6
7
  refresh: `${authApiPrefix}/refresh`,
7
8
  logout: `${authApiPrefix}/logout`
8
9
  };
9
10
 
11
+ // src/constants/http-status.ts
12
+ var httpStatus = {
13
+ ok: 200,
14
+ noContent: 204,
15
+ unauthorized: 401
16
+ };
17
+
10
18
  // src/errors/auth-sdk-error.ts
11
19
  var AuthSdkError = class extends Error {
12
20
  code;
@@ -68,13 +76,6 @@ function resolveApiEndpoint(path, apiOrigin) {
68
76
  return `${normalizeApiOrigin(apiOrigin)}${path}`;
69
77
  }
70
78
 
71
- // src/constants/http-status.ts
72
- var httpStatus = {
73
- ok: 200,
74
- noContent: 204,
75
- unauthorized: 401
76
- };
77
-
78
79
  // src/constants/auth-keys.ts
79
80
  var authBroadcast = {
80
81
  channelName: "ghostly-auth-channel",
@@ -152,115 +153,6 @@ function createBroadcastSync(options) {
152
153
  };
153
154
  }
154
155
 
155
- // src/core/http-client.ts
156
- var jsonContentType = "application/json";
157
- var jsonHeaderName = "content-type";
158
- var includeCredentials = "include";
159
- var noStoreCache = "no-store";
160
- function toTypedValue(value) {
161
- return value;
162
- }
163
- function mapHttpStatusToAuthErrorCode(status) {
164
- if (status === httpStatus.unauthorized) {
165
- return authErrorCode.unauthorized;
166
- }
167
- return authErrorCode.apiError;
168
- }
169
- async function parseJsonPayload(response) {
170
- try {
171
- return await response.json();
172
- } catch {
173
- return null;
174
- }
175
- }
176
- async function parseErrorPayload(response) {
177
- const payload = await parseJsonPayload(response);
178
- if (!isObjectRecord(payload)) {
179
- return {
180
- code: null,
181
- details: null,
182
- message: null
183
- };
184
- }
185
- const maybeCode = payload.code;
186
- const maybeMessage = payload.message;
187
- return {
188
- code: isStringValue(maybeCode) ? maybeCode : null,
189
- details: "details" in payload ? payload.details : null,
190
- message: isStringValue(maybeMessage) ? maybeMessage : null
191
- };
192
- }
193
- function buildApiErrorMessage(method, path) {
194
- return `Auth API request failed: ${method} ${path}`;
195
- }
196
- function buildNetworkErrorMessage(method, path) {
197
- return `Auth API network failure: ${method} ${path}`;
198
- }
199
- async function request(options) {
200
- const expectedStatus = options.expectedStatus ?? httpStatus.ok;
201
- const headers = new Headers();
202
- const hasBody = typeof options.body !== "undefined";
203
- if (hasBody) {
204
- headers.set(jsonHeaderName, jsonContentType);
205
- }
206
- const requestInit = {
207
- cache: noStoreCache,
208
- credentials: includeCredentials,
209
- headers,
210
- method: options.method
211
- };
212
- if (hasBody) {
213
- requestInit.body = JSON.stringify(options.body);
214
- }
215
- let response;
216
- try {
217
- response = await fetch(options.path, requestInit);
218
- } catch (error) {
219
- throw new AuthSdkError({
220
- code: authErrorCode.networkError,
221
- details: error,
222
- message: buildNetworkErrorMessage(options.method, options.path),
223
- status: null
224
- });
225
- }
226
- if (response.status !== expectedStatus) {
227
- const parsed = await parseErrorPayload(response);
228
- throw new AuthSdkError({
229
- code: mapHttpStatusToAuthErrorCode(response.status),
230
- details: {
231
- apiCode: parsed.code,
232
- apiDetails: parsed.details
233
- },
234
- message: parsed.message ?? buildApiErrorMessage(options.method, options.path),
235
- status: response.status
236
- });
237
- }
238
- if (response.status === httpStatus.noContent) {
239
- return toTypedValue(null);
240
- }
241
- const payload = await parseJsonPayload(response);
242
- return toTypedValue(payload);
243
- }
244
- function getJson(path) {
245
- return request({
246
- method: "GET",
247
- path
248
- });
249
- }
250
- function postJsonWithoutBody(path) {
251
- return request({
252
- method: "POST",
253
- path
254
- });
255
- }
256
- function postEmpty(path) {
257
- return request({
258
- expectedStatus: httpStatus.noContent,
259
- method: "POST",
260
- path
261
- });
262
- }
263
-
264
156
  // src/core/runtime.ts
265
157
  var browserRuntimeErrorMessage = "Browser runtime is required for this auth operation.";
266
158
  function isBrowserRuntime() {
@@ -330,27 +222,11 @@ var SessionStore = class {
330
222
  }
331
223
  };
332
224
 
333
- // src/core/auth-client.ts
334
- function createInvalidSessionPayloadError(path) {
335
- return new AuthSdkError({
336
- code: authErrorCode.apiError,
337
- details: null,
338
- message: `Auth API response has invalid session shape: ${path}`,
339
- status: null
340
- });
341
- }
342
- function toValidatedSession(payload, path) {
343
- if (!isGhostlySession(payload)) {
344
- throw createInvalidSessionPayloadError(path);
345
- }
346
- return payload;
347
- }
348
- function toSessionPayload(payload, path) {
349
- if (payload === null) {
350
- return null;
351
- }
352
- return toValidatedSession(payload, path);
353
- }
225
+ // src/adapters/extension/auth-client.ts
226
+ var extensionSessionHeader = "X-Ghostly-Session-Id";
227
+ var includeCredentials = "include";
228
+ var noStoreCache = "no-store";
229
+ var tokenFreshnessLeewayMs = 6e4;
354
230
  function createNoopBroadcastSync() {
355
231
  return {
356
232
  close() {
@@ -371,25 +247,142 @@ function createSafeBroadcastSync(onSessionUpdated) {
371
247
  throw error;
372
248
  }
373
249
  }
250
+ function buildApiErrorMessage(method, path) {
251
+ return `Auth API request failed: ${method} ${path}`;
252
+ }
253
+ function buildNetworkErrorMessage(method, path) {
254
+ return `Auth API network failure: ${method} ${path}`;
255
+ }
256
+ function createInvalidSessionPayloadError(path) {
257
+ return new AuthSdkError({
258
+ code: authErrorCode.apiError,
259
+ details: null,
260
+ message: `Auth API response has invalid session shape: ${path}`,
261
+ status: null
262
+ });
263
+ }
374
264
  function toInitResult(session) {
375
265
  return {
376
266
  session,
377
267
  status: session ? "authenticated" : "unauthenticated"
378
268
  };
379
269
  }
380
- function createAuthClient(options = {}) {
381
- let initPromise = null;
382
- const defaultApplication = options.application?.trim() || "";
383
- const sessionStore = new SessionStore();
384
- const broadcastSync = createSafeBroadcastSync((session) => {
385
- sessionStore.setSession(session);
386
- });
270
+ function parseSessionPayload(payload, path) {
271
+ if (payload === null) {
272
+ return null;
273
+ }
274
+ if (!isGhostlySession(payload)) {
275
+ throw createInvalidSessionPayloadError(path);
276
+ }
277
+ return payload;
278
+ }
279
+ function isExtensionAccessToken(value) {
280
+ if (!isObjectRecord(value)) {
281
+ return false;
282
+ }
283
+ return isStringValue(value.accessToken) && isStringValue(value.application) && (value.expiresAt === null || isStringValue(value.expiresAt)) && (value.session === null || isGhostlySession(value.session)) && isStringValue(value.tokenType);
284
+ }
285
+ function isStoredTokenFresh(token) {
286
+ if (!token || !token.accessToken.trim()) {
287
+ return false;
288
+ }
289
+ if (!token.expiresAt) {
290
+ return true;
291
+ }
292
+ const expiresAt = Date.parse(token.expiresAt);
293
+ if (Number.isNaN(expiresAt)) {
294
+ return false;
295
+ }
296
+ return expiresAt - Date.now() > tokenFreshnessLeewayMs;
297
+ }
298
+ function buildAuthorizeUrl(apiOrigin, returnTo, application) {
299
+ const authorizeEndpoint = resolveApiEndpoint(authEndpoints.authorize, apiOrigin);
300
+ const authorizeUrl = new URL(authorizeEndpoint, window.location.origin);
301
+ authorizeUrl.searchParams.set("return_to", returnTo);
302
+ if (application?.trim()) {
303
+ authorizeUrl.searchParams.set("app", application.trim());
304
+ }
305
+ return authorizeUrl.toString();
306
+ }
307
+ function resolveExtensionReturnToPath(requestedReturnTo, defaultReturnToPath) {
308
+ if (requestedReturnTo?.trim()) {
309
+ return resolveReturnToPath(requestedReturnTo);
310
+ }
311
+ if (defaultReturnToPath?.trim()) {
312
+ return resolveReturnToPath(defaultReturnToPath);
313
+ }
314
+ return resolveReturnToPath(requestedReturnTo);
315
+ }
316
+ function parseErrorPayload(payload) {
317
+ if (!isObjectRecord(payload)) {
318
+ return {
319
+ details: null,
320
+ message: null
321
+ };
322
+ }
323
+ return {
324
+ details: "details" in payload ? payload.details : null,
325
+ message: isStringValue(payload.message) ? payload.message : isStringValue(payload.error) ? payload.error : null
326
+ };
327
+ }
328
+ function createRequest(options) {
387
329
  const resolveEndpoint = (path) => resolveApiEndpoint(path, options.apiOrigin);
388
- const loadSession = async () => {
389
- const payload = await getJson(resolveEndpoint(authEndpoints.session));
390
- return toSessionPayload(payload, authEndpoints.session);
330
+ return async function request(requestOptions) {
331
+ const expectedStatus = requestOptions.expectedStatus ?? httpStatus.ok;
332
+ const headers = new Headers();
333
+ const sessionId = (await options.resolveSessionId?.())?.trim() || "";
334
+ if (sessionId) {
335
+ headers.set(extensionSessionHeader, sessionId);
336
+ }
337
+ let response;
338
+ try {
339
+ response = await fetch(resolveEndpoint(requestOptions.path), {
340
+ cache: noStoreCache,
341
+ credentials: includeCredentials,
342
+ headers,
343
+ method: requestOptions.method
344
+ });
345
+ } catch (error) {
346
+ throw new AuthSdkError({
347
+ code: authErrorCode.networkError,
348
+ details: error,
349
+ message: buildNetworkErrorMessage(requestOptions.method, requestOptions.path),
350
+ status: null
351
+ });
352
+ }
353
+ if (response.status !== expectedStatus) {
354
+ let parsedPayload = null;
355
+ try {
356
+ parsedPayload = await response.json();
357
+ } catch {
358
+ parsedPayload = null;
359
+ }
360
+ const parsed = parseErrorPayload(parsedPayload);
361
+ throw new AuthSdkError({
362
+ code: response.status === httpStatus.unauthorized ? authErrorCode.unauthorized : authErrorCode.apiError,
363
+ details: parsed.details,
364
+ message: parsed.message ?? buildApiErrorMessage(requestOptions.method, requestOptions.path),
365
+ status: response.status
366
+ });
367
+ }
368
+ if (response.status === httpStatus.noContent) {
369
+ return null;
370
+ }
371
+ return await response.json();
372
+ };
373
+ }
374
+ function createLoadSession(request) {
375
+ return async () => {
376
+ const payload = await request({
377
+ method: "GET",
378
+ path: authEndpoints.session
379
+ });
380
+ return parseSessionPayload(payload, authEndpoints.session);
391
381
  };
392
- const init = async (initOptions) => {
382
+ }
383
+ function createInit(sessionStore, loadSession, publishSession) {
384
+ let initPromise = null;
385
+ return async (initOptions) => {
393
386
  const forceRefresh = initOptions?.forceRefresh ?? false;
394
387
  if (sessionStore.hasResolvedSession() && !forceRefresh) {
395
388
  return toInitResult(sessionStore.getSessionIfResolved());
@@ -400,7 +393,7 @@ function createAuthClient(options = {}) {
400
393
  initPromise = (async () => {
401
394
  const session = await loadSession();
402
395
  sessionStore.setSession(session);
403
- broadcastSync.publishSession(session);
396
+ publishSession(session);
404
397
  return toInitResult(session);
405
398
  })();
406
399
  try {
@@ -409,19 +402,91 @@ function createAuthClient(options = {}) {
409
402
  initPromise = null;
410
403
  }
411
404
  };
405
+ }
406
+ function createRefresh(request, sessionStore, publishSession) {
407
+ return async () => {
408
+ const payload = await request({
409
+ method: "POST",
410
+ path: authEndpoints.refresh
411
+ });
412
+ const session = parseSessionPayload(payload, authEndpoints.refresh);
413
+ sessionStore.setSession(session);
414
+ publishSession(session);
415
+ return session;
416
+ };
417
+ }
418
+ function createGetAccessToken(request, sessionStore, publishSession, persistAccessToken, restoreAccessToken) {
419
+ return async (requestOptions) => {
420
+ const forceRefresh = requestOptions?.forceRefresh ?? false;
421
+ const stored = await restoreAccessToken?.() ?? null;
422
+ if (!forceRefresh && isStoredTokenFresh(stored)) {
423
+ if (stored.session) {
424
+ sessionStore.setSession(stored.session);
425
+ }
426
+ return stored;
427
+ }
428
+ const payload = await request({
429
+ method: "GET",
430
+ path: authEndpoints.extensionToken
431
+ });
432
+ if (!isExtensionAccessToken(payload)) {
433
+ throw new AuthSdkError({
434
+ code: authErrorCode.apiError,
435
+ details: null,
436
+ message: `Auth API response has invalid extension token shape: ${authEndpoints.extensionToken}`,
437
+ status: null
438
+ });
439
+ }
440
+ const nextToken = {
441
+ accessToken: payload.accessToken,
442
+ application: payload.application,
443
+ expiresAt: payload.expiresAt,
444
+ session: payload.session,
445
+ tokenType: payload.tokenType
446
+ };
447
+ sessionStore.setSession(nextToken.session);
448
+ publishSession(nextToken.session);
449
+ await persistAccessToken?.(nextToken);
450
+ return nextToken;
451
+ };
452
+ }
453
+ function createLogout(request, sessionStore, publishSession, options) {
454
+ return async () => {
455
+ await request({
456
+ expectedStatus: httpStatus.noContent,
457
+ method: "POST",
458
+ path: authEndpoints.logout
459
+ });
460
+ await options.clearSessionId?.();
461
+ sessionStore.setSession(null);
462
+ publishSession(null);
463
+ await options.persistAccessToken?.(null);
464
+ };
465
+ }
466
+ function createCustomExtensionAuthClient(options) {
467
+ const sessionStore = new SessionStore();
468
+ const broadcastSync = createSafeBroadcastSync((session) => {
469
+ sessionStore.setSession(session);
470
+ });
471
+ const publishSession = broadcastSync.publishSession.bind(broadcastSync);
472
+ const request = createRequest(options);
473
+ const loadSession = createLoadSession(request);
474
+ const init = createInit(sessionStore, loadSession, publishSession);
475
+ const refresh = createRefresh(request, sessionStore, publishSession);
476
+ const getAccessToken = createGetAccessToken(
477
+ request,
478
+ sessionStore,
479
+ publishSession,
480
+ options.persistAccessToken,
481
+ options.restoreAccessToken
482
+ );
483
+ const logout = createLogout(request, sessionStore, publishSession, options);
412
484
  const getSession = async (requestOptions) => {
413
485
  const result = await init({
414
486
  forceRefresh: requestOptions?.forceRefresh ?? false
415
487
  });
416
488
  return result.session;
417
489
  };
418
- const refresh = async () => {
419
- const payload = await postJsonWithoutBody(resolveEndpoint(authEndpoints.refresh));
420
- const session = toSessionPayload(payload, authEndpoints.refresh);
421
- sessionStore.setSession(session);
422
- broadcastSync.publishSession(session);
423
- return session;
424
- };
425
490
  const requireSession = async () => {
426
491
  const session = await getSession();
427
492
  if (session) {
@@ -434,50 +499,31 @@ function createAuthClient(options = {}) {
434
499
  status: httpStatus.unauthorized
435
500
  });
436
501
  };
437
- const login = (loginOptions) => {
438
- const returnTo = resolveReturnToPath(loginOptions?.returnTo);
439
- const authorizeUrl = new URL(resolveEndpoint(authEndpoints.authorize), window.location.origin);
440
- authorizeUrl.searchParams.set("return_to", returnTo);
441
- const application = loginOptions?.application?.trim() || defaultApplication;
442
- if (application) {
443
- authorizeUrl.searchParams.set("app", application);
502
+ const loginWithTabFlow = async (loginOptions) => {
503
+ if (!options.openAuthorizePage) {
504
+ throw new Error("Extension auth client requires openAuthorizePage for tab login flow.");
444
505
  }
445
- window.location.assign(authorizeUrl.toString());
446
- };
447
- const logout = async () => {
448
- await postEmpty(resolveEndpoint(authEndpoints.logout));
449
- sessionStore.setSession(null);
450
- broadcastSync.publishSession(null);
451
- };
452
- const subscribe = sessionStore.subscribe.bind(sessionStore);
453
- return {
454
- init,
455
- getSession,
456
- login,
457
- logout,
458
- refresh,
459
- requireSession,
460
- subscribe
506
+ const returnTo = resolveExtensionReturnToPath(
507
+ loginOptions?.returnTo,
508
+ options.defaultReturnToPath
509
+ );
510
+ const authorizeUrl = buildAuthorizeUrl(
511
+ options.apiOrigin,
512
+ returnTo,
513
+ loginOptions?.application ?? options.application
514
+ );
515
+ await options.openAuthorizePage({
516
+ authorizeUrl
517
+ });
461
518
  };
462
- }
463
-
464
- // src/adapters/extension/auth-client.ts
465
- function buildAuthorizeUrl(apiOrigin, returnTo, application) {
466
- const authorizeEndpoint = resolveApiEndpoint(authEndpoints.authorize, apiOrigin);
467
- const authorizeUrl = new URL(authorizeEndpoint, window.location.origin);
468
- authorizeUrl.searchParams.set("return_to", returnTo);
469
- if (application?.trim()) {
470
- authorizeUrl.searchParams.set("app", application.trim());
471
- }
472
- return authorizeUrl.toString();
473
- }
474
- function createExtensionAuthClient(options) {
475
- const baseClient = createAuthClient({
476
- apiOrigin: options.apiOrigin,
477
- application: options.application
478
- });
479
519
  const loginWithWebAuthFlow = async (loginOptions) => {
480
- const returnTo = resolveReturnToPath(loginOptions?.returnTo);
520
+ if (!options.launchWebAuthFlow) {
521
+ throw new Error("Extension auth client requires launchWebAuthFlow for web auth flow.");
522
+ }
523
+ const returnTo = resolveExtensionReturnToPath(
524
+ loginOptions?.returnTo,
525
+ options.defaultReturnToPath
526
+ );
481
527
  const authorizeUrl = buildAuthorizeUrl(
482
528
  options.apiOrigin,
483
529
  returnTo,
@@ -486,17 +532,145 @@ function createExtensionAuthClient(options) {
486
532
  await options.launchWebAuthFlow({
487
533
  authorizeUrl
488
534
  });
489
- await baseClient.refresh();
535
+ await refresh();
490
536
  };
491
537
  return {
492
- ...baseClient,
538
+ init,
539
+ getSession,
493
540
  login(loginOptions) {
541
+ if (options.openAuthorizePage) {
542
+ void loginWithTabFlow(loginOptions);
543
+ return;
544
+ }
494
545
  void loginWithWebAuthFlow(loginOptions);
495
546
  },
547
+ logout,
548
+ refresh,
549
+ requireSession,
550
+ subscribe(listener) {
551
+ return sessionStore.subscribe(listener);
552
+ },
553
+ getAccessToken,
554
+ loginWithTabFlow,
496
555
  loginWithWebAuthFlow
497
556
  };
498
557
  }
499
558
 
500
- export { createExtensionAuthClient };
559
+ // src/adapters/extension/chrome-auth-client.ts
560
+ function hasChromeExtensionRuntime() {
561
+ const chrome = getChromeRuntime();
562
+ return Boolean(chrome.cookies && chrome.storage?.local && chrome.tabs);
563
+ }
564
+ var defaultSessionCookieName = "gs_auth_session";
565
+ var defaultAccessTokenStorageKey = "authToken";
566
+ var defaultTokenExpiresAtStorageKey = "authTokenExpiresAt";
567
+ var defaultSessionStorageKey = "authSession";
568
+ var defaultExtensionReturnToPath = "/oauth/complete";
569
+ function getChromeRuntime() {
570
+ return globalThis.chrome ?? {};
571
+ }
572
+ function createChromeExtensionAuthClient(options) {
573
+ const sessionCookieName = options.sessionCookieName?.trim() || defaultSessionCookieName;
574
+ const accessTokenStorageKey = options.accessTokenStorageKey?.trim() || defaultAccessTokenStorageKey;
575
+ const tokenExpiresAtStorageKey = options.tokenExpiresAtStorageKey?.trim() || defaultTokenExpiresAtStorageKey;
576
+ const sessionStorageKey = options.sessionStorageKey?.trim() || defaultSessionStorageKey;
577
+ const resolveSessionId = async () => {
578
+ const cookies = getChromeRuntime().cookies;
579
+ if (!cookies) {
580
+ return null;
581
+ }
582
+ const cookie = await cookies.get({
583
+ name: sessionCookieName,
584
+ url: `${options.apiOrigin}/`
585
+ });
586
+ return cookie?.value?.trim() || null;
587
+ };
588
+ const clearSessionId = async () => {
589
+ const cookies = getChromeRuntime().cookies;
590
+ if (!cookies) {
591
+ return;
592
+ }
593
+ await cookies.remove({
594
+ name: sessionCookieName,
595
+ url: `${options.apiOrigin}/`
596
+ });
597
+ };
598
+ const persistAccessToken = async (token) => {
599
+ const storage = getChromeRuntime().storage?.local;
600
+ if (!storage) {
601
+ return;
602
+ }
603
+ if (!token) {
604
+ await storage.remove([accessTokenStorageKey, tokenExpiresAtStorageKey, sessionStorageKey]);
605
+ return;
606
+ }
607
+ await storage.set({
608
+ [accessTokenStorageKey]: token.accessToken,
609
+ [tokenExpiresAtStorageKey]: token.expiresAt || null,
610
+ [sessionStorageKey]: token.session || null
611
+ });
612
+ };
613
+ const restoreAccessToken = async () => {
614
+ const storage = getChromeRuntime().storage?.local;
615
+ if (!storage) {
616
+ return null;
617
+ }
618
+ const stored = await storage.get([
619
+ accessTokenStorageKey,
620
+ tokenExpiresAtStorageKey,
621
+ sessionStorageKey
622
+ ]);
623
+ const rawAccessToken = stored[accessTokenStorageKey];
624
+ if (typeof rawAccessToken !== "string" || rawAccessToken.trim() === "") {
625
+ return null;
626
+ }
627
+ return {
628
+ accessToken: rawAccessToken,
629
+ application: options.application?.trim() || "",
630
+ expiresAt: typeof stored[tokenExpiresAtStorageKey] === "string" ? stored[tokenExpiresAtStorageKey] : null,
631
+ session: stored[sessionStorageKey] ?? null,
632
+ tokenType: "Bearer"
633
+ };
634
+ };
635
+ const openAuthorizePage = async ({ authorizeUrl }) => {
636
+ const tabs = getChromeRuntime().tabs;
637
+ if (!tabs) {
638
+ throw new Error("Chrome tabs API is unavailable.");
639
+ }
640
+ await tabs.create({ active: true, url: authorizeUrl });
641
+ };
642
+ return createCustomExtensionAuthClient({
643
+ ...options,
644
+ defaultReturnToPath: options.defaultReturnToPath?.trim() || defaultExtensionReturnToPath,
645
+ clearSessionId,
646
+ openAuthorizePage,
647
+ persistAccessToken,
648
+ resolveSessionId,
649
+ restoreAccessToken
650
+ });
651
+ }
652
+
653
+ // src/extension.ts
654
+ function hasCustomExtensionHooks(options) {
655
+ return Boolean(
656
+ options.clearSessionId || options.launchWebAuthFlow || options.openAuthorizePage || options.persistAccessToken || options.resolveSessionId || options.restoreAccessToken
657
+ );
658
+ }
659
+ function createExtensionAuthClient(options) {
660
+ if (hasChromeExtensionRuntime()) {
661
+ return createChromeExtensionAuthClient(options);
662
+ }
663
+ if (hasCustomExtensionHooks(options)) {
664
+ return createCustomExtensionAuthClient(options);
665
+ }
666
+ throw new AuthSdkError({
667
+ code: authErrorCode.apiError,
668
+ details: null,
669
+ message: "No supported extension runtime detected. Chromium browsers are supported automatically. Safari requires a dedicated adapter or explicit custom hooks.",
670
+ status: null
671
+ });
672
+ }
673
+
674
+ export { createChromeExtensionAuthClient, createCustomExtensionAuthClient, createExtensionAuthClient };
501
675
  //# sourceMappingURL=extension.js.map
502
676
  //# sourceMappingURL=extension.js.map