@mesyncapp/sdk 0.1.0 → 0.1.1

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.
@@ -124,8 +124,10 @@ async function buildAuthorizeUrl(scopes, manifest) {
124
124
  code_challenge: challenge,
125
125
  code_challenge_method: "S256",
126
126
  state,
127
- // The app's own origin, so the platform can link back to it once connected.
128
- app_url: window.location.origin
127
+ // Where the platform links back to once connected — origin *and* path, since
128
+ // some hosts (e.g. hosty) serve multiple apps as path prefixes under one
129
+ // shared origin rather than one app per origin.
130
+ app_url: window.location.origin + window.location.pathname
129
131
  });
130
132
  if (crossEntries.length) {
131
133
  query.set("scope", crossEntries.join(","));
@@ -383,4 +385,4 @@ export {
383
385
  MesyncManifestDivergedError,
384
386
  mesync
385
387
  };
386
- //# sourceMappingURL=chunk-YPMJ6DJU.js.map
388
+ //# sourceMappingURL=chunk-DTCWSLLH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["function base64UrlEncode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nexport function randomUrlSafeString(byteLength = 32): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\nexport async function pkceChallengeFromVerifier(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n","const ACCESS_TOKEN_KEY = \"mesync_access_token\";\nconst REFRESH_TOKEN_KEY = \"mesync_refresh_token\";\nconst PKCE_VERIFIER_KEY = \"mesync_pkce_verifier\";\nconst OAUTH_STATE_KEY = \"mesync_oauth_state\";\n\nexport const tokenStorage = {\n getAccessToken: (): string | null => localStorage.getItem(ACCESS_TOKEN_KEY),\n getRefreshToken: (): string | null => localStorage.getItem(REFRESH_TOKEN_KEY),\n setTokens(accessToken: string, refreshToken: string): void {\n localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);\n localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);\n },\n clear(): void {\n localStorage.removeItem(ACCESS_TOKEN_KEY);\n localStorage.removeItem(REFRESH_TOKEN_KEY);\n },\n};\n\n// Deliberately localStorage, not sessionStorage: in popup mode, the popup\n// navigates to a different origin (mesyncUrl) for consent and back, and an\n// auxiliary window only shares its opener's sessionStorage while it stays\n// same-origin — that round trip can lose it. localStorage is keyed purely by\n// origin with no such browsing-context restriction, so it survives reliably\n// in both the popup and plain-redirect flows. consume() deletes it right\n// after reading, so it's still short-lived in practice.\nexport const pkceStorage = {\n save(verifier: string, state: string): void {\n localStorage.setItem(PKCE_VERIFIER_KEY, verifier);\n localStorage.setItem(OAUTH_STATE_KEY, state);\n },\n consume(): { verifier: string; state: string } | null {\n const verifier = localStorage.getItem(PKCE_VERIFIER_KEY);\n const state = localStorage.getItem(OAUTH_STATE_KEY);\n localStorage.removeItem(PKCE_VERIFIER_KEY);\n localStorage.removeItem(OAUTH_STATE_KEY);\n if (!verifier || !state) return null;\n return { verifier, state };\n },\n};\n","import { pkceChallengeFromVerifier, randomUrlSafeString } from \"./pkce\";\nimport { pkceStorage, tokenStorage } from \"./storage\";\nimport type {\n CatalogTable,\n MesyncConfig,\n MesyncManifest,\n MesyncManifestResult,\n Row,\n RowList,\n ScopeRequest,\n} from \"./types\";\n\nclass MesyncAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MesyncAuthError\";\n }\n}\n\nclass MesyncUniqueIdConflictError extends Error {\n constructor(\n public readonly uniqueId: string,\n public readonly suggestedUniqueId: string | null,\n message: string\n ) {\n super(message);\n this.name = \"MesyncUniqueIdConflictError\";\n }\n}\n\nclass MesyncManifestDivergedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MesyncManifestDivergedError\";\n }\n}\n\nlet config: MesyncConfig | null = null;\n\nfunction requireConfig(): MesyncConfig {\n if (!config) {\n throw new Error(\"mesync.configure(...) must be called before using the SDK\");\n }\n return config;\n}\n\nfunction configure(newConfig: MesyncConfig): void {\n config = newConfig;\n}\n\nasync function resolveTableDefId(namespace: string, table: string): Promise<number> {\n const { mesyncUrl } = requireConfig();\n const resp = await fetch(`${mesyncUrl}/api/catalog/apps/${namespace}/tables`);\n if (resp.status === 404) {\n throw new Error(\n `App '${namespace}' isn't registered in the mesync catalog yet, so '${namespace}.${table}' can't be resolved. ` +\n `It needs to complete its own connect flow (with its manifest) at least once before other apps can request cross-app access to its tables.`\n );\n }\n if (!resp.ok) {\n throw new Error(\n `Could not look up table '${namespace}.${table}' in the mesync catalog (HTTP ${resp.status})`\n );\n }\n const tables: CatalogTable[] = await resp.json();\n const match = tables.find(t => t.name === table);\n if (!match) {\n throw new Error(`Table '${table}' not found under namespace '${namespace}'`);\n }\n return match.table_def_id;\n}\n\nfunction isOwnScope(s: ScopeRequest, uniqueId: string | undefined): boolean {\n return uniqueId !== undefined && s.namespace === uniqueId;\n}\n\n/**\n * Builds the mesync consent-screen URL. Scopes on the connecting app's own\n * `manifest.uniqueId` are sent as name-based `own_scope` entries — deferred,\n * server-side resolution, since that table may not exist yet (a brand-new\n * app) or be mid-migration. Every other scope (\"cross\", reading another\n * app's already-existing table) is still resolved here via the catalog, as\n * that table must already exist for a read to make sense.\n *\n * `manifest`, if given, rides along as an extra query param — mesync's own\n * hosted authorize page (not this SDK) decides whether it needs registering,\n * needs a schema update, or is already in sync, and renders the appropriate\n * screen. This SDK never makes that decision itself.\n */\nasync function buildAuthorizeUrl(scopes: ScopeRequest[], manifest?: MesyncManifest): Promise<string> {\n const { mesyncUrl, uniqueId, redirectUri } = requireConfig();\n if (scopes.length === 0) {\n throw new Error(\"login() requires at least one scope\");\n }\n\n const ownScopes = scopes.filter(s => isOwnScope(s, manifest?.uniqueId));\n const crossScopes = scopes.filter(s => !isOwnScope(s, manifest?.uniqueId));\n\n const crossEntries = await Promise.all(\n crossScopes.map(async s => `${await resolveTableDefId(s.namespace, s.table)}:${s.permission}`)\n );\n\n const verifier = randomUrlSafeString();\n const challenge = await pkceChallengeFromVerifier(verifier);\n const state = randomUrlSafeString(16);\n pkceStorage.save(verifier, state);\n\n const query = new URLSearchParams({\n // Wire-protocol param name per OAuth2 convention — its value is the app's uniqueId.\n client_id: uniqueId,\n redirect_uri: redirectUri,\n code_challenge: challenge,\n code_challenge_method: \"S256\",\n state,\n // Where the platform links back to once connected — origin *and* path, since\n // some hosts (e.g. hosty) serve multiple apps as path prefixes under one\n // shared origin rather than one app per origin.\n app_url: window.location.origin + window.location.pathname,\n });\n if (crossEntries.length) {\n query.set(\"scope\", crossEntries.join(\",\"));\n if (crossScopes.some(s => s.reason)) {\n query.set(\"reasons\", crossScopes.map(s => encodeURIComponent(s.reason ?? \"\")).join(\",\"));\n }\n }\n if (ownScopes.length) {\n query.set(\"own_scope\", ownScopes.map(s => `${s.table}:${s.permission}`).join(\",\"));\n if (ownScopes.some(s => s.reason)) {\n query.set(\"own_reasons\", ownScopes.map(s => encodeURIComponent(s.reason ?? \"\")).join(\",\"));\n }\n }\n if (manifest) {\n query.set(\n \"manifest\",\n JSON.stringify({\n unique_id: manifest.uniqueId,\n name: manifest.name,\n description: manifest.description,\n redirect_uris: manifest.redirectUris,\n tables: manifest.tables,\n })\n );\n }\n return `${mesyncUrl}/oauth/authorize?${query.toString()}`;\n}\n\ninterface LoginOptions {\n /** If given, mesync's own hosted authorize page decides whether\n * `manifest.uniqueId` needs registering, needs a schema update, or is\n * already in sync, and shows the right screen — nothing about that\n * decision happens here. */\n manifest?: MesyncManifest;\n}\n\n/**\n * Redirect the browser to the mesync consent screen requesting the given scopes.\n * Call this from your \"Connect with mesync\" button's onClick handler.\n */\nasync function login(scopes: ScopeRequest[], options: LoginOptions = {}): Promise<void> {\n window.location.href = await buildAuthorizeUrl(scopes, options.manifest);\n}\n\n/**\n * Same as login(), but drives the consent screen in a popup window instead of\n * navigating the current page away. Resolves once the popup completes (and\n * closes itself) via handleCallback()'s popup-completion handshake; rejects if\n * the popup is blocked, fails, or is closed before completing.\n *\n * Must be called directly from a user gesture (e.g. a click handler) with no\n * `await` ahead of it — the popup is opened synchronously first, then\n * navigated once the URL is built, so browsers don't treat it as blocked.\n */\nfunction loginPopup(scopes: ScopeRequest[], options: LoginOptions = {}): Promise<void> {\n const popup = window.open(\"\", \"mesync-connect\", \"width=480,height=640,menubar=no,toolbar=no,status=no\");\n if (!popup) {\n return Promise.reject(\n new MesyncAuthError(\"Popup blocked — allow popups for this site, or use mesync.login() instead.\")\n );\n }\n\n return new Promise((resolve, reject) => {\n let settled = false;\n\n function cleanup() {\n window.removeEventListener(\"message\", handleMessage);\n clearInterval(closePoll);\n }\n\n function handleMessage(e: MessageEvent) {\n if (e.source !== popup || e.origin !== window.location.origin) return;\n settled = true;\n cleanup();\n if (e.data?.type === \"mesync-connected\") {\n resolve();\n } else if (e.data?.type === \"mesync-error\") {\n reject(new MesyncAuthError(e.data.message ?? \"Could not connect to mesync.\"));\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n const closePoll = window.setInterval(() => {\n if (popup.closed && !settled) {\n settled = true;\n cleanup();\n reject(new MesyncAuthError(\"Connection window was closed before completing.\"));\n }\n }, 500);\n\n buildAuthorizeUrl(scopes, options.manifest)\n .then(url => {\n popup.location.href = url;\n })\n .catch(err => {\n if (settled) return;\n settled = true;\n cleanup();\n popup.close();\n reject(err instanceof Error ? err : new MesyncAuthError(\"Could not connect to mesync.\"));\n });\n });\n}\n\nfunction isPopup(): boolean {\n return !!window.opener && window.opener !== window;\n}\n\nfunction notifyOpenerAndClose(payload: { type: \"mesync-connected\" } | { type: \"mesync-error\"; message: string }) {\n window.opener.postMessage(payload, window.location.origin);\n window.close();\n}\n\nasync function exchangeCodeForTokens(): Promise<void> {\n const { mesyncUrl, uniqueId, redirectUri } = requireConfig();\n const params = new URLSearchParams(window.location.search);\n\n const errorParam = params.get(\"error\");\n if (errorParam) {\n throw new MesyncAuthError(`mesync authorization was denied: ${errorParam}`);\n }\n\n const code = params.get(\"code\");\n const returnedState = params.get(\"state\");\n if (!code || !returnedState) {\n throw new MesyncAuthError(\"Missing code/state in callback URL\");\n }\n\n const saved = pkceStorage.consume();\n if (!saved || saved.state !== returnedState) {\n throw new MesyncAuthError(\"OAuth state mismatch — possible CSRF, aborting\");\n }\n\n const resp = await fetch(`${mesyncUrl}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n code_verifier: saved.verifier,\n client_id: uniqueId,\n }),\n });\n if (!resp.ok) {\n throw new MesyncAuthError(`Token exchange failed: ${await resp.text()}`);\n }\n const tokens = await resp.json();\n tokenStorage.setTokens(tokens.access_token, tokens.refresh_token);\n}\n\n// Keyed by the `code` query param so a duplicate handleCallback() call for the\n// same callback (e.g. React 18 StrictMode's dev-only mount/unmount/remount of\n// <mesync-callback>) awaits the original exchange instead of re-consuming the\n// already-spent PKCE verifier/state and failing with a false CSRF error.\nlet inFlightCallback: { code: string; promise: Promise<void> } | null = null;\n\n/**\n * Call this on your redirect_uri page after the user approves (or denies) the\n * consent screen. Exchanges the authorization code for tokens and stores them.\n *\n * If this page is running inside a popup opened by loginPopup(), it instead\n * reports success/failure back to the opener via postMessage and closes\n * itself — the opener's loginPopup() promise settles from that message.\n */\nfunction handleCallback(): Promise<void> {\n const code = new URLSearchParams(window.location.search).get(\"code\");\n if (code && inFlightCallback?.code === code) {\n return inFlightCallback.promise;\n }\n\n const promise = (async () => {\n try {\n await exchangeCodeForTokens();\n } catch (err) {\n if (isPopup()) {\n const message = err instanceof Error ? err.message : \"Could not connect to mesync.\";\n notifyOpenerAndClose({ type: \"mesync-error\", message });\n return;\n }\n throw err;\n }\n if (isPopup()) {\n notifyOpenerAndClose({ type: \"mesync-connected\" });\n }\n })();\n\n if (code) inFlightCallback = { code, promise };\n return promise;\n}\n\n// Refresh tokens rotate on every use — a second concurrent call (e.g. two\n// authedFetch calls 401ing at once) would race to redeem the same refresh\n// token, and the loser would be treated as a stolen/reused token by the\n// server. Dedup concurrent calls into a single in-flight exchange so only\n// one redemption ever happens at a time, mirroring inFlightCallback above.\nlet inFlightRefresh: Promise<string> | null = null;\n\nasync function refreshAccessToken(): Promise<string> {\n if (inFlightRefresh) return inFlightRefresh;\n\n const { mesyncUrl } = requireConfig();\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new MesyncAuthError(\"Not logged in\");\n\n inFlightRefresh = (async () => {\n const resp = await fetch(`${mesyncUrl}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ grant_type: \"refresh_token\", refresh_token: refreshToken }),\n });\n if (!resp.ok) {\n tokenStorage.clear();\n throw new MesyncAuthError(\"Session expired — call mesync.login(...) again\");\n }\n const tokens = await resp.json();\n tokenStorage.setTokens(tokens.access_token, tokens.refresh_token);\n return tokens.access_token;\n })();\n\n try {\n return await inFlightRefresh;\n } finally {\n inFlightRefresh = null;\n }\n}\n\nasync function authedFetch(path: string, init: RequestInit = {}): Promise<Response> {\n const { mesyncUrl } = requireConfig();\n let accessToken = tokenStorage.getAccessToken();\n if (!accessToken) throw new MesyncAuthError(\"Not logged in — call mesync.login(...) first\");\n\n const doFetch = (token: string) =>\n fetch(`${mesyncUrl}${path}`, {\n ...init,\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${token}`, ...init.headers },\n });\n\n let resp = await doFetch(accessToken);\n if (resp.status === 401) {\n accessToken = await refreshAccessToken();\n resp = await doFetch(accessToken);\n }\n return resp;\n}\n\nasync function asJson<T>(resp: Response): Promise<T> {\n if (!resp.ok) throw new Error(`mesync request failed (${resp.status}): ${await resp.text()}`);\n if (resp.status === 204) return undefined as T;\n return resp.json() as Promise<T>;\n}\n\n/** A handle for reading/writing a single mesync-hosted table. */\nfunction table(namespace: string, name: string) {\n const path = `/api/store/${namespace}/${name}`;\n return {\n async list(page = 1, pageSize = 20): Promise<RowList> {\n return asJson<RowList>(await authedFetch(`${path}?page=${page}&page_size=${pageSize}`));\n },\n async get(rowId: number): Promise<Row> {\n return asJson<Row>(await authedFetch(`${path}/${rowId}`));\n },\n async insert(data: Record<string, unknown>): Promise<Row> {\n return asJson<Row>(await authedFetch(path, { method: \"POST\", body: JSON.stringify(data) }));\n },\n async update(rowId: number, data: Record<string, unknown>): Promise<Row> {\n return asJson<Row>(await authedFetch(`${path}/${rowId}`, { method: \"PATCH\", body: JSON.stringify(data) }));\n },\n async remove(rowId: number): Promise<void> {\n return asJson<void>(await authedFetch(`${path}/${rowId}`, { method: \"DELETE\" }));\n },\n };\n}\n\nfunction isLoggedIn(): boolean {\n return tokenStorage.getAccessToken() !== null;\n}\n\nfunction logout(): void {\n tokenStorage.clear();\n}\n\n/**\n * Direct, scriptable registration call — for CI/setup scripts, not the\n * interactive connect flow (that's handled entirely by mesync's own hosted\n * authorize page now; see `login`/`loginPopup`'s `manifest` option). Takes\n * a bearer token the caller already obtained however they like (e.g. their\n * own `POST /api/auth/login` call) — this function does no credential\n * management of its own.\n *\n * Safe to call any number of times: a new `uniqueId` creates the app; the\n * same owner resubmitting diffs tables and applies only safe schema changes\n * (new tables/columns, safe retypes); a `uniqueId` already owned by someone\n * else throws MesyncUniqueIdConflictError; a manifest that shares no tables\n * at all with what's already registered under your own `uniqueId` throws\n * MesyncManifestDivergedError unless `force: true` is passed.\n */\nasync function registerManifest(\n manifest: MesyncManifest,\n token: string,\n options: { force?: boolean } = {}\n): Promise<MesyncManifestResult> {\n const { mesyncUrl } = requireConfig();\n\n const resp = await fetch(`${mesyncUrl}/api/apps/manifest`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${token}` },\n body: JSON.stringify({\n unique_id: manifest.uniqueId,\n name: manifest.name,\n description: manifest.description,\n redirect_uris: manifest.redirectUris,\n tables: manifest.tables,\n force: options.force ?? false,\n }),\n });\n\n if (resp.status === 409) {\n const body = await resp.json();\n const detail = body.detail ?? {};\n if (detail.error === \"diverged\") {\n throw new MesyncManifestDivergedError(detail.message ?? \"This manifest doesn't match what's registered.\");\n }\n throw new MesyncUniqueIdConflictError(\n manifest.uniqueId,\n detail.suggested_unique_id ?? null,\n detail.message ?? `unique_id ${manifest.uniqueId} is already taken`\n );\n }\n if (!resp.ok) {\n throw new Error(`registerManifest failed: ${resp.status} ${await resp.text()}`);\n }\n\n const result = await resp.json();\n return { status: result.status, migrationsApplied: result.migrations_applied ?? {} };\n}\n\nexport const mesync = {\n configure,\n login,\n loginPopup,\n handleCallback,\n table,\n isLoggedIn,\n logout,\n registerManifest,\n};\n\nexport { MesyncAuthError, MesyncUniqueIdConflictError, MesyncManifestDivergedError };\nexport type { MesyncConfig, ScopeRequest, Row, RowList };\n"],"mappings":"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","names":["table"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  mesync
3
- } from "./chunk-YPMJ6DJU.js";
3
+ } from "./chunk-DTCWSLLH.js";
4
4
 
