@kadanza/extension-sdk 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/dist/extension-sdk.cjs +1 -1
- package/dist/extension-sdk.cjs.map +1 -1
- package/dist/extension-sdk.js +222 -5
- package/dist/extension-sdk.js.map +1 -1
- package/dist/index.d.cts +109 -0
- package/dist/index.d.ts +109 -0
- package/package.json +6 -5
- package/dist/extension-sdk.d.cts +0 -1
- package/dist/extension-sdk.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kadanza/extension-sdk
|
|
2
2
|
|
|
3
|
-
Helpers for building extensions
|
|
3
|
+
Helpers for building Kadanza extensions embedded in the parent app via iframe `postMessage`.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -13,10 +13,21 @@ npm install @kadanza/extension-sdk
|
|
|
13
13
|
### ESM
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import { createExtensionSDK, type
|
|
16
|
+
import { createExtensionSDK, type IExtensionSDK } from "@kadanza/extension-sdk";
|
|
17
17
|
|
|
18
|
-
const sdk:
|
|
19
|
-
|
|
18
|
+
const sdk: IExtensionSDK = createExtensionSDK();
|
|
19
|
+
|
|
20
|
+
const { authToken, extensionDetails, designTokens } = await sdk.connect();
|
|
21
|
+
|
|
22
|
+
sdk.onLoadPageSettings((settings) => {
|
|
23
|
+
// open settings UI with values from parent
|
|
24
|
+
console.log(settings);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const refreshed = await sdk.requestTokenRefresh();
|
|
28
|
+
console.log(refreshed.jwt);
|
|
29
|
+
|
|
30
|
+
await sdk.updatePageSettings({ setting1: 123 });
|
|
20
31
|
```
|
|
21
32
|
|
|
22
33
|
### CommonJS
|
|
@@ -25,9 +36,14 @@ console.log(sdk.wip);
|
|
|
25
36
|
const { createExtensionSDK } = require("@kadanza/extension-sdk");
|
|
26
37
|
|
|
27
38
|
const sdk = createExtensionSDK();
|
|
28
|
-
|
|
39
|
+
|
|
40
|
+
sdk.connect().then(({ authToken }) => {
|
|
41
|
+
console.log(authToken.jwt);
|
|
42
|
+
});
|
|
29
43
|
```
|
|
30
44
|
|
|
45
|
+
The parent must pass `tenantUrl` as a search param on the iframe `src`. See [docs/communication-process.md](docs/communication-process.md).
|
|
46
|
+
|
|
31
47
|
## Local development
|
|
32
48
|
|
|
33
49
|
```bash
|
package/dist/extension-sdk.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={handshakeInit:`HANDSHAKE_INIT`,handshakeAck:`HANDSHAKE_ACK`,requestTokenRefresh:`REQUEST_TOKEN_REFRESH`,tokenRefresh:`TOKEN_REFRESH`,loadPageSettings:`LOAD_PAGE_SETTINGS`,updatePageSettings:`UPDATE_PAGE_SETTINGS`,pageSettingsUpdated:`PAGE_SETTINGS_UPDATED`};function t(e,t,n){if(!window.parent||window.parent===window)throw Error(`Extension is not running inside a parent frame.`);let r=n===void 0?{type:e}:{type:e,payload:n};window.parent.postMessage(r,t)}function n(e,t){let n=n=>{n.origin===e&&(!n.data||typeof n.data!=`object`||!(`type`in n.data)||t(n))};return window.addEventListener(`message`,n),()=>window.removeEventListener(`message`,n)}var r=new Set([`localhost`,`127.0.0.1`,`[::1]`]),i=class extends Error{constructor(e){super(e),this.name=`InvalidOriginError`}};function a(e){if(!e)throw new i(`Missing "tenantUrl" search parameter required to establish parent origin.`);let t;try{t=new URL(e)}catch{throw new i(`Invalid tenantUrl: "${e}".`)}let n=r.has(t.hostname),a=t.protocol===`https:`,o=t.protocol===`http:`&&n;if(!a&&!o)throw new i(`tenantUrl must use https (or http on localhost). Received: "${t.protocol}//${t.host}".`);return t.origin}function o(e=window.location.search){return new URLSearchParams(e).get(`tenantUrl`)}var s=1e4,c=class{#e=null;#t=null;#n=!1;#r=!1;#i=null;#a=null;#o=null;#s=null;#c=null;#l=null;#u=null;#d=new Set;#f=new Set;get isConnected(){return this.#n}async connect(r={}){if(this.#v(),this.#n&&this.#i&&this.#a&&this.#o)return{authToken:this.#i,extensionDetails:this.#a,designTokens:this.#o,pageSettings:this.#s};if(this.#c)throw Error(`Handshake already in progress.`);let i=o();this.#e=a(i),this.#t?.(),this.#t=n(this.#e,e=>this.#p(e));let c=r.timeoutMs??s,l=new Promise((e,t)=>{let n=setTimeout(()=>{this.#c=null,t(Error(`Handshake timed out after ${c}ms.`))},c);this.#c={resolve:e,reject:t,timer:n}});try{t(e.handshakeInit,this.#e)}catch(e){throw this.#b(this.#c),this.#c=null,this.#t?.(),this.#t=null,this.#e=null,e}return l}destroy(){this.#r=!0,this.#n=!1,this.#t?.(),this.#t=null,this.#e=null,this.#S(this.#c,Error(`SDK destroyed.`)),this.#S(this.#l,Error(`SDK destroyed.`)),this.#S(this.#u,Error(`SDK destroyed.`)),this.#c=null,this.#l=null,this.#u=null,this.#d.clear(),this.#f.clear(),this.#i=null,this.#a=null,this.#o=null,this.#s=null}getAuthToken(){return this.#i}getExtensionDetails(){return this.#a}getDesignTokens(){return this.#o}getPageSettings(){return this.#s}async requestTokenRefresh(n={}){if(this.#y(),this.#l)throw Error(`Token refresh already in progress.`);let r=n.timeoutMs??s,i=this.#e,a=new Promise((e,t)=>{let n=setTimeout(()=>{this.#l=null,t(Error(`Token refresh timed out after ${r}ms.`))},r);this.#l={resolve:e,reject:t,timer:n}});return t(e.requestTokenRefresh,i),a}async updatePageSettings(n,r={}){if(this.#y(),this.#u)throw Error(`Page settings update already in progress.`);let i=r.timeoutMs??s,a=this.#e,o=new Promise((e,t)=>{let n=setTimeout(()=>{this.#u=null,t(Error(`Page settings update timed out after ${i}ms.`))},i);this.#u={resolve:e,reject:t,timer:n}});return t(e.updatePageSettings,a,n),o}onLoadPageSettings(e){return this.#d.add(e),()=>{this.#d.delete(e)}}onTokenRefresh(e){return this.#f.add(e),()=>{this.#f.delete(e)}}#p(t){let{type:n,payload:r}=t.data;switch(n){case e.handshakeAck:this.#m(r);break;case e.tokenRefresh:this.#h(r);break;case e.loadPageSettings:this.#g(r);break;case e.pageSettingsUpdated:this.#_(r);break;default:break}}#m(e){if(!e?.authToken||!e.extensionDetails||!e.designTokens){this.#S(this.#c,Error(`Invalid HANDSHAKE_ACK payload.`)),this.#c=null;return}this.#i=e.authToken,this.#a=e.extensionDetails,this.#o=e.designTokens,this.#s=e.pageSettings??null,this.#n=!0;let t=this.#c;this.#c=null,this.#x(t,{authToken:this.#i,extensionDetails:this.#a,designTokens:this.#o,pageSettings:this.#s})}#h(e){if(!e?.authToken){this.#S(this.#l,Error(`Invalid TOKEN_REFRESH payload.`)),this.#l=null;return}this.#i=e.authToken;let t=this.#l;this.#l=null,this.#x(t,this.#i);for(let e of this.#f)e(this.#i)}#g(e){let t=e??{};this.#s=t;for(let e of this.#d)e(t)}#_(e){if(!e||typeof e.success!=`boolean`){this.#S(this.#u,Error(`Invalid PAGE_SETTINGS_UPDATED payload.`)),this.#u=null;return}let t=this.#u;this.#u=null,this.#x(t,e)}#v(){if(this.#r)throw Error(`SDK has been destroyed.`)}#y(){if(this.#v(),!this.#n||!this.#e)throw Error(`SDK is not connected. Call connect() first.`)}#b(e){e&&clearTimeout(e.timer)}#x(e,t){e&&(clearTimeout(e.timer),e.resolve(t))}#S(e,t){e&&(clearTimeout(e.timer),e.reject(t))}},l=()=>new c;exports.CONNECTION_EVENTS=e,exports.ExtensionSDK=c,exports.InvalidOriginError=i,exports.createExtensionSDK=l,exports.readTenantUrlFromLocation=o,exports.resolveAllowedOrigin=a;
|
|
2
2
|
//# sourceMappingURL=extension-sdk.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-sdk.cjs","names":[],"sources":["../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["export interface IExtensionSDK {\n wip: boolean;\n}\n\nexport class ExtensionSDK implements IExtensionSDK {\n wip = true;\n\n constructor() {}\n}\n","export { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nimport { ExtensionSDK } from \"./ExtensionSDK\";\nimport type { IExtensionSDK } from \"./ExtensionSDK\";\n\nexport const createExtensionSDK = (): IExtensionSDK => {\n return new ExtensionSDK();\n};\n"],"mappings":"mEAIA,IAAa,EAAb,KAAmD,CACjD,IAAM,GAEN,aAAc,CAAC,CACjB,ECHa,MACJ,IAAI"}
|
|
1
|
+
{"version":3,"file":"extension-sdk.cjs","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#pageSettings","#pendingHandshake","#allowedOrigin","#unsubscribe","#onMessage","#clearPending","#destroyed","#rejectPending","#pendingTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#tokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending"],"sources":["../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["export const CONNECTION_EVENTS = {\n // Child -> Parent\n handshakeInit: \"HANDSHAKE_INIT\",\n // Parent -> Child\n handshakeAck: \"HANDSHAKE_ACK\",\n // Child -> Parent\n requestTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n // Parent -> Child\n tokenRefresh: \"TOKEN_REFRESH\",\n // Parent -> Child\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n // Child -> Parent\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n // Parent -> Child\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n} as const;\n\nexport type ConnectionEvent =\n (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS];\n","import type { ConnectionEvent } from \"./events\";\nimport type { ExtensionMessage } from \"./types\";\n\nexport type MessageHandler = (event: MessageEvent<ExtensionMessage>) => void;\n\nexport function postToParent(\n type: ConnectionEvent,\n allowedOrigin: string,\n payload?: unknown,\n): void {\n if (!window.parent || window.parent === window) {\n throw new Error(\"Extension is not running inside a parent frame.\");\n }\n\n const message: ExtensionMessage = payload === undefined ? { type } : { type, payload };\n window.parent.postMessage(message, allowedOrigin);\n}\n\nexport function subscribeToParentMessages(\n allowedOrigin: string,\n handler: MessageHandler,\n): () => void {\n const listener = (event: MessageEvent) => {\n if (event.origin !== allowedOrigin) {\n return;\n }\n\n if (!event.data || typeof event.data !== \"object\" || !(\"type\" in event.data)) {\n return;\n }\n\n handler(event as MessageEvent<ExtensionMessage>);\n };\n\n window.addEventListener(\"message\", listener);\n return () => window.removeEventListener(\"message\", listener);\n}\n","const LOCALHOST_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\n\nexport class InvalidOriginError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidOriginError\";\n }\n}\n\n/**\n * Parses and validates a tenant URL into a postMessage-safe origin.\n * Allows https always; http only for localhost / 127.0.0.1 / [::1].\n */\nexport function resolveAllowedOrigin(tenantUrl: string | null): string {\n if (!tenantUrl) {\n throw new InvalidOriginError(\n 'Missing \"tenantUrl\" search parameter required to establish parent origin.',\n );\n }\n\n let url: URL;\n try {\n url = new URL(tenantUrl);\n } catch {\n throw new InvalidOriginError(`Invalid tenantUrl: \"${tenantUrl}\".`);\n }\n\n const isLocalhost = LOCALHOST_HOSTS.has(url.hostname);\n const isHttps = url.protocol === \"https:\";\n const isLocalHttp = url.protocol === \"http:\" && isLocalhost;\n\n if (!isHttps && !isLocalHttp) {\n throw new InvalidOriginError(\n `tenantUrl must use https (or http on localhost). Received: \"${url.protocol}//${url.host}\".`,\n );\n }\n\n return url.origin;\n}\n\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n ScopedExtensionToken,\n TokenRefreshPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface IExtensionSDK {\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n destroy(): void;\n\n readonly isConnected: boolean;\n getAuthToken(): ScopedExtensionToken | null;\n getExtensionDetails(): ExtensionDetails | null;\n getDesignTokens(): DesignTokens | null;\n getPageSettings(): PageSettings | null;\n\n requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;\n updatePageSettings(\n settings: PageSettings,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: ScopedExtensionToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #pendingTokenRefresh: Pending<ScopedExtensionToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #tokenRefreshHandlers = new Set<(token: ScopedExtensionToken) => void>();\n\n get isConnected(): boolean {\n return this.#connected;\n }\n\n async connect(options: ConnectOptions = {}): Promise<HandshakePayload> {\n this.#assertNotDestroyed();\n\n if (this.#connected && this.#authToken && this.#extensionDetails && this.#designTokens) {\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#pendingHandshake) {\n throw new Error(\"Handshake already in progress.\");\n }\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n\n this.#unsubscribe?.();\n this.#unsubscribe = subscribeToParentMessages(\n this.#allowedOrigin,\n (event) => this.#onMessage(event),\n );\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const handshakePromise = new Promise<HandshakePayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingHandshake = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#pendingTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#tokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): ScopedExtensionToken | null {\n return this.#authToken;\n }\n\n getExtensionDetails(): ExtensionDetails | null {\n return this.#extensionDetails;\n }\n\n getDesignTokens(): DesignTokens | null {\n return this.#designTokens;\n }\n\n getPageSettings(): PageSettings | null {\n return this.#pageSettings;\n }\n\n async requestTokenRefresh(\n options: RequestOptions = {},\n ): Promise<ScopedExtensionToken> {\n this.#assertConnected();\n\n if (this.#pendingTokenRefresh) {\n throw new Error(\"Token refresh already in progress.\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const allowedOrigin = this.#allowedOrigin!;\n\n const promise = new Promise<ScopedExtensionToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingTokenRefresh = null;\n reject(new Error(`Token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async updatePageSettings(\n settings: PageSettings,\n options: RequestOptions = {},\n ): Promise<PageSettingsUpdatedPayload> {\n this.#assertConnected();\n\n if (this.#pendingPageSettingsUpdate) {\n throw new Error(\"Page settings update already in progress.\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const allowedOrigin = this.#allowedOrigin!;\n\n const promise = new Promise<PageSettingsUpdatedPayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingPageSettingsUpdate = null;\n reject(new Error(`Page settings update timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingPageSettingsUpdate = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.updatePageSettings, allowedOrigin, settings);\n return promise;\n }\n\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void {\n this.#loadPageSettingsHandlers.add(handler);\n return () => {\n this.#loadPageSettingsHandlers.delete(handler);\n };\n }\n\n onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void {\n this.#tokenRefreshHandlers.add(handler);\n return () => {\n this.#tokenRefreshHandlers.delete(handler);\n };\n }\n\n #onMessage(event: MessageEvent<ExtensionMessage>): void {\n const { type, payload } = event.data;\n\n switch (type) {\n case CONNECTION_EVENTS.handshakeAck:\n this.#handleHandshakeAck(payload as HandshakePayload | undefined);\n break;\n case CONNECTION_EVENTS.tokenRefresh:\n this.#handleTokenRefresh(payload as TokenRefreshPayload | undefined);\n break;\n case CONNECTION_EVENTS.loadPageSettings:\n this.#handleLoadPageSettings(payload as PageSettings | undefined);\n break;\n case CONNECTION_EVENTS.pageSettingsUpdated:\n this.#handlePageSettingsUpdated(\n payload as PageSettingsUpdatedPayload | undefined,\n );\n break;\n default:\n break;\n }\n }\n\n #handleHandshakeAck(payload: HandshakePayload | undefined): void {\n if (!payload?.authToken || !payload.extensionDetails || !payload.designTokens) {\n this.#rejectPending(\n this.#pendingHandshake,\n new Error(\"Invalid HANDSHAKE_ACK payload.\"),\n );\n this.#pendingHandshake = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n this.#extensionDetails = payload.extensionDetails;\n this.#designTokens = payload.designTokens;\n this.#pageSettings = payload.pageSettings ?? null;\n this.#connected = true;\n\n const pending = this.#pendingHandshake;\n this.#pendingHandshake = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n }\n\n #handleTokenRefresh(payload: TokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingTokenRefresh;\n this.#pendingTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#tokenRefreshHandlers) {\n handler(this.#authToken);\n }\n }\n\n #handleLoadPageSettings(payload: PageSettings | undefined): void {\n const settings = payload ?? {};\n this.#pageSettings = settings;\n\n for (const handler of this.#loadPageSettingsHandlers) {\n handler(settings);\n }\n }\n\n #handlePageSettingsUpdated(\n payload: PageSettingsUpdatedPayload | undefined,\n ): void {\n if (!payload || typeof payload.success !== \"boolean\") {\n this.#rejectPending(\n this.#pendingPageSettingsUpdate,\n new Error(\"Invalid PAGE_SETTINGS_UPDATED payload.\"),\n );\n this.#pendingPageSettingsUpdate = null;\n return;\n }\n\n const pending = this.#pendingPageSettingsUpdate;\n this.#pendingPageSettingsUpdate = null;\n this.#resolvePending(pending, payload);\n }\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new Error(\"SDK has been destroyed.\");\n }\n }\n\n #assertConnected(): void {\n this.#assertNotDestroyed();\n if (!this.#connected || !this.#allowedOrigin) {\n throw new Error(\"SDK is not connected. Call connect() first.\");\n }\n }\n\n #clearPending(pending: { timer: ReturnType<typeof setTimeout> } | null): void {\n if (pending) {\n clearTimeout(pending.timer);\n }\n }\n\n #resolvePending<T>(pending: Pending<T> | null, value: T): void {\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(value);\n }\n\n #rejectPending(\n pending: { timer: ReturnType<typeof setTimeout>; reject: (reason?: unknown) => void } | null,\n reason: unknown,\n ): void {\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.reject(reason);\n }\n}\n","import { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nexport { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\nexport { CONNECTION_EVENTS, type ConnectionEvent } from \"./events\";\nexport {\n InvalidOriginError,\n readTenantUrlFromLocation,\n resolveAllowedOrigin,\n} from \"./origin\";\nexport type {\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n ScopedExtensionToken,\n TokenRefreshPayload,\n} from \"./types\";\n\nexport const createExtensionSDK = (): IExtensionSDK => {\n return new ExtensionSDK();\n};\n"],"mappings":"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"}
|
package/dist/extension-sdk.js
CHANGED
|
@@ -1,9 +1,226 @@
|
|
|
1
|
+
//#region src/events.ts
|
|
2
|
+
var e = {
|
|
3
|
+
handshakeInit: "HANDSHAKE_INIT",
|
|
4
|
+
handshakeAck: "HANDSHAKE_ACK",
|
|
5
|
+
requestTokenRefresh: "REQUEST_TOKEN_REFRESH",
|
|
6
|
+
tokenRefresh: "TOKEN_REFRESH",
|
|
7
|
+
loadPageSettings: "LOAD_PAGE_SETTINGS",
|
|
8
|
+
updatePageSettings: "UPDATE_PAGE_SETTINGS",
|
|
9
|
+
pageSettingsUpdated: "PAGE_SETTINGS_UPDATED"
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/messaging.ts
|
|
13
|
+
function t(e, t, n) {
|
|
14
|
+
if (!window.parent || window.parent === window) throw Error("Extension is not running inside a parent frame.");
|
|
15
|
+
let r = n === void 0 ? { type: e } : {
|
|
16
|
+
type: e,
|
|
17
|
+
payload: n
|
|
18
|
+
};
|
|
19
|
+
window.parent.postMessage(r, t);
|
|
20
|
+
}
|
|
21
|
+
function n(e, t) {
|
|
22
|
+
let n = (n) => {
|
|
23
|
+
n.origin === e && (!n.data || typeof n.data != "object" || !("type" in n.data) || t(n));
|
|
24
|
+
};
|
|
25
|
+
return window.addEventListener("message", n), () => window.removeEventListener("message", n);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/origin.ts
|
|
29
|
+
var r = /* @__PURE__ */ new Set([
|
|
30
|
+
"localhost",
|
|
31
|
+
"127.0.0.1",
|
|
32
|
+
"[::1]"
|
|
33
|
+
]), i = class extends Error {
|
|
34
|
+
constructor(e) {
|
|
35
|
+
super(e), this.name = "InvalidOriginError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function a(e) {
|
|
39
|
+
if (!e) throw new i("Missing \"tenantUrl\" search parameter required to establish parent origin.");
|
|
40
|
+
let t;
|
|
41
|
+
try {
|
|
42
|
+
t = new URL(e);
|
|
43
|
+
} catch {
|
|
44
|
+
throw new i(`Invalid tenantUrl: "${e}".`);
|
|
45
|
+
}
|
|
46
|
+
let n = r.has(t.hostname), a = t.protocol === "https:", o = t.protocol === "http:" && n;
|
|
47
|
+
if (!a && !o) throw new i(`tenantUrl must use https (or http on localhost). Received: "${t.protocol}//${t.host}".`);
|
|
48
|
+
return t.origin;
|
|
49
|
+
}
|
|
50
|
+
function o(e = window.location.search) {
|
|
51
|
+
return new URLSearchParams(e).get("tenantUrl");
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
1
54
|
//#region src/ExtensionSDK.ts
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
55
|
+
var s = 1e4, c = class {
|
|
56
|
+
#e = null;
|
|
57
|
+
#t = null;
|
|
58
|
+
#n = !1;
|
|
59
|
+
#r = !1;
|
|
60
|
+
#i = null;
|
|
61
|
+
#a = null;
|
|
62
|
+
#o = null;
|
|
63
|
+
#s = null;
|
|
64
|
+
#c = null;
|
|
65
|
+
#l = null;
|
|
66
|
+
#u = null;
|
|
67
|
+
#d = /* @__PURE__ */ new Set();
|
|
68
|
+
#f = /* @__PURE__ */ new Set();
|
|
69
|
+
get isConnected() {
|
|
70
|
+
return this.#n;
|
|
71
|
+
}
|
|
72
|
+
async connect(r = {}) {
|
|
73
|
+
if (this.#v(), this.#n && this.#i && this.#a && this.#o) return {
|
|
74
|
+
authToken: this.#i,
|
|
75
|
+
extensionDetails: this.#a,
|
|
76
|
+
designTokens: this.#o,
|
|
77
|
+
pageSettings: this.#s
|
|
78
|
+
};
|
|
79
|
+
if (this.#c) throw Error("Handshake already in progress.");
|
|
80
|
+
let i = o();
|
|
81
|
+
this.#e = a(i), this.#t?.(), this.#t = n(this.#e, (e) => this.#p(e));
|
|
82
|
+
let c = r.timeoutMs ?? s, l = new Promise((e, t) => {
|
|
83
|
+
let n = setTimeout(() => {
|
|
84
|
+
this.#c = null, t(/* @__PURE__ */ Error(`Handshake timed out after ${c}ms.`));
|
|
85
|
+
}, c);
|
|
86
|
+
this.#c = {
|
|
87
|
+
resolve: e,
|
|
88
|
+
reject: t,
|
|
89
|
+
timer: n
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
try {
|
|
93
|
+
t(e.handshakeInit, this.#e);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
throw this.#b(this.#c), this.#c = null, this.#t?.(), this.#t = null, this.#e = null, e;
|
|
96
|
+
}
|
|
97
|
+
return l;
|
|
98
|
+
}
|
|
99
|
+
destroy() {
|
|
100
|
+
this.#r = !0, this.#n = !1, this.#t?.(), this.#t = null, this.#e = null, this.#S(this.#c, /* @__PURE__ */ Error("SDK destroyed.")), this.#S(this.#l, /* @__PURE__ */ Error("SDK destroyed.")), this.#S(this.#u, /* @__PURE__ */ Error("SDK destroyed.")), this.#c = null, this.#l = null, this.#u = null, this.#d.clear(), this.#f.clear(), this.#i = null, this.#a = null, this.#o = null, this.#s = null;
|
|
101
|
+
}
|
|
102
|
+
getAuthToken() {
|
|
103
|
+
return this.#i;
|
|
104
|
+
}
|
|
105
|
+
getExtensionDetails() {
|
|
106
|
+
return this.#a;
|
|
107
|
+
}
|
|
108
|
+
getDesignTokens() {
|
|
109
|
+
return this.#o;
|
|
110
|
+
}
|
|
111
|
+
getPageSettings() {
|
|
112
|
+
return this.#s;
|
|
113
|
+
}
|
|
114
|
+
async requestTokenRefresh(n = {}) {
|
|
115
|
+
if (this.#y(), this.#l) throw Error("Token refresh already in progress.");
|
|
116
|
+
let r = n.timeoutMs ?? s, i = this.#e, a = new Promise((e, t) => {
|
|
117
|
+
let n = setTimeout(() => {
|
|
118
|
+
this.#l = null, t(/* @__PURE__ */ Error(`Token refresh timed out after ${r}ms.`));
|
|
119
|
+
}, r);
|
|
120
|
+
this.#l = {
|
|
121
|
+
resolve: e,
|
|
122
|
+
reject: t,
|
|
123
|
+
timer: n
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
return t(e.requestTokenRefresh, i), a;
|
|
127
|
+
}
|
|
128
|
+
async updatePageSettings(n, r = {}) {
|
|
129
|
+
if (this.#y(), this.#u) throw Error("Page settings update already in progress.");
|
|
130
|
+
let i = r.timeoutMs ?? s, a = this.#e, o = new Promise((e, t) => {
|
|
131
|
+
let n = setTimeout(() => {
|
|
132
|
+
this.#u = null, t(/* @__PURE__ */ Error(`Page settings update timed out after ${i}ms.`));
|
|
133
|
+
}, i);
|
|
134
|
+
this.#u = {
|
|
135
|
+
resolve: e,
|
|
136
|
+
reject: t,
|
|
137
|
+
timer: n
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
return t(e.updatePageSettings, a, n), o;
|
|
141
|
+
}
|
|
142
|
+
onLoadPageSettings(e) {
|
|
143
|
+
return this.#d.add(e), () => {
|
|
144
|
+
this.#d.delete(e);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
onTokenRefresh(e) {
|
|
148
|
+
return this.#f.add(e), () => {
|
|
149
|
+
this.#f.delete(e);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
#p(t) {
|
|
153
|
+
let { type: n, payload: r } = t.data;
|
|
154
|
+
switch (n) {
|
|
155
|
+
case e.handshakeAck:
|
|
156
|
+
this.#m(r);
|
|
157
|
+
break;
|
|
158
|
+
case e.tokenRefresh:
|
|
159
|
+
this.#h(r);
|
|
160
|
+
break;
|
|
161
|
+
case e.loadPageSettings:
|
|
162
|
+
this.#g(r);
|
|
163
|
+
break;
|
|
164
|
+
case e.pageSettingsUpdated:
|
|
165
|
+
this.#_(r);
|
|
166
|
+
break;
|
|
167
|
+
default: break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
#m(e) {
|
|
171
|
+
if (!e?.authToken || !e.extensionDetails || !e.designTokens) {
|
|
172
|
+
this.#S(this.#c, /* @__PURE__ */ Error("Invalid HANDSHAKE_ACK payload.")), this.#c = null;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.#i = e.authToken, this.#a = e.extensionDetails, this.#o = e.designTokens, this.#s = e.pageSettings ?? null, this.#n = !0;
|
|
176
|
+
let t = this.#c;
|
|
177
|
+
this.#c = null, this.#x(t, {
|
|
178
|
+
authToken: this.#i,
|
|
179
|
+
extensionDetails: this.#a,
|
|
180
|
+
designTokens: this.#o,
|
|
181
|
+
pageSettings: this.#s
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
#h(e) {
|
|
185
|
+
if (!e?.authToken) {
|
|
186
|
+
this.#S(this.#l, /* @__PURE__ */ Error("Invalid TOKEN_REFRESH payload.")), this.#l = null;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.#i = e.authToken;
|
|
190
|
+
let t = this.#l;
|
|
191
|
+
this.#l = null, this.#x(t, this.#i);
|
|
192
|
+
for (let e of this.#f) e(this.#i);
|
|
193
|
+
}
|
|
194
|
+
#g(e) {
|
|
195
|
+
let t = e ?? {};
|
|
196
|
+
this.#s = t;
|
|
197
|
+
for (let e of this.#d) e(t);
|
|
198
|
+
}
|
|
199
|
+
#_(e) {
|
|
200
|
+
if (!e || typeof e.success != "boolean") {
|
|
201
|
+
this.#S(this.#u, /* @__PURE__ */ Error("Invalid PAGE_SETTINGS_UPDATED payload.")), this.#u = null;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let t = this.#u;
|
|
205
|
+
this.#u = null, this.#x(t, e);
|
|
206
|
+
}
|
|
207
|
+
#v() {
|
|
208
|
+
if (this.#r) throw Error("SDK has been destroyed.");
|
|
209
|
+
}
|
|
210
|
+
#y() {
|
|
211
|
+
if (this.#v(), !this.#n || !this.#e) throw Error("SDK is not connected. Call connect() first.");
|
|
212
|
+
}
|
|
213
|
+
#b(e) {
|
|
214
|
+
e && clearTimeout(e.timer);
|
|
215
|
+
}
|
|
216
|
+
#x(e, t) {
|
|
217
|
+
e && (clearTimeout(e.timer), e.resolve(t));
|
|
218
|
+
}
|
|
219
|
+
#S(e, t) {
|
|
220
|
+
e && (clearTimeout(e.timer), e.reject(t));
|
|
221
|
+
}
|
|
222
|
+
}, l = () => new c();
|
|
6
223
|
//#endregion
|
|
7
|
-
export { e as ExtensionSDK,
|
|
224
|
+
export { e as CONNECTION_EVENTS, c as ExtensionSDK, i as InvalidOriginError, l as createExtensionSDK, o as readTenantUrlFromLocation, a as resolveAllowedOrigin };
|
|
8
225
|
|
|
9
226
|
//# sourceMappingURL=extension-sdk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-sdk.js","names":[],"sources":["../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["export interface IExtensionSDK {\n wip: boolean;\n}\n\nexport class ExtensionSDK implements IExtensionSDK {\n wip = true;\n\n constructor() {}\n}\n","export { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nimport { ExtensionSDK } from \"./ExtensionSDK\";\nimport type { IExtensionSDK } from \"./ExtensionSDK\";\n\nexport const createExtensionSDK = (): IExtensionSDK => {\n return new ExtensionSDK();\n};\n"],"mappings":";AAIA,IAAa,IAAb,MAAmD;CACjD,MAAM;CAEN,cAAc,CAAC;AACjB,GCHa,UACJ,IAAI,EAAa"}
|
|
1
|
+
{"version":3,"file":"extension-sdk.js","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#pageSettings","#pendingHandshake","#allowedOrigin","#unsubscribe","#onMessage","#clearPending","#destroyed","#rejectPending","#pendingTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#tokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending"],"sources":["../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["export const CONNECTION_EVENTS = {\n // Child -> Parent\n handshakeInit: \"HANDSHAKE_INIT\",\n // Parent -> Child\n handshakeAck: \"HANDSHAKE_ACK\",\n // Child -> Parent\n requestTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n // Parent -> Child\n tokenRefresh: \"TOKEN_REFRESH\",\n // Parent -> Child\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n // Child -> Parent\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n // Parent -> Child\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n} as const;\n\nexport type ConnectionEvent =\n (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS];\n","import type { ConnectionEvent } from \"./events\";\nimport type { ExtensionMessage } from \"./types\";\n\nexport type MessageHandler = (event: MessageEvent<ExtensionMessage>) => void;\n\nexport function postToParent(\n type: ConnectionEvent,\n allowedOrigin: string,\n payload?: unknown,\n): void {\n if (!window.parent || window.parent === window) {\n throw new Error(\"Extension is not running inside a parent frame.\");\n }\n\n const message: ExtensionMessage = payload === undefined ? { type } : { type, payload };\n window.parent.postMessage(message, allowedOrigin);\n}\n\nexport function subscribeToParentMessages(\n allowedOrigin: string,\n handler: MessageHandler,\n): () => void {\n const listener = (event: MessageEvent) => {\n if (event.origin !== allowedOrigin) {\n return;\n }\n\n if (!event.data || typeof event.data !== \"object\" || !(\"type\" in event.data)) {\n return;\n }\n\n handler(event as MessageEvent<ExtensionMessage>);\n };\n\n window.addEventListener(\"message\", listener);\n return () => window.removeEventListener(\"message\", listener);\n}\n","const LOCALHOST_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\n\nexport class InvalidOriginError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidOriginError\";\n }\n}\n\n/**\n * Parses and validates a tenant URL into a postMessage-safe origin.\n * Allows https always; http only for localhost / 127.0.0.1 / [::1].\n */\nexport function resolveAllowedOrigin(tenantUrl: string | null): string {\n if (!tenantUrl) {\n throw new InvalidOriginError(\n 'Missing \"tenantUrl\" search parameter required to establish parent origin.',\n );\n }\n\n let url: URL;\n try {\n url = new URL(tenantUrl);\n } catch {\n throw new InvalidOriginError(`Invalid tenantUrl: \"${tenantUrl}\".`);\n }\n\n const isLocalhost = LOCALHOST_HOSTS.has(url.hostname);\n const isHttps = url.protocol === \"https:\";\n const isLocalHttp = url.protocol === \"http:\" && isLocalhost;\n\n if (!isHttps && !isLocalHttp) {\n throw new InvalidOriginError(\n `tenantUrl must use https (or http on localhost). Received: \"${url.protocol}//${url.host}\".`,\n );\n }\n\n return url.origin;\n}\n\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n ScopedExtensionToken,\n TokenRefreshPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface IExtensionSDK {\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n destroy(): void;\n\n readonly isConnected: boolean;\n getAuthToken(): ScopedExtensionToken | null;\n getExtensionDetails(): ExtensionDetails | null;\n getDesignTokens(): DesignTokens | null;\n getPageSettings(): PageSettings | null;\n\n requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;\n updatePageSettings(\n settings: PageSettings,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: ScopedExtensionToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #pendingTokenRefresh: Pending<ScopedExtensionToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #tokenRefreshHandlers = new Set<(token: ScopedExtensionToken) => void>();\n\n get isConnected(): boolean {\n return this.#connected;\n }\n\n async connect(options: ConnectOptions = {}): Promise<HandshakePayload> {\n this.#assertNotDestroyed();\n\n if (this.#connected && this.#authToken && this.#extensionDetails && this.#designTokens) {\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#pendingHandshake) {\n throw new Error(\"Handshake already in progress.\");\n }\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n\n this.#unsubscribe?.();\n this.#unsubscribe = subscribeToParentMessages(\n this.#allowedOrigin,\n (event) => this.#onMessage(event),\n );\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const handshakePromise = new Promise<HandshakePayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingHandshake = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#pendingTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#tokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): ScopedExtensionToken | null {\n return this.#authToken;\n }\n\n getExtensionDetails(): ExtensionDetails | null {\n return this.#extensionDetails;\n }\n\n getDesignTokens(): DesignTokens | null {\n return this.#designTokens;\n }\n\n getPageSettings(): PageSettings | null {\n return this.#pageSettings;\n }\n\n async requestTokenRefresh(\n options: RequestOptions = {},\n ): Promise<ScopedExtensionToken> {\n this.#assertConnected();\n\n if (this.#pendingTokenRefresh) {\n throw new Error(\"Token refresh already in progress.\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const allowedOrigin = this.#allowedOrigin!;\n\n const promise = new Promise<ScopedExtensionToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingTokenRefresh = null;\n reject(new Error(`Token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async updatePageSettings(\n settings: PageSettings,\n options: RequestOptions = {},\n ): Promise<PageSettingsUpdatedPayload> {\n this.#assertConnected();\n\n if (this.#pendingPageSettingsUpdate) {\n throw new Error(\"Page settings update already in progress.\");\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const allowedOrigin = this.#allowedOrigin!;\n\n const promise = new Promise<PageSettingsUpdatedPayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingPageSettingsUpdate = null;\n reject(new Error(`Page settings update timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingPageSettingsUpdate = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.updatePageSettings, allowedOrigin, settings);\n return promise;\n }\n\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void {\n this.#loadPageSettingsHandlers.add(handler);\n return () => {\n this.#loadPageSettingsHandlers.delete(handler);\n };\n }\n\n onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void {\n this.#tokenRefreshHandlers.add(handler);\n return () => {\n this.#tokenRefreshHandlers.delete(handler);\n };\n }\n\n #onMessage(event: MessageEvent<ExtensionMessage>): void {\n const { type, payload } = event.data;\n\n switch (type) {\n case CONNECTION_EVENTS.handshakeAck:\n this.#handleHandshakeAck(payload as HandshakePayload | undefined);\n break;\n case CONNECTION_EVENTS.tokenRefresh:\n this.#handleTokenRefresh(payload as TokenRefreshPayload | undefined);\n break;\n case CONNECTION_EVENTS.loadPageSettings:\n this.#handleLoadPageSettings(payload as PageSettings | undefined);\n break;\n case CONNECTION_EVENTS.pageSettingsUpdated:\n this.#handlePageSettingsUpdated(\n payload as PageSettingsUpdatedPayload | undefined,\n );\n break;\n default:\n break;\n }\n }\n\n #handleHandshakeAck(payload: HandshakePayload | undefined): void {\n if (!payload?.authToken || !payload.extensionDetails || !payload.designTokens) {\n this.#rejectPending(\n this.#pendingHandshake,\n new Error(\"Invalid HANDSHAKE_ACK payload.\"),\n );\n this.#pendingHandshake = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n this.#extensionDetails = payload.extensionDetails;\n this.#designTokens = payload.designTokens;\n this.#pageSettings = payload.pageSettings ?? null;\n this.#connected = true;\n\n const pending = this.#pendingHandshake;\n this.#pendingHandshake = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n }\n\n #handleTokenRefresh(payload: TokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingTokenRefresh;\n this.#pendingTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#tokenRefreshHandlers) {\n handler(this.#authToken);\n }\n }\n\n #handleLoadPageSettings(payload: PageSettings | undefined): void {\n const settings = payload ?? {};\n this.#pageSettings = settings;\n\n for (const handler of this.#loadPageSettingsHandlers) {\n handler(settings);\n }\n }\n\n #handlePageSettingsUpdated(\n payload: PageSettingsUpdatedPayload | undefined,\n ): void {\n if (!payload || typeof payload.success !== \"boolean\") {\n this.#rejectPending(\n this.#pendingPageSettingsUpdate,\n new Error(\"Invalid PAGE_SETTINGS_UPDATED payload.\"),\n );\n this.#pendingPageSettingsUpdate = null;\n return;\n }\n\n const pending = this.#pendingPageSettingsUpdate;\n this.#pendingPageSettingsUpdate = null;\n this.#resolvePending(pending, payload);\n }\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new Error(\"SDK has been destroyed.\");\n }\n }\n\n #assertConnected(): void {\n this.#assertNotDestroyed();\n if (!this.#connected || !this.#allowedOrigin) {\n throw new Error(\"SDK is not connected. Call connect() first.\");\n }\n }\n\n #clearPending(pending: { timer: ReturnType<typeof setTimeout> } | null): void {\n if (pending) {\n clearTimeout(pending.timer);\n }\n }\n\n #resolvePending<T>(pending: Pending<T> | null, value: T): void {\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(value);\n }\n\n #rejectPending(\n pending: { timer: ReturnType<typeof setTimeout>; reject: (reason?: unknown) => void } | null,\n reason: unknown,\n ): void {\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.reject(reason);\n }\n}\n","import { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nexport { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\nexport { CONNECTION_EVENTS, type ConnectionEvent } from \"./events\";\nexport {\n InvalidOriginError,\n readTenantUrlFromLocation,\n resolveAllowedOrigin,\n} from \"./origin\";\nexport type {\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n ScopedExtensionToken,\n TokenRefreshPayload,\n} from \"./types\";\n\nexport const createExtensionSDK = (): IExtensionSDK => {\n return new ExtensionSDK();\n};\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export declare const CONNECTION_EVENTS: {
|
|
2
|
+
readonly handshakeInit: "HANDSHAKE_INIT";
|
|
3
|
+
readonly handshakeAck: "HANDSHAKE_ACK";
|
|
4
|
+
readonly requestTokenRefresh: "REQUEST_TOKEN_REFRESH";
|
|
5
|
+
readonly tokenRefresh: "TOKEN_REFRESH";
|
|
6
|
+
readonly loadPageSettings: "LOAD_PAGE_SETTINGS";
|
|
7
|
+
readonly updatePageSettings: "UPDATE_PAGE_SETTINGS";
|
|
8
|
+
readonly pageSettingsUpdated: "PAGE_SETTINGS_UPDATED";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export declare type ConnectionEvent = (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS];
|
|
12
|
+
|
|
13
|
+
export declare interface ConnectOptions {
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export declare const createExtensionSDK: () => IExtensionSDK;
|
|
18
|
+
|
|
19
|
+
export declare interface DesignTokens {
|
|
20
|
+
primaryColor: string;
|
|
21
|
+
fontFamily: string;
|
|
22
|
+
borderRadius: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare interface ExtensionDetails {
|
|
26
|
+
extensionId: string;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
tenantDomain: string;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
spaceId: string;
|
|
31
|
+
pageId: string;
|
|
32
|
+
locale: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface ExtensionMessage<TPayload = unknown> {
|
|
36
|
+
type: ConnectionEvent | string;
|
|
37
|
+
payload?: TPayload;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare class ExtensionSDK implements IExtensionSDK {
|
|
41
|
+
#private;
|
|
42
|
+
get isConnected(): boolean;
|
|
43
|
+
connect(options?: ConnectOptions): Promise<HandshakePayload>;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
getAuthToken(): ScopedExtensionToken | null;
|
|
46
|
+
getExtensionDetails(): ExtensionDetails | null;
|
|
47
|
+
getDesignTokens(): DesignTokens | null;
|
|
48
|
+
getPageSettings(): PageSettings | null;
|
|
49
|
+
requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;
|
|
50
|
+
updatePageSettings(settings: PageSettings, options?: RequestOptions): Promise<PageSettingsUpdatedPayload>;
|
|
51
|
+
onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
|
|
52
|
+
onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export declare interface HandshakePayload {
|
|
56
|
+
authToken: ScopedExtensionToken;
|
|
57
|
+
extensionDetails: ExtensionDetails;
|
|
58
|
+
designTokens: DesignTokens;
|
|
59
|
+
pageSettings: PageSettings | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export declare interface IExtensionSDK {
|
|
63
|
+
connect(options?: ConnectOptions): Promise<HandshakePayload>;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
readonly isConnected: boolean;
|
|
66
|
+
getAuthToken(): ScopedExtensionToken | null;
|
|
67
|
+
getExtensionDetails(): ExtensionDetails | null;
|
|
68
|
+
getDesignTokens(): DesignTokens | null;
|
|
69
|
+
getPageSettings(): PageSettings | null;
|
|
70
|
+
requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;
|
|
71
|
+
updatePageSettings(settings: PageSettings, options?: RequestOptions): Promise<PageSettingsUpdatedPayload>;
|
|
72
|
+
onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
|
|
73
|
+
onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export declare class InvalidOriginError extends Error {
|
|
77
|
+
constructor(message: string);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export declare type PageSettings = Record<string, unknown>;
|
|
81
|
+
|
|
82
|
+
export declare interface PageSettingsUpdatedPayload {
|
|
83
|
+
success: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare function readTenantUrlFromLocation(search?: string): string | null;
|
|
87
|
+
|
|
88
|
+
export declare interface RequestOptions {
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Parses and validates a tenant URL into a postMessage-safe origin.
|
|
94
|
+
* Allows https always; http only for localhost / 127.0.0.1 / [::1].
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveAllowedOrigin(tenantUrl: string | null): string;
|
|
97
|
+
|
|
98
|
+
/** Scoped extension token issued by the parent (`authToken` on the wire). */
|
|
99
|
+
export declare interface ScopedExtensionToken {
|
|
100
|
+
jwt: string;
|
|
101
|
+
/** Unix expiry seconds as a string, per parent payload. */
|
|
102
|
+
expires: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export declare interface TokenRefreshPayload {
|
|
106
|
+
authToken: ScopedExtensionToken;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { }
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export declare const CONNECTION_EVENTS: {
|
|
2
|
+
readonly handshakeInit: "HANDSHAKE_INIT";
|
|
3
|
+
readonly handshakeAck: "HANDSHAKE_ACK";
|
|
4
|
+
readonly requestTokenRefresh: "REQUEST_TOKEN_REFRESH";
|
|
5
|
+
readonly tokenRefresh: "TOKEN_REFRESH";
|
|
6
|
+
readonly loadPageSettings: "LOAD_PAGE_SETTINGS";
|
|
7
|
+
readonly updatePageSettings: "UPDATE_PAGE_SETTINGS";
|
|
8
|
+
readonly pageSettingsUpdated: "PAGE_SETTINGS_UPDATED";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export declare type ConnectionEvent = (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS];
|
|
12
|
+
|
|
13
|
+
export declare interface ConnectOptions {
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export declare const createExtensionSDK: () => IExtensionSDK;
|
|
18
|
+
|
|
19
|
+
export declare interface DesignTokens {
|
|
20
|
+
primaryColor: string;
|
|
21
|
+
fontFamily: string;
|
|
22
|
+
borderRadius: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare interface ExtensionDetails {
|
|
26
|
+
extensionId: string;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
tenantDomain: string;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
spaceId: string;
|
|
31
|
+
pageId: string;
|
|
32
|
+
locale: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface ExtensionMessage<TPayload = unknown> {
|
|
36
|
+
type: ConnectionEvent | string;
|
|
37
|
+
payload?: TPayload;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare class ExtensionSDK implements IExtensionSDK {
|
|
41
|
+
#private;
|
|
42
|
+
get isConnected(): boolean;
|
|
43
|
+
connect(options?: ConnectOptions): Promise<HandshakePayload>;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
getAuthToken(): ScopedExtensionToken | null;
|
|
46
|
+
getExtensionDetails(): ExtensionDetails | null;
|
|
47
|
+
getDesignTokens(): DesignTokens | null;
|
|
48
|
+
getPageSettings(): PageSettings | null;
|
|
49
|
+
requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;
|
|
50
|
+
updatePageSettings(settings: PageSettings, options?: RequestOptions): Promise<PageSettingsUpdatedPayload>;
|
|
51
|
+
onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
|
|
52
|
+
onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export declare interface HandshakePayload {
|
|
56
|
+
authToken: ScopedExtensionToken;
|
|
57
|
+
extensionDetails: ExtensionDetails;
|
|
58
|
+
designTokens: DesignTokens;
|
|
59
|
+
pageSettings: PageSettings | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export declare interface IExtensionSDK {
|
|
63
|
+
connect(options?: ConnectOptions): Promise<HandshakePayload>;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
readonly isConnected: boolean;
|
|
66
|
+
getAuthToken(): ScopedExtensionToken | null;
|
|
67
|
+
getExtensionDetails(): ExtensionDetails | null;
|
|
68
|
+
getDesignTokens(): DesignTokens | null;
|
|
69
|
+
getPageSettings(): PageSettings | null;
|
|
70
|
+
requestTokenRefresh(options?: RequestOptions): Promise<ScopedExtensionToken>;
|
|
71
|
+
updatePageSettings(settings: PageSettings, options?: RequestOptions): Promise<PageSettingsUpdatedPayload>;
|
|
72
|
+
onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
|
|
73
|
+
onTokenRefresh(handler: (token: ScopedExtensionToken) => void): () => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export declare class InvalidOriginError extends Error {
|
|
77
|
+
constructor(message: string);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export declare type PageSettings = Record<string, unknown>;
|
|
81
|
+
|
|
82
|
+
export declare interface PageSettingsUpdatedPayload {
|
|
83
|
+
success: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare function readTenantUrlFromLocation(search?: string): string | null;
|
|
87
|
+
|
|
88
|
+
export declare interface RequestOptions {
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Parses and validates a tenant URL into a postMessage-safe origin.
|
|
94
|
+
* Allows https always; http only for localhost / 127.0.0.1 / [::1].
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveAllowedOrigin(tenantUrl: string | null): string;
|
|
97
|
+
|
|
98
|
+
/** Scoped extension token issued by the parent (`authToken` on the wire). */
|
|
99
|
+
export declare interface ScopedExtensionToken {
|
|
100
|
+
jwt: string;
|
|
101
|
+
/** Unix expiry seconds as a string, per parent payload. */
|
|
102
|
+
expires: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export declare interface TokenRefreshPayload {
|
|
106
|
+
authToken: ScopedExtensionToken;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadanza/extension-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Helpers for building extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kadanza",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
24
24
|
"import": {
|
|
25
|
-
"types": "./dist/
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
26
|
"default": "./dist/extension-sdk.js"
|
|
27
27
|
},
|
|
28
28
|
"require": {
|
|
29
|
-
"types": "./dist/
|
|
29
|
+
"types": "./dist/index.d.cts",
|
|
30
30
|
"default": "./dist/extension-sdk.cjs"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"main": "./dist/extension-sdk.cjs",
|
|
35
|
-
"types": "./dist/
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
36
|
"files": [
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"dev": "vite",
|
|
41
41
|
"build": "tsc -p tsconfig.lib.json --noEmit && vite build",
|
|
42
42
|
"preview": "vite preview",
|
|
43
|
-
"
|
|
43
|
+
"check:types": "npx --yes @arethetypeswrong/cli --pack .",
|
|
44
|
+
"prepublishOnly": "npm run build && npm run check:types"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"typescript": "~6.0.2",
|
package/dist/extension-sdk.d.cts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { }
|
package/dist/extension-sdk.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { }
|