{"version":3,"file":"sw.cjs","sources":["../src/sw/ensure-offscreen.ts","../src/sw/oauth-watcher.ts","../src/sw/forwarder.ts","../src/sw/index.ts"],"sourcesContent":["// Singleton guard for the offscreen document. Chrome 116+ has\n// chrome.runtime.getContexts (Promise-overload in MV3); we use it as\n// the primary path. Race: several parallel onConnects in the same tick\n// may call ensureOffscreen simultaneously — we remember the in-flight promise,\n// so each subsequent one waits on the shared create.\n\ninterface EnsureOffscreenOptions {\n  url: string;\n  reasons: chrome.offscreen.Reason[];\n  justification: string;\n}\n\nlet inflight: Promise<void> | null = null;\n\nexport async function ensureOffscreen(opts: EnsureOffscreenOptions): Promise<void> {\n  if (inflight) return inflight;\n  inflight = doEnsure(opts).finally(() => {\n    inflight = null;\n  });\n  return inflight;\n}\n\nasync function doEnsure(opts: EnsureOffscreenOptions): Promise<void> {\n  if (await offscreenExists(opts.url)) return;\n  try {\n    await chrome.offscreen.createDocument({\n      url: opts.url,\n      reasons: opts.reasons,\n      justification: opts.justification\n    });\n  } catch (e) {\n    // Race: between our check and create another onConnect managed to create it.\n    // Chrome throws 'Only a single offscreen document may be created' — that's\n    // OK, the document exists. Any other error — rethrow.\n    if (e instanceof Error && /single offscreen document/i.test(e.message)) return;\n    throw e;\n  }\n}\n\n/** Whether the offscreen document is currently up. Exported for the OAuth\n *  watcher, which must not *create* one — a fresh document holds no PKCE\n *  verifier, so there would be nothing to adopt. */\nexport async function offscreenExists(url: string): Promise<boolean> {\n  if (typeof chrome.runtime.getContexts === 'function') {\n    const contexts = await chrome.runtime.getContexts({\n      contextTypes: [chrome.runtime.ContextType.OFFSCREEN_DOCUMENT],\n      documentUrls: [url]\n    });\n    return contexts.length > 0;\n  }\n  return false;\n}\n","// OAuth rescue path in the service worker.\n//\n// The normal flow hands the auth code back through `postMessage` to\n// `window.opener` — which requires the surface that started sign-in to still be\n// alive. A toolbar action popup often isn't: Chrome destroys it the moment the\n// provider window takes focus, and whether that happens is decided by the OS\n// window manager, so the same extension works on one machine and fails on\n// another. When the popup dies its message listener dies with it, the code is\n// delivered into a closed window, and the user is left signed out with no error.\n//\n// The code, however, is also sitting in plain sight — in the URL of the tab that\n// the provider redirected to our callback page. A worker can see that navigation\n// (`chrome.tabs.onUpdated` delivers `changeInfo.url` for any origin the\n// extension already holds a host permission for, which the apiOrigin always is),\n// read the code, and hand it to offscreen, which owns the PKCE verifier and can\n// finish alone.\n//\n// Why this over `chrome.identity.launchWebAuthFlow`: no `identity` permission,\n// no `chromiumapp.org` redirect to register per extension ID, and it does not\n// depend on the worker staying alive for the whole sign-in — `tabs.onUpdated` is\n// an event that *wakes* the worker, so a Google login that takes two minutes is\n// fine.\n\nimport { RELAY_PORT_NAME } from '../shared/port-name';\nimport { portToChannel } from '../shared/chrome-port';\nimport { TransportClient } from '../shared/transport-client';\nimport { ensureOffscreen, offscreenExists } from './ensure-offscreen';\n\n/** Path of the OAuth callback page served from the paywall's custom domain.\n *  Contract shared with `online/app/paywall/v3/auth/callback`. */\nconst CALLBACK_PATH = '/paywall/v3/auth/callback';\n\n/** Where an acquirer sends the user back to when the payment finishes or is\n *  abandoned: `/paywall/<id>/checkout/success` and `.../error`. */\nconst CHECKOUT_RETURN_PATH = /^\\/paywall\\/[^/]+\\/checkout\\/(success|error)$/;\n\n/**\n * How long a finished-payment page stays up before we close it.\n *\n * The page shows \"Payment Done!\" and closes itself after ~3.5s — except it\n * cannot when the extension opened the tab: a script may only close a window\n * that a script opened (\"Scripts may close only the windows that were opened by\n * them\"). So we do it, on the same schedule, to keep the confirmation readable.\n */\nconst CHECKOUT_RETURN_CLOSE_MS = 3800;\n\n/**\n * How long to let a still-living surface claim the flow before we step in.\n *\n * When the popup survives, it receives the code by `postMessage` the instant the\n * callback page loads and exchanges it right away, which consumes the pending\n * flow — after that we find nothing to adopt and correctly stay out of the way.\n * Racing it instead would mean two checkouts for one sign-in. The delay costs\n * nothing in the case this whole path exists for: there the surface is already\n * gone and nobody is competing.\n */\nconst SURFACE_CLAIM_GRACE_MS = 700;\n\nexport interface OAuthWatcherOptions {\n  apiOrigin: string | (() => string | Promise<string>);\n  offscreenUrl: string | (() => string | Promise<string>);\n  offscreenReasons: chrome.offscreen.Reason[];\n  offscreenJustification: string;\n}\n\n/**\n * Returns the auth code if `url` is our OAuth callback for `apiOrigin`, else\n * null.\n *\n * Host matching accepts the edge mirror in both directions: `/oauth/init` builds\n * `redirect_to` with `resolveEdgeAwareOrigin`, so a client that failed over to\n * `edge.<domain>` gets its callback there while its configured apiOrigin is\n * still the canonical host (and vice versa).\n *\n * Only the query string is consulted. GoTrue returns PKCE codes there; it puts\n * *errors* in the fragment, which a worker cannot see at all — those keep\n * surfacing the old way (the window closes without a code and the flow reports\n * a cancellation).\n */\n/** Our origin, allowing the edge mirror in both directions: a client that failed\n *  over to `edge.<domain>` gets its callback there while the configured\n *  apiOrigin is still the canonical host, and vice versa. */\nfunction onOurOrigin(url: string, apiOrigin: string): URL | null {\n  let target: URL;\n  let origin: URL;\n  try {\n    target = new URL(url);\n    origin = new URL(apiOrigin);\n  } catch {\n    return null;\n  }\n  if (target.protocol !== 'https:' && target.protocol !== 'http:') return null;\n\n  const host = target.hostname;\n  const configured = origin.hostname;\n  const sameHost =\n    host === configured || host === `edge.${configured}` || `edge.${host}` === configured;\n  return sameHost ? target : null;\n}\n\nexport function matchOAuthCallback(url: string, apiOrigin: string): string | null {\n  const target = onOurOrigin(url, apiOrigin);\n  if (!target || target.pathname !== CALLBACK_PATH) return null;\n  return target.searchParams.get('code');\n}\n\n/** Whether this is one of our post-payment return pages. Matched by path only —\n *  the outcome lives in the URL fragment, which we neither need nor can rely on\n *  seeing here. */\nexport function isCheckoutReturn(url: string, apiOrigin: string): boolean {\n  const target = onOurOrigin(url, apiOrigin);\n  return !!target && CHECKOUT_RETURN_PATH.test(target.pathname);\n}\n\n/**\n * Opens the checkout as a normal tab in the window the user is actually working\n * in — not the provider popup window, which is small, transient, and by this\n * point usually closed.\n *\n * Picks the last focused `normal` window explicitly: at this moment the focused\n * window may well be the provider popup, and `tabs.create` without a target\n * would drop the payment page in there.\n */\nasync function openCheckoutTab(url: string): Promise<void> {\n  let windowId: number | undefined;\n  try {\n    const focused = await chrome.windows.getLastFocused();\n    if (focused?.type === 'normal' && focused.id != null) {\n      windowId = focused.id;\n    } else {\n      const normals = await chrome.windows.getAll({ windowTypes: ['normal'] });\n      windowId = normals[normals.length - 1]?.id ?? undefined;\n    }\n  } catch {\n    // No windows API answer — let Chrome place the tab itself.\n  }\n  try {\n    await chrome.tabs.create(\n      windowId != null ? { url, windowId, active: true } : { url, active: true }\n    );\n  } catch {\n    // Nowhere to put it (every window closed). The checkout exists and the\n    // pending marker is set, so the next surface the user opens resumes it.\n  }\n}\n\nexport function installOAuthWatcher(opts: OAuthWatcherOptions): void {\n  // `tabs` is not in our required permission set; the listener exists only when\n  // the host extension's manifest happens to grant the API surface.\n  if (typeof chrome === 'undefined' || !chrome.tabs?.onUpdated) return;\n\n  // onUpdated fires several times per navigation (loading → complete) and the\n  // URL is unchanged across them. A code is single-use at GoTrue, so adopting it\n  // twice would burn a perfectly good session.\n  const seenCodes = new Set<string>();\n\n  chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {\n    // Present only when the extension holds a host permission for the URL —\n    // which, for our own apiOrigin, it must.\n    if (!changeInfo.url) return;\n    void handleNavigation(tabId, changeInfo.url, seenCodes, opts);\n  });\n}\n\n/**\n * Closes a finished-payment tab the extension opened.\n *\n * Deliberately keyed on the URL rather than on remembering which tabs we\n * created: paying takes minutes, the worker idles out after 30 seconds, and any\n * in-memory list would be long gone by the time the user comes back. Matching\n * the return page instead survives a worker restart.\n *\n * When the checkout was opened the old way (a surviving popup called\n * window.open), the page closes itself and this is a no-op on an already-gone\n * tab.\n */\nasync function closeCheckoutReturnTab(\n  tabId: number,\n  opts: OAuthWatcherOptions\n): Promise<void> {\n  await new Promise((resolve) => setTimeout(resolve, CHECKOUT_RETURN_CLOSE_MS));\n  try {\n    // A paywall with success_redirect_url configured navigates onward instead of\n    // closing — by now this tab may be showing the merchant's own page, which is\n    // not ours to close.\n    const tab = await chrome.tabs.get(tabId);\n    const apiOrigin =\n      typeof opts.apiOrigin === 'function' ? await opts.apiOrigin() : opts.apiOrigin;\n    if (!tab.url || !apiOrigin || !isCheckoutReturn(tab.url, apiOrigin)) return;\n    await chrome.tabs.remove(tabId);\n  } catch {\n    // Tab already gone, or the worker lost the race — nothing to clean up.\n  }\n}\n\nasync function handleNavigation(\n  tabId: number,\n  url: string,\n  seenCodes: Set<string>,\n  opts: OAuthWatcherOptions\n): Promise<void> {\n  let apiOrigin: string;\n  try {\n    apiOrigin = typeof opts.apiOrigin === 'function' ? await opts.apiOrigin() : opts.apiOrigin;\n  } catch {\n    return;\n  }\n  if (!apiOrigin) return;\n\n  if (isCheckoutReturn(url, apiOrigin)) {\n    await closeCheckoutReturnTab(tabId, opts);\n    return;\n  }\n\n  const code = matchOAuthCallback(url, apiOrigin);\n  if (!code || seenCodes.has(code)) return;\n  seenCodes.add(code);\n  // Bounded: one entry per completed sign-in, and the worker is torn down long\n  // before this could matter. The cap is a belt-and-braces against a page that\n  // reloads the callback in a loop.\n  if (seenCodes.size > 32) seenCodes.clear();\n\n  // Do NOT spin offscreen up just to ask. A fresh document has no pending flow\n  // and no verifier, so it could only answer `no_pending_flow` — and creating it\n  // here would race the forwarder's own ensureOffscreen.\n  let alive = false;\n  try {\n    const offscreenUrl =\n      typeof opts.offscreenUrl === 'function' ? await opts.offscreenUrl() : opts.offscreenUrl;\n    alive = await offscreenExists(offscreenUrl);\n    if (!alive) return;\n    // Present but possibly mid-teardown; ensureOffscreen is a no-op when it's up.\n    await ensureOffscreen({\n      url: offscreenUrl,\n      reasons: opts.offscreenReasons,\n      justification: opts.offscreenJustification\n    });\n  } catch {\n    return;\n  }\n\n  await new Promise((resolve) => setTimeout(resolve, SURFACE_CLAIM_GRACE_MS));\n\n  const client = new TransportClient(() => {\n    const port = chrome.runtime.connect({ name: RELAY_PORT_NAME });\n    // Reading lastError marks it handled. Without this Chrome logs \"Unchecked\n    // runtime.lastError: Could not establish connection\" into the worker console\n    // whenever offscreen went away between our check above and this connect.\n    port.onDisconnect.addListener(() => void chrome.runtime.lastError);\n    return portToChannel(port);\n  });\n  try {\n    const result = await client.request('auth.oauthAdopt', { code });\n    if (!result.adopted) return;\n\n    if (result.checkoutUrl) {\n      // A purchase was waiting behind the sign-in. It does NOT go into this tab:\n      // the callback page closes itself moments after loading, so by now the tab\n      // is usually gone — and even alive it is the 480x640 provider window,\n      // which is no place to pay. Put it where the user actually works.\n      await openCheckoutTab(result.checkoutUrl);\n    }\n\n    // The callback page normally closes itself; this covers the case where it\n    // couldn't (no opener to post to, so it stayed on screen with a message).\n    try {\n      await chrome.tabs.remove(tabId);\n    } catch {\n      /* already gone */\n    }\n  } catch {\n    // Offscreen died between the check and the request, or the port broke. The\n    // user can retry sign-in; nothing here is worth surfacing.\n  } finally {\n    client.destroy();\n  }\n}\n","// Service worker forwarder. Stateless by design — its only job is to\n// route content↔offscreen ports. The SW can die at any moment\n// (after 30s idle); reconnect happens organically: the next content\n// runtime.connect wakes the SW, which recreates offscreen (if it died — though it\n// usually hasn't) and brings up a fresh pipe.\n\nimport type { RouterOptions } from './types';\nimport { ensureOffscreen } from './ensure-offscreen';\nimport { installOAuthWatcher } from './oauth-watcher';\nimport { PORT_NAME, RELAY_PORT_NAME } from '../shared/port-name';\n\nconst DEFAULT_REASONS: chrome.offscreen.Reason[] = [chrome.offscreen.Reason.LOCAL_STORAGE];\nconst DEFAULT_JUSTIFICATION =\n  'Persist auth session and bootstrap cache across all extension surfaces ' +\n  'via localStorage, which is unavailable in service workers.';\n\nexport function installForwarder(opts: RouterOptions): void {\n  const reasons = opts.offscreenReasons ?? DEFAULT_REASONS;\n  const justification = opts.offscreenJustification ?? DEFAULT_JUSTIFICATION;\n\n  if (opts.apiOrigin) {\n    installOAuthWatcher({\n      apiOrigin: opts.apiOrigin,\n      offscreenUrl: opts.offscreenUrl,\n      offscreenReasons: reasons,\n      offscreenJustification: justification\n    });\n  }\n\n  chrome.runtime.onConnect.addListener((contentPort) => {\n    if (contentPort.name !== PORT_NAME) return;\n\n    // Bring up offscreen and proxy. ensureOffscreen is async — content may\n    // send requests before it resolves: we buffer them in a queue until the offscreen\n    // port is created. Better than dropping them — content routes absolutely everything\n    // through us anyway.\n    void connectAndPipe(contentPort, opts.offscreenUrl, reasons, justification);\n  });\n}\n\nasync function connectAndPipe(\n  contentPort: chrome.runtime.Port,\n  offscreenUrlOrResolver: string | (() => string | Promise<string>),\n  reasons: chrome.offscreen.Reason[],\n  justification: string\n): Promise<void> {\n  const queue: unknown[] = [];\n  const queueListener = (msg: unknown): void => {\n    queue.push(msg);\n  };\n  contentPort.onMessage.addListener(queueListener);\n\n  let disconnected = false;\n  contentPort.onDisconnect.addListener(() => {\n    disconnected = true;\n  });\n\n  try {\n    const offscreenUrl =\n      typeof offscreenUrlOrResolver === 'function'\n        ? await offscreenUrlOrResolver()\n        : offscreenUrlOrResolver;\n    await ensureOffscreen({ url: offscreenUrl, reasons, justification });\n  } catch (e) {\n    console.error('[sdk-extension/sw] ensureOffscreen failed', e);\n    contentPort.disconnect();\n    return;\n  }\n\n  if (disconnected) return;\n\n  let offscreenPort: chrome.runtime.Port;\n  try {\n    // We use a separate relay-port name (not PORT_NAME), so offscreen\n    // accepts only SW-relay connections and ignores direct connects\n    // from popup/content (which also trigger onConnect in offscreen — in MV3\n    // chrome.runtime.connect is delivered to ALL extension contexts with an\n    // onConnect listener, not just the SW).\n    offscreenPort = chrome.runtime.connect({ name: RELAY_PORT_NAME });\n  } catch (e) {\n    console.error('[sdk-extension/sw] connect to offscreen failed', e);\n    contentPort.disconnect();\n    return;\n  }\n\n  // Remove the buffer-listener, install the direct forwarder.\n  contentPort.onMessage.removeListener(queueListener);\n  contentPort.onMessage.addListener((msg) => {\n    try {\n      offscreenPort.postMessage(msg);\n    } catch {\n      /* offscreen already dropped — the disconnect cascade will tear down content */\n    }\n  });\n  offscreenPort.onMessage.addListener((msg) => {\n    try {\n      contentPort.postMessage(msg);\n    } catch {\n      /* content already dropped */\n    }\n  });\n\n  contentPort.onDisconnect.addListener(() => {\n    try {\n      offscreenPort.disconnect();\n    } catch {\n      /* ignore */\n    }\n  });\n  offscreenPort.onDisconnect.addListener(() => {\n    try {\n      contentPort.disconnect();\n    } catch {\n      /* ignore */\n    }\n  });\n\n  // Flush the accumulated buffer.\n  for (const msg of queue) {\n    try {\n      offscreenPort.postMessage(msg);\n    } catch {\n      break;\n    }\n  }\n}\n","// Service worker entry. A thin forwarder between content-scripts and offscreen.\n// Holds no state — all truth lives in offscreen, the SW is just a route. The SW can\n// die at any moment; the next content runtime.connect wakes it and the\n// pipe is recreated.\n//\n// OAuth uses the same web flow as on websites — window.open to our domain, the\n// callback page posting the code back to its opener. chrome.identity is\n// deliberately not used (it needs a chrome-extension:// redirect URL registered\n// at the provider, which breaks parity with web).\n//\n// Pass `apiOrigin` and the SW additionally watches for that callback landing in\n// a tab, so a sign-in survives its originating surface being destroyed — see\n// ./oauth-watcher. Without it, behaviour is unchanged.\n//\n// Usage in the host:\n//   import { installRouter } from '@monetize.software/sdk-extension/sw';\n//   installRouter({\n//     offscreenUrl: chrome.runtime.getURL('offscreen.html'),\n//     apiOrigin: 'https://your-custom-domain.com'\n//   });\n\nimport { installForwarder } from './forwarder';\nimport type { RouterOptions } from './types';\n\nexport type { RouterOptions };\nexport { matchOAuthCallback } from './oauth-watcher';\n\nexport function installRouter(opts: RouterOptions): void {\n  if (typeof chrome === 'undefined' || !chrome.runtime) {\n    throw new Error('@monetize.software/sdk-extension/sw requires chrome.runtime');\n  }\n  installForwarder(opts);\n}\n"],"names":["inflight","ensureOffscreen","opts","doEnsure","offscreenExists","e","url","CALLBACK_PATH","CHECKOUT_RETURN_PATH","CHECKOUT_RETURN_CLOSE_MS","SURFACE_CLAIM_GRACE_MS","onOurOrigin","apiOrigin","target","origin","host","configured","matchOAuthCallback","isCheckoutReturn","openCheckoutTab","windowId","focused","normals","installOAuthWatcher","seenCodes","tabId","changeInfo","handleNavigation","closeCheckoutReturnTab","resolve","tab","code","alive","offscreenUrl","client","TransportClient","port","RELAY_PORT_NAME","portToChannel","result","DEFAULT_REASONS","DEFAULT_JUSTIFICATION","installForwarder","reasons","justification","contentPort","PORT_NAME","connectAndPipe","offscreenUrlOrResolver","queue","queueListener","msg","disconnected","offscreenPort","installRouter"],"mappings":"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"}