5
5
  // src/elements/connect-element.ts
6
6
  var STYLE = `
@@ -133,4 +133,4 @@ export {
133
133
  MesyncConnectElement,
134
134
  MesyncCallbackElement
135
135
  };
136
- //# sourceMappingURL=chunk-E3FGRAPR.js.map
136
+ //# sourceMappingURL=chunk-TERGXEEO.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  MesyncCallbackElement,
3
3
  MesyncConnectElement
4
- } from "../chunk-E3FGRAPR.js";
5
- import "../chunk-YPMJ6DJU.js";
4
+ } from "../chunk-TERGXEEO.js";
5
+ import "../chunk-DTCWSLLH.js";
6
6
  export {
7
7
  MesyncCallbackElement,
8
8
  MesyncConnectElement
@@ -1,7 +1,7 @@
1
- import "../chunk-E3FGRAPR.js";
1
+ import "../chunk-TERGXEEO.js";
2
2
  import {
3
3
  mesync
4
- } from "../chunk-YPMJ6DJU.js";
4
+ } from "../chunk-DTCWSLLH.js";
5
5
 
6
6
  // src/elements/react.tsx
7
7
  import { createElement, useCallback, useEffect, useRef, useState } from "react";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  MesyncManifestDivergedError,
4
4
  MesyncUniqueIdConflictError,
5
5
  mesync
6
- } from "./chunk-YPMJ6DJU.js";
6
+ } from "./chunk-DTCWSLLH.js";
7
7
 
8
8
  // src/defineManifest.ts
9
9
  function defineManifest(manifest) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mesyncapp/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Client library for storing your app's data in the user's personal mesync backend instead of localStorage.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/senluchen2015/mesync/tree/main/sdk#readme",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["function base64UrlEncode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nexport function randomUrlSafeString(byteLength = 32): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\nexport async function pkceChallengeFromVerifier(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n","const ACCESS_TOKEN_KEY = \"mesync_access_token\";\nconst REFRESH_TOKEN_KEY = \"mesync_refresh_token\";\nconst PKCE_VERIFIER_KEY = \"mesync_pkce_verifier\";\nconst OAUTH_STATE_KEY = \"mesync_oauth_state\";\n\nexport const tokenStorage = {\n getAccessToken: (): string | null => localStorage.getItem(ACCESS_TOKEN_KEY),\n getRefreshToken: (): string | null => localStorage.getItem(REFRESH_TOKEN_KEY),\n setTokens(accessToken: string, refreshToken: string): void {\n localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);\n localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);\n },\n clear(): void {\n localStorage.removeItem(ACCESS_TOKEN_KEY);\n localStorage.removeItem(REFRESH_TOKEN_KEY);\n },\n};\n\n// Deliberately localStorage, not sessionStorage: in popup mode, the popup\n// navigates to a different origin (mesyncUrl) for consent and back, and an\n// auxiliary window only shares its opener's sessionStorage while it stays\n// same-origin — that round trip can lose it. localStorage is keyed purely by\n// origin with no such browsing-context restriction, so it survives reliably\n// in both the popup and plain-redirect flows. consume() deletes it right\n// after reading, so it's still short-lived in practice.\nexport const pkceStorage = {\n save(verifier: string, state: string): void {\n localStorage.setItem(PKCE_VERIFIER_KEY, verifier);\n localStorage.setItem(OAUTH_STATE_KEY, state);\n },\n consume(): { verifier: string; state: string } | null {\n const verifier = localStorage.getItem(PKCE_VERIFIER_KEY);\n const state = localStorage.getItem(OAUTH_STATE_KEY);\n localStorage.removeItem(PKCE_VERIFIER_KEY);\n localStorage.removeItem(OAUTH_STATE_KEY);\n if (!verifier || !state) return null;\n return { verifier, state };\n },\n};\n","import { pkceChallengeFromVerifier, randomUrlSafeString } from \"./pkce\";\nimport { pkceStorage, tokenStorage } from \"./storage\";\nimport type {\n CatalogTable,\n MesyncConfig,\n MesyncManifest,\n MesyncManifestResult,\n Row,\n RowList,\n ScopeRequest,\n} from \"./types\";\n\nclass MesyncAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MesyncAuthError\";\n }\n}\n\nclass MesyncUniqueIdConflictError extends Error {\n constructor(\n public readonly uniqueId: string,\n public readonly suggestedUniqueId: string | null,\n message: string\n ) {\n super(message);\n this.name = \"MesyncUniqueIdConflictError\";\n }\n}\n\nclass MesyncManifestDivergedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MesyncManifestDivergedError\";\n }\n}\n\nlet config: MesyncConfig | null = null;\n\nfunction requireConfig(): MesyncConfig {\n if (!config) {\n throw new Error(\"mesync.configure(...) must be called before using the SDK\");\n }\n return config;\n}\n\nfunction configure(newConfig: MesyncConfig): void {\n config = newConfig;\n}\n\nasync function resolveTableDefId(namespace: string, table: string): Promise<number> {\n const { mesyncUrl } = requireConfig();\n const resp = await fetch(`${mesyncUrl}/api/catalog/apps/${namespace}/tables`);\n if (resp.status === 404) {\n throw new Error(\n `App '${namespace}' isn't registered in the mesync catalog yet, so '${namespace}.${table}' can't be resolved. ` +\n `It needs to complete its own connect flow (with its manifest) at least once before other apps can request cross-app access to its tables.`\n );\n }\n if (!resp.ok) {\n throw new Error(\n `Could not look up table '${namespace}.${table}' in the mesync catalog (HTTP ${resp.status})`\n );\n }\n const tables: CatalogTable[] = await resp.json();\n const match = tables.find(t => t.name === table);\n if (!match) {\n throw new Error(`Table '${table}' not found under namespace '${namespace}'`);\n }\n return match.table_def_id;\n}\n\nfunction isOwnScope(s: ScopeRequest, uniqueId: string | undefined): boolean {\n return uniqueId !== undefined && s.namespace === uniqueId;\n}\n\n/**\n * Builds the mesync consent-screen URL. Scopes on the connecting app's own\n * `manifest.uniqueId` are sent as name-based `own_scope` entries — deferred,\n * server-side resolution, since that table may not exist yet (a brand-new\n * app) or be mid-migration. Every other scope (\"cross\", reading another\n * app's already-existing table) is still resolved here via the catalog, as\n * that table must already exist for a read to make sense.\n *\n * `manifest`, if given, rides along as an extra query param — mesync's own\n * hosted authorize page (not this SDK) decides whether it needs registering,\n * needs a schema update, or is already in sync, and renders the appropriate\n * screen. This SDK never makes that decision itself.\n */\nasync function buildAuthorizeUrl(scopes: ScopeRequest[], manifest?: MesyncManifest): Promise<string> {\n const { mesyncUrl, uniqueId, redirectUri } = requireConfig();\n if (scopes.length === 0) {\n throw new Error(\"login() requires at least one scope\");\n }\n\n const ownScopes = scopes.filter(s => isOwnScope(s, manifest?.uniqueId));\n const crossScopes = scopes.filter(s => !isOwnScope(s, manifest?.uniqueId));\n\n const crossEntries = await Promise.all(\n crossScopes.map(async s => `${await resolveTableDefId(s.namespace, s.table)}:${s.permission}`)\n );\n\n const verifier = randomUrlSafeString();\n const challenge = await pkceChallengeFromVerifier(verifier);\n const state = randomUrlSafeString(16);\n pkceStorage.save(verifier, state);\n\n const query = new URLSearchParams({\n // Wire-protocol param name per OAuth2 convention — its value is the app's uniqueId.\n client_id: uniqueId,\n redirect_uri: redirectUri,\n code_challenge: challenge,\n code_challenge_method: \"S256\",\n state,\n // The app's own origin, so the platform can link back to it once connected.\n app_url: window.location.origin,\n });\n if (crossEntries.length) {\n query.set(\"scope\", crossEntries.join(\",\"));\n if (crossScopes.some(s => s.reason)) {\n query.set(\"reasons\", crossScopes.map(s => encodeURIComponent(s.reason ?? \"\")).join(\",\"));\n }\n }\n if (ownScopes.length) {\n query.set(\"own_scope\", ownScopes.map(s => `${s.table}:${s.permission}`).join(\",\"));\n if (ownScopes.some(s => s.reason)) {\n query.set(\"own_reasons\", ownScopes.map(s => encodeURIComponent(s.reason ?? \"\")).join(\",\"));\n }\n }\n if (manifest) {\n query.set(\n \"manifest\",\n JSON.stringify({\n unique_id: manifest.uniqueId,\n name: manifest.name,\n description: manifest.description,\n redirect_uris: manifest.redirectUris,\n tables: manifest.tables,\n })\n );\n }\n return `${mesyncUrl}/oauth/authorize?${query.toString()}`;\n}\n\ninterface LoginOptions {\n /** If given, mesync's own hosted authorize page decides whether\n * `manifest.uniqueId` needs registering, needs a schema update, or is\n * already in sync, and shows the right screen — nothing about that\n * decision happens here. */\n manifest?: MesyncManifest;\n}\n\n/**\n * Redirect the browser to the mesync consent screen requesting the given scopes.\n * Call this from your \"Connect with mesync\" button's onClick handler.\n */\nasync function login(scopes: ScopeRequest[], options: LoginOptions = {}): Promise<void> {\n window.location.href = await buildAuthorizeUrl(scopes, options.manifest);\n}\n\n/**\n * Same as login(), but drives the consent screen in a popup window instead of\n * navigating the current page away. Resolves once the popup completes (and\n * closes itself) via handleCallback()'s popup-completion handshake; rejects if\n * the popup is blocked, fails, or is closed before completing.\n *\n * Must be called directly from a user gesture (e.g. a click handler) with no\n * `await` ahead of it — the popup is opened synchronously first, then\n * navigated once the URL is built, so browsers don't treat it as blocked.\n */\nfunction loginPopup(scopes: ScopeRequest[], options: LoginOptions = {}): Promise<void> {\n const popup = window.open(\"\", \"mesync-connect\", \"width=480,height=640,menubar=no,toolbar=no,status=no\");\n if (!popup) {\n return Promise.reject(\n new MesyncAuthError(\"Popup blocked — allow popups for this site, or use mesync.login() instead.\")\n );\n }\n\n return new Promise((resolve, reject) => {\n let settled = false;\n\n function cleanup() {\n window.removeEventListener(\"message\", handleMessage);\n clearInterval(closePoll);\n }\n\n function handleMessage(e: MessageEvent) {\n if (e.source !== popup || e.origin !== window.location.origin) return;\n settled = true;\n cleanup();\n if (e.data?.type === \"mesync-connected\") {\n resolve();\n } else if (e.data?.type === \"mesync-error\") {\n reject(new MesyncAuthError(e.data.message ?? \"Could not connect to mesync.\"));\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n const closePoll = window.setInterval(() => {\n if (popup.closed && !settled) {\n settled = true;\n cleanup();\n reject(new MesyncAuthError(\"Connection window was closed before completing.\"));\n }\n }, 500);\n\n buildAuthorizeUrl(scopes, options.manifest)\n .then(url => {\n popup.location.href = url;\n })\n .catch(err => {\n if (settled) return;\n settled = true;\n cleanup();\n popup.close();\n reject(err instanceof Error ? err : new MesyncAuthError(\"Could not connect to mesync.\"));\n });\n });\n}\n\nfunction isPopup(): boolean {\n return !!window.opener && window.opener !== window;\n}\n\nfunction notifyOpenerAndClose(payload: { type: \"mesync-connected\" } | { type: \"mesync-error\"; message: string }) {\n window.opener.postMessage(payload, window.location.origin);\n window.close();\n}\n\nasync function exchangeCodeForTokens(): Promise<void> {\n const { mesyncUrl, uniqueId, redirectUri } = requireConfig();\n const params = new URLSearchParams(window.location.search);\n\n const errorParam = params.get(\"error\");\n if (errorParam) {\n throw new MesyncAuthError(`mesync authorization was denied: ${errorParam}`);\n }\n\n const code = params.get(\"code\");\n const returnedState = params.get(\"state\");\n if (!code || !returnedState) {\n throw new MesyncAuthError(\"Missing code/state in callback URL\");\n }\n\n const saved = pkceStorage.consume();\n if (!saved || saved.state !== returnedState) {\n throw new MesyncAuthError(\"OAuth state mismatch — possible CSRF, aborting\");\n }\n\n const resp = await fetch(`${mesyncUrl}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n code_verifier: saved.verifier,\n client_id: uniqueId,\n }),\n });\n if (!resp.ok) {\n throw new MesyncAuthError(`Token exchange failed: ${await resp.text()}`);\n }\n const tokens = await resp.json();\n tokenStorage.setTokens(tokens.access_token, tokens.refresh_token);\n}\n\n// Keyed by the `code` query param so a duplicate handleCallback() call for the\n// same callback (e.g. React 18 StrictMode's dev-only mount/unmount/remount of\n// <mesync-callback>) awaits the original exchange instead of re-consuming the\n// already-spent PKCE verifier/state and failing with a false CSRF error.\nlet inFlightCallback: { code: string; promise: Promise<void> } | null = null;\n\n/**\n * Call this on your redirect_uri page after the user approves (or denies) the\n * consent screen. Exchanges the authorization code for tokens and stores them.\n *\n * If this page is running inside a popup opened by loginPopup(), it instead\n * reports success/failure back to the opener via postMessage and closes\n * itself — the opener's loginPopup() promise settles from that message.\n */\nfunction handleCallback(): Promise<void> {\n const code = new URLSearchParams(window.location.search).get(\"code\");\n if (code && inFlightCallback?.code === code) {\n return inFlightCallback.promise;\n }\n\n const promise = (async () => {\n try {\n await exchangeCodeForTokens();\n } catch (err) {\n if (isPopup()) {\n const message = err instanceof Error ? err.message : \"Could not connect to mesync.\";\n notifyOpenerAndClose({ type: \"mesync-error\", message });\n return;\n }\n throw err;\n }\n if (isPopup()) {\n notifyOpenerAndClose({ type: \"mesync-connected\" });\n }\n })();\n\n if (code) inFlightCallback = { code, promise };\n return promise;\n}\n\n// Refresh tokens rotate on every use — a second concurrent call (e.g. two\n// authedFetch calls 401ing at once) would race to redeem the same refresh\n// token, and the loser would be treated as a stolen/reused token by the\n// server. Dedup concurrent calls into a single in-flight exchange so only\n// one redemption ever happens at a time, mirroring inFlightCallback above.\nlet inFlightRefresh: Promise<string> | null = null;\n\nasync function refreshAccessToken(): Promise<string> {\n if (inFlightRefresh) return inFlightRefresh;\n\n const { mesyncUrl } = requireConfig();\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new MesyncAuthError(\"Not logged in\");\n\n inFlightRefresh = (async () => {\n const resp = await fetch(`${mesyncUrl}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ grant_type: \"refresh_token\", refresh_token: refreshToken }),\n });\n if (!resp.ok) {\n tokenStorage.clear();\n throw new MesyncAuthError(\"Session expired — call mesync.login(...) again\");\n }\n const tokens = await resp.json();\n tokenStorage.setTokens(tokens.access_token, tokens.refresh_token);\n return tokens.access_token;\n })();\n\n try {\n return await inFlightRefresh;\n } finally {\n inFlightRefresh = null;\n }\n}\n\nasync function authedFetch(path: string, init: RequestInit = {}): Promise<Response> {\n const { mesyncUrl } = requireConfig();\n let accessToken = tokenStorage.getAccessToken();\n if (!accessToken) throw new MesyncAuthError(\"Not logged in — call mesync.login(...) first\");\n\n const doFetch = (token: string) =>\n fetch(`${mesyncUrl}${path}`, {\n ...init,\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${token}`, ...init.headers },\n });\n\n let resp = await doFetch(accessToken);\n if (resp.status === 401) {\n accessToken = await refreshAccessToken();\n resp = await doFetch(accessToken);\n }\n return resp;\n}\n\nasync function asJson<T>(resp: Response): Promise<T> {\n if (!resp.ok) throw new Error(`mesync request failed (${resp.status}): ${await resp.text()}`);\n if (resp.status === 204) return undefined as T;\n return resp.json() as Promise<T>;\n}\n\n/** A handle for reading/writing a single mesync-hosted table. */\nfunction table(namespace: string, name: string) {\n const path = `/api/store/${namespace}/${name}`;\n return {\n async list(page = 1, pageSize = 20): Promise<RowList> {\n return asJson<RowList>(await authedFetch(`${path}?page=${page}&page_size=${pageSize}`));\n },\n async get(rowId: number): Promise<Row> {\n return asJson<Row>(await authedFetch(`${path}/${rowId}`));\n },\n async insert(data: Record<string, unknown>): Promise<Row> {\n return asJson<Row>(await authedFetch(path, { method: \"POST\", body: JSON.stringify(data) }));\n },\n async update(rowId: number, data: Record<string, unknown>): Promise<Row> {\n return asJson<Row>(await authedFetch(`${path}/${rowId}`, { method: \"PATCH\", body: JSON.stringify(data) }));\n },\n async remove(rowId: number): Promise<void> {\n return asJson<void>(await authedFetch(`${path}/${rowId}`, { method: \"DELETE\" }));\n },\n };\n}\n\nfunction isLoggedIn(): boolean {\n return tokenStorage.getAccessToken() !== null;\n}\n\nfunction logout(): void {\n tokenStorage.clear();\n}\n\n/**\n * Direct, scriptable registration call — for CI/setup scripts, not the\n * interactive connect flow (that's handled entirely by mesync's own hosted\n * authorize page now; see `login`/`loginPopup`'s `manifest` option). Takes\n * a bearer token the caller already obtained however they like (e.g. their\n * own `POST /api/auth/login` call) — this function does no credential\n * management of its own.\n *\n * Safe to call any number of times: a new `uniqueId` creates the app; the\n * same owner resubmitting diffs tables and applies only safe schema changes\n * (new tables/columns, safe retypes); a `uniqueId` already owned by someone\n * else throws MesyncUniqueIdConflictError; a manifest that shares no tables\n * at all with what's already registered under your own `uniqueId` throws\n * MesyncManifestDivergedError unless `force: true` is passed.\n */\nasync function registerManifest(\n manifest: MesyncManifest,\n token: string,\n options: { force?: boolean } = {}\n): Promise<MesyncManifestResult> {\n const { mesyncUrl } = requireConfig();\n\n const resp = await fetch(`${mesyncUrl}/api/apps/manifest`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${token}` },\n body: JSON.stringify({\n unique_id: manifest.uniqueId,\n name: manifest.name,\n description: manifest.description,\n redirect_uris: manifest.redirectUris,\n tables: manifest.tables,\n force: options.force ?? false,\n }),\n });\n\n if (resp.status === 409) {\n const body = await resp.json();\n const detail = body.detail ?? {};\n if (detail.error === \"diverged\") {\n throw new MesyncManifestDivergedError(detail.message ?? \"This manifest doesn't match what's registered.\");\n }\n throw new MesyncUniqueIdConflictError(\n manifest.uniqueId,\n detail.suggested_unique_id ?? null,\n detail.message ?? `unique_id ${manifest.uniqueId} is already taken`\n );\n }\n if (!resp.ok) {\n throw new Error(`registerManifest failed: ${resp.status} ${await resp.text()}`);\n }\n\n const result = await resp.json();\n return { status: result.status, migrationsApplied: result.migrations_applied ?? {} };\n}\n\nexport const mesync = {\n configure,\n login,\n loginPopup,\n handleCallback,\n table,\n isLoggedIn,\n logout,\n registerManifest,\n};\n\nexport { MesyncAuthError, MesyncUniqueIdConflictError, MesyncManifestDivergedError };\nexport type { MesyncConfig, ScopeRequest, Row, RowList };\n"],"mappings":"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","names":["table"]}