@stringpush/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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/chunk-FROJCNV7.umd.cjs","../src/edit-token.ts","../src/edit-token-decode.ts"],"names":[],"mappings":"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","file":"/home/runner/work/translations/translations/packages/sdk/dist/chunk-FROJCNV7.umd.cjs","sourcesContent":[null,"/**\n * Resolves overlay edit JWT from init options, URL, or sessionStorage (M2-SDK-01).\n */\nimport type { InitOptions } from \"./types.js\";\n\nexport const EDIT_TOKEN_QUERY_PARAM = \"edit_token\";\nexport const EDIT_MODE_QUERY_PARAM = \"translation_edit\";\nexport const EDIT_LAUNCHER_KEY_QUERY_PARAM = \"translation_edit_key\";\nexport const EDIT_TOKEN_STORAGE_KEY = \"@translation/edit-token\";\n\nexport function isEditModeQueryEnabled(): boolean {\n if (typeof globalThis === \"undefined\" || !(\"location\" in globalThis)) {\n return false;\n }\n const params = new URLSearchParams(\n (globalThis as Window & typeof globalThis).location.search,\n );\n const value = params.get(EDIT_MODE_QUERY_PARAM);\n return value === \"1\" || value === \"true\";\n}\n\nexport function readEditTokenFromQuery(): string | null {\n if (typeof globalThis === \"undefined\" || !(\"location\" in globalThis)) {\n return null;\n }\n const params = new URLSearchParams(\n (globalThis as Window & typeof globalThis).location.search,\n );\n return params.get(EDIT_TOKEN_QUERY_PARAM);\n}\n\nexport function readEditTokenFromHash(): string | null {\n if (typeof globalThis === \"undefined\" || !(\"location\" in globalThis)) {\n return null;\n }\n const hash = (globalThis as Window & typeof globalThis).location.hash;\n if (!hash || hash.length <= 1) {\n return null;\n }\n const params = new URLSearchParams(hash.startsWith(\"#\") ? hash.slice(1) : hash);\n return params.get(EDIT_TOKEN_QUERY_PARAM);\n}\n\nexport function isEditLauncherArmed(launcher?: { armingKey?: string }): boolean {\n if (!isEditModeQueryEnabled()) {\n return false;\n }\n if (!launcher?.armingKey) {\n return true;\n }\n if (typeof globalThis === \"undefined\" || !(\"location\" in globalThis)) {\n return false;\n }\n const params = new URLSearchParams(\n (globalThis as Window & typeof globalThis).location.search,\n );\n return params.get(EDIT_LAUNCHER_KEY_QUERY_PARAM) === launcher.armingKey;\n}\n\nexport function readEditTokenFromStorage(): string | null {\n if (typeof globalThis === \"undefined\" || !(\"sessionStorage\" in globalThis)) {\n return null;\n }\n try {\n return (\n (globalThis as Window & typeof globalThis).sessionStorage.getItem(\n EDIT_TOKEN_STORAGE_KEY,\n ) ?? null\n );\n } catch {\n return null;\n }\n}\n\nexport function persistEditToken(token: string): void {\n if (typeof globalThis === \"undefined\" || !(\"sessionStorage\" in globalThis)) {\n return;\n }\n try {\n (globalThis as Window & typeof globalThis).sessionStorage.setItem(\n EDIT_TOKEN_STORAGE_KEY,\n token,\n );\n } catch {\n // Intent: private mode or blocked storage — overlay still works for this page load.\n }\n}\n\nexport function resolveEditToken(\n options: InitOptions,\n override?: string,\n): string | null {\n const token =\n override ??\n options.editToken ??\n readEditTokenFromQuery() ??\n readEditTokenFromHash() ??\n readEditTokenFromStorage();\n if (token) {\n persistEditToken(token);\n }\n return token;\n}\n\n/**\n * Removes edit JWT from the URL after persisting to sessionStorage (M2-SDK-05).\n */\nexport function stripEditTokenFromUrl(): void {\n if (typeof globalThis === \"undefined\" || !(\"location\" in globalThis)) {\n return;\n }\n const win = globalThis as Window & typeof globalThis;\n const url = new URL(win.location.href);\n url.searchParams.delete(EDIT_TOKEN_QUERY_PARAM);\n if (url.hash) {\n const hashParams = new URLSearchParams(url.hash.slice(1));\n if (hashParams.has(EDIT_TOKEN_QUERY_PARAM)) {\n hashParams.delete(EDIT_TOKEN_QUERY_PARAM);\n const nextHash = hashParams.toString();\n url.hash = nextHash ? `#${nextHash}` : \"\";\n }\n }\n const next = `${url.pathname}${url.search}${url.hash}`;\n win.history.replaceState(win.history.state, \"\", next);\n}\n\nexport function shouldAutoEnableEditMode(options: InitOptions): boolean {\n if (options.autoEnableEditFromQuery === false) {\n return false;\n }\n return isEditModeQueryEnabled() && resolveEditToken(options) !== null;\n}\n","/**\n * Reads edit JWT claims without verification (M2-SDK-04).\n *\n * Intent: token was already issued by our API; overlay uses claims for env scoping only.\n */\nexport type EditTokenClaims = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n environmentName: string;\n};\n\nexport function decodeEditTokenClaims(token: string): EditTokenClaims | null {\n const parts = token.split(\".\");\n if (parts.length < 2) {\n return null;\n }\n\n try {\n const payloadJson = base64UrlDecode(parts[1]!);\n const payload = JSON.parse(payloadJson) as Record<string, unknown>;\n const project = payload.project;\n const app = payload.app;\n const env = payload.env;\n const envName = payload.envName;\n if (\n typeof project !== \"string\" ||\n typeof app !== \"string\" ||\n typeof env !== \"string\" ||\n typeof envName !== \"string\"\n ) {\n return null;\n }\n return {\n projectId: project,\n applicationId: app,\n environmentId: env,\n environmentName: envName,\n };\n } catch {\n return null;\n }\n}\n\nfunction base64UrlDecode(segment: string): string {\n const padded = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = padded.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (padded.length % 4));\n if (typeof atob === \"function\") {\n return atob(padded + pad);\n }\n return Buffer.from(padded + pad, \"base64\").toString(\"utf8\");\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/mitopalov/work/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.umd.cjs","../src/edit-launcher/constants.ts","../src/edit-launcher/index.ts"],"names":[],"mappings":"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","file":"/home/mitopalov/work/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded edit launcher chunk (M2-SDK-05).\n *\n * Intent: stable ids/markers keep overlay UI out of the default SDK entry until edit mode activates.\n */\nexport const LAUNCHER_ROOT_ID = \"translation-edit-launcher-root\";\n\n/** Marker string used in bundle-split tests to keep launcher out of the main entry. */\nexport const LAUNCHER_CHUNK_MARKER = \"translation-edit-launcher-chunk-v1\";\n","/**\n * Lazy-loaded bottom bar launcher for staging overlay (M2-SDK-05, UI-09).\n *\n * Intent: `?translation_edit=1` arms the UI only; auth + enableEditMode happen on user action.\n */\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.js\";\nimport type { EditLauncherOptions, InitOptions } from \"../types.js\";\nimport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport type EditLauncherMountContext = {\n initOptions: InitOptions;\n launcherOptions: EditLauncherOptions;\n isEditModeActive: () => boolean;\n enableEditMode: () => Promise<void>;\n disableEditMode: () => void;\n};\n\nexport type EditLauncherHandle = {\n destroy: () => void;\n};\n\nfunction tokenMatchesInit(token: string, options: InitOptions): boolean {\n const claims = decodeEditTokenClaims(token);\n if (!claims) {\n return false;\n }\n return (\n claims.applicationId === options.applicationId &&\n claims.environmentName === options.environment\n );\n}\n\nfunction storedEditToken(options: InitOptions): string | null {\n const token = readEditTokenFromStorage();\n if (!token || !tokenMatchesInit(token, options)) {\n return null;\n }\n return token;\n}\n\n/**\n * Mounts bottom edit bar and optional sign-in panel in a shadow root.\n */\nexport function mountEditLauncher(context: EditLauncherMountContext): EditLauncherHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(LAUNCHER_ROOT_ID)?.remove();\n\n const host = document.createElement(\"div\");\n host.id = LAUNCHER_ROOT_ID;\n host.setAttribute(\"data-translation-edit-launcher\", \"true\");\n\n const shadow = host.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `\n :host { all: initial; }\n .wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }\n .edit-bar {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147483645;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n background: #fff;\n border-top: 1px solid #e2e8f0;\n box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);\n }\n .edit-bar__left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n .edit-mode-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 999px;\n background: #d1fae5;\n color: #047857;\n font-size: 12px;\n font-weight: 600;\n white-space: nowrap;\n }\n .edit-mode-pill::before {\n content: \"\";\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #10b981;\n }\n .edit-bar__hint {\n color: #64748b;\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .fab {\n flex-shrink: 0;\n border: none;\n border-radius: 8px;\n padding: 10px 18px;\n background: #4338ca;\n color: #fff;\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 600;\n box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);\n }\n .fab:hover { background: #3730a3; }\n .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }\n .panel {\n position: fixed;\n bottom: 72px;\n left: 20px;\n z-index: 2147483645;\n width: min(300px, calc(100vw - 40px));\n padding: 14px;\n border-radius: 12px;\n background: #fff;\n color: #0f172a;\n border: 1px solid #e2e8f0;\n box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);\n }\n .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }\n .panel button {\n width: 100%;\n border: none;\n border-radius: 8px;\n padding: 9px 12px;\n cursor: pointer;\n font: inherit;\n font-weight: 600;\n }\n .panel .primary { background: #4338ca; color: #fff; }\n .panel .primary:disabled { opacity: 0.6; cursor: wait; }\n .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }\n .marker { display: none; }\n `;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"wrap\";\n\n const marker = document.createElement(\"span\");\n marker.className = \"marker\";\n marker.textContent = LAUNCHER_CHUNK_MARKER;\n\n const bar = document.createElement(\"div\");\n bar.className = \"edit-bar\";\n\n const left = document.createElement(\"div\");\n left.className = \"edit-bar__left\";\n\n const pill = document.createElement(\"span\");\n pill.className = \"edit-mode-pill\";\n pill.textContent = \"Edit mode active\";\n pill.hidden = true;\n\n const hint = document.createElement(\"span\");\n hint.className = \"edit-bar__hint\";\n hint.textContent = \"Translate copy on this page\";\n\n left.append(pill, hint);\n\n const fab = document.createElement(\"button\");\n fab.type = \"button\";\n fab.className = \"fab\";\n fab.textContent = \"Translate\";\n\n bar.append(left, fab);\n\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.hidden = true;\n panel.innerHTML =\n '<p>Sign in to edit copy on this page.</p><button type=\"button\" class=\"primary\">Sign in</button><p class=\"error\" hidden></p>';\n\n const signInBtn = panel.querySelector<HTMLButtonElement>(\".primary\")!;\n const errorEl = panel.querySelector<HTMLParagraphElement>(\".error\")!;\n\n const syncFabLabel = (): void => {\n if (context.isEditModeActive()) {\n fab.textContent = \"Stop editing\";\n fab.classList.add(\"is-editing\");\n pill.hidden = false;\n hint.hidden = true;\n panel.hidden = true;\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n }\n };\n\n const showError = (message: string): void => {\n errorEl.hidden = false;\n errorEl.textContent = message;\n };\n\n const clearError = (): void => {\n errorEl.hidden = true;\n errorEl.textContent = \"\";\n };\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n if (context.isEditModeActive()) {\n context.disableEditMode();\n syncFabLabel();\n return;\n }\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n panel.hidden = false;\n })();\n });\n\n signInBtn.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n const { signInUrl, requestEditToken } = context.launcherOptions;\n\n if (requestEditToken) {\n signInBtn.disabled = true;\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n panel.hidden = true;\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n } finally {\n signInBtn.disabled = false;\n }\n return;\n }\n\n if (signInUrl) {\n const params = new URLSearchParams();\n params.set(\n \"returnUrl\",\n window.location.href.split(\"#\")[0] ?? window.location.href,\n );\n params.set(\"applicationId\", context.initOptions.applicationId);\n params.set(\"environment\", context.initOptions.environment);\n const separator = signInUrl.includes(\"?\") ? \"&\" : \"?\";\n window.location.href = `${signInUrl}${separator}${params.toString()}`;\n return;\n }\n\n showError(\"Edit launcher auth is not configured.\");\n })();\n });\n\n wrap.append(marker, bar, panel);\n shadow.append(style, wrap);\n document.body.append(host);\n syncFabLabel();\n\n return {\n destroy: () => {\n host.remove();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.umd.cjs","../src/edit-launcher/constants.ts","../src/edit-launcher/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/translations/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded edit launcher chunk (M2-SDK-05).\n *\n * Intent: stable ids/markers keep overlay UI out of the default SDK entry until edit mode activates.\n */\nexport const LAUNCHER_ROOT_ID = \"translation-edit-launcher-root\";\n\n/** Marker string used in bundle-split tests to keep launcher out of the main entry. */\nexport const LAUNCHER_CHUNK_MARKER = \"translation-edit-launcher-chunk-v1\";\n","/**\n * Lazy-loaded bottom bar launcher for staging overlay (M2-SDK-05, UI-09).\n *\n * Intent: `?translation_edit=1` arms the UI only; auth + enableEditMode happen on user action.\n */\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.js\";\nimport type { EditLauncherOptions, InitOptions } from \"../types.js\";\nimport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport type EditLauncherMountContext = {\n initOptions: InitOptions;\n launcherOptions: EditLauncherOptions;\n isEditModeActive: () => boolean;\n enableEditMode: () => Promise<void>;\n disableEditMode: () => void;\n};\n\nexport type EditLauncherHandle = {\n destroy: () => void;\n};\n\nfunction tokenMatchesInit(token: string, options: InitOptions): boolean {\n const claims = decodeEditTokenClaims(token);\n if (!claims) {\n return false;\n }\n return (\n claims.applicationId === options.applicationId &&\n claims.environmentName === options.environment\n );\n}\n\nfunction storedEditToken(options: InitOptions): string | null {\n const token = readEditTokenFromStorage();\n if (!token || !tokenMatchesInit(token, options)) {\n return null;\n }\n return token;\n}\n\n/**\n * Mounts bottom edit bar and optional sign-in panel in a shadow root.\n */\nexport function mountEditLauncher(context: EditLauncherMountContext): EditLauncherHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(LAUNCHER_ROOT_ID)?.remove();\n\n const host = document.createElement(\"div\");\n host.id = LAUNCHER_ROOT_ID;\n host.setAttribute(\"data-translation-edit-launcher\", \"true\");\n\n const shadow = host.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `\n :host { all: initial; }\n .wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }\n .edit-bar {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147483645;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n background: #fff;\n border-top: 1px solid #e2e8f0;\n box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);\n }\n .edit-bar__left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n .edit-mode-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 999px;\n background: #d1fae5;\n color: #047857;\n font-size: 12px;\n font-weight: 600;\n white-space: nowrap;\n }\n .edit-mode-pill::before {\n content: \"\";\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #10b981;\n }\n .edit-bar__hint {\n color: #64748b;\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .fab {\n flex-shrink: 0;\n border: none;\n border-radius: 8px;\n padding: 10px 18px;\n background: #4338ca;\n color: #fff;\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 600;\n box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);\n }\n .fab:hover { background: #3730a3; }\n .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }\n .panel {\n position: fixed;\n bottom: 72px;\n left: 20px;\n z-index: 2147483645;\n width: min(300px, calc(100vw - 40px));\n padding: 14px;\n border-radius: 12px;\n background: #fff;\n color: #0f172a;\n border: 1px solid #e2e8f0;\n box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);\n }\n .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }\n .panel button {\n width: 100%;\n border: none;\n border-radius: 8px;\n padding: 9px 12px;\n cursor: pointer;\n font: inherit;\n font-weight: 600;\n }\n .panel .primary { background: #4338ca; color: #fff; }\n .panel .primary:disabled { opacity: 0.6; cursor: wait; }\n .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }\n .marker { display: none; }\n `;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"wrap\";\n\n const marker = document.createElement(\"span\");\n marker.className = \"marker\";\n marker.textContent = LAUNCHER_CHUNK_MARKER;\n\n const bar = document.createElement(\"div\");\n bar.className = \"edit-bar\";\n\n const left = document.createElement(\"div\");\n left.className = \"edit-bar__left\";\n\n const pill = document.createElement(\"span\");\n pill.className = \"edit-mode-pill\";\n pill.textContent = \"Edit mode active\";\n pill.hidden = true;\n\n const hint = document.createElement(\"span\");\n hint.className = \"edit-bar__hint\";\n hint.textContent = \"Translate copy on this page\";\n\n left.append(pill, hint);\n\n const fab = document.createElement(\"button\");\n fab.type = \"button\";\n fab.className = \"fab\";\n fab.textContent = \"Translate\";\n\n bar.append(left, fab);\n\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.hidden = true;\n panel.innerHTML =\n '<p>Sign in to edit copy on this page.</p><button type=\"button\" class=\"primary\">Sign in</button><p class=\"error\" hidden></p>';\n\n const signInBtn = panel.querySelector<HTMLButtonElement>(\".primary\")!;\n const errorEl = panel.querySelector<HTMLParagraphElement>(\".error\")!;\n\n const syncFabLabel = (): void => {\n if (context.isEditModeActive()) {\n fab.textContent = \"Stop editing\";\n fab.classList.add(\"is-editing\");\n pill.hidden = false;\n hint.hidden = true;\n panel.hidden = true;\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n }\n };\n\n const showError = (message: string): void => {\n errorEl.hidden = false;\n errorEl.textContent = message;\n };\n\n const clearError = (): void => {\n errorEl.hidden = true;\n errorEl.textContent = \"\";\n };\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n if (context.isEditModeActive()) {\n context.disableEditMode();\n syncFabLabel();\n return;\n }\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n panel.hidden = false;\n })();\n });\n\n signInBtn.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n const { signInUrl, requestEditToken } = context.launcherOptions;\n\n if (requestEditToken) {\n signInBtn.disabled = true;\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n panel.hidden = true;\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n } finally {\n signInBtn.disabled = false;\n }\n return;\n }\n\n if (signInUrl) {\n const params = new URLSearchParams();\n params.set(\n \"returnUrl\",\n window.location.href.split(\"#\")[0] ?? window.location.href,\n );\n params.set(\"applicationId\", context.initOptions.applicationId);\n params.set(\"environment\", context.initOptions.environment);\n const separator = signInUrl.includes(\"?\") ? \"&\" : \"?\";\n window.location.href = `${signInUrl}${separator}${params.toString()}`;\n return;\n }\n\n showError(\"Edit launcher auth is not configured.\");\n })();\n });\n\n wrap.append(marker, bar, panel);\n shadow.append(style, wrap);\n document.body.append(host);\n syncFabLabel();\n\n return {\n destroy: () => {\n host.remove();\n },\n };\n}\n"]}
|
package/dist/index.umd.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/mitopalov/work/translations/packages/sdk/dist/index.umd.cjs","../src/resolve-key.ts","../src/index.ts","../src/edit-mode.ts","../src/runtime.ts","../src/urls.ts","../src/edit-launcher-lifecycle.ts"],"names":["current","getRuntime","disableEditMode"],"mappings":"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","file":"/home/mitopalov/work/translations/packages/sdk/dist/index.umd.cjs","sourcesContent":[null,"/**\n * Maps DOM nodes to translation keys for overlay edit mode (M2-SDK-02).\n *\n * Intent: host `registerResolver` first; `data-i18n-key` on the element or an ancestor is the fallback.\n */\nimport type { KeyResolver } from \"./runtime.js\";\n\nexport const I18N_KEY_ATTR = \"data-i18n-key\";\n\n/**\n * Resolves a translation key for a DOM node by walking ancestors.\n */\nexport function resolveKeyForElement(\n start: Element,\n resolver: KeyResolver | null,\n): string | null {\n if (resolver) {\n let current: Element | null = start;\n while (current) {\n const resolved = resolver(current);\n if (resolved) {\n return resolved;\n }\n current = current.parentElement;\n }\n }\n\n let current: Element | null = start;\n while (current) {\n const fromAttr = current.getAttribute(I18N_KEY_ATTR);\n if (fromAttr) {\n return fromAttr;\n }\n current = current.parentElement;\n }\n\n return null;\n}\n","/**\n * @stringpush/sdk — plain JS runtime: load manifest + bundles, expose `t()` (ICU in M1-SDK-03).\n *\n * Intent: customer-facing surface; runtime catalog/manifest delegated to @stringpush/runtime-core.\n */\nexport { I18N_KEY_ATTR } from \"./resolve-key.js\";\n\nexport type {\n EditLauncherOptions,\n EnableEditModeOptions,\n Environment,\n InitOptions,\n ManifestLocaleEntry,\n MissingKeyFallback,\n RetryOptions,\n TranslateValues,\n TranslationCatalog,\n TranslationManifest,\n} from \"./types.js\";\n\nimport type { EnableEditModeOptions, InitOptions, TranslateValues } from \"./types.js\";\nimport {\n changeLocale,\n destroyRuntime,\n getRuntime,\n getRuntimeLocale,\n initializeRuntime,\n translateKey,\n} from \"@stringpush/runtime-core\";\nimport { teardownEditLauncher, mountEditLauncherIfNeeded } from \"./edit-launcher-lifecycle.js\";\nimport {\n readEditTokenFromHash,\n readEditTokenFromQuery,\n resolveEditToken,\n shouldAutoEnableEditMode,\n stripEditTokenFromUrl,\n} from \"./edit-token.js\";\nimport {\n disableEditMode as disableEditModeInternal,\n enableEditMode as enableEditModeInternal,\n teardownEditMode,\n} from \"./edit-mode.js\";\nimport { getKeyResolver, setKeyResolver } from \"./runtime.js\";\n\n/**\n * Initialize the SDK: fetch manifest and the active locale bundle into an in-memory catalog.\n *\n * Intent: entry point for customer apps; triggers `onTranslationsUpdated` when the first catalog is ready.\n */\nexport async function init(options: InitOptions): Promise<void> {\n await initializeRuntime(options);\n\n const tokenFromUrl = readEditTokenFromQuery() ?? readEditTokenFromHash();\n if (tokenFromUrl || options.editToken) {\n resolveEditToken(options);\n if (tokenFromUrl) {\n stripEditTokenFromUrl();\n }\n }\n\n if (shouldAutoEnableEditMode(options)) {\n await enableEditModeInternal();\n } else {\n await mountEditLauncherIfNeeded(options);\n }\n}\n\nexport function destroy(): void {\n teardownEditLauncher();\n teardownEditMode();\n setKeyResolver(null);\n destroyRuntime();\n}\n\nexport function getLocale(): string {\n return getRuntimeLocale();\n}\n\n/**\n * Switch active locale and reload its bundle from the manifest.\n */\nexport async function setLocale(locale: string): Promise<void> {\n await changeLocale(locale);\n}\n\n/**\n * Translate a key using the loaded catalog and ICU MessageFormat.\n */\nexport function t(key: string, values?: TranslateValues): string {\n // Intent: preserve @stringpush/sdk error text for public `t()` callers (runtime-core uses initializeRuntime wording).\n if (!getRuntime().options) {\n throw new Error(\"@stringpush/sdk: call init() before using the runtime\");\n }\n return translateKey(key, values);\n}\n\nexport function loadNamespace(_namespace: string): Promise<void> {\n return Promise.resolve();\n}\n\nexport async function enableEditMode(options?: EnableEditModeOptions): Promise<void> {\n await enableEditModeInternal(options);\n}\n\nexport function disableEditMode(): void {\n disableEditModeInternal();\n}\n\nexport function registerResolver(resolver: (element: Element) => string | null): () => void {\n setKeyResolver(resolver);\n return () => {\n if (getKeyResolver() === resolver) {\n setKeyResolver(null);\n }\n };\n}\n","/**\n * Edit-mode lifecycle: lazy overlay chunk load and teardown (M2-SDK-01).\n */\nimport {\n patchCatalogEntry,\n resolveApiBaseUrl,\n} from \"@stringpush/runtime-core\";\nimport { decodeEditTokenClaims } from \"./edit-token-decode.js\";\nimport { resolveEditToken } from \"./edit-token.js\";\nimport { resolveKeyForElement } from \"./resolve-key.js\";\nimport { assertInitialized, getKeyResolver, getRuntime } from \"./runtime.js\";\nimport type { EnableEditModeOptions } from \"./types.js\";\nimport { resolveRealtimeUrl } from \"./urls.js\";\n\ntype OverlayModule = typeof import(\"./overlay/index.js\");\n\nlet overlayHandle: { destroy: () => void } | null = null;\nlet overlayLoadPromise: Promise<OverlayModule> | null = null;\n\nfunction loadOverlayModule(): Promise<OverlayModule> {\n if (!overlayLoadPromise) {\n // Intent: dynamic import keeps overlay + WS client out of the default SDK bundle.\n overlayLoadPromise = import(\"./overlay/index.js\");\n }\n return overlayLoadPromise;\n}\n\nexport function isEditModeActive(): boolean {\n return overlayHandle !== null;\n}\n\n/**\n * Activates overlay edit mode when a valid edit JWT is available.\n */\nexport async function enableEditMode(options: EnableEditModeOptions = {}): Promise<void> {\n if (overlayHandle) {\n return;\n }\n\n const initOptions = assertInitialized();\n const state = getRuntime();\n const manifest = state.manifest;\n if (!manifest) {\n throw new Error(\"@stringpush/sdk: manifest is not loaded — call init() before enableEditMode()\");\n }\n\n const editToken = resolveEditToken(initOptions, options.editToken);\n if (!editToken) {\n throw new Error(\n \"@stringpush/sdk: edit token required — pass editToken, ?edit_token=, or sessionStorage\",\n );\n }\n\n const apiBaseUrl = resolveApiBaseUrl(initOptions.apiBaseUrl);\n const realtimeUrl = resolveRealtimeUrl(apiBaseUrl, initOptions.realtimeUrl);\n const overlay = await loadOverlayModule();\n const editClaims = decodeEditTokenClaims(editToken);\n if (!editClaims) {\n throw new Error(\"@stringpush/sdk: edit token is malformed\");\n }\n if (editClaims.projectId !== manifest.projectId) {\n throw new Error(\"@stringpush/sdk: edit token project does not match loaded manifest\");\n }\n if (editClaims.applicationId !== manifest.applicationId) {\n throw new Error(\"@stringpush/sdk: edit token application does not match init()\");\n }\n if (editClaims.environmentName !== initOptions.environment) {\n throw new Error(\n \"@stringpush/sdk: edit token environment does not match init() environment\",\n );\n }\n\n overlayHandle = overlay.mountOverlay({\n projectId: manifest.projectId,\n applicationId: manifest.applicationId,\n environmentId: editClaims.environmentId,\n realtimeUrl,\n editToken,\n apiBaseUrl,\n origin: initOptions.origin,\n activeLocaleCode: state.locale,\n resolveKey: (element) => resolveKeyForElement(element, getKeyResolver()),\n onCatalogPatched: (keyPath, localeCode, value, version) => {\n if (patchCatalogEntry(keyPath, localeCode, value, version)) {\n initOptions.onTranslationsUpdated?.();\n }\n },\n onTranslationsUpdated: () => {\n initOptions.onTranslationsUpdated?.();\n },\n });\n}\n\n/**\n * Tears down overlay DOM and closes the realtime WebSocket.\n */\nexport function disableEditMode(): void {\n overlayHandle?.destroy();\n overlayHandle = null;\n}\n\nexport function teardownEditMode(): void {\n disableEditMode();\n overlayLoadPromise = null;\n}\n","/**\n * DOM-specific runtime helpers (key resolver) atop @stringpush/runtime-core state.\n *\n * Intent: overlay `registerResolver` stays in sdk; catalog/manifest live in runtime-core.\n */\nimport {\n assertInitialized as assertCoreInitialized,\n getRuntime,\n resetRuntime,\n type SdkRuntime,\n} from \"@stringpush/runtime-core\";\nimport type { InitOptions } from \"./types.js\";\n\nexport { getRuntime, resetRuntime, type SdkRuntime };\n\n/** SDK init stores overlay fields on the same options object passed to runtime-core. */\nexport function assertInitialized(): InitOptions {\n return assertCoreInitialized() as InitOptions;\n}\n\nexport type KeyResolver = (element: Element) => string | null;\n\nlet keyResolver: KeyResolver | null = null;\n\nexport function setKeyResolver(resolver: KeyResolver | null): void {\n keyResolver = resolver;\n}\n\nexport function getKeyResolver(): KeyResolver | null {\n return keyResolver;\n}\n","/**\n * WebSocket URL helper for overlay realtime (M2-SDK-01).\n *\n * Intent: overlay-specific; runtime-core has no realtime dependency.\n */\nexport function resolveRealtimeUrl(apiBaseUrl: string, explicit?: string): string {\n if (explicit) {\n return explicit.replace(/\\/$/, \"\");\n }\n\n const api = new URL(apiBaseUrl);\n if (api.hostname === \"localhost\" && (api.port === \"3000\" || api.port === \"\")) {\n return \"ws://localhost:3001\";\n }\n\n const protocol = api.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${protocol}//${api.host}`;\n}\n","/**\n * Edit launcher mount/teardown (M2-SDK-05).\n *\n * Intent: dynamic import keeps launcher UI out of the default SDK bundle.\n */\nimport { isEditLauncherArmed, shouldAutoEnableEditMode } from \"./edit-token.js\";\nimport {\n disableEditMode as disableEditModeInternal,\n enableEditMode as enableEditModeInternal,\n isEditModeActive,\n} from \"./edit-mode.js\";\nimport type { InitOptions } from \"./types.js\";\n\ntype EditLauncherModule = typeof import(\"./edit-launcher/index.js\");\n\nlet launcherHandle: { destroy: () => void } | null = null;\nlet launcherLoadPromise: Promise<EditLauncherModule> | null = null;\n\nfunction loadLauncherModule(): Promise<EditLauncherModule> {\n if (!launcherLoadPromise) {\n launcherLoadPromise = import(\"./edit-launcher/index.js\");\n }\n return launcherLoadPromise;\n}\n\nexport function shouldMountEditLauncher(options: InitOptions): boolean {\n if (!options.editLauncher) {\n return false;\n }\n if (options.environment !== \"staging\") {\n return false;\n }\n if (shouldAutoEnableEditMode(options)) {\n return false;\n }\n return isEditLauncherArmed(options.editLauncher);\n}\n\nexport async function mountEditLauncherIfNeeded(options: InitOptions): Promise<void> {\n if (!shouldMountEditLauncher(options)) {\n return;\n }\n\n const launcherOptions = options.editLauncher!;\n const mod = await loadLauncherModule();\n launcherHandle = mod.mountEditLauncher({\n initOptions: options,\n launcherOptions,\n isEditModeActive,\n enableEditMode: () => enableEditModeInternal(),\n disableEditMode: disableEditModeInternal,\n });\n}\n\nexport function teardownEditLauncher(): void {\n launcherHandle?.destroy();\n launcherHandle = null;\n launcherLoadPromise = null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/index.umd.cjs","../src/resolve-key.ts","../src/index.ts","../src/edit-mode.ts","../src/runtime.ts","../src/urls.ts","../src/edit-launcher-lifecycle.ts"],"names":["current","getRuntime","disableEditMode"],"mappings":"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","file":"/home/runner/work/translations/translations/packages/sdk/dist/index.umd.cjs","sourcesContent":[null,"/**\n * Maps DOM nodes to translation keys for overlay edit mode (M2-SDK-02).\n *\n * Intent: host `registerResolver` first; `data-i18n-key` on the element or an ancestor is the fallback.\n */\nimport type { KeyResolver } from \"./runtime.js\";\n\nexport const I18N_KEY_ATTR = \"data-i18n-key\";\n\n/**\n * Resolves a translation key for a DOM node by walking ancestors.\n */\nexport function resolveKeyForElement(\n start: Element,\n resolver: KeyResolver | null,\n): string | null {\n if (resolver) {\n let current: Element | null = start;\n while (current) {\n const resolved = resolver(current);\n if (resolved) {\n return resolved;\n }\n current = current.parentElement;\n }\n }\n\n let current: Element | null = start;\n while (current) {\n const fromAttr = current.getAttribute(I18N_KEY_ATTR);\n if (fromAttr) {\n return fromAttr;\n }\n current = current.parentElement;\n }\n\n return null;\n}\n","/**\n * @stringpush/sdk — plain JS runtime: load manifest + bundles, expose `t()` (ICU in M1-SDK-03).\n *\n * Intent: customer-facing surface; runtime catalog/manifest delegated to @stringpush/runtime-core.\n */\nexport { I18N_KEY_ATTR } from \"./resolve-key.js\";\n\nexport type {\n EditLauncherOptions,\n EnableEditModeOptions,\n Environment,\n InitOptions,\n ManifestLocaleEntry,\n MissingKeyFallback,\n RetryOptions,\n TranslateValues,\n TranslationCatalog,\n TranslationManifest,\n} from \"./types.js\";\n\nimport type { EnableEditModeOptions, InitOptions, TranslateValues } from \"./types.js\";\nimport {\n changeLocale,\n destroyRuntime,\n getRuntime,\n getRuntimeLocale,\n initializeRuntime,\n translateKey,\n} from \"@stringpush/runtime-core\";\nimport { teardownEditLauncher, mountEditLauncherIfNeeded } from \"./edit-launcher-lifecycle.js\";\nimport {\n readEditTokenFromHash,\n readEditTokenFromQuery,\n resolveEditToken,\n shouldAutoEnableEditMode,\n stripEditTokenFromUrl,\n} from \"./edit-token.js\";\nimport {\n disableEditMode as disableEditModeInternal,\n enableEditMode as enableEditModeInternal,\n teardownEditMode,\n} from \"./edit-mode.js\";\nimport { getKeyResolver, setKeyResolver } from \"./runtime.js\";\n\n/**\n * Initialize the SDK: fetch manifest and the active locale bundle into an in-memory catalog.\n *\n * Intent: entry point for customer apps; triggers `onTranslationsUpdated` when the first catalog is ready.\n */\nexport async function init(options: InitOptions): Promise<void> {\n await initializeRuntime(options);\n\n const tokenFromUrl = readEditTokenFromQuery() ?? readEditTokenFromHash();\n if (tokenFromUrl || options.editToken) {\n resolveEditToken(options);\n if (tokenFromUrl) {\n stripEditTokenFromUrl();\n }\n }\n\n if (shouldAutoEnableEditMode(options)) {\n await enableEditModeInternal();\n } else {\n await mountEditLauncherIfNeeded(options);\n }\n}\n\nexport function destroy(): void {\n teardownEditLauncher();\n teardownEditMode();\n setKeyResolver(null);\n destroyRuntime();\n}\n\nexport function getLocale(): string {\n return getRuntimeLocale();\n}\n\n/**\n * Switch active locale and reload its bundle from the manifest.\n */\nexport async function setLocale(locale: string): Promise<void> {\n await changeLocale(locale);\n}\n\n/**\n * Translate a key using the loaded catalog and ICU MessageFormat.\n */\nexport function t(key: string, values?: TranslateValues): string {\n // Intent: preserve @stringpush/sdk error text for public `t()` callers (runtime-core uses initializeRuntime wording).\n if (!getRuntime().options) {\n throw new Error(\"@stringpush/sdk: call init() before using the runtime\");\n }\n return translateKey(key, values);\n}\n\nexport function loadNamespace(_namespace: string): Promise<void> {\n return Promise.resolve();\n}\n\nexport async function enableEditMode(options?: EnableEditModeOptions): Promise<void> {\n await enableEditModeInternal(options);\n}\n\nexport function disableEditMode(): void {\n disableEditModeInternal();\n}\n\nexport function registerResolver(resolver: (element: Element) => string | null): () => void {\n setKeyResolver(resolver);\n return () => {\n if (getKeyResolver() === resolver) {\n setKeyResolver(null);\n }\n };\n}\n","/**\n * Edit-mode lifecycle: lazy overlay chunk load and teardown (M2-SDK-01).\n */\nimport {\n patchCatalogEntry,\n resolveApiBaseUrl,\n} from \"@stringpush/runtime-core\";\nimport { decodeEditTokenClaims } from \"./edit-token-decode.js\";\nimport { resolveEditToken } from \"./edit-token.js\";\nimport { resolveKeyForElement } from \"./resolve-key.js\";\nimport { assertInitialized, getKeyResolver, getRuntime } from \"./runtime.js\";\nimport type { EnableEditModeOptions } from \"./types.js\";\nimport { resolveRealtimeUrl } from \"./urls.js\";\n\ntype OverlayModule = typeof import(\"./overlay/index.js\");\n\nlet overlayHandle: { destroy: () => void } | null = null;\nlet overlayLoadPromise: Promise<OverlayModule> | null = null;\n\nfunction loadOverlayModule(): Promise<OverlayModule> {\n if (!overlayLoadPromise) {\n // Intent: dynamic import keeps overlay + WS client out of the default SDK bundle.\n overlayLoadPromise = import(\"./overlay/index.js\");\n }\n return overlayLoadPromise;\n}\n\nexport function isEditModeActive(): boolean {\n return overlayHandle !== null;\n}\n\n/**\n * Activates overlay edit mode when a valid edit JWT is available.\n */\nexport async function enableEditMode(options: EnableEditModeOptions = {}): Promise<void> {\n if (overlayHandle) {\n return;\n }\n\n const initOptions = assertInitialized();\n const state = getRuntime();\n const manifest = state.manifest;\n if (!manifest) {\n throw new Error(\"@stringpush/sdk: manifest is not loaded — call init() before enableEditMode()\");\n }\n\n const editToken = resolveEditToken(initOptions, options.editToken);\n if (!editToken) {\n throw new Error(\n \"@stringpush/sdk: edit token required — pass editToken, ?edit_token=, or sessionStorage\",\n );\n }\n\n const apiBaseUrl = resolveApiBaseUrl(initOptions.apiBaseUrl);\n const realtimeUrl = resolveRealtimeUrl(apiBaseUrl, initOptions.realtimeUrl);\n const overlay = await loadOverlayModule();\n const editClaims = decodeEditTokenClaims(editToken);\n if (!editClaims) {\n throw new Error(\"@stringpush/sdk: edit token is malformed\");\n }\n if (editClaims.projectId !== manifest.projectId) {\n throw new Error(\"@stringpush/sdk: edit token project does not match loaded manifest\");\n }\n if (editClaims.applicationId !== manifest.applicationId) {\n throw new Error(\"@stringpush/sdk: edit token application does not match init()\");\n }\n if (editClaims.environmentName !== initOptions.environment) {\n throw new Error(\n \"@stringpush/sdk: edit token environment does not match init() environment\",\n );\n }\n\n overlayHandle = overlay.mountOverlay({\n projectId: manifest.projectId,\n applicationId: manifest.applicationId,\n environmentId: editClaims.environmentId,\n realtimeUrl,\n editToken,\n apiBaseUrl,\n origin: initOptions.origin,\n activeLocaleCode: state.locale,\n resolveKey: (element) => resolveKeyForElement(element, getKeyResolver()),\n onCatalogPatched: (keyPath, localeCode, value, version) => {\n if (patchCatalogEntry(keyPath, localeCode, value, version)) {\n initOptions.onTranslationsUpdated?.();\n }\n },\n onTranslationsUpdated: () => {\n initOptions.onTranslationsUpdated?.();\n },\n });\n}\n\n/**\n * Tears down overlay DOM and closes the realtime WebSocket.\n */\nexport function disableEditMode(): void {\n overlayHandle?.destroy();\n overlayHandle = null;\n}\n\nexport function teardownEditMode(): void {\n disableEditMode();\n overlayLoadPromise = null;\n}\n","/**\n * DOM-specific runtime helpers (key resolver) atop @stringpush/runtime-core state.\n *\n * Intent: overlay `registerResolver` stays in sdk; catalog/manifest live in runtime-core.\n */\nimport {\n assertInitialized as assertCoreInitialized,\n getRuntime,\n resetRuntime,\n type SdkRuntime,\n} from \"@stringpush/runtime-core\";\nimport type { InitOptions } from \"./types.js\";\n\nexport { getRuntime, resetRuntime, type SdkRuntime };\n\n/** SDK init stores overlay fields on the same options object passed to runtime-core. */\nexport function assertInitialized(): InitOptions {\n return assertCoreInitialized() as InitOptions;\n}\n\nexport type KeyResolver = (element: Element) => string | null;\n\nlet keyResolver: KeyResolver | null = null;\n\nexport function setKeyResolver(resolver: KeyResolver | null): void {\n keyResolver = resolver;\n}\n\nexport function getKeyResolver(): KeyResolver | null {\n return keyResolver;\n}\n","/**\n * WebSocket URL helper for overlay realtime (M2-SDK-01).\n *\n * Intent: overlay-specific; runtime-core has no realtime dependency.\n */\nexport function resolveRealtimeUrl(apiBaseUrl: string, explicit?: string): string {\n if (explicit) {\n return explicit.replace(/\\/$/, \"\");\n }\n\n const api = new URL(apiBaseUrl);\n if (api.hostname === \"localhost\" && (api.port === \"3000\" || api.port === \"\")) {\n return \"ws://localhost:3001\";\n }\n\n const protocol = api.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${protocol}//${api.host}`;\n}\n","/**\n * Edit launcher mount/teardown (M2-SDK-05).\n *\n * Intent: dynamic import keeps launcher UI out of the default SDK bundle.\n */\nimport { isEditLauncherArmed, shouldAutoEnableEditMode } from \"./edit-token.js\";\nimport {\n disableEditMode as disableEditModeInternal,\n enableEditMode as enableEditModeInternal,\n isEditModeActive,\n} from \"./edit-mode.js\";\nimport type { InitOptions } from \"./types.js\";\n\ntype EditLauncherModule = typeof import(\"./edit-launcher/index.js\");\n\nlet launcherHandle: { destroy: () => void } | null = null;\nlet launcherLoadPromise: Promise<EditLauncherModule> | null = null;\n\nfunction loadLauncherModule(): Promise<EditLauncherModule> {\n if (!launcherLoadPromise) {\n launcherLoadPromise = import(\"./edit-launcher/index.js\");\n }\n return launcherLoadPromise;\n}\n\nexport function shouldMountEditLauncher(options: InitOptions): boolean {\n if (!options.editLauncher) {\n return false;\n }\n if (options.environment !== \"staging\") {\n return false;\n }\n if (shouldAutoEnableEditMode(options)) {\n return false;\n }\n return isEditLauncherArmed(options.editLauncher);\n}\n\nexport async function mountEditLauncherIfNeeded(options: InitOptions): Promise<void> {\n if (!shouldMountEditLauncher(options)) {\n return;\n }\n\n const launcherOptions = options.editLauncher!;\n const mod = await loadLauncherModule();\n launcherHandle = mod.mountEditLauncher({\n initOptions: options,\n launcherOptions,\n isEditModeActive,\n enableEditMode: () => enableEditModeInternal(),\n disableEditMode: disableEditModeInternal,\n });\n}\n\nexport function teardownEditLauncher(): void {\n launcherHandle?.destroy();\n launcherHandle = null;\n launcherLoadPromise = null;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/mitopalov/work/translations/packages/sdk/dist/overlay-MLOXYRPA.umd.cjs","../src/overlay/constants.ts","../src/overlay/overlay-api.ts","../src/overlay/locale-display.ts","../src/overlay/edit-panel.ts","../src/overlay/edit-panel-remote.ts","../src/overlay/highlights.ts","../src/overlay/realtime-sync.ts","../src/overlay/realtime-reconnect.ts","../src/overlay/realtime-client.ts","../src/overlay/index.ts"],"names":[],"mappings":"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","file":"/home/mitopalov/work/translations/packages/sdk/dist/overlay-MLOXYRPA.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded overlay chunk.\n *\n * Intent: stable ids/markers isolate overlay CSS/DOM and keep the main SDK chunk small.\n */\nexport const OVERLAY_ROOT_ID = \"translation-overlay-root\";\nexport const EDIT_PANEL_HOST_ID = \"translation-edit-panel-host\";\n/** Marker string used in bundle-split tests — must not appear in the main SDK chunk. */\nexport const OVERLAY_CHUNK_MARKER = \"translation-overlay-chunk-v1\";\n","/**\n * Overlay HTTP client (edit JWT) for locales, key values, and saves (M2-SDK-03).\n *\n * Intent: thin fetch wrapper shared by edit panel; maps 409 VERSION_CONFLICT for conflict UI.\n */\nimport { resolveRequestOrigin } from \"@stringpush/runtime-core\";\n\nexport type OverlayLocale = {\n id: string;\n code: string;\n};\n\nexport type OverlayKeyValueEntry = {\n localeId: string;\n localeCode: string;\n value: string;\n version: number | null;\n};\n\nexport type OverlayKeyValuesResponse = {\n keyId: string;\n keyPath: string;\n defaultMessage: string | null;\n entries: OverlayKeyValueEntry[];\n};\n\nexport type OverlayTranslationValue = {\n id: string;\n translationKeyId: string;\n localeId: string;\n environmentId: string;\n value: string;\n status: string;\n version: number;\n};\n\nexport type OverlayApiConfig = {\n apiBaseUrl: string;\n editToken: string;\n origin?: string;\n fetch?: typeof fetch;\n};\n\nexport class OverlayApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly body: unknown,\n ) {\n super(message);\n this.name = \"OverlayApiError\";\n }\n\n get isVersionConflict(): boolean {\n return (\n this.status === 409 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"VERSION_CONFLICT\"\n );\n }\n\n get isKeyNotFound(): boolean {\n return (\n this.status === 404 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"NOT_FOUND\"\n );\n }\n}\n\nfunction overlayHeaders(config: OverlayApiConfig): HeadersInit {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.editToken}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n const origin = resolveRequestOrigin(config.origin);\n if (origin) {\n headers.Origin = origin;\n }\n return headers;\n}\n\nasync function overlayFetch(\n config: OverlayApiConfig,\n path: string,\n init: RequestInit = {},\n): Promise<Response> {\n const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n const url = `${config.apiBaseUrl.replace(/\\/$/, \"\")}${path}`;\n return fetchFn(url, {\n ...init,\n headers: {\n ...overlayHeaders(config),\n ...(init.headers as Record<string, string> | undefined),\n },\n });\n}\n\nasync function parseJson<T>(response: Response): Promise<T> {\n return (await response.json()) as T;\n}\n\nexport async function fetchOverlayLocales(config: OverlayApiConfig): Promise<OverlayLocale[]> {\n const response = await overlayFetch(config, \"/v1/overlay/locales\");\n const body = await parseJson<{ data: Array<{ id: string; code: string }> } | { error: unknown }>(\n response,\n );\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load locales (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return (body as { data: Array<{ id: string; code: string }> }).data.map((row) => ({\n id: row.id,\n code: row.code,\n }));\n}\n\nexport async function fetchOverlayKeyValues(\n config: OverlayApiConfig,\n keyPath: string,\n): Promise<OverlayKeyValuesResponse> {\n const params = new URLSearchParams({ key: keyPath });\n const response = await overlayFetch(config, `/v1/overlay/keys/values?${params}`);\n const body = await parseJson<OverlayKeyValuesResponse | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load key values (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayKeyValuesResponse;\n}\n\nexport async function createOverlayKey(\n config: OverlayApiConfig,\n input: { keyPath: string; defaultMessage?: string | null },\n): Promise<{ id: string; keyPath: string }> {\n const response = await overlayFetch(config, \"/v1/overlay/keys\", {\n method: \"POST\",\n body: JSON.stringify({\n keyPath: input.keyPath,\n defaultMessage: input.defaultMessage ?? null,\n }),\n });\n\n const body = await parseJson<\n | { id: string; key: string; namespace: string }\n | { error: unknown }\n >(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to create translation key (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n const record = body as { id: string; key: string; namespace: string };\n const keyPath = record.namespace ? `${record.namespace}.${record.key}` : record.key;\n return { id: record.id, keyPath };\n}\n\nexport async function patchOverlayValue(\n config: OverlayApiConfig,\n input: { keyId: string; localeId: string; value: string; version: number | null },\n): Promise<OverlayTranslationValue> {\n const headers: Record<string, string> = {};\n if (input.version !== null) {\n headers[\"If-Match\"] = String(input.version);\n }\n\n const response = await overlayFetch(config, \"/v1/overlay/values\", {\n method: \"PATCH\",\n headers,\n body: JSON.stringify({\n keyId: input.keyId,\n localeId: input.localeId,\n value: input.value,\n }),\n });\n\n const body = await parseJson<OverlayTranslationValue | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to save translation (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayTranslationValue;\n}\n","const LOCALE_FLAGS: Record<string, string> = {\n en: \"🇺🇸\",\n fr: \"🇫🇷\",\n de: \"🇩🇪\",\n \"de-DE\": \"🇩🇪\",\n es: \"🇪🇸\",\n};\n\nconst LOCALE_NAMES: Record<string, string> = {\n en: \"English\",\n fr: \"French\",\n de: \"German\",\n \"de-DE\": \"German\",\n es: \"Spanish\",\n};\n\nexport function overlayLocaleFlag(code: string): string {\n return LOCALE_FLAGS[code] ?? \"🌐\";\n}\n\nexport function overlayLocaleLabel(code: string): string {\n const name = LOCALE_NAMES[code] ?? code.toUpperCase();\n return `${name} (${code})`;\n}\n","/**\n * Side panel UI to edit all locales for one translation key (M2-SDK-03).\n *\n * Intent: opened from overlay click; loads locales/values via edit JWT; saves with If-Match and surfaces 409 reload.\n */\nimport {\n createOverlayKey,\n fetchOverlayKeyValues,\n fetchOverlayLocales,\n OverlayApiError,\n patchOverlayValue,\n type OverlayApiConfig,\n type OverlayKeyValueEntry,\n} from \"./overlay-api.js\";\nimport { overlayLocaleFlag, overlayLocaleLabel } from \"./locale-display.js\";\nimport { recordTranslationVersion, shouldApplyTranslationUpdate } from \"@stringpush/runtime-core\";\nimport { EDIT_PANEL_HOST_ID } from \"./constants.js\";\nimport {\n registerEditPanelRemoteHandler,\n type RemoteTranslationUpdate,\n} from \"./edit-panel-remote.js\";\n\nconst PANEL_STYLES = `\n:host {\n all: initial;\n font-family: system-ui, -apple-system, Segoe UI, sans-serif;\n --sp-bg: #ffffff;\n --sp-text: #0f172a;\n --sp-muted: #64748b;\n --sp-border: #e2e8f0;\n --sp-accent: #4338ca;\n --sp-accent-hover: #3730a3;\n --sp-warning-bg: #fef3c7;\n --sp-warning-text: #92400e;\n --sp-warning-border: #fcd34d;\n --sp-badge-draft-bg: #fef3c6;\n --sp-badge-draft-text: #973c00;\n --sp-badge-default-bg: #f1f5f9;\n --sp-badge-default-text: #475569;\n}\n.panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n max-width: 100vw;\n height: 100vh;\n background: var(--sp-bg);\n color: var(--sp-text);\n box-shadow: -8px 0 32px rgba(15, 23, 42, 0.12);\n display: flex;\n flex-direction: column;\n z-index: 2147483647;\n}\n.header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--sp-border);\n}\n.header-main {\n min-width: 0;\n}\n.brand {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--sp-accent);\n}\n.title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n}\n.key-path {\n margin: 4px 0 0;\n font: 12px/1.4 ui-monospace, Menlo, monospace;\n color: var(--sp-muted);\n word-break: break-all;\n}\n.close-btn {\n border: none;\n background: transparent;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n color: var(--sp-muted);\n padding: 2px 4px;\n}\n.body {\n flex: 1;\n overflow: auto;\n padding: 12px 16px;\n}\n.locale-row {\n margin-bottom: 16px;\n padding-bottom: 16px;\n border-bottom: 1px solid var(--sp-border);\n}\n.locale-row:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.locale-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n.locale-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: var(--sp-text);\n}\n.locale-flag {\n font-size: 15px;\n line-height: 1;\n}\n.locale-badges {\n display: inline-flex;\n gap: 4px;\n}\n.badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n}\n.badge-default {\n background: var(--sp-badge-default-bg);\n color: var(--sp-badge-default-text);\n}\n.badge-draft {\n background: var(--sp-badge-draft-bg);\n color: var(--sp-badge-draft-text);\n}\n.locale-row textarea {\n width: 100%;\n min-height: 80px;\n box-sizing: border-box;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n padding: 10px 12px;\n font: 14px/1.45 inherit;\n resize: vertical;\n color: var(--sp-text);\n}\n.locale-row textarea:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 1px;\n border-color: var(--sp-accent);\n}\n.banner {\n margin: 0 16px 12px;\n padding: 12px 14px;\n border-radius: 8px;\n border: 1px solid var(--sp-warning-border);\n background: var(--sp-warning-bg);\n color: var(--sp-warning-text);\n font-size: 13px;\n line-height: 1.45;\n}\n.banner p {\n margin: 0;\n}\n.banner button {\n margin-top: 10px;\n border: 1px solid #d97706;\n background: #fff;\n border-radius: 6px;\n padding: 6px 12px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--sp-warning-text);\n}\n.footer {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n padding: 12px 16px 16px;\n border-top: 1px solid var(--sp-border);\n}\n.footer button {\n border-radius: 8px;\n padding: 9px 14px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.footer .secondary {\n border: 1px solid #cbd5e1;\n background: #fff;\n color: #334155;\n}\n.footer .primary {\n border: none;\n background: var(--sp-accent);\n color: #fff;\n}\n.footer .primary:hover:not(:disabled) {\n background: var(--sp-accent-hover);\n}\n.footer .primary::before {\n content: \"\";\n width: 14px;\n height: 14px;\n background: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M11.667 3.5 5.25 9.917 2.333 7' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n center / contain no-repeat;\n}\n.status {\n padding: 24px 16px;\n color: var(--sp-muted);\n font-size: 14px;\n}\n.create-panel {\n padding: 8px 0;\n}\n.create-panel p {\n margin: 0 0 12px;\n font-size: 14px;\n color: #334155;\n line-height: 1.5;\n}\n.create-panel label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #334155;\n margin-bottom: 6px;\n}\n.create-panel input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n padding: 8px 10px;\n font: 14px/1.4 inherit;\n margin-bottom: 12px;\n}\n.create-panel .create-btn {\n border: none;\n border-radius: 8px;\n padding: 9px 14px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n background: var(--sp-accent);\n color: #fff;\n}\n.create-panel .error {\n margin: 0 0 12px;\n color: #b91c1c;\n font-size: 13px;\n}\n`;\n\nexport type EditPanelContext = OverlayApiConfig & {\n activeLocaleCode: string;\n suggestedDefaultMessage?: string;\n onSaved: (keyPath: string, localeCode: string, value: string, version: number) => void;\n};\n\nexport type EditPanelHandle = {\n destroy: () => void;\n};\n\ntype LocaleRowState = OverlayKeyValueEntry & {\n draftValue: string;\n};\n\nexport function openEditPanel(keyPath: string, context: EditPanelContext): EditPanelHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n closeEditPanel();\n\n const host = document.createElement(\"div\");\n host.id = EDIT_PANEL_HOST_ID;\n const shadow = host.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = PANEL_STYLES;\n\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Edit translation\");\n\n const header = document.createElement(\"header\");\n header.className = \"header\";\n const headerMain = document.createElement(\"div\");\n headerMain.className = \"header-main\";\n const brand = document.createElement(\"p\");\n brand.className = \"brand\";\n brand.textContent = \"StringPush\";\n const title = document.createElement(\"h2\");\n title.className = \"title\";\n title.textContent = \"Edit translation\";\n const keyEl = document.createElement(\"p\");\n keyEl.className = \"key-path\";\n keyEl.textContent = keyPath;\n headerMain.append(brand, title, keyEl);\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"close-btn\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n closeBtn.textContent = \"×\";\n header.append(headerMain, closeBtn);\n\n const bodyEl = document.createElement(\"div\");\n bodyEl.className = \"body\";\n\n const status = document.createElement(\"div\");\n status.className = \"status\";\n status.textContent = \"Loading…\";\n bodyEl.append(status);\n\n const banner = document.createElement(\"div\");\n banner.className = \"banner\";\n banner.hidden = true;\n\n const footer = document.createElement(\"footer\");\n footer.className = \"footer\";\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"secondary\";\n cancelBtn.textContent = \"Cancel\";\n const saveBtn = document.createElement(\"button\");\n saveBtn.type = \"button\";\n saveBtn.className = \"primary\";\n saveBtn.textContent = \"Save draft\";\n saveBtn.disabled = true;\n footer.append(cancelBtn, saveBtn);\n\n panel.append(header, banner, bodyEl, footer);\n shadow.append(style, panel);\n document.body.append(host);\n\n let destroyed = false;\n let rows: LocaleRowState[] = [];\n let keyId = \"\";\n\n const destroy = () => {\n if (destroyed) {\n return;\n }\n destroyed = true;\n registerEditPanelRemoteHandler(null);\n host.remove();\n };\n\n function applyRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!shouldApplyTranslationUpdate(update.keyPath, update.localeCode, update.version)) {\n return false;\n }\n\n const row = rows.find((entry) => entry.localeCode === update.localeCode);\n if (!row) {\n return false;\n }\n\n if ((row.version ?? 0) > update.version) {\n return false;\n }\n\n if (row.draftValue !== row.value) {\n showConflict(\n `${update.localeCode} was updated in another tab. Reload to merge your changes.`,\n );\n return true;\n }\n\n row.value = update.value;\n row.draftValue = update.value;\n row.version = update.version;\n recordTranslationVersion(update.keyPath, update.localeCode, update.version);\n renderRows();\n return true;\n }\n\n closeBtn.addEventListener(\"click\", destroy);\n cancelBtn.addEventListener(\"click\", destroy);\n\n function renderRows(): void {\n bodyEl.replaceChildren();\n for (const row of rows) {\n const wrap = document.createElement(\"div\");\n wrap.className = \"locale-row\";\n\n const localeHeader = document.createElement(\"div\");\n localeHeader.className = \"locale-header\";\n\n const label = document.createElement(\"span\");\n label.className = \"locale-label\";\n const flag = document.createElement(\"span\");\n flag.className = \"locale-flag\";\n flag.textContent = overlayLocaleFlag(row.localeCode);\n flag.setAttribute(\"aria-hidden\", \"true\");\n label.append(flag, document.createTextNode(overlayLocaleLabel(row.localeCode)));\n\n const badges = document.createElement(\"span\");\n badges.className = \"locale-badges\";\n if (row.localeCode === context.activeLocaleCode) {\n const defaultBadge = document.createElement(\"span\");\n defaultBadge.className = \"badge badge-default\";\n defaultBadge.textContent = \"Default\";\n badges.append(defaultBadge);\n }\n const draftBadge = document.createElement(\"span\");\n draftBadge.className = \"badge badge-draft\";\n draftBadge.textContent = row.draftValue !== row.value ? \"Unsaved\" : \"Draft\";\n badges.append(draftBadge);\n\n localeHeader.append(label, badges);\n\n const textarea = document.createElement(\"textarea\");\n textarea.id = `locale-${row.localeId}`;\n textarea.setAttribute(\"aria-label\", `${row.localeCode} translation`);\n textarea.value = row.draftValue;\n textarea.addEventListener(\"input\", () => {\n row.draftValue = textarea.value;\n draftBadge.textContent = row.draftValue !== row.value ? \"Unsaved\" : \"Draft\";\n });\n\n wrap.append(localeHeader, textarea);\n bodyEl.append(wrap);\n }\n saveBtn.disabled = false;\n }\n\n async function load(): Promise<void> {\n try {\n const [locales, keyValues] = await Promise.all([\n fetchOverlayLocales(context),\n fetchOverlayKeyValues(context, keyPath),\n ]);\n\n if (destroyed) {\n return;\n }\n\n keyId = keyValues.keyId;\n const localeCodes = new Set(locales.map((l) => l.code));\n rows = keyValues.entries\n .filter((entry) => localeCodes.has(entry.localeCode))\n .map((entry) => ({\n ...entry,\n draftValue: entry.value,\n }));\n\n for (const row of rows) {\n if (row.version !== null) {\n recordTranslationVersion(keyPath, row.localeCode, row.version);\n }\n }\n\n banner.hidden = true;\n footer.hidden = false;\n renderRows();\n } catch (error) {\n if (error instanceof OverlayApiError && error.isKeyNotFound) {\n showCreateKeyPrompt();\n return;\n }\n status.textContent =\n error instanceof Error ? error.message : \"Failed to load translations\";\n }\n }\n\n function showCreateKeyPrompt(): void {\n footer.hidden = true;\n bodyEl.replaceChildren();\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"create-panel\";\n\n const intro = document.createElement(\"p\");\n intro.textContent =\n \"This key is not in your project yet. Create it to start editing translations for all locales.\";\n\n const label = document.createElement(\"label\");\n label.textContent = \"Default message (optional)\";\n label.htmlFor = \"create-default-message\";\n\n const input = document.createElement(\"input\");\n input.id = \"create-default-message\";\n input.type = \"text\";\n input.placeholder = \"Text shown when no translation is saved\";\n if (context.suggestedDefaultMessage) {\n input.value = context.suggestedDefaultMessage;\n }\n\n const errorEl = document.createElement(\"p\");\n errorEl.className = \"error\";\n errorEl.hidden = true;\n\n const createBtn = document.createElement(\"button\");\n createBtn.type = \"button\";\n createBtn.className = \"create-btn\";\n createBtn.textContent = \"Create key\";\n\n createBtn.addEventListener(\"click\", () => {\n void (async () => {\n createBtn.disabled = true;\n errorEl.hidden = true;\n\n try {\n await createOverlayKey(context, {\n keyPath,\n defaultMessage: input.value.trim() || null,\n });\n if (destroyed) {\n return;\n }\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n footer.hidden = false;\n saveBtn.disabled = true;\n await load();\n } catch (createError) {\n createBtn.disabled = false;\n errorEl.hidden = false;\n errorEl.textContent =\n createError instanceof Error ? createError.message : \"Failed to create key\";\n }\n })();\n });\n\n wrap.append(intro, label, input, errorEl, createBtn);\n bodyEl.append(wrap);\n }\n\n function showConflict(message: string): void {\n banner.hidden = false;\n banner.replaceChildren();\n const text = document.createElement(\"p\");\n text.textContent = message;\n banner.append(text);\n const reloadBtn = document.createElement(\"button\");\n reloadBtn.type = \"button\";\n reloadBtn.textContent = \"Reload\";\n reloadBtn.addEventListener(\"click\", () => {\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n saveBtn.disabled = true;\n void load();\n });\n banner.append(reloadBtn);\n }\n\n saveBtn.addEventListener(\"click\", () => {\n void (async () => {\n saveBtn.disabled = true;\n let hadConflict = false;\n\n for (const row of rows) {\n if (row.draftValue === row.value) {\n continue;\n }\n\n try {\n const saved = await patchOverlayValue(context, {\n keyId,\n localeId: row.localeId,\n value: row.draftValue,\n version: row.version,\n });\n row.value = saved.value;\n row.version = saved.version;\n row.draftValue = saved.value;\n context.onSaved(keyPath, row.localeCode, saved.value, saved.version);\n } catch (error) {\n if (error instanceof OverlayApiError && error.isVersionConflict) {\n hadConflict = true;\n showConflict(\n \"Someone else updated this key. Reload to fetch the latest versions, then try again.\",\n );\n break;\n }\n showConflict(error instanceof Error ? error.message : \"Save failed\");\n hadConflict = true;\n break;\n }\n }\n\n saveBtn.disabled = hadConflict;\n if (!hadConflict) {\n destroy();\n }\n })();\n });\n\n registerEditPanelRemoteHandler({ keyPath, applyRemoteUpdate });\n\n void load();\n\n return { destroy };\n}\n\nexport function closeEditPanel(): void {\n registerEditPanelRemoteHandler(null);\n document.getElementById(EDIT_PANEL_HOST_ID)?.remove();\n}\n","/**\n * Routes realtime `translation.updated` events to the open edit panel (M2-SDK-04).\n *\n * Intent: single active handler per key so concurrent editors see live value/version without full reload.\n */\nexport type RemoteTranslationUpdate = {\n keyPath: string;\n localeCode: string;\n value: string;\n version: number;\n};\n\nexport type EditPanelRemoteHandler = {\n keyPath: string;\n applyRemoteUpdate: (update: RemoteTranslationUpdate) => boolean;\n};\n\nlet activeHandler: EditPanelRemoteHandler | null = null;\n\nexport function registerEditPanelRemoteHandler(handler: EditPanelRemoteHandler | null): void {\n activeHandler = handler;\n}\n\nexport function notifyEditPanelRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!activeHandler || activeHandler.keyPath !== update.keyPath) {\n return false;\n }\n return activeHandler.applyRemoteUpdate(update);\n}\n","/**\n * Hover/focus highlights for translatable DOM nodes in edit mode (M2-SDK-02).\n *\n * Intent: pointer-events stay on the host page — only outlines and a read-only key tooltip are added.\n */\nimport { EDIT_PANEL_HOST_ID, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nconst HIGHLIGHT_CLASS = \"translation-sdk-target-highlight\";\nconst STYLES_ID = \"translation-sdk-highlight-styles\";\nconst TOOLTIP_ID = \"translation-sdk-key-tooltip\";\n\nconst HIGHLIGHT_CSS = `\n.${HIGHLIGHT_CLASS} {\n outline: 2px solid #2563eb !important;\n outline-offset: 2px !important;\n box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.28) !important;\n}\n.${HIGHLIGHT_CLASS}:focus-visible {\n outline: 3px solid #1d4ed8 !important;\n outline-offset: 2px !important;\n}\n#${TOOLTIP_ID} {\n position: fixed;\n z-index: 2147483645;\n max-width: min(360px, 90vw);\n padding: 4px 8px;\n border-radius: 6px;\n font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #f8fafc;\n background: #0f172a;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.28);\n pointer-events: none;\n display: none;\n}\n#${TOOLTIP_ID}[data-visible=\"true\"] {\n display: block;\n}\n`;\n\nexport type HighlightControllerOptions = {\n resolveKey: (element: Element) => string | null;\n onActivateKey?: (keyPath: string, element: Element) => void;\n};\n\nexport type HighlightController = {\n destroy: () => void;\n};\n\nexport function createHighlightController(\n options: HighlightControllerOptions,\n): HighlightController {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n const abort = new AbortController();\n const { signal } = abort;\n\n let highlighted: Element | null = null;\n\n const styleEl = document.createElement(\"style\");\n styleEl.id = STYLES_ID;\n styleEl.textContent = HIGHLIGHT_CSS;\n document.head.append(styleEl);\n\n const tip = document.createElement(\"div\");\n tip.id = TOOLTIP_ID;\n document.body.append(tip);\n\n function isInsideOverlay(node: Node | null): boolean {\n if (!node || !(node instanceof Element)) {\n return false;\n }\n return Boolean(\n node.closest(`#${OVERLAY_ROOT_ID}, #${EDIT_PANEL_HOST_ID}`),\n );\n }\n\n function clearHighlight(): void {\n if (highlighted) {\n highlighted.classList.remove(HIGHLIGHT_CLASS);\n highlighted = null;\n }\n tip.dataset.visible = \"false\";\n tip.textContent = \"\";\n }\n\n function positionTooltip(key: string, target: Element, clientX: number): void {\n tip.textContent = key;\n tip.dataset.visible = \"true\";\n\n const rect = target.getBoundingClientRect();\n const top = Math.min(rect.bottom + 8, window.innerHeight - 32);\n const left = Math.min(Math.max(clientX, 8), window.innerWidth - 200);\n tip.style.top = `${top}px`;\n tip.style.left = `${left}px`;\n }\n\n function applyHighlight(target: Element, key: string, clientX: number): void {\n if (highlighted !== target) {\n clearHighlight();\n highlighted = target;\n target.classList.add(HIGHLIGHT_CLASS);\n }\n positionTooltip(key, target, clientX);\n }\n\n function inspectTarget(target: Element | null, clientX: number, _clientY: number): void {\n if (!target || isInsideOverlay(target)) {\n clearHighlight();\n return;\n }\n\n const key = options.resolveKey(target);\n if (!key) {\n clearHighlight();\n return;\n }\n\n applyHighlight(target, key, clientX);\n }\n\n document.addEventListener(\n \"pointermove\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n inspectTarget(target, event.clientX, event.clientY);\n },\n { signal },\n );\n\n document.addEventListener(\n \"focusin\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target) {\n return;\n }\n const rect = target.getBoundingClientRect();\n inspectTarget(target, rect.left + rect.width / 2, rect.top);\n },\n { capture: true, signal },\n );\n\n document.addEventListener(\n \"click\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target || isInsideOverlay(target)) {\n return;\n }\n const key = options.resolveKey(target);\n if (!key) {\n return;\n }\n options.onActivateKey?.(key, target);\n },\n { capture: true, signal },\n );\n\n return {\n destroy: () => {\n abort.abort();\n clearHighlight();\n styleEl.remove();\n tip.remove();\n },\n };\n}\n","/**\n * Applies realtime gateway events to the in-memory SDK catalog (M2-SDK-04).\n */\nimport { patchCatalogEntry, reloadIfBundlePublishedNewer } from \"@stringpush/runtime-core\";\nimport type { RealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { parseRealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { notifyEditPanelRemoteUpdate } from \"./edit-panel-remote.js\";\n\nexport type RealtimeSyncContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n notifyTranslationsUpdated: () => void;\n};\n\nexport function handleRealtimeWireMessage(raw: string, context: RealtimeSyncContext): void {\n let event: RealtimeEvent;\n try {\n event = parseRealtimeEvent(raw);\n } catch {\n return;\n }\n\n void applyRealtimeEvent(event, context).catch(() => {\n // Intent: network errors on bundle reload must not surface as unhandled rejections.\n });\n}\n\nasync function applyRealtimeEvent(\n event: RealtimeEvent,\n context: RealtimeSyncContext,\n): Promise<void> {\n if (event.type === \"translation.updated\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n notifyEditPanelRemoteUpdate({\n keyPath: payload.keyPath,\n localeCode: payload.localeCode,\n value: payload.value,\n version: payload.version,\n });\n\n if (\n patchCatalogEntry(\n payload.keyPath,\n payload.localeCode,\n payload.value,\n payload.version,\n )\n ) {\n context.notifyTranslationsUpdated();\n }\n return;\n }\n\n if (event.type === \"bundle.published\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.applicationId !== context.applicationId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n const reloaded = await reloadIfBundlePublishedNewer(payload.releases);\n if (reloaded) {\n context.notifyTranslationsUpdated();\n }\n }\n}\n","/**\n * Exponential backoff for overlay WebSocket reconnect (M2-SDK-04).\n *\n * Intent: cap delay growth so transient disconnects recover without hammering the gateway.\n */\nexport const REALTIME_RECONNECT_INITIAL_MS = 1_000;\nexport const REALTIME_RECONNECT_MAX_MS = 30_000;\n\nexport function computeRealtimeReconnectDelayMs(\n attempt: number,\n initialMs = REALTIME_RECONNECT_INITIAL_MS,\n maxMs = REALTIME_RECONNECT_MAX_MS,\n): number {\n if (attempt <= 0) {\n return initialMs;\n }\n return Math.min(initialMs * 2 ** (attempt - 1), maxMs);\n}\n","/**\n * WebSocket client for the realtime gateway (M2-SDK-01, M2-SDK-04).\n *\n * Intent: lives only in the lazy overlay chunk — torn down by `disableEditMode()`; reconnects with backoff.\n */\nimport { handleRealtimeWireMessage, type RealtimeSyncContext } from \"./realtime-sync.js\";\nimport { computeRealtimeReconnectDelayMs } from \"./realtime-reconnect.js\";\n\nexport type RealtimeClientOptions = {\n realtimeUrl: string;\n editToken: string;\n projectId: string;\n sync: RealtimeSyncContext;\n};\n\nexport type RealtimeClient = {\n close: () => void;\n};\n\nexport function connectRealtime(options: RealtimeClientOptions): RealtimeClient | null {\n if (typeof globalThis === \"undefined\" || !(\"WebSocket\" in globalThis)) {\n return null;\n }\n\n const WebSocketCtor = (globalThis as typeof globalThis & { WebSocket: typeof WebSocket })\n .WebSocket;\n\n let intentionalClose = false;\n let reconnectAttempt = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let socket: WebSocket | null = null;\n\n const clearReconnectTimer = () => {\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n };\n\n const sendAuth = (ws: WebSocket) => {\n ws.send(\n JSON.stringify({\n type: \"auth\",\n token: options.editToken,\n projectId: options.projectId,\n }),\n );\n };\n\n const attachSocket = (ws: WebSocket) => {\n ws.addEventListener(\"open\", () => {\n reconnectAttempt = 0;\n sendAuth(ws);\n });\n\n ws.addEventListener(\"message\", (event) => {\n const raw = typeof event.data === \"string\" ? event.data : null;\n if (!raw) {\n return;\n }\n handleRealtimeWireMessage(raw, options.sync);\n });\n\n ws.addEventListener(\"close\", () => {\n if (socket === ws) {\n socket = null;\n }\n if (!intentionalClose) {\n scheduleReconnect();\n }\n });\n };\n\n const scheduleReconnect = () => {\n if (intentionalClose) {\n return;\n }\n clearReconnectTimer();\n reconnectAttempt += 1;\n const delayMs = computeRealtimeReconnectDelayMs(reconnectAttempt);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n openSocket();\n }, delayMs);\n };\n\n const openSocket = () => {\n if (intentionalClose) {\n return;\n }\n const ws = new WebSocketCtor(options.realtimeUrl);\n socket = ws;\n attachSocket(ws);\n };\n\n openSocket();\n\n return {\n close: () => {\n intentionalClose = true;\n clearReconnectTimer();\n if (\n socket &&\n (socket.readyState === WebSocketCtor.OPEN || socket.readyState === WebSocketCtor.CONNECTING)\n ) {\n socket.close();\n }\n socket = null;\n },\n };\n}\n","/**\n * Lazy-loaded overlay UI chunk (M2-SDK-01+).\n *\n * Intent: not imported by the default SDK entry — loaded via dynamic `import()` when edit mode starts.\n */\nimport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\nimport { closeEditPanel, openEditPanel } from \"./edit-panel.js\";\nimport { createHighlightController } from \"./highlights.js\";\nimport { connectRealtime } from \"./realtime-client.js\";\n\nexport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nexport type OverlayMountContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n realtimeUrl: string;\n editToken: string;\n apiBaseUrl: string;\n origin?: string;\n activeLocaleCode: string;\n resolveKey: (element: Element) => string | null;\n onCatalogPatched: (\n keyPath: string,\n localeCode: string,\n value: string,\n version: number,\n ) => void;\n onTranslationsUpdated: () => void;\n};\n\nexport type OverlayHandle = {\n destroy: () => void;\n};\n\n/**\n * Mounts overlay shell (shadow DOM), hover highlights, edit panel, and realtime WebSocket.\n */\nexport function mountOverlay(context: OverlayMountContext): OverlayHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(OVERLAY_ROOT_ID)?.remove();\n closeEditPanel();\n\n const mountHost = document.createElement(\"div\");\n mountHost.id = OVERLAY_ROOT_ID;\n mountHost.setAttribute(\"data-translation-overlay\", \"true\");\n\n const shadow = mountHost.attachShadow({ mode: \"open\" });\n const label = document.createElement(\"div\");\n label.setAttribute(\"data-overlay-marker\", OVERLAY_CHUNK_MARKER);\n label.textContent = \"Translation edit mode — click text to edit\";\n label.style.cssText = [\n \"position:fixed\",\n \"bottom:16px\",\n \"right:16px\",\n \"z-index:2147483646\",\n \"padding:8px 12px\",\n \"border-radius:8px\",\n \"font:600 13px/1.4 system-ui,sans-serif\",\n \"color:#fff\",\n \"background:#1e3a5f\",\n \"box-shadow:0 4px 12px rgba(0,0,0,.25)\",\n \"pointer-events:none\",\n ].join(\";\");\n\n shadow.append(label);\n document.body.append(mountHost);\n\n let panelHandle: { destroy: () => void } | null = null;\n\n const highlights = createHighlightController({\n resolveKey: context.resolveKey,\n onActivateKey: (keyPath, element) => {\n panelHandle?.destroy();\n const suggestedDefaultMessage = element.textContent?.trim() || undefined;\n panelHandle = openEditPanel(keyPath, {\n apiBaseUrl: context.apiBaseUrl,\n editToken: context.editToken,\n origin: context.origin,\n activeLocaleCode: context.activeLocaleCode,\n suggestedDefaultMessage,\n onSaved: context.onCatalogPatched,\n });\n },\n });\n\n const realtime = connectRealtime({\n realtimeUrl: context.realtimeUrl,\n editToken: context.editToken,\n projectId: context.projectId,\n sync: {\n projectId: context.projectId,\n applicationId: context.applicationId,\n environmentId: context.environmentId,\n notifyTranslationsUpdated: context.onTranslationsUpdated,\n },\n });\n\n return {\n destroy: () => {\n panelHandle?.destroy();\n highlights.destroy();\n realtime?.close();\n mountHost.remove();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/overlay-MLOXYRPA.umd.cjs","../src/overlay/constants.ts","../src/overlay/overlay-api.ts","../src/overlay/locale-display.ts","../src/overlay/edit-panel.ts","../src/overlay/edit-panel-remote.ts","../src/overlay/highlights.ts","../src/overlay/realtime-sync.ts","../src/overlay/realtime-reconnect.ts","../src/overlay/realtime-client.ts","../src/overlay/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/translations/translations/packages/sdk/dist/overlay-MLOXYRPA.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded overlay chunk.\n *\n * Intent: stable ids/markers isolate overlay CSS/DOM and keep the main SDK chunk small.\n */\nexport const OVERLAY_ROOT_ID = \"translation-overlay-root\";\nexport const EDIT_PANEL_HOST_ID = \"translation-edit-panel-host\";\n/** Marker string used in bundle-split tests — must not appear in the main SDK chunk. */\nexport const OVERLAY_CHUNK_MARKER = \"translation-overlay-chunk-v1\";\n","/**\n * Overlay HTTP client (edit JWT) for locales, key values, and saves (M2-SDK-03).\n *\n * Intent: thin fetch wrapper shared by edit panel; maps 409 VERSION_CONFLICT for conflict UI.\n */\nimport { resolveRequestOrigin } from \"@stringpush/runtime-core\";\n\nexport type OverlayLocale = {\n id: string;\n code: string;\n};\n\nexport type OverlayKeyValueEntry = {\n localeId: string;\n localeCode: string;\n value: string;\n version: number | null;\n};\n\nexport type OverlayKeyValuesResponse = {\n keyId: string;\n keyPath: string;\n defaultMessage: string | null;\n entries: OverlayKeyValueEntry[];\n};\n\nexport type OverlayTranslationValue = {\n id: string;\n translationKeyId: string;\n localeId: string;\n environmentId: string;\n value: string;\n status: string;\n version: number;\n};\n\nexport type OverlayApiConfig = {\n apiBaseUrl: string;\n editToken: string;\n origin?: string;\n fetch?: typeof fetch;\n};\n\nexport class OverlayApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly body: unknown,\n ) {\n super(message);\n this.name = \"OverlayApiError\";\n }\n\n get isVersionConflict(): boolean {\n return (\n this.status === 409 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"VERSION_CONFLICT\"\n );\n }\n\n get isKeyNotFound(): boolean {\n return (\n this.status === 404 &&\n typeof this.body === \"object\" &&\n this.body !== null &&\n \"error\" in this.body &&\n (this.body as { error?: { code?: string } }).error?.code === \"NOT_FOUND\"\n );\n }\n}\n\nfunction overlayHeaders(config: OverlayApiConfig): HeadersInit {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.editToken}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n const origin = resolveRequestOrigin(config.origin);\n if (origin) {\n headers.Origin = origin;\n }\n return headers;\n}\n\nasync function overlayFetch(\n config: OverlayApiConfig,\n path: string,\n init: RequestInit = {},\n): Promise<Response> {\n const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n const url = `${config.apiBaseUrl.replace(/\\/$/, \"\")}${path}`;\n return fetchFn(url, {\n ...init,\n headers: {\n ...overlayHeaders(config),\n ...(init.headers as Record<string, string> | undefined),\n },\n });\n}\n\nasync function parseJson<T>(response: Response): Promise<T> {\n return (await response.json()) as T;\n}\n\nexport async function fetchOverlayLocales(config: OverlayApiConfig): Promise<OverlayLocale[]> {\n const response = await overlayFetch(config, \"/v1/overlay/locales\");\n const body = await parseJson<{ data: Array<{ id: string; code: string }> } | { error: unknown }>(\n response,\n );\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load locales (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return (body as { data: Array<{ id: string; code: string }> }).data.map((row) => ({\n id: row.id,\n code: row.code,\n }));\n}\n\nexport async function fetchOverlayKeyValues(\n config: OverlayApiConfig,\n keyPath: string,\n): Promise<OverlayKeyValuesResponse> {\n const params = new URLSearchParams({ key: keyPath });\n const response = await overlayFetch(config, `/v1/overlay/keys/values?${params}`);\n const body = await parseJson<OverlayKeyValuesResponse | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to load key values (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayKeyValuesResponse;\n}\n\nexport async function createOverlayKey(\n config: OverlayApiConfig,\n input: { keyPath: string; defaultMessage?: string | null },\n): Promise<{ id: string; keyPath: string }> {\n const response = await overlayFetch(config, \"/v1/overlay/keys\", {\n method: \"POST\",\n body: JSON.stringify({\n keyPath: input.keyPath,\n defaultMessage: input.defaultMessage ?? null,\n }),\n });\n\n const body = await parseJson<\n | { id: string; key: string; namespace: string }\n | { error: unknown }\n >(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to create translation key (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n const record = body as { id: string; key: string; namespace: string };\n const keyPath = record.namespace ? `${record.namespace}.${record.key}` : record.key;\n return { id: record.id, keyPath };\n}\n\nexport async function patchOverlayValue(\n config: OverlayApiConfig,\n input: { keyId: string; localeId: string; value: string; version: number | null },\n): Promise<OverlayTranslationValue> {\n const headers: Record<string, string> = {};\n if (input.version !== null) {\n headers[\"If-Match\"] = String(input.version);\n }\n\n const response = await overlayFetch(config, \"/v1/overlay/values\", {\n method: \"PATCH\",\n headers,\n body: JSON.stringify({\n keyId: input.keyId,\n localeId: input.localeId,\n value: input.value,\n }),\n });\n\n const body = await parseJson<OverlayTranslationValue | { error: unknown }>(response);\n if (!response.ok) {\n throw new OverlayApiError(\n `Failed to save translation (HTTP ${response.status})`,\n response.status,\n body,\n );\n }\n return body as OverlayTranslationValue;\n}\n","const LOCALE_FLAGS: Record<string, string> = {\n en: \"🇺🇸\",\n fr: \"🇫🇷\",\n de: \"🇩🇪\",\n \"de-DE\": \"🇩🇪\",\n es: \"🇪🇸\",\n};\n\nconst LOCALE_NAMES: Record<string, string> = {\n en: \"English\",\n fr: \"French\",\n de: \"German\",\n \"de-DE\": \"German\",\n es: \"Spanish\",\n};\n\nexport function overlayLocaleFlag(code: string): string {\n return LOCALE_FLAGS[code] ?? \"🌐\";\n}\n\nexport function overlayLocaleLabel(code: string): string {\n const name = LOCALE_NAMES[code] ?? code.toUpperCase();\n return `${name} (${code})`;\n}\n","/**\n * Side panel UI to edit all locales for one translation key (M2-SDK-03).\n *\n * Intent: opened from overlay click; loads locales/values via edit JWT; saves with If-Match and surfaces 409 reload.\n */\nimport {\n createOverlayKey,\n fetchOverlayKeyValues,\n fetchOverlayLocales,\n OverlayApiError,\n patchOverlayValue,\n type OverlayApiConfig,\n type OverlayKeyValueEntry,\n} from \"./overlay-api.js\";\nimport { overlayLocaleFlag, overlayLocaleLabel } from \"./locale-display.js\";\nimport { recordTranslationVersion, shouldApplyTranslationUpdate } from \"@stringpush/runtime-core\";\nimport { EDIT_PANEL_HOST_ID } from \"./constants.js\";\nimport {\n registerEditPanelRemoteHandler,\n type RemoteTranslationUpdate,\n} from \"./edit-panel-remote.js\";\n\nconst PANEL_STYLES = `\n:host {\n all: initial;\n font-family: system-ui, -apple-system, Segoe UI, sans-serif;\n --sp-bg: #ffffff;\n --sp-text: #0f172a;\n --sp-muted: #64748b;\n --sp-border: #e2e8f0;\n --sp-accent: #4338ca;\n --sp-accent-hover: #3730a3;\n --sp-warning-bg: #fef3c7;\n --sp-warning-text: #92400e;\n --sp-warning-border: #fcd34d;\n --sp-badge-draft-bg: #fef3c6;\n --sp-badge-draft-text: #973c00;\n --sp-badge-default-bg: #f1f5f9;\n --sp-badge-default-text: #475569;\n}\n.panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n max-width: 100vw;\n height: 100vh;\n background: var(--sp-bg);\n color: var(--sp-text);\n box-shadow: -8px 0 32px rgba(15, 23, 42, 0.12);\n display: flex;\n flex-direction: column;\n z-index: 2147483647;\n}\n.header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--sp-border);\n}\n.header-main {\n min-width: 0;\n}\n.brand {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--sp-accent);\n}\n.title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n}\n.key-path {\n margin: 4px 0 0;\n font: 12px/1.4 ui-monospace, Menlo, monospace;\n color: var(--sp-muted);\n word-break: break-all;\n}\n.close-btn {\n border: none;\n background: transparent;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n color: var(--sp-muted);\n padding: 2px 4px;\n}\n.body {\n flex: 1;\n overflow: auto;\n padding: 12px 16px;\n}\n.locale-row {\n margin-bottom: 16px;\n padding-bottom: 16px;\n border-bottom: 1px solid var(--sp-border);\n}\n.locale-row:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n}\n.locale-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n.locale-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: var(--sp-text);\n}\n.locale-flag {\n font-size: 15px;\n line-height: 1;\n}\n.locale-badges {\n display: inline-flex;\n gap: 4px;\n}\n.badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n}\n.badge-default {\n background: var(--sp-badge-default-bg);\n color: var(--sp-badge-default-text);\n}\n.badge-draft {\n background: var(--sp-badge-draft-bg);\n color: var(--sp-badge-draft-text);\n}\n.locale-row textarea {\n width: 100%;\n min-height: 80px;\n box-sizing: border-box;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n padding: 10px 12px;\n font: 14px/1.45 inherit;\n resize: vertical;\n color: var(--sp-text);\n}\n.locale-row textarea:focus-visible {\n outline: 2px solid var(--sp-accent);\n outline-offset: 1px;\n border-color: var(--sp-accent);\n}\n.banner {\n margin: 0 16px 12px;\n padding: 12px 14px;\n border-radius: 8px;\n border: 1px solid var(--sp-warning-border);\n background: var(--sp-warning-bg);\n color: var(--sp-warning-text);\n font-size: 13px;\n line-height: 1.45;\n}\n.banner p {\n margin: 0;\n}\n.banner button {\n margin-top: 10px;\n border: 1px solid #d97706;\n background: #fff;\n border-radius: 6px;\n padding: 6px 12px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--sp-warning-text);\n}\n.footer {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n padding: 12px 16px 16px;\n border-top: 1px solid var(--sp-border);\n}\n.footer button {\n border-radius: 8px;\n padding: 9px 14px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.footer .secondary {\n border: 1px solid #cbd5e1;\n background: #fff;\n color: #334155;\n}\n.footer .primary {\n border: none;\n background: var(--sp-accent);\n color: #fff;\n}\n.footer .primary:hover:not(:disabled) {\n background: var(--sp-accent-hover);\n}\n.footer .primary::before {\n content: \"\";\n width: 14px;\n height: 14px;\n background: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M11.667 3.5 5.25 9.917 2.333 7' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n center / contain no-repeat;\n}\n.status {\n padding: 24px 16px;\n color: var(--sp-muted);\n font-size: 14px;\n}\n.create-panel {\n padding: 8px 0;\n}\n.create-panel p {\n margin: 0 0 12px;\n font-size: 14px;\n color: #334155;\n line-height: 1.5;\n}\n.create-panel label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #334155;\n margin-bottom: 6px;\n}\n.create-panel input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n padding: 8px 10px;\n font: 14px/1.4 inherit;\n margin-bottom: 12px;\n}\n.create-panel .create-btn {\n border: none;\n border-radius: 8px;\n padding: 9px 14px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n background: var(--sp-accent);\n color: #fff;\n}\n.create-panel .error {\n margin: 0 0 12px;\n color: #b91c1c;\n font-size: 13px;\n}\n`;\n\nexport type EditPanelContext = OverlayApiConfig & {\n activeLocaleCode: string;\n suggestedDefaultMessage?: string;\n onSaved: (keyPath: string, localeCode: string, value: string, version: number) => void;\n};\n\nexport type EditPanelHandle = {\n destroy: () => void;\n};\n\ntype LocaleRowState = OverlayKeyValueEntry & {\n draftValue: string;\n};\n\nexport function openEditPanel(keyPath: string, context: EditPanelContext): EditPanelHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n closeEditPanel();\n\n const host = document.createElement(\"div\");\n host.id = EDIT_PANEL_HOST_ID;\n const shadow = host.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = PANEL_STYLES;\n\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.setAttribute(\"role\", \"dialog\");\n panel.setAttribute(\"aria-label\", \"Edit translation\");\n\n const header = document.createElement(\"header\");\n header.className = \"header\";\n const headerMain = document.createElement(\"div\");\n headerMain.className = \"header-main\";\n const brand = document.createElement(\"p\");\n brand.className = \"brand\";\n brand.textContent = \"StringPush\";\n const title = document.createElement(\"h2\");\n title.className = \"title\";\n title.textContent = \"Edit translation\";\n const keyEl = document.createElement(\"p\");\n keyEl.className = \"key-path\";\n keyEl.textContent = keyPath;\n headerMain.append(brand, title, keyEl);\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"close-btn\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n closeBtn.textContent = \"×\";\n header.append(headerMain, closeBtn);\n\n const bodyEl = document.createElement(\"div\");\n bodyEl.className = \"body\";\n\n const status = document.createElement(\"div\");\n status.className = \"status\";\n status.textContent = \"Loading…\";\n bodyEl.append(status);\n\n const banner = document.createElement(\"div\");\n banner.className = \"banner\";\n banner.hidden = true;\n\n const footer = document.createElement(\"footer\");\n footer.className = \"footer\";\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"secondary\";\n cancelBtn.textContent = \"Cancel\";\n const saveBtn = document.createElement(\"button\");\n saveBtn.type = \"button\";\n saveBtn.className = \"primary\";\n saveBtn.textContent = \"Save draft\";\n saveBtn.disabled = true;\n footer.append(cancelBtn, saveBtn);\n\n panel.append(header, banner, bodyEl, footer);\n shadow.append(style, panel);\n document.body.append(host);\n\n let destroyed = false;\n let rows: LocaleRowState[] = [];\n let keyId = \"\";\n\n const destroy = () => {\n if (destroyed) {\n return;\n }\n destroyed = true;\n registerEditPanelRemoteHandler(null);\n host.remove();\n };\n\n function applyRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!shouldApplyTranslationUpdate(update.keyPath, update.localeCode, update.version)) {\n return false;\n }\n\n const row = rows.find((entry) => entry.localeCode === update.localeCode);\n if (!row) {\n return false;\n }\n\n if ((row.version ?? 0) > update.version) {\n return false;\n }\n\n if (row.draftValue !== row.value) {\n showConflict(\n `${update.localeCode} was updated in another tab. Reload to merge your changes.`,\n );\n return true;\n }\n\n row.value = update.value;\n row.draftValue = update.value;\n row.version = update.version;\n recordTranslationVersion(update.keyPath, update.localeCode, update.version);\n renderRows();\n return true;\n }\n\n closeBtn.addEventListener(\"click\", destroy);\n cancelBtn.addEventListener(\"click\", destroy);\n\n function renderRows(): void {\n bodyEl.replaceChildren();\n for (const row of rows) {\n const wrap = document.createElement(\"div\");\n wrap.className = \"locale-row\";\n\n const localeHeader = document.createElement(\"div\");\n localeHeader.className = \"locale-header\";\n\n const label = document.createElement(\"span\");\n label.className = \"locale-label\";\n const flag = document.createElement(\"span\");\n flag.className = \"locale-flag\";\n flag.textContent = overlayLocaleFlag(row.localeCode);\n flag.setAttribute(\"aria-hidden\", \"true\");\n label.append(flag, document.createTextNode(overlayLocaleLabel(row.localeCode)));\n\n const badges = document.createElement(\"span\");\n badges.className = \"locale-badges\";\n if (row.localeCode === context.activeLocaleCode) {\n const defaultBadge = document.createElement(\"span\");\n defaultBadge.className = \"badge badge-default\";\n defaultBadge.textContent = \"Default\";\n badges.append(defaultBadge);\n }\n const draftBadge = document.createElement(\"span\");\n draftBadge.className = \"badge badge-draft\";\n draftBadge.textContent = row.draftValue !== row.value ? \"Unsaved\" : \"Draft\";\n badges.append(draftBadge);\n\n localeHeader.append(label, badges);\n\n const textarea = document.createElement(\"textarea\");\n textarea.id = `locale-${row.localeId}`;\n textarea.setAttribute(\"aria-label\", `${row.localeCode} translation`);\n textarea.value = row.draftValue;\n textarea.addEventListener(\"input\", () => {\n row.draftValue = textarea.value;\n draftBadge.textContent = row.draftValue !== row.value ? \"Unsaved\" : \"Draft\";\n });\n\n wrap.append(localeHeader, textarea);\n bodyEl.append(wrap);\n }\n saveBtn.disabled = false;\n }\n\n async function load(): Promise<void> {\n try {\n const [locales, keyValues] = await Promise.all([\n fetchOverlayLocales(context),\n fetchOverlayKeyValues(context, keyPath),\n ]);\n\n if (destroyed) {\n return;\n }\n\n keyId = keyValues.keyId;\n const localeCodes = new Set(locales.map((l) => l.code));\n rows = keyValues.entries\n .filter((entry) => localeCodes.has(entry.localeCode))\n .map((entry) => ({\n ...entry,\n draftValue: entry.value,\n }));\n\n for (const row of rows) {\n if (row.version !== null) {\n recordTranslationVersion(keyPath, row.localeCode, row.version);\n }\n }\n\n banner.hidden = true;\n footer.hidden = false;\n renderRows();\n } catch (error) {\n if (error instanceof OverlayApiError && error.isKeyNotFound) {\n showCreateKeyPrompt();\n return;\n }\n status.textContent =\n error instanceof Error ? error.message : \"Failed to load translations\";\n }\n }\n\n function showCreateKeyPrompt(): void {\n footer.hidden = true;\n bodyEl.replaceChildren();\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"create-panel\";\n\n const intro = document.createElement(\"p\");\n intro.textContent =\n \"This key is not in your project yet. Create it to start editing translations for all locales.\";\n\n const label = document.createElement(\"label\");\n label.textContent = \"Default message (optional)\";\n label.htmlFor = \"create-default-message\";\n\n const input = document.createElement(\"input\");\n input.id = \"create-default-message\";\n input.type = \"text\";\n input.placeholder = \"Text shown when no translation is saved\";\n if (context.suggestedDefaultMessage) {\n input.value = context.suggestedDefaultMessage;\n }\n\n const errorEl = document.createElement(\"p\");\n errorEl.className = \"error\";\n errorEl.hidden = true;\n\n const createBtn = document.createElement(\"button\");\n createBtn.type = \"button\";\n createBtn.className = \"create-btn\";\n createBtn.textContent = \"Create key\";\n\n createBtn.addEventListener(\"click\", () => {\n void (async () => {\n createBtn.disabled = true;\n errorEl.hidden = true;\n\n try {\n await createOverlayKey(context, {\n keyPath,\n defaultMessage: input.value.trim() || null,\n });\n if (destroyed) {\n return;\n }\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n footer.hidden = false;\n saveBtn.disabled = true;\n await load();\n } catch (createError) {\n createBtn.disabled = false;\n errorEl.hidden = false;\n errorEl.textContent =\n createError instanceof Error ? createError.message : \"Failed to create key\";\n }\n })();\n });\n\n wrap.append(intro, label, input, errorEl, createBtn);\n bodyEl.append(wrap);\n }\n\n function showConflict(message: string): void {\n banner.hidden = false;\n banner.replaceChildren();\n const text = document.createElement(\"p\");\n text.textContent = message;\n banner.append(text);\n const reloadBtn = document.createElement(\"button\");\n reloadBtn.type = \"button\";\n reloadBtn.textContent = \"Reload\";\n reloadBtn.addEventListener(\"click\", () => {\n status.textContent = \"Loading…\";\n bodyEl.replaceChildren(status);\n saveBtn.disabled = true;\n void load();\n });\n banner.append(reloadBtn);\n }\n\n saveBtn.addEventListener(\"click\", () => {\n void (async () => {\n saveBtn.disabled = true;\n let hadConflict = false;\n\n for (const row of rows) {\n if (row.draftValue === row.value) {\n continue;\n }\n\n try {\n const saved = await patchOverlayValue(context, {\n keyId,\n localeId: row.localeId,\n value: row.draftValue,\n version: row.version,\n });\n row.value = saved.value;\n row.version = saved.version;\n row.draftValue = saved.value;\n context.onSaved(keyPath, row.localeCode, saved.value, saved.version);\n } catch (error) {\n if (error instanceof OverlayApiError && error.isVersionConflict) {\n hadConflict = true;\n showConflict(\n \"Someone else updated this key. Reload to fetch the latest versions, then try again.\",\n );\n break;\n }\n showConflict(error instanceof Error ? error.message : \"Save failed\");\n hadConflict = true;\n break;\n }\n }\n\n saveBtn.disabled = hadConflict;\n if (!hadConflict) {\n destroy();\n }\n })();\n });\n\n registerEditPanelRemoteHandler({ keyPath, applyRemoteUpdate });\n\n void load();\n\n return { destroy };\n}\n\nexport function closeEditPanel(): void {\n registerEditPanelRemoteHandler(null);\n document.getElementById(EDIT_PANEL_HOST_ID)?.remove();\n}\n","/**\n * Routes realtime `translation.updated` events to the open edit panel (M2-SDK-04).\n *\n * Intent: single active handler per key so concurrent editors see live value/version without full reload.\n */\nexport type RemoteTranslationUpdate = {\n keyPath: string;\n localeCode: string;\n value: string;\n version: number;\n};\n\nexport type EditPanelRemoteHandler = {\n keyPath: string;\n applyRemoteUpdate: (update: RemoteTranslationUpdate) => boolean;\n};\n\nlet activeHandler: EditPanelRemoteHandler | null = null;\n\nexport function registerEditPanelRemoteHandler(handler: EditPanelRemoteHandler | null): void {\n activeHandler = handler;\n}\n\nexport function notifyEditPanelRemoteUpdate(update: RemoteTranslationUpdate): boolean {\n if (!activeHandler || activeHandler.keyPath !== update.keyPath) {\n return false;\n }\n return activeHandler.applyRemoteUpdate(update);\n}\n","/**\n * Hover/focus highlights for translatable DOM nodes in edit mode (M2-SDK-02).\n *\n * Intent: pointer-events stay on the host page — only outlines and a read-only key tooltip are added.\n */\nimport { EDIT_PANEL_HOST_ID, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nconst HIGHLIGHT_CLASS = \"translation-sdk-target-highlight\";\nconst STYLES_ID = \"translation-sdk-highlight-styles\";\nconst TOOLTIP_ID = \"translation-sdk-key-tooltip\";\n\nconst HIGHLIGHT_CSS = `\n.${HIGHLIGHT_CLASS} {\n outline: 2px solid #2563eb !important;\n outline-offset: 2px !important;\n box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.28) !important;\n}\n.${HIGHLIGHT_CLASS}:focus-visible {\n outline: 3px solid #1d4ed8 !important;\n outline-offset: 2px !important;\n}\n#${TOOLTIP_ID} {\n position: fixed;\n z-index: 2147483645;\n max-width: min(360px, 90vw);\n padding: 4px 8px;\n border-radius: 6px;\n font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #f8fafc;\n background: #0f172a;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.28);\n pointer-events: none;\n display: none;\n}\n#${TOOLTIP_ID}[data-visible=\"true\"] {\n display: block;\n}\n`;\n\nexport type HighlightControllerOptions = {\n resolveKey: (element: Element) => string | null;\n onActivateKey?: (keyPath: string, element: Element) => void;\n};\n\nexport type HighlightController = {\n destroy: () => void;\n};\n\nexport function createHighlightController(\n options: HighlightControllerOptions,\n): HighlightController {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n const abort = new AbortController();\n const { signal } = abort;\n\n let highlighted: Element | null = null;\n\n const styleEl = document.createElement(\"style\");\n styleEl.id = STYLES_ID;\n styleEl.textContent = HIGHLIGHT_CSS;\n document.head.append(styleEl);\n\n const tip = document.createElement(\"div\");\n tip.id = TOOLTIP_ID;\n document.body.append(tip);\n\n function isInsideOverlay(node: Node | null): boolean {\n if (!node || !(node instanceof Element)) {\n return false;\n }\n return Boolean(\n node.closest(`#${OVERLAY_ROOT_ID}, #${EDIT_PANEL_HOST_ID}`),\n );\n }\n\n function clearHighlight(): void {\n if (highlighted) {\n highlighted.classList.remove(HIGHLIGHT_CLASS);\n highlighted = null;\n }\n tip.dataset.visible = \"false\";\n tip.textContent = \"\";\n }\n\n function positionTooltip(key: string, target: Element, clientX: number): void {\n tip.textContent = key;\n tip.dataset.visible = \"true\";\n\n const rect = target.getBoundingClientRect();\n const top = Math.min(rect.bottom + 8, window.innerHeight - 32);\n const left = Math.min(Math.max(clientX, 8), window.innerWidth - 200);\n tip.style.top = `${top}px`;\n tip.style.left = `${left}px`;\n }\n\n function applyHighlight(target: Element, key: string, clientX: number): void {\n if (highlighted !== target) {\n clearHighlight();\n highlighted = target;\n target.classList.add(HIGHLIGHT_CLASS);\n }\n positionTooltip(key, target, clientX);\n }\n\n function inspectTarget(target: Element | null, clientX: number, _clientY: number): void {\n if (!target || isInsideOverlay(target)) {\n clearHighlight();\n return;\n }\n\n const key = options.resolveKey(target);\n if (!key) {\n clearHighlight();\n return;\n }\n\n applyHighlight(target, key, clientX);\n }\n\n document.addEventListener(\n \"pointermove\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n inspectTarget(target, event.clientX, event.clientY);\n },\n { signal },\n );\n\n document.addEventListener(\n \"focusin\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target) {\n return;\n }\n const rect = target.getBoundingClientRect();\n inspectTarget(target, rect.left + rect.width / 2, rect.top);\n },\n { capture: true, signal },\n );\n\n document.addEventListener(\n \"click\",\n (event) => {\n const target = event.target instanceof Element ? event.target : null;\n if (!target || isInsideOverlay(target)) {\n return;\n }\n const key = options.resolveKey(target);\n if (!key) {\n return;\n }\n options.onActivateKey?.(key, target);\n },\n { capture: true, signal },\n );\n\n return {\n destroy: () => {\n abort.abort();\n clearHighlight();\n styleEl.remove();\n tip.remove();\n },\n };\n}\n","/**\n * Applies realtime gateway events to the in-memory SDK catalog (M2-SDK-04).\n */\nimport { patchCatalogEntry, reloadIfBundlePublishedNewer } from \"@stringpush/runtime-core\";\nimport type { RealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { parseRealtimeEvent } from \"@stringpush/realtime-protocol\";\nimport { notifyEditPanelRemoteUpdate } from \"./edit-panel-remote.js\";\n\nexport type RealtimeSyncContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n notifyTranslationsUpdated: () => void;\n};\n\nexport function handleRealtimeWireMessage(raw: string, context: RealtimeSyncContext): void {\n let event: RealtimeEvent;\n try {\n event = parseRealtimeEvent(raw);\n } catch {\n return;\n }\n\n void applyRealtimeEvent(event, context).catch(() => {\n // Intent: network errors on bundle reload must not surface as unhandled rejections.\n });\n}\n\nasync function applyRealtimeEvent(\n event: RealtimeEvent,\n context: RealtimeSyncContext,\n): Promise<void> {\n if (event.type === \"translation.updated\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n notifyEditPanelRemoteUpdate({\n keyPath: payload.keyPath,\n localeCode: payload.localeCode,\n value: payload.value,\n version: payload.version,\n });\n\n if (\n patchCatalogEntry(\n payload.keyPath,\n payload.localeCode,\n payload.value,\n payload.version,\n )\n ) {\n context.notifyTranslationsUpdated();\n }\n return;\n }\n\n if (event.type === \"bundle.published\") {\n const { payload } = event;\n if (\n payload.projectId !== context.projectId ||\n payload.applicationId !== context.applicationId ||\n payload.environmentId !== context.environmentId\n ) {\n return;\n }\n\n const reloaded = await reloadIfBundlePublishedNewer(payload.releases);\n if (reloaded) {\n context.notifyTranslationsUpdated();\n }\n }\n}\n","/**\n * Exponential backoff for overlay WebSocket reconnect (M2-SDK-04).\n *\n * Intent: cap delay growth so transient disconnects recover without hammering the gateway.\n */\nexport const REALTIME_RECONNECT_INITIAL_MS = 1_000;\nexport const REALTIME_RECONNECT_MAX_MS = 30_000;\n\nexport function computeRealtimeReconnectDelayMs(\n attempt: number,\n initialMs = REALTIME_RECONNECT_INITIAL_MS,\n maxMs = REALTIME_RECONNECT_MAX_MS,\n): number {\n if (attempt <= 0) {\n return initialMs;\n }\n return Math.min(initialMs * 2 ** (attempt - 1), maxMs);\n}\n","/**\n * WebSocket client for the realtime gateway (M2-SDK-01, M2-SDK-04).\n *\n * Intent: lives only in the lazy overlay chunk — torn down by `disableEditMode()`; reconnects with backoff.\n */\nimport { handleRealtimeWireMessage, type RealtimeSyncContext } from \"./realtime-sync.js\";\nimport { computeRealtimeReconnectDelayMs } from \"./realtime-reconnect.js\";\n\nexport type RealtimeClientOptions = {\n realtimeUrl: string;\n editToken: string;\n projectId: string;\n sync: RealtimeSyncContext;\n};\n\nexport type RealtimeClient = {\n close: () => void;\n};\n\nexport function connectRealtime(options: RealtimeClientOptions): RealtimeClient | null {\n if (typeof globalThis === \"undefined\" || !(\"WebSocket\" in globalThis)) {\n return null;\n }\n\n const WebSocketCtor = (globalThis as typeof globalThis & { WebSocket: typeof WebSocket })\n .WebSocket;\n\n let intentionalClose = false;\n let reconnectAttempt = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let socket: WebSocket | null = null;\n\n const clearReconnectTimer = () => {\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n };\n\n const sendAuth = (ws: WebSocket) => {\n ws.send(\n JSON.stringify({\n type: \"auth\",\n token: options.editToken,\n projectId: options.projectId,\n }),\n );\n };\n\n const attachSocket = (ws: WebSocket) => {\n ws.addEventListener(\"open\", () => {\n reconnectAttempt = 0;\n sendAuth(ws);\n });\n\n ws.addEventListener(\"message\", (event) => {\n const raw = typeof event.data === \"string\" ? event.data : null;\n if (!raw) {\n return;\n }\n handleRealtimeWireMessage(raw, options.sync);\n });\n\n ws.addEventListener(\"close\", () => {\n if (socket === ws) {\n socket = null;\n }\n if (!intentionalClose) {\n scheduleReconnect();\n }\n });\n };\n\n const scheduleReconnect = () => {\n if (intentionalClose) {\n return;\n }\n clearReconnectTimer();\n reconnectAttempt += 1;\n const delayMs = computeRealtimeReconnectDelayMs(reconnectAttempt);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n openSocket();\n }, delayMs);\n };\n\n const openSocket = () => {\n if (intentionalClose) {\n return;\n }\n const ws = new WebSocketCtor(options.realtimeUrl);\n socket = ws;\n attachSocket(ws);\n };\n\n openSocket();\n\n return {\n close: () => {\n intentionalClose = true;\n clearReconnectTimer();\n if (\n socket &&\n (socket.readyState === WebSocketCtor.OPEN || socket.readyState === WebSocketCtor.CONNECTING)\n ) {\n socket.close();\n }\n socket = null;\n },\n };\n}\n","/**\n * Lazy-loaded overlay UI chunk (M2-SDK-01+).\n *\n * Intent: not imported by the default SDK entry — loaded via dynamic `import()` when edit mode starts.\n */\nimport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\nimport { closeEditPanel, openEditPanel } from \"./edit-panel.js\";\nimport { createHighlightController } from \"./highlights.js\";\nimport { connectRealtime } from \"./realtime-client.js\";\n\nexport { OVERLAY_CHUNK_MARKER, OVERLAY_ROOT_ID } from \"./constants.js\";\n\nexport type OverlayMountContext = {\n projectId: string;\n applicationId: string;\n environmentId: string;\n realtimeUrl: string;\n editToken: string;\n apiBaseUrl: string;\n origin?: string;\n activeLocaleCode: string;\n resolveKey: (element: Element) => string | null;\n onCatalogPatched: (\n keyPath: string,\n localeCode: string,\n value: string,\n version: number,\n ) => void;\n onTranslationsUpdated: () => void;\n};\n\nexport type OverlayHandle = {\n destroy: () => void;\n};\n\n/**\n * Mounts overlay shell (shadow DOM), hover highlights, edit panel, and realtime WebSocket.\n */\nexport function mountOverlay(context: OverlayMountContext): OverlayHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(OVERLAY_ROOT_ID)?.remove();\n closeEditPanel();\n\n const mountHost = document.createElement(\"div\");\n mountHost.id = OVERLAY_ROOT_ID;\n mountHost.setAttribute(\"data-translation-overlay\", \"true\");\n\n const shadow = mountHost.attachShadow({ mode: \"open\" });\n const label = document.createElement(\"div\");\n label.setAttribute(\"data-overlay-marker\", OVERLAY_CHUNK_MARKER);\n label.textContent = \"Translation edit mode — click text to edit\";\n label.style.cssText = [\n \"position:fixed\",\n \"bottom:16px\",\n \"right:16px\",\n \"z-index:2147483646\",\n \"padding:8px 12px\",\n \"border-radius:8px\",\n \"font:600 13px/1.4 system-ui,sans-serif\",\n \"color:#fff\",\n \"background:#1e3a5f\",\n \"box-shadow:0 4px 12px rgba(0,0,0,.25)\",\n \"pointer-events:none\",\n ].join(\";\");\n\n shadow.append(label);\n document.body.append(mountHost);\n\n let panelHandle: { destroy: () => void } | null = null;\n\n const highlights = createHighlightController({\n resolveKey: context.resolveKey,\n onActivateKey: (keyPath, element) => {\n panelHandle?.destroy();\n const suggestedDefaultMessage = element.textContent?.trim() || undefined;\n panelHandle = openEditPanel(keyPath, {\n apiBaseUrl: context.apiBaseUrl,\n editToken: context.editToken,\n origin: context.origin,\n activeLocaleCode: context.activeLocaleCode,\n suggestedDefaultMessage,\n onSaved: context.onCatalogPatched,\n });\n },\n });\n\n const realtime = connectRealtime({\n realtimeUrl: context.realtimeUrl,\n editToken: context.editToken,\n projectId: context.projectId,\n sync: {\n projectId: context.projectId,\n applicationId: context.applicationId,\n environmentId: context.environmentId,\n notifyTranslationsUpdated: context.onTranslationsUpdated,\n },\n });\n\n return {\n destroy: () => {\n panelHandle?.destroy();\n highlights.destroy();\n realtime?.close();\n mountHost.remove();\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stringpush/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Plain JS SDK for in-context translation",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"files": [
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"@stringpush/realtime-protocol": "0.1.0",
|
|
35
|
-
"@stringpush/runtime-core": "0.1.0"
|
|
36
|
-
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"jsdom": "^25.0.1",
|
|
39
|
-
"tsup": "^8.3.5"
|
|
40
|
-
},
|
|
41
33
|
"scripts": {
|
|
42
34
|
"build": "tsup",
|
|
43
35
|
"dev": "tsup --watch",
|
|
44
36
|
"lint": "eslint src",
|
|
45
37
|
"test": "vitest run",
|
|
46
38
|
"typecheck": "tsc --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@stringpush/realtime-protocol": "workspace:*",
|
|
42
|
+
"@stringpush/runtime-core": "workspace:*"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"jsdom": "^25.0.1",
|
|
46
|
+
"tsup": "^8.3.5"
|
|
47
47
|
}
|
|
48
|
-
}
|
|
48
|
+
}
|