@usethrottle/extension-bridge 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ import { SessionContext } from './protocol.cjs';
2
+ export { BRIDGE_PROTOCOL_VERSION, BRIDGE_SOURCE_HOST, BRIDGE_SOURCE_IFRAME, HostToIframeMessage, IframeToHostMessage, MESSAGE_NAMESPACE, NavigateMessage, ReadyMessage, RefreshMessage, ResizeMessage, SessionMessage, ToastMessage } from './protocol.cjs';
3
+
4
+ /**
5
+ * @usethrottle/extension-bridge
6
+ *
7
+ * Runtime bridge for Throttle extensions running inside a dashboard iframe.
8
+ *
9
+ * Two sides:
10
+ * - IFRAME (extension) side: `createBridge()` — handles the handshake, exposes
11
+ * `bridge.api` for authenticated REST calls, and helpers for communicating
12
+ * resize/toast/navigate events back to the host.
13
+ * - HOST (dashboard) side: `createBridgeHost()` — listens for messages from the
14
+ * iframe, calls `mintToken()` on `ready`/`refresh`, and routes UI events to
15
+ * optional callbacks.
16
+ *
17
+ * WEBHOOK verification: import `verifyWebhook` from the subpath entry
18
+ * `@usethrottle/extension-bridge/webhook` (node-only; uses `node:crypto`).
19
+ */
20
+
21
+ interface BridgeApiClient {
22
+ get(path: string): Promise<unknown>;
23
+ post(path: string, body?: unknown): Promise<unknown>;
24
+ patch(path: string, body?: unknown): Promise<unknown>;
25
+ del(path: string): Promise<unknown>;
26
+ }
27
+ interface Bridge {
28
+ /** Resolves when the first `session` message arrives from the host. */
29
+ ready: Promise<SessionContext>;
30
+ /** Typed REST client. Each method fetches with `Authorization: Bearer <token>`. */
31
+ api: BridgeApiClient;
32
+ /** Ask the host to resize the iframe to `heightPx` pixels. */
33
+ resize(heightPx: number): void;
34
+ /** Ask the host to show a toast notification. */
35
+ toast(message: string, level?: 'info' | 'success' | 'warning' | 'error'): void;
36
+ /** Ask the host to navigate the parent application. */
37
+ navigate(path: string): void;
38
+ /** Return the current session token, or null if no session has been received yet. */
39
+ getToken(): string | null;
40
+ /** Remove the message listener and clean up. */
41
+ destroy(): void;
42
+ }
43
+ type ThrottleBridge = Bridge;
44
+ type BridgeSessionContext = SessionContext;
45
+ type CreateBridgeOptions = CreateBridgeOpts;
46
+ interface CreateBridgeOpts {
47
+ /**
48
+ * Base URL for `bridge.api` calls.
49
+ * Overridden by the `apiBaseUrl` field in the host's `session` message.
50
+ * Defaults to `https://api.usethrottle.dev` if neither is present.
51
+ */
52
+ apiBaseUrl?: string;
53
+ /**
54
+ * Expected origin of the parent dashboard. When set, inbound messages from
55
+ * other origins are silently ignored.
56
+ *
57
+ * When sending postMessage to the parent, the library targets:
58
+ * 1. `targetOrigin` if explicitly provided.
59
+ * 2. `document.referrer` origin if available.
60
+ * 3. `'*'` as a last resort (logs a console.warn).
61
+ *
62
+ * Set this in production to prevent cross-origin token leaks.
63
+ */
64
+ targetOrigin?: string;
65
+ }
66
+ /**
67
+ * Create a bridge instance inside an extension iframe.
68
+ *
69
+ * Call this once at startup. Immediately posts a `ready` message to the
70
+ * parent window, then waits for the host to reply with a `session` message.
71
+ */
72
+ declare function createBridge(opts?: CreateBridgeOpts): Bridge;
73
+ interface MintTokenResult {
74
+ token: string;
75
+ /** ISO 8601 expiry timestamp. */
76
+ expiresAt: string;
77
+ context: SessionContext;
78
+ }
79
+ interface CreateBridgeHostOpts {
80
+ iframe: HTMLIFrameElement;
81
+ /** Origin of the extension iframe. Messages from other origins are ignored. */
82
+ targetOrigin: string;
83
+ /** Called on `ready` and `refresh` messages to produce a fresh signed token. */
84
+ mintToken: () => Promise<MintTokenResult>;
85
+ /**
86
+ * Throttle API base URL to send to the iframe in the `session` message.
87
+ * Defaults to `https://api.usethrottle.dev`.
88
+ */
89
+ apiBaseUrl?: string;
90
+ /** Called when the extension requests an iframe height change. */
91
+ onResize?: (heightPx: number) => void;
92
+ /** Called when the extension requests a toast notification. */
93
+ onToast?: (message: string, level?: 'info' | 'success' | 'warning' | 'error') => void;
94
+ /** Called when the extension requests a navigation. */
95
+ onNavigate?: (path: string) => void;
96
+ }
97
+ interface BridgeHost {
98
+ /** Remove the message listener and clean up. */
99
+ destroy(): void;
100
+ }
101
+ /**
102
+ * Create a bridge host inside the dashboard.
103
+ *
104
+ * Attaches a `message` listener on `window`. Validates that messages come from
105
+ * `opts.iframe.contentWindow` at `opts.targetOrigin`.
106
+ */
107
+ declare function createBridgeHost(opts: CreateBridgeHostOpts): BridgeHost;
108
+
109
+ export { type Bridge, type BridgeApiClient, type BridgeHost, type BridgeSessionContext, type CreateBridgeHostOpts, type CreateBridgeOptions, type CreateBridgeOpts, type MintTokenResult, SessionContext, type ThrottleBridge, createBridge, createBridgeHost };
@@ -0,0 +1,109 @@
1
+ import { SessionContext } from './protocol.js';
2
+ export { BRIDGE_PROTOCOL_VERSION, BRIDGE_SOURCE_HOST, BRIDGE_SOURCE_IFRAME, HostToIframeMessage, IframeToHostMessage, MESSAGE_NAMESPACE, NavigateMessage, ReadyMessage, RefreshMessage, ResizeMessage, SessionMessage, ToastMessage } from './protocol.js';
3
+
4
+ /**
5
+ * @usethrottle/extension-bridge
6
+ *
7
+ * Runtime bridge for Throttle extensions running inside a dashboard iframe.
8
+ *
9
+ * Two sides:
10
+ * - IFRAME (extension) side: `createBridge()` — handles the handshake, exposes
11
+ * `bridge.api` for authenticated REST calls, and helpers for communicating
12
+ * resize/toast/navigate events back to the host.
13
+ * - HOST (dashboard) side: `createBridgeHost()` — listens for messages from the
14
+ * iframe, calls `mintToken()` on `ready`/`refresh`, and routes UI events to
15
+ * optional callbacks.
16
+ *
17
+ * WEBHOOK verification: import `verifyWebhook` from the subpath entry
18
+ * `@usethrottle/extension-bridge/webhook` (node-only; uses `node:crypto`).
19
+ */
20
+
21
+ interface BridgeApiClient {
22
+ get(path: string): Promise<unknown>;
23
+ post(path: string, body?: unknown): Promise<unknown>;
24
+ patch(path: string, body?: unknown): Promise<unknown>;
25
+ del(path: string): Promise<unknown>;
26
+ }
27
+ interface Bridge {
28
+ /** Resolves when the first `session` message arrives from the host. */
29
+ ready: Promise<SessionContext>;
30
+ /** Typed REST client. Each method fetches with `Authorization: Bearer <token>`. */
31
+ api: BridgeApiClient;
32
+ /** Ask the host to resize the iframe to `heightPx` pixels. */
33
+ resize(heightPx: number): void;
34
+ /** Ask the host to show a toast notification. */
35
+ toast(message: string, level?: 'info' | 'success' | 'warning' | 'error'): void;
36
+ /** Ask the host to navigate the parent application. */
37
+ navigate(path: string): void;
38
+ /** Return the current session token, or null if no session has been received yet. */
39
+ getToken(): string | null;
40
+ /** Remove the message listener and clean up. */
41
+ destroy(): void;
42
+ }
43
+ type ThrottleBridge = Bridge;
44
+ type BridgeSessionContext = SessionContext;
45
+ type CreateBridgeOptions = CreateBridgeOpts;
46
+ interface CreateBridgeOpts {
47
+ /**
48
+ * Base URL for `bridge.api` calls.
49
+ * Overridden by the `apiBaseUrl` field in the host's `session` message.
50
+ * Defaults to `https://api.usethrottle.dev` if neither is present.
51
+ */
52
+ apiBaseUrl?: string;
53
+ /**
54
+ * Expected origin of the parent dashboard. When set, inbound messages from
55
+ * other origins are silently ignored.
56
+ *
57
+ * When sending postMessage to the parent, the library targets:
58
+ * 1. `targetOrigin` if explicitly provided.
59
+ * 2. `document.referrer` origin if available.
60
+ * 3. `'*'` as a last resort (logs a console.warn).
61
+ *
62
+ * Set this in production to prevent cross-origin token leaks.
63
+ */
64
+ targetOrigin?: string;
65
+ }
66
+ /**
67
+ * Create a bridge instance inside an extension iframe.
68
+ *
69
+ * Call this once at startup. Immediately posts a `ready` message to the
70
+ * parent window, then waits for the host to reply with a `session` message.
71
+ */
72
+ declare function createBridge(opts?: CreateBridgeOpts): Bridge;
73
+ interface MintTokenResult {
74
+ token: string;
75
+ /** ISO 8601 expiry timestamp. */
76
+ expiresAt: string;
77
+ context: SessionContext;
78
+ }
79
+ interface CreateBridgeHostOpts {
80
+ iframe: HTMLIFrameElement;
81
+ /** Origin of the extension iframe. Messages from other origins are ignored. */
82
+ targetOrigin: string;
83
+ /** Called on `ready` and `refresh` messages to produce a fresh signed token. */
84
+ mintToken: () => Promise<MintTokenResult>;
85
+ /**
86
+ * Throttle API base URL to send to the iframe in the `session` message.
87
+ * Defaults to `https://api.usethrottle.dev`.
88
+ */
89
+ apiBaseUrl?: string;
90
+ /** Called when the extension requests an iframe height change. */
91
+ onResize?: (heightPx: number) => void;
92
+ /** Called when the extension requests a toast notification. */
93
+ onToast?: (message: string, level?: 'info' | 'success' | 'warning' | 'error') => void;
94
+ /** Called when the extension requests a navigation. */
95
+ onNavigate?: (path: string) => void;
96
+ }
97
+ interface BridgeHost {
98
+ /** Remove the message listener and clean up. */
99
+ destroy(): void;
100
+ }
101
+ /**
102
+ * Create a bridge host inside the dashboard.
103
+ *
104
+ * Attaches a `message` listener on `window`. Validates that messages come from
105
+ * `opts.iframe.contentWindow` at `opts.targetOrigin`.
106
+ */
107
+ declare function createBridgeHost(opts: CreateBridgeHostOpts): BridgeHost;
108
+
109
+ export { type Bridge, type BridgeApiClient, type BridgeHost, type BridgeSessionContext, type CreateBridgeHostOpts, type CreateBridgeOptions, type CreateBridgeOpts, type MintTokenResult, SessionContext, type ThrottleBridge, createBridge, createBridgeHost };
package/dist/index.js ADDED
@@ -0,0 +1,208 @@
1
+ import {
2
+ BRIDGE_PROTOCOL_VERSION,
3
+ BRIDGE_SOURCE_HOST,
4
+ BRIDGE_SOURCE_IFRAME,
5
+ MESSAGE_NAMESPACE
6
+ } from "./chunk-XBGZRIHG.js";
7
+
8
+ // src/index.ts
9
+ function createBridge(opts = {}) {
10
+ const { targetOrigin } = opts;
11
+ let resolvedApiBaseUrl = opts.apiBaseUrl ?? "https://api.usethrottle.dev";
12
+ let resolveReady;
13
+ const readyPromise = new Promise((resolve) => {
14
+ resolveReady = resolve;
15
+ });
16
+ let currentToken = null;
17
+ let currentExpiresAt = null;
18
+ let refreshPromise = null;
19
+ let resolveRefresh = null;
20
+ function isTokenExpiringSoon() {
21
+ if (!currentToken || !currentExpiresAt) return true;
22
+ return currentExpiresAt - Date.now() / 1e3 < 60;
23
+ }
24
+ function requestRefresh() {
25
+ if (!refreshPromise) {
26
+ refreshPromise = new Promise((resolve) => {
27
+ resolveRefresh = resolve;
28
+ });
29
+ postToParent({ type: "refresh" });
30
+ }
31
+ return refreshPromise;
32
+ }
33
+ function handleSession(msg) {
34
+ currentToken = msg.token;
35
+ currentExpiresAt = Math.floor(new Date(msg.expiresAt).getTime() / 1e3);
36
+ if (msg.apiBaseUrl) {
37
+ resolvedApiBaseUrl = msg.apiBaseUrl;
38
+ }
39
+ resolveReady(msg.context);
40
+ if (resolveRefresh) {
41
+ const r = resolveRefresh;
42
+ resolveRefresh = null;
43
+ refreshPromise = null;
44
+ r(msg.context);
45
+ }
46
+ }
47
+ function getParentTarget() {
48
+ if (targetOrigin) return targetOrigin;
49
+ if (typeof document !== "undefined" && document.referrer) {
50
+ try {
51
+ return new URL(document.referrer).origin;
52
+ } catch {
53
+ }
54
+ }
55
+ if (typeof console !== "undefined") {
56
+ console.warn(
57
+ '[ThrottleBridge] targetOrigin not set and document.referrer unavailable; posting to parent with origin="*". Set `targetOrigin` in production.'
58
+ );
59
+ }
60
+ return "*";
61
+ }
62
+ function onMessage(event) {
63
+ if (typeof window !== "undefined" && event.source !== null && event.source !== window.parent) return;
64
+ if (targetOrigin && event.origin !== targetOrigin) return;
65
+ const data = event.data;
66
+ if (!data || typeof data !== "object") return;
67
+ if (data["source"] !== BRIDGE_SOURCE_HOST) return;
68
+ if (data["type"] === "session") {
69
+ handleSession(data);
70
+ }
71
+ }
72
+ if (typeof window !== "undefined") {
73
+ window.addEventListener("message", onMessage);
74
+ postToParent({ type: "ready" });
75
+ }
76
+ function postToParent(payload) {
77
+ if (typeof window !== "undefined" && window.parent !== window) {
78
+ window.parent.postMessage(
79
+ { source: BRIDGE_SOURCE_IFRAME, ...payload },
80
+ getParentTarget()
81
+ );
82
+ }
83
+ }
84
+ async function apiFetch(method, path, body) {
85
+ if (isTokenExpiringSoon()) {
86
+ await requestRefresh();
87
+ }
88
+ const doFetch = async (token) => {
89
+ const headers = {
90
+ "Content-Type": "application/json",
91
+ Authorization: `Bearer ${token}`
92
+ };
93
+ const init = { method, headers };
94
+ if (body !== void 0) {
95
+ init.body = JSON.stringify(body);
96
+ }
97
+ const res2 = await fetch(`${resolvedApiBaseUrl}${path}`, init);
98
+ return res2;
99
+ };
100
+ let res = await doFetch(currentToken);
101
+ if (res.status === 401) {
102
+ await requestRefresh();
103
+ res = await doFetch(currentToken);
104
+ }
105
+ if (!res.ok) {
106
+ const text = await res.text().catch(() => "");
107
+ throw new Error(`ThrottleBridge fetch error: ${res.status} ${res.statusText} \u2014 ${text}`);
108
+ }
109
+ return res.json();
110
+ }
111
+ const api = {
112
+ get: (path) => apiFetch("GET", path),
113
+ post: (path, body) => apiFetch("POST", path, body),
114
+ patch: (path, body) => apiFetch("PATCH", path, body),
115
+ del: (path) => apiFetch("DELETE", path)
116
+ };
117
+ function destroy() {
118
+ if (typeof window !== "undefined") {
119
+ window.removeEventListener("message", onMessage);
120
+ }
121
+ }
122
+ return {
123
+ ready: readyPromise,
124
+ api,
125
+ resize(heightPx) {
126
+ postToParent({ type: "resize", height: heightPx });
127
+ },
128
+ toast(message, level) {
129
+ postToParent({ type: "toast", message, level });
130
+ },
131
+ navigate(path) {
132
+ postToParent({ type: "navigate", path });
133
+ },
134
+ getToken() {
135
+ return currentToken;
136
+ },
137
+ destroy
138
+ };
139
+ }
140
+ function createBridgeHost(opts) {
141
+ const {
142
+ iframe,
143
+ targetOrigin,
144
+ mintToken,
145
+ apiBaseUrl = "https://api.usethrottle.dev",
146
+ onResize,
147
+ onToast,
148
+ onNavigate
149
+ } = opts;
150
+ async function sendSession() {
151
+ const { token, expiresAt, context } = await mintToken();
152
+ iframe.contentWindow?.postMessage(
153
+ {
154
+ source: BRIDGE_SOURCE_HOST,
155
+ type: "session",
156
+ token,
157
+ expiresAt,
158
+ context,
159
+ apiBaseUrl
160
+ },
161
+ targetOrigin
162
+ );
163
+ }
164
+ async function onMessage(event) {
165
+ if (event.source !== iframe.contentWindow) return;
166
+ if (event.origin !== targetOrigin) return;
167
+ const data = event.data;
168
+ if (!data || typeof data !== "object") return;
169
+ if (data["source"] !== BRIDGE_SOURCE_IFRAME) return;
170
+ switch (data["type"]) {
171
+ case "ready":
172
+ case "refresh":
173
+ await sendSession();
174
+ break;
175
+ case "resize":
176
+ onResize?.(data["height"]);
177
+ break;
178
+ case "toast":
179
+ onToast?.(data["message"], data["level"]);
180
+ break;
181
+ case "navigate":
182
+ onNavigate?.(data["path"]);
183
+ break;
184
+ }
185
+ }
186
+ const boundListener = (event) => {
187
+ void onMessage(event);
188
+ };
189
+ if (typeof window !== "undefined") {
190
+ window.addEventListener("message", boundListener);
191
+ }
192
+ return {
193
+ destroy() {
194
+ if (typeof window !== "undefined") {
195
+ window.removeEventListener("message", boundListener);
196
+ }
197
+ }
198
+ };
199
+ }
200
+ export {
201
+ BRIDGE_PROTOCOL_VERSION,
202
+ BRIDGE_SOURCE_HOST,
203
+ BRIDGE_SOURCE_IFRAME,
204
+ MESSAGE_NAMESPACE,
205
+ createBridge,
206
+ createBridgeHost
207
+ };
208
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @usethrottle/extension-bridge\n *\n * Runtime bridge for Throttle extensions running inside a dashboard iframe.\n *\n * Two sides:\n * - IFRAME (extension) side: `createBridge()` — handles the handshake, exposes\n * `bridge.api` for authenticated REST calls, and helpers for communicating\n * resize/toast/navigate events back to the host.\n * - HOST (dashboard) side: `createBridgeHost()` — listens for messages from the\n * iframe, calls `mintToken()` on `ready`/`refresh`, and routes UI events to\n * optional callbacks.\n *\n * WEBHOOK verification: import `verifyWebhook` from the subpath entry\n * `@usethrottle/extension-bridge/webhook` (node-only; uses `node:crypto`).\n */\n\n// Re-export protocol types + constants so dashboard host imports the SAME shapes\nexport type {\n SessionContext,\n IframeToHostMessage,\n HostToIframeMessage,\n ReadyMessage,\n RefreshMessage,\n ResizeMessage,\n ToastMessage,\n NavigateMessage,\n SessionMessage,\n} from './protocol.js';\n\nexport {\n MESSAGE_NAMESPACE,\n BRIDGE_SOURCE_IFRAME,\n BRIDGE_SOURCE_HOST,\n BRIDGE_PROTOCOL_VERSION,\n} from './protocol.js';\n\n// ---------------------------------------------------------------------------\n// Internal imports from protocol\n// ---------------------------------------------------------------------------\n\nimport {\n BRIDGE_SOURCE_IFRAME,\n BRIDGE_SOURCE_HOST,\n} from './protocol.js';\n\nimport type { SessionContext } from './protocol.js';\n\n// ---------------------------------------------------------------------------\n// IFRAME side — Bridge\n// ---------------------------------------------------------------------------\n\nexport interface BridgeApiClient {\n get(path: string): Promise<unknown>;\n post(path: string, body?: unknown): Promise<unknown>;\n patch(path: string, body?: unknown): Promise<unknown>;\n del(path: string): Promise<unknown>;\n}\n\nexport interface Bridge {\n /** Resolves when the first `session` message arrives from the host. */\n ready: Promise<SessionContext>;\n /** Typed REST client. Each method fetches with `Authorization: Bearer <token>`. */\n api: BridgeApiClient;\n /** Ask the host to resize the iframe to `heightPx` pixels. */\n resize(heightPx: number): void;\n /** Ask the host to show a toast notification. */\n toast(message: string, level?: 'info' | 'success' | 'warning' | 'error'): void;\n /** Ask the host to navigate the parent application. */\n navigate(path: string): void;\n /** Return the current session token, or null if no session has been received yet. */\n getToken(): string | null;\n /** Remove the message listener and clean up. */\n destroy(): void;\n}\n\n// Canonical spec name aliases for public consumers\nexport type ThrottleBridge = Bridge;\nexport type BridgeSessionContext = SessionContext;\nexport type CreateBridgeOptions = CreateBridgeOpts;\n\nexport interface CreateBridgeOpts {\n /**\n * Base URL for `bridge.api` calls.\n * Overridden by the `apiBaseUrl` field in the host's `session` message.\n * Defaults to `https://api.usethrottle.dev` if neither is present.\n */\n apiBaseUrl?: string;\n /**\n * Expected origin of the parent dashboard. When set, inbound messages from\n * other origins are silently ignored.\n *\n * When sending postMessage to the parent, the library targets:\n * 1. `targetOrigin` if explicitly provided.\n * 2. `document.referrer` origin if available.\n * 3. `'*'` as a last resort (logs a console.warn).\n *\n * Set this in production to prevent cross-origin token leaks.\n */\n targetOrigin?: string;\n}\n\n/**\n * Create a bridge instance inside an extension iframe.\n *\n * Call this once at startup. Immediately posts a `ready` message to the\n * parent window, then waits for the host to reply with a `session` message.\n */\nexport function createBridge(opts: CreateBridgeOpts = {}): Bridge {\n const { targetOrigin } = opts;\n // apiBaseUrl can be overridden by the session message; start with the option\n // or the default Throttle API base.\n let resolvedApiBaseUrl = opts.apiBaseUrl ?? 'https://api.usethrottle.dev';\n\n let resolveReady!: (ctx: SessionContext) => void;\n\n const readyPromise = new Promise<SessionContext>((resolve) => {\n resolveReady = resolve;\n });\n\n // Current token state\n let currentToken: string | null = null;\n let currentExpiresAt: number | null = null; // Unix seconds\n\n // Pending refresh waiters\n let refreshPromise: Promise<SessionContext> | null = null;\n let resolveRefresh: ((ctx: SessionContext) => void) | null = null;\n\n function isTokenExpiringSoon(): boolean {\n if (!currentToken || !currentExpiresAt) return true;\n return currentExpiresAt - Date.now() / 1000 < 60;\n }\n\n function requestRefresh(): Promise<SessionContext> {\n if (!refreshPromise) {\n refreshPromise = new Promise<SessionContext>((resolve) => {\n resolveRefresh = resolve;\n });\n postToParent({ type: 'refresh' });\n }\n return refreshPromise;\n }\n\n function handleSession(msg: {\n token: string;\n expiresAt: string;\n context: SessionContext;\n apiBaseUrl?: string;\n }) {\n currentToken = msg.token;\n currentExpiresAt = Math.floor(new Date(msg.expiresAt).getTime() / 1000);\n\n // Host may override the API base URL\n if (msg.apiBaseUrl) {\n resolvedApiBaseUrl = msg.apiBaseUrl;\n }\n\n // Resolve the initial ready promise (idempotent — Promise ignores second resolve)\n resolveReady(msg.context);\n\n // Resolve any pending refresh waiter\n if (resolveRefresh) {\n const r = resolveRefresh;\n resolveRefresh = null;\n refreshPromise = null;\n r(msg.context);\n }\n }\n\n // Determine the target origin for outgoing postMessage calls.\n function getParentTarget(): string {\n if (targetOrigin) return targetOrigin;\n if (typeof document !== 'undefined' && document.referrer) {\n try {\n return new URL(document.referrer).origin;\n } catch {\n // ignore\n }\n }\n // Last resort — wildcard. Warn the developer.\n if (typeof console !== 'undefined') {\n console.warn(\n '[ThrottleBridge] targetOrigin not set and document.referrer unavailable; ' +\n 'posting to parent with origin=\"*\". Set `targetOrigin` in production.',\n );\n }\n return '*';\n }\n\n function onMessage(event: MessageEvent) {\n // Source-window guard: only accept messages from the direct parent.\n // Skip the check when event.source is null (e.g. jsdom synthetic events in tests).\n if (typeof window !== 'undefined' && event.source !== null && event.source !== window.parent) return;\n\n // Origin guard\n if (targetOrigin && event.origin !== targetOrigin) return;\n\n const data = event.data as Record<string, unknown>;\n if (!data || typeof data !== 'object') return;\n if (data['source'] !== BRIDGE_SOURCE_HOST) return;\n\n if (data['type'] === 'session') {\n handleSession(data as Parameters<typeof handleSession>[0]);\n }\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('message', onMessage);\n // Announce readiness to the parent\n postToParent({ type: 'ready' });\n }\n\n function postToParent(payload: Record<string, unknown>) {\n if (typeof window !== 'undefined' && window.parent !== window) {\n window.parent.postMessage(\n { source: BRIDGE_SOURCE_IFRAME, ...payload },\n getParentTarget(),\n );\n }\n }\n\n async function apiFetch(method: string, path: string, body?: unknown): Promise<unknown> {\n // Proactively refresh if within 60s of expiry\n if (isTokenExpiringSoon()) {\n await requestRefresh();\n }\n\n const doFetch = async (token: string) => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n const res = await fetch(`${resolvedApiBaseUrl}${path}`, init);\n return res;\n };\n\n let res = await doFetch(currentToken!);\n\n if (res.status === 401) {\n // Token rejected — request a fresh one and retry once\n await requestRefresh();\n res = await doFetch(currentToken!);\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`ThrottleBridge fetch error: ${res.status} ${res.statusText} — ${text}`);\n }\n\n return res.json();\n }\n\n const api: BridgeApiClient = {\n get: (path) => apiFetch('GET', path),\n post: (path, body) => apiFetch('POST', path, body),\n patch: (path, body) => apiFetch('PATCH', path, body),\n del: (path) => apiFetch('DELETE', path),\n };\n\n function destroy() {\n if (typeof window !== 'undefined') {\n window.removeEventListener('message', onMessage);\n }\n }\n\n return {\n ready: readyPromise,\n api,\n resize(heightPx: number) {\n postToParent({ type: 'resize', height: heightPx });\n },\n toast(message: string, level?: 'info' | 'success' | 'warning' | 'error') {\n postToParent({ type: 'toast', message, level });\n },\n navigate(path: string) {\n postToParent({ type: 'navigate', path });\n },\n getToken() {\n return currentToken;\n },\n destroy,\n };\n}\n\n// ---------------------------------------------------------------------------\n// HOST side — BridgeHost\n// ---------------------------------------------------------------------------\n\nexport interface MintTokenResult {\n token: string;\n /** ISO 8601 expiry timestamp. */\n expiresAt: string;\n context: SessionContext;\n}\n\nexport interface CreateBridgeHostOpts {\n iframe: HTMLIFrameElement;\n /** Origin of the extension iframe. Messages from other origins are ignored. */\n targetOrigin: string;\n /** Called on `ready` and `refresh` messages to produce a fresh signed token. */\n mintToken: () => Promise<MintTokenResult>;\n /**\n * Throttle API base URL to send to the iframe in the `session` message.\n * Defaults to `https://api.usethrottle.dev`.\n */\n apiBaseUrl?: string;\n /** Called when the extension requests an iframe height change. */\n onResize?: (heightPx: number) => void;\n /** Called when the extension requests a toast notification. */\n onToast?: (message: string, level?: 'info' | 'success' | 'warning' | 'error') => void;\n /** Called when the extension requests a navigation. */\n onNavigate?: (path: string) => void;\n}\n\nexport interface BridgeHost {\n /** Remove the message listener and clean up. */\n destroy(): void;\n}\n\n/**\n * Create a bridge host inside the dashboard.\n *\n * Attaches a `message` listener on `window`. Validates that messages come from\n * `opts.iframe.contentWindow` at `opts.targetOrigin`.\n */\nexport function createBridgeHost(opts: CreateBridgeHostOpts): BridgeHost {\n const {\n iframe,\n targetOrigin,\n mintToken,\n apiBaseUrl = 'https://api.usethrottle.dev',\n onResize,\n onToast,\n onNavigate,\n } = opts;\n\n async function sendSession() {\n const { token, expiresAt, context } = await mintToken();\n iframe.contentWindow?.postMessage(\n {\n source: BRIDGE_SOURCE_HOST,\n type: 'session',\n token,\n expiresAt,\n context,\n apiBaseUrl,\n },\n targetOrigin,\n );\n }\n\n async function onMessage(event: MessageEvent) {\n // Source + origin guard\n if (event.source !== iframe.contentWindow) return;\n if (event.origin !== targetOrigin) return;\n\n const data = event.data as Record<string, unknown>;\n if (!data || typeof data !== 'object') return;\n if (data['source'] !== BRIDGE_SOURCE_IFRAME) return;\n\n switch (data['type']) {\n case 'ready':\n case 'refresh':\n await sendSession();\n break;\n\n case 'resize':\n onResize?.(data['height'] as number);\n break;\n\n case 'toast':\n onToast?.(data['message'] as string, data['level'] as 'info' | 'success' | 'warning' | 'error' | undefined);\n break;\n\n case 'navigate':\n onNavigate?.(data['path'] as string);\n break;\n }\n }\n\n const boundListener = (event: Event) => {\n void onMessage(event as MessageEvent);\n };\n\n if (typeof window !== 'undefined') {\n window.addEventListener('message', boundListener);\n }\n\n return {\n destroy() {\n if (typeof window !== 'undefined') {\n window.removeEventListener('message', boundListener);\n }\n },\n };\n}\n"],"mappings":"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","names":["res"]}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/protocol.ts
21
+ var protocol_exports = {};
22
+ __export(protocol_exports, {
23
+ BRIDGE_PROTOCOL_VERSION: () => BRIDGE_PROTOCOL_VERSION,
24
+ BRIDGE_SOURCE_HOST: () => BRIDGE_SOURCE_HOST,
25
+ BRIDGE_SOURCE_IFRAME: () => BRIDGE_SOURCE_IFRAME,
26
+ MESSAGE_NAMESPACE: () => MESSAGE_NAMESPACE
27
+ });
28
+ module.exports = __toCommonJS(protocol_exports);
29
+ var MESSAGE_NAMESPACE = "throttle:bridge";
30
+ var BRIDGE_SOURCE_IFRAME = "throttle-extension-bridge";
31
+ var BRIDGE_SOURCE_HOST = "throttle-extension-host";
32
+ var BRIDGE_PROTOCOL_VERSION = 1;
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ BRIDGE_PROTOCOL_VERSION,
36
+ BRIDGE_SOURCE_HOST,
37
+ BRIDGE_SOURCE_IFRAME,
38
+ MESSAGE_NAMESPACE
39
+ });
40
+ //# sourceMappingURL=protocol.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * @usethrottle/extension-bridge — postMessage protocol types\n *\n * Both the extension iframe and the dashboard host import from this file\n * to ensure both sides of the channel use identical message shapes.\n */\n\n/** Discriminant prefix used in all bridge message payloads. */\nexport const MESSAGE_NAMESPACE = 'throttle:bridge' as const;\n\n/** `source` field set on every message sent BY the extension iframe. */\nexport const BRIDGE_SOURCE_IFRAME = 'throttle-extension-bridge' as const;\n\n/** `source` field set on every message sent BY the dashboard host. */\nexport const BRIDGE_SOURCE_HOST = 'throttle-extension-host' as const;\n\n/** Incremented when the handshake protocol changes in a breaking way. */\nexport const BRIDGE_PROTOCOL_VERSION = 1 as const;\n\nexport interface SessionContext {\n user: { id: string; email: string };\n workspace: { id: string; slug: string };\n application: { id: string; slug: string };\n mode: 'test' | 'live';\n installationId: string;\n extensionId: string;\n version: string;\n role: string;\n scopes: string[];\n}\n\nexport interface ReadyMessage { source: typeof BRIDGE_SOURCE_IFRAME; type: 'ready'; }\nexport interface RefreshMessage { source: typeof BRIDGE_SOURCE_IFRAME; type: 'refresh'; }\nexport interface ResizeMessage { source: typeof BRIDGE_SOURCE_IFRAME; type: 'resize'; height: number; }\nexport interface ToastMessage { source: typeof BRIDGE_SOURCE_IFRAME; type: 'toast'; message: string; level?: 'info' | 'success' | 'warning' | 'error'; }\nexport interface NavigateMessage { source: typeof BRIDGE_SOURCE_IFRAME; type: 'navigate'; path: string; }\nexport type IframeToHostMessage = ReadyMessage | RefreshMessage | ResizeMessage | ToastMessage | NavigateMessage;\n\nexport interface SessionMessage {\n source: typeof BRIDGE_SOURCE_HOST;\n type: 'session';\n token: string;\n expiresAt: string;\n context: SessionContext;\n apiBaseUrl: string;\n}\nexport type HostToIframeMessage = SessionMessage;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,IAAM,oBAAoB;AAG1B,IAAM,uBAAuB;AAG7B,IAAM,qBAAqB;AAG3B,IAAM,0BAA0B;","names":[]}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @usethrottle/extension-bridge — postMessage protocol types
3
+ *
4
+ * Both the extension iframe and the dashboard host import from this file
5
+ * to ensure both sides of the channel use identical message shapes.
6
+ */
7
+ /** Discriminant prefix used in all bridge message payloads. */
8
+ declare const MESSAGE_NAMESPACE: "throttle:bridge";
9
+ /** `source` field set on every message sent BY the extension iframe. */
10
+ declare const BRIDGE_SOURCE_IFRAME: "throttle-extension-bridge";
11
+ /** `source` field set on every message sent BY the dashboard host. */
12
+ declare const BRIDGE_SOURCE_HOST: "throttle-extension-host";
13
+ /** Incremented when the handshake protocol changes in a breaking way. */
14
+ declare const BRIDGE_PROTOCOL_VERSION: 1;
15
+ interface SessionContext {
16
+ user: {
17
+ id: string;
18
+ email: string;
19
+ };
20
+ workspace: {
21
+ id: string;
22
+ slug: string;
23
+ };
24
+ application: {
25
+ id: string;
26
+ slug: string;
27
+ };
28
+ mode: 'test' | 'live';
29
+ installationId: string;
30
+ extensionId: string;
31
+ version: string;
32
+ role: string;
33
+ scopes: string[];
34
+ }
35
+ interface ReadyMessage {
36
+ source: typeof BRIDGE_SOURCE_IFRAME;
37
+ type: 'ready';
38
+ }
39
+ interface RefreshMessage {
40
+ source: typeof BRIDGE_SOURCE_IFRAME;
41
+ type: 'refresh';
42
+ }
43
+ interface ResizeMessage {
44
+ source: typeof BRIDGE_SOURCE_IFRAME;
45
+ type: 'resize';
46
+ height: number;
47
+ }
48
+ interface ToastMessage {
49
+ source: typeof BRIDGE_SOURCE_IFRAME;
50
+ type: 'toast';
51
+ message: string;
52
+ level?: 'info' | 'success' | 'warning' | 'error';
53
+ }
54
+ interface NavigateMessage {
55
+ source: typeof BRIDGE_SOURCE_IFRAME;
56
+ type: 'navigate';
57
+ path: string;
58
+ }
59
+ type IframeToHostMessage = ReadyMessage | RefreshMessage | ResizeMessage | ToastMessage | NavigateMessage;
60
+ interface SessionMessage {
61
+ source: typeof BRIDGE_SOURCE_HOST;
62
+ type: 'session';
63
+ token: string;
64
+ expiresAt: string;
65
+ context: SessionContext;
66
+ apiBaseUrl: string;
67
+ }
68
+ type HostToIframeMessage = SessionMessage;
69
+
70
+ export { BRIDGE_PROTOCOL_VERSION, BRIDGE_SOURCE_HOST, BRIDGE_SOURCE_IFRAME, type HostToIframeMessage, type IframeToHostMessage, MESSAGE_NAMESPACE, type NavigateMessage, type ReadyMessage, type RefreshMessage, type ResizeMessage, type SessionContext, type SessionMessage, type ToastMessage };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @usethrottle/extension-bridge — postMessage protocol types
3
+ *
4
+ * Both the extension iframe and the dashboard host import from this file
5
+ * to ensure both sides of the channel use identical message shapes.
6
+ */
7
+ /** Discriminant prefix used in all bridge message payloads. */
8
+ declare const MESSAGE_NAMESPACE: "throttle:bridge";
9
+ /** `source` field set on every message sent BY the extension iframe. */
10
+ declare const BRIDGE_SOURCE_IFRAME: "throttle-extension-bridge";
11
+ /** `source` field set on every message sent BY the dashboard host. */
12
+ declare const BRIDGE_SOURCE_HOST: "throttle-extension-host";
13
+ /** Incremented when the handshake protocol changes in a breaking way. */
14
+ declare const BRIDGE_PROTOCOL_VERSION: 1;
15
+ interface SessionContext {
16
+ user: {
17
+ id: string;
18
+ email: string;
19
+ };
20
+ workspace: {
21
+ id: string;
22
+ slug: string;
23
+ };
24
+ application: {
25
+ id: string;
26
+ slug: string;
27
+ };
28
+ mode: 'test' | 'live';
29
+ installationId: string;
30
+ extensionId: string;
31
+ version: string;
32
+ role: string;
33
+ scopes: string[];
34
+ }
35
+ interface ReadyMessage {
36
+ source: typeof BRIDGE_SOURCE_IFRAME;
37
+ type: 'ready';
38
+ }
39
+ interface RefreshMessage {
40
+ source: typeof BRIDGE_SOURCE_IFRAME;
41
+ type: 'refresh';
42
+ }
43
+ interface ResizeMessage {
44
+ source: typeof BRIDGE_SOURCE_IFRAME;
45
+ type: 'resize';
46
+ height: number;
47
+ }
48
+ interface ToastMessage {
49
+ source: typeof BRIDGE_SOURCE_IFRAME;
50
+ type: 'toast';
51
+ message: string;
52
+ level?: 'info' | 'success' | 'warning' | 'error';
53
+ }
54
+ interface NavigateMessage {
55
+ source: typeof BRIDGE_SOURCE_IFRAME;
56
+ type: 'navigate';
57
+ path: string;
58
+ }
59
+ type IframeToHostMessage = ReadyMessage | RefreshMessage | ResizeMessage | ToastMessage | NavigateMessage;
60
+ interface SessionMessage {
61
+ source: typeof BRIDGE_SOURCE_HOST;
62
+ type: 'session';
63
+ token: string;
64
+ expiresAt: string;
65
+ context: SessionContext;
66
+ apiBaseUrl: string;
67
+ }
68
+ type HostToIframeMessage = SessionMessage;
69
+
70
+ export { BRIDGE_PROTOCOL_VERSION, BRIDGE_SOURCE_HOST, BRIDGE_SOURCE_IFRAME, type HostToIframeMessage, type IframeToHostMessage, MESSAGE_NAMESPACE, type NavigateMessage, type ReadyMessage, type RefreshMessage, type ResizeMessage, type SessionContext, type SessionMessage, type ToastMessage };
@@ -0,0 +1,13 @@
1
+ import {
2
+ BRIDGE_PROTOCOL_VERSION,
3
+ BRIDGE_SOURCE_HOST,
4
+ BRIDGE_SOURCE_IFRAME,
5
+ MESSAGE_NAMESPACE
6
+ } from "./chunk-XBGZRIHG.js";
7
+ export {
8
+ BRIDGE_PROTOCOL_VERSION,
9
+ BRIDGE_SOURCE_HOST,
10
+ BRIDGE_SOURCE_IFRAME,
11
+ MESSAGE_NAMESPACE
12
+ };
13
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}