@kadanza/extension-sdk 0.0.11 → 0.0.14
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 +22 -9
- package/dist/extension-sdk.cjs +1 -1
- package/dist/extension-sdk.cjs.map +1 -1
- package/dist/extension-sdk.js +125 -4
- package/dist/extension-sdk.js.map +1 -1
- package/dist/index.d.cts +98 -0
- package/dist/index.d.ts +98 -0
- package/package.json +12 -1
package/README.md
CHANGED
|
@@ -32,9 +32,18 @@ Fill in `<APP_NAME>` and `<EXTENSION_BUILDERS_DESCRIBE_THEIR_ADDITIONAL_FEATURE_
|
|
|
32
32
|
### Install
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
# Stable
|
|
35
36
|
npm install @kadanza/extension-sdk
|
|
37
|
+
|
|
38
|
+
# RC (shared testing channel on main)
|
|
39
|
+
npm install @kadanza/extension-sdk@rc
|
|
40
|
+
|
|
41
|
+
# Canary (feature-branch preview — see releasing docs)
|
|
42
|
+
npm install @kadanza/extension-sdk@canary
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
How we version and publish: [Releasing](docs/releasing.md).
|
|
46
|
+
|
|
38
47
|
### Usage
|
|
39
48
|
|
|
40
49
|
```ts
|
|
@@ -56,8 +65,9 @@ Check [Flows.md](/docs/flows.md) for all available flows. Building with React? S
|
|
|
56
65
|
## Documentation
|
|
57
66
|
|
|
58
67
|
- [Documentation folder](docs/) — This is were the important information is stored
|
|
59
|
-
- [How Kadanza embeds an extension](docs/embedding.md) — Host iframe, allowed origins, and `
|
|
68
|
+
- [How Kadanza embeds an extension](docs/embedding.md) — Host iframe, allowed origins, `tenantUrl`, and host API (`ExtensionSDKHost`)
|
|
60
69
|
- [Building a React extension](docs/react-guide.md) — Bootstrap a React app with the Extension SDK
|
|
70
|
+
- [Releasing](docs/releasing.md) — Changesets, stable and RC publishes
|
|
61
71
|
- [API reference](https://kadanza-io.github.io/extension-sdk/) — TypeDoc generated documentation describing all the small details, classes, interfaces, payloads...
|
|
62
72
|
|
|
63
73
|
## Local development
|
|
@@ -66,14 +76,17 @@ Check [Flows.md](/docs/flows.md) for all available flows. Building with React? S
|
|
|
66
76
|
npm install
|
|
67
77
|
```
|
|
68
78
|
|
|
69
|
-
| Command
|
|
70
|
-
|
|
|
71
|
-
| `npm run dev`
|
|
72
|
-
| `npm run build`
|
|
73
|
-
| `npm run preview`
|
|
74
|
-
| `npm run docs`
|
|
75
|
-
| `npm run docs:check`
|
|
76
|
-
| `npm run check:types`
|
|
79
|
+
| Command | Description |
|
|
80
|
+
| ------------------------- | ---------------------------------------------------- |
|
|
81
|
+
| `npm run dev` | Serve the playground for embedding in the parent app |
|
|
82
|
+
| `npm run build` | Emit `dist/` (ESM + CJS + types) |
|
|
83
|
+
| `npm run preview` | Preview the production playground build |
|
|
84
|
+
| `npm run docs` | Generate HTML API docs under `docs/api/` |
|
|
85
|
+
| `npm run docs:check` | Validate TypeDoc without writing output |
|
|
86
|
+
| `npm run check:types` | Check published package types with arethetypeswrong |
|
|
87
|
+
| `npm run changeset` | Add a changeset for the next release |
|
|
88
|
+
| `npm run release:rc:enter`| Enter RC prerelease mode on `main` |
|
|
89
|
+
| `npm run release:rc:exit` | Exit RC prerelease mode |
|
|
77
90
|
|
|
78
91
|
`npm run dev` serves the playground at `https://localhost:5000` (accept the
|
|
79
92
|
self-signed certificate warning once). Use this HTTPS origin for the parent
|
package/dist/extension-sdk.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){if(e.startsWith(`/`))return e;let t=new URL(e);return t.hostname=`api.${t.hostname}`,t.origin}function t(t,n){if(!n.startsWith(`/`)||n.startsWith(`//`))throw Error(`API endpoint must be a root-relative path starting with a single "/".`);return`${e(t).replace(/\/+$/,``)}${n}`}async function n(e,n,r={}){let i=new Headers(r.headers);i.has(`Content-Type`)||i.set(`Content-Type`,`application/json`),i.set(`Authorization`,`Bearer ${n.authTokenJwt}`),i.set(`X-Tenant`,n.tenantDomain);let a=await fetch(t(n.baseUrl,e),{...r,headers:i});if(!a.ok){let e=a.statusText?` ${a.statusText}`:``;throw Error(`API call failed: ${a.status}${e}.`)}return await a.json()}var r={handshakeInit:`HANDSHAKE_INIT`,handshakeAck:`HANDSHAKE_ACK`,requestAuthTokenRefresh:`REQUEST_TOKEN_REFRESH`,authTokenRefresh:`TOKEN_REFRESH`,loadPageSettings:`LOAD_PAGE_SETTINGS`,updatePageSettings:`UPDATE_PAGE_SETTINGS`,pageSettingsUpdated:`PAGE_SETTINGS_UPDATED`,navigationChange:`NAVIGATION_CHANGE`};function i(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 a(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 o=new Set([`localhost`,`127.0.0.1`,`[::1]`]),s=class extends Error{constructor(e){super(e),this.name=`InvalidOriginError`}};function c(e){if(!e)throw new s(`Missing "tenantUrl" search parameter required to establish parent origin.`);let t;try{t=new URL(e)}catch{throw new s(`Invalid tenantUrl: "${e}".`)}let n=o.has(t.hostname),r=t.protocol===`https:`,i=t.protocol===`http:`&&n;if(!r&&!i)throw new s(`tenantUrl must use https (or http on localhost). Received: "${t.protocol}//${t.host}".`);return t.origin}function l(e=window.location.search){return new URLSearchParams(e).get(`tenantUrl`)}var u=1e4,d=12e4,f=3e4,p=2147483647,m=class{#e=null;#t=null;#n=null;#r=!1;#i=!1;#a=null;#o=null;#s=null;#c=null;#l=!1;#u=d;#d=null;#f=!1;#p=null;#m=null;#h=null;#g=null;#_=new Set;#v=new Set;get isConnected(){return this.#r}async connect(e={}){if(this.#w(),this.#r&&this.#a&&this.#o&&this.#s)return this.#E(e),this.#A(),{authToken:this.#a,extensionDetails:this.#o,designTokens:this.#s,pageSettings:this.#c};if(this.#m)return this.#m;this.#E(e);let t=l();this.#e=c(t),this.#t=t,this.#n?.(),this.#n=a(this.#e,e=>this.#y(e));let n=e.timeoutMs??u,o=new Promise((e,t)=>{let r=setTimeout(()=>{this.#p=null,this.#m=null,t(Error(`Handshake timed out after ${n}ms.`))},n);this.#p={resolve:e,reject:t,timer:r}});this.#m=o;try{i(r.handshakeInit,this.#e)}catch(e){throw this.#N(this.#p),this.#p=null,this.#m=null,this.#n?.(),this.#n=null,this.#e=null,this.#t=null,this.#k(),e}return o}destroy(){this.#i=!0,this.#r=!1,this.#k(),this.#l=!1,this.#f=!1,this.#n?.(),this.#n=null,this.#e=null,this.#t=null,this.#F(this.#p,Error(`SDK destroyed.`)),this.#F(this.#h,Error(`SDK destroyed.`)),this.#F(this.#g,Error(`SDK destroyed.`)),this.#p=null,this.#m=null,this.#h=null,this.#g=null,this.#_.clear(),this.#v.clear(),this.#a=null,this.#o=null,this.#s=null,this.#c=null}getAuthToken(){return this.#a}getExtensionDetails(){return this.#o}getDesignTokens(){return this.#s}getPageSettings(){return this.#c}getAllowedOrigin(){return this.#e}getTenantUrl(){return this.#t}getApiUrl(){return this.#o?e(this.#o.baseUrl):null}async apiCall(e,t={}){return this.#T(),n(e,{baseUrl:this.#o.baseUrl,tenantDomain:this.#o.tenantDomain,authTokenJwt:this.#a.jwt},t)}async emitRequestAuthTokenRefresh(e={}){if(this.#T(),this.#h)throw Error(`Auth token refresh already in progress.`);let t=e.timeoutMs??u,n=this.#e,a=new Promise((e,n)=>{let r=setTimeout(()=>{this.#h=null,n(Error(`Auth token refresh timed out after ${t}ms.`))},t);this.#h={resolve:e,reject:n,timer:r}});return i(r.requestAuthTokenRefresh,n),a}async emitUpdatePageSettings(e,t={}){if(this.#T(),this.#g)throw Error(`Page settings update already in progress.`);let n=t.timeoutMs??u,a=this.#e,o=new Promise((e,t)=>{let r=setTimeout(()=>{this.#g=null,t(Error(`Page settings update timed out after ${n}ms.`))},n);this.#g={resolve:e,reject:t,timer:r}});return i(r.updatePageSettings,a,e),o}onLoadPageSettings(e){return this.#_.add(e),()=>{this.#_.delete(e)}}onAuthTokenRefresh(e){return this.#v.add(e),()=>{this.#v.delete(e)}}emitNavigationChange(e){this.#T(),i(r.navigationChange,this.#e,e)}#y(e){let{type:t,payload:n}=e.data;switch(t){case r.handshakeAck:this.#b(n);break;case r.authTokenRefresh:this.#x(n);break;case r.loadPageSettings:this.#S(n);break;case r.pageSettingsUpdated:this.#C(n);break;default:break}}#b(e){if(!e?.authToken||!e.extensionDetails||!e.designTokens){this.#F(this.#p,Error(`Invalid HANDSHAKE_ACK payload.`)),this.#p=null,this.#m=null;return}this.#a=e.authToken,this.#o=e.extensionDetails,this.#s=e.designTokens,this.#c=e.pageSettings??null,this.#r=!0;let t=this.#p;this.#p=null,this.#m=null,this.#P(t,{authToken:this.#a,extensionDetails:this.#o,designTokens:this.#s,pageSettings:this.#c}),this.#f=!1,this.#A()}#x(e){if(!e?.authToken){this.#F(this.#h,Error(`Invalid TOKEN_REFRESH payload.`)),this.#h=null;return}this.#a=e.authToken;let t=this.#h;this.#h=null,this.#P(t,this.#a);for(let e of this.#v)e(this.#a);this.#f=!1,this.#A()}#S(e){let t=e??{};this.#c=t;for(let e of this.#_)e(t)}#C(e){if(!e||typeof e.success!=`boolean`){this.#F(this.#g,Error(`Invalid PAGE_SETTINGS_UPDATED payload.`)),this.#g=null;return}let t=this.#g;this.#g=null,this.#P(t,e)}#w(){if(this.#i)throw Error(`SDK has been destroyed.`)}#T(){if(this.#w(),!this.#r||!this.#e)throw Error(`SDK is not connected. Call connect() first.`)}#E(e){if(e.authTokenBufferMs!==void 0&&(!Number.isFinite(e.authTokenBufferMs)||e.authTokenBufferMs<0))throw Error(`authTokenBufferMs must be a finite non-negative number.`);this.#l=e.authTokenAutoRefresh===!0,this.#u=e.authTokenBufferMs??d,this.#f=!1,this.#k()}#D(e){let t=Number(e.expires);return Number.isFinite(t)?t*1e3:null}#O(e){let t=this.#D(e);return t===null?null:Math.max(0,t-this.#u-Date.now())}#k(){this.#d!==null&&(clearTimeout(this.#d),this.#d=null)}#A(){if(this.#k(),!this.#l||!this.#r||this.#i||!this.#a)return;let e=this.#O(this.#a);if(e===null)return;let t=Math.min(e,p);this.#d=setTimeout(()=>{this.#d=null,this.#j()},t)}async#j(){if(!this.#l||!this.#r||this.#i||!this.#a)return;let e=this.#a,t=this.#O(e);if(t!==null){if(t>0){this.#A();return}if(this.#h){this.#M(e);return}try{await this.emitRequestAuthTokenRefresh()}catch{this.#M(e)}}}#M(e){if(!this.#l||!this.#r||this.#i||this.#a?.jwt!==e.jwt||this.#f)return;let t=this.#D(e);if(t===null)return;let n=t-Date.now();if(n<=0)return;this.#f=!0,this.#k();let r=Math.min(f,n);this.#d=setTimeout(()=>{this.#d=null,this.#j()},r)}#N(e){e&&clearTimeout(e.timer)}#P(e,t){e&&(clearTimeout(e.timer),e.resolve(t))}#F(e,t){e&&(clearTimeout(e.timer),e.reject(t))}},h=null,
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){if(e.startsWith(`/`))return e;let t=new URL(e);return t.hostname=`api.${t.hostname}`,t.origin}function t(t,n){if(!n.startsWith(`/`)||n.startsWith(`//`))throw Error(`API endpoint must be a root-relative path starting with a single "/".`);return`${e(t).replace(/\/+$/,``)}${n}`}async function n(e,n,r={}){let i=new Headers(r.headers);i.has(`Content-Type`)||i.set(`Content-Type`,`application/json`),i.set(`Authorization`,`Bearer ${n.authTokenJwt}`),i.set(`X-Tenant`,n.tenantDomain);let a=await fetch(t(n.baseUrl,e),{...r,headers:i});if(!a.ok){let e=a.statusText?` ${a.statusText}`:``;throw Error(`API call failed: ${a.status}${e}.`)}return await a.json()}var r={handshakeInit:`HANDSHAKE_INIT`,handshakeAck:`HANDSHAKE_ACK`,requestAuthTokenRefresh:`REQUEST_TOKEN_REFRESH`,authTokenRefresh:`TOKEN_REFRESH`,loadPageSettings:`LOAD_PAGE_SETTINGS`,updatePageSettings:`UPDATE_PAGE_SETTINGS`,pageSettingsUpdated:`PAGE_SETTINGS_UPDATED`,navigationChange:`NAVIGATION_CHANGE`};function i(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 a(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 o=new Set([`localhost`,`127.0.0.1`,`[::1]`]),s=class extends Error{constructor(e){super(e),this.name=`InvalidOriginError`}};function c(e){if(!e)throw new s(`Missing "tenantUrl" search parameter required to establish parent origin.`);let t;try{t=new URL(e)}catch{throw new s(`Invalid tenantUrl: "${e}".`)}let n=o.has(t.hostname),r=t.protocol===`https:`,i=t.protocol===`http:`&&n;if(!r&&!i)throw new s(`tenantUrl must use https (or http on localhost). Received: "${t.protocol}//${t.host}".`);return t.origin}function l(e=window.location.search){return new URLSearchParams(e).get(`tenantUrl`)}var u=1e4,d=12e4,f=3e4,p=2147483647,m=class{#e=null;#t=null;#n=null;#r=!1;#i=!1;#a=null;#o=null;#s=null;#c=null;#l=!1;#u=d;#d=null;#f=!1;#p=null;#m=null;#h=null;#g=null;#_=new Set;#v=new Set;get isConnected(){return this.#r}async connect(e={}){if(this.#w(),this.#r&&this.#a&&this.#o&&this.#s)return this.#E(e),this.#A(),{authToken:this.#a,extensionDetails:this.#o,designTokens:this.#s,pageSettings:this.#c};if(this.#m)return this.#m;this.#E(e);let t=l();this.#e=c(t),this.#t=t,this.#n?.(),this.#n=a(this.#e,e=>this.#y(e));let n=e.timeoutMs??u,o=new Promise((e,t)=>{let r=setTimeout(()=>{this.#p=null,this.#m=null,t(Error(`Handshake timed out after ${n}ms.`))},n);this.#p={resolve:e,reject:t,timer:r}});this.#m=o;try{i(r.handshakeInit,this.#e)}catch(e){throw this.#N(this.#p),this.#p=null,this.#m=null,this.#n?.(),this.#n=null,this.#e=null,this.#t=null,this.#k(),e}return o}destroy(){this.#i=!0,this.#r=!1,this.#k(),this.#l=!1,this.#f=!1,this.#n?.(),this.#n=null,this.#e=null,this.#t=null,this.#F(this.#p,Error(`SDK destroyed.`)),this.#F(this.#h,Error(`SDK destroyed.`)),this.#F(this.#g,Error(`SDK destroyed.`)),this.#p=null,this.#m=null,this.#h=null,this.#g=null,this.#_.clear(),this.#v.clear(),this.#a=null,this.#o=null,this.#s=null,this.#c=null}getAuthToken(){return this.#a}getExtensionDetails(){return this.#o}getDesignTokens(){return this.#s}getPageSettings(){return this.#c}getAllowedOrigin(){return this.#e}getTenantUrl(){return this.#t}getApiUrl(){return this.#o?e(this.#o.baseUrl):null}async apiCall(e,t={}){return this.#T(),n(e,{baseUrl:this.#o.baseUrl,tenantDomain:this.#o.tenantDomain,authTokenJwt:this.#a.jwt},t)}async emitRequestAuthTokenRefresh(e={}){if(this.#T(),this.#h)throw Error(`Auth token refresh already in progress.`);let t=e.timeoutMs??u,n=this.#e,a=new Promise((e,n)=>{let r=setTimeout(()=>{this.#h=null,n(Error(`Auth token refresh timed out after ${t}ms.`))},t);this.#h={resolve:e,reject:n,timer:r}});return i(r.requestAuthTokenRefresh,n),a}async emitUpdatePageSettings(e,t={}){if(this.#T(),this.#g)throw Error(`Page settings update already in progress.`);let n=t.timeoutMs??u,a=this.#e,o=new Promise((e,t)=>{let r=setTimeout(()=>{this.#g=null,t(Error(`Page settings update timed out after ${n}ms.`))},n);this.#g={resolve:e,reject:t,timer:r}});return i(r.updatePageSettings,a,e),o}onLoadPageSettings(e){return this.#_.add(e),()=>{this.#_.delete(e)}}onAuthTokenRefresh(e){return this.#v.add(e),()=>{this.#v.delete(e)}}emitNavigationChange(e){this.#T(),i(r.navigationChange,this.#e,e)}#y(e){let{type:t,payload:n}=e.data;switch(t){case r.handshakeAck:this.#b(n);break;case r.authTokenRefresh:this.#x(n);break;case r.loadPageSettings:this.#S(n);break;case r.pageSettingsUpdated:this.#C(n);break;default:break}}#b(e){if(!e?.authToken||!e.extensionDetails||!e.designTokens){this.#F(this.#p,Error(`Invalid HANDSHAKE_ACK payload.`)),this.#p=null,this.#m=null;return}this.#a=e.authToken,this.#o=e.extensionDetails,this.#s=e.designTokens,this.#c=e.pageSettings??null,this.#r=!0;let t=this.#p;this.#p=null,this.#m=null,this.#P(t,{authToken:this.#a,extensionDetails:this.#o,designTokens:this.#s,pageSettings:this.#c}),this.#f=!1,this.#A()}#x(e){if(!e?.authToken){this.#F(this.#h,Error(`Invalid TOKEN_REFRESH payload.`)),this.#h=null;return}this.#a=e.authToken;let t=this.#h;this.#h=null,this.#P(t,this.#a);for(let e of this.#v)e(this.#a);this.#f=!1,this.#A()}#S(e){let t=e??{};this.#c=t;for(let e of this.#_)e(t)}#C(e){if(!e||typeof e.success!=`boolean`){this.#F(this.#g,Error(`Invalid PAGE_SETTINGS_UPDATED payload.`)),this.#g=null;return}let t=this.#g;this.#g=null,this.#P(t,e)}#w(){if(this.#i)throw Error(`SDK has been destroyed.`)}#T(){if(this.#w(),!this.#r||!this.#e)throw Error(`SDK is not connected. Call connect() first.`)}#E(e){if(e.authTokenBufferMs!==void 0&&(!Number.isFinite(e.authTokenBufferMs)||e.authTokenBufferMs<0))throw Error(`authTokenBufferMs must be a finite non-negative number.`);this.#l=e.authTokenAutoRefresh===!0,this.#u=e.authTokenBufferMs??d,this.#f=!1,this.#k()}#D(e){let t=Number(e.expires);return Number.isFinite(t)?t*1e3:null}#O(e){let t=this.#D(e);return t===null?null:Math.max(0,t-this.#u-Date.now())}#k(){this.#d!==null&&(clearTimeout(this.#d),this.#d=null)}#A(){if(this.#k(),!this.#l||!this.#r||this.#i||!this.#a)return;let e=this.#O(this.#a);if(e===null)return;let t=Math.min(e,p);this.#d=setTimeout(()=>{this.#d=null,this.#j()},t)}async#j(){if(!this.#l||!this.#r||this.#i||!this.#a)return;let e=this.#a,t=this.#O(e);if(t!==null){if(t>0){this.#A();return}if(this.#h){this.#M(e);return}try{await this.emitRequestAuthTokenRefresh()}catch{this.#M(e)}}}#M(e){if(!this.#l||!this.#r||this.#i||this.#a?.jwt!==e.jwt||this.#f)return;let t=this.#D(e);if(t===null)return;let n=t-Date.now();if(n<=0)return;this.#f=!0,this.#k();let r=Math.min(f,n);this.#d=setTimeout(()=>{this.#d=null,this.#j()},r)}#N(e){e&&clearTimeout(e.timer)}#P(e,t){e&&(clearTimeout(e.timer),e.resolve(t))}#F(e,t){e&&(clearTimeout(e.timer),e.reject(t))}};function h(e,t,n,r){let i=r===void 0?{type:n}:{type:n,payload:r};e.postMessage(i,t)}function g(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 _=class{#e;#t=null;#n=!1;#r=!1;constructor(e){this.#e=e}start(){this.#r||this.#n||(this.#t=g(this.#e.origin,e=>{this.#i(e)}),this.#n=!0)}destroy(){this.#r=!0,this.#n=!1,this.#t?.(),this.#t=null}emitLoadPageSettings(e){this.#u(),this.#l(r.loadPageSettings,e)}async#i(e){if(this.#r)return;let{type:t,payload:n}=e.data;switch(t){case r.handshakeInit:await this.#a();break;case r.requestAuthTokenRefresh:await this.#o();break;case r.updatePageSettings:await this.#s(n);break;default:break}}async#a(){try{let e=await this.#e.resolveHandshakePayload();if(this.#r)return;this.#l(r.handshakeAck,e)}catch{}}async#o(){try{let e=await this.#e.resolveAuthToken();if(this.#r)return;this.#l(r.authTokenRefresh,{authToken:e})}catch{}}async#s(e){let t=e?.settings;if(!t||typeof t!=`object`){this.#c(!1);return}let n=this.#e.onUpdatePageSettings;if(!n){this.#c(!1);return}try{let e=await n(t);if(this.#r)return;this.#c(!!e)}catch{if(this.#r)return;this.#c(!1)}}#c(e){let t={success:e};this.#l(r.pageSettingsUpdated,t)}#l(e,t){let n=this.#e.getContentWindow();n&&h(n,this.#e.origin,e,t)}#u(){if(this.#r)throw Error(`ExtensionSDKHost has been destroyed.`)}};function v(e,t){if(typeof e!=`string`)return!1;try{let n=new URL(e),r=n.protocol===`https:`,i=!t||t(n.origin);return r&&i}catch{return!1}}function y(e,t=window.location.origin){if(typeof e!=`string`)return null;try{let n=new URL(e);return typeof t==`string`&&n.searchParams.set(`tenantUrl`,t),n}catch{return null}}var b=null,x=!1,S=class extends m{destroy(){super.destroy(),b===this&&(b=null,x=!1)}},C=()=>b?(x||=(console.warn(`[@kadanza/extension-sdk] createExtensionSDK() was called more than once. The SDK is a singleton — reuse the same instance for the whole app.`),!0),b):(b=new S,b);exports.CONNECTION_EVENTS=r,exports.ExtensionSDK=m,exports.ExtensionSDKHost=_,exports.InvalidOriginError=s,exports.createExtensionSDK=C,exports.enrichExtensionUrl=y,exports.isValidExtensionUrl=v,exports.postToChild=h,exports.readTenantUrlFromLocation=l,exports.resolveAllowedOrigin=c,exports.subscribeToChildMessages=g;
|
|
2
2
|
//# sourceMappingURL=extension-sdk.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-sdk.cjs","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#configureAuthTokenAutoRefresh","#scheduleProactiveAuthTokenRefresh","#pageSettings","#handshakePromise","#allowedOrigin","#tenantUrl","#unsubscribe","#onMessage","#pendingHandshake","#clearPending","#clearProactiveAuthTokenRefreshTimer","#destroyed","#authTokenAutoRefresh","#authTokenRefreshRetryUsed","#rejectPending","#pendingAuthTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#authTokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleAuthTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending","#authTokenBufferMs","#parseExpiresAtMs","#authTokenRefreshTimer","#getProactiveRefreshDelayMs","#runProactiveAuthTokenRefresh","#handleProactiveAuthTokenRefreshFailure"],"sources":["../src/api.ts","../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["interface ApiRequestContext {\n baseUrl: string;\n tenantDomain: string;\n authTokenJwt: string;\n}\n\n/** Derives the Platform API origin from handshake `baseUrl`. */\nexport function deriveApiUrl(baseUrl: string): string {\n if (baseUrl.startsWith(\"/\")) {\n return baseUrl;\n }\n\n const url = new URL(baseUrl);\n url.hostname = `api.${url.hostname}`;\n return url.origin;\n}\n\nfunction resolveApiEndpoint(baseUrl: string, endpoint: string): string {\n if (!endpoint.startsWith(\"/\") || endpoint.startsWith(\"//\")) {\n throw new Error(\n 'API endpoint must be a root-relative path starting with a single \"/\".',\n );\n }\n\n return `${deriveApiUrl(baseUrl).replace(/\\/+$/, \"\")}${endpoint}`;\n}\n\nexport async function callApi<T>(\n endpoint: string,\n context: ApiRequestContext,\n options: RequestInit = {},\n): Promise<T> {\n const headers = new Headers(options.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Authorization\", `Bearer ${context.authTokenJwt}`);\n headers.set(\"X-Tenant\", context.tenantDomain);\n\n const response = await fetch(resolveApiEndpoint(context.baseUrl, endpoint), {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const statusText = response.statusText ? ` ${response.statusText}` : \"\";\n throw new Error(`API call failed: ${response.status}${statusText}.`);\n }\n\n return (await response.json()) as T;\n}\n","/**\n * Wire event type strings for parent/child `postMessage` communication.\n *\n * Keys are SDK-facing names; values are the strings sent on the wire.\n * Comments mark message direction relative to the extension (child).\n */\nexport const CONNECTION_EVENTS = {\n /** Child → parent: start handshake. */\n handshakeInit: \"HANDSHAKE_INIT\",\n /** Parent → child: handshake success with auth and context. */\n handshakeAck: \"HANDSHAKE_ACK\",\n /** Child → parent: request a new auth token. */\n requestAuthTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n /** Parent → child: new auth token (reply or push). */\n authTokenRefresh: \"TOKEN_REFRESH\",\n /** Parent → child: open/load page settings UI with values. */\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n /** Child → parent: persist updated page settings. */\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n /** Parent → child: result of a page settings update. */\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n /** Child → parent: extension route changed. */\n navigationChange: \"NAVIGATION_CHANGE\",\n} as const;\n\n/** Union of all {@link CONNECTION_EVENTS} wire string values. */\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\n/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */\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 *\n * @param tenantUrl - Absolute URL from the `tenantUrl` search param.\n * @returns Origin string suitable for `postMessage` targetOrigin.\n * @throws {InvalidOriginError} When missing, unparsable, or disallowed scheme/host.\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\n/**\n * Reads the parent-supplied `tenantUrl` from a URL search string.\n *\n * The parent must pass `tenantUrl` on the iframe `src` so\n * {@link resolveAllowedOrigin} can whitelist the parent origin.\n *\n * @param search - Query string (defaults to `window.location.search`).\n */\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { callApi, deriveApiUrl } from \"./api\";\nimport { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_AUTH_TOKEN_BUFFER_MS = 120_000;\nconst AUTO_REFRESH_RETRY_DELAY_MS = 30_000;\n/** Largest delay `setTimeout` can reliably use in browsers. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Public contract for talking to the Kadanza parent frame over `postMessage`.\n *\n * Requires a `tenantUrl` search param on the extension URL so\n * {@link connect} can validate the parent origin.\n */\nexport interface IExtensionSDK {\n /**\n * Establishes the parent connection and waits for `HANDSHAKE_ACK`.\n *\n * Safe to call repeatedly (e.g. from a React `useEffect` with deps):\n * - Already connected: returns the cached handshake and re-applies\n * `authTokenAutoRefresh` / `authTokenBufferMs` without another init.\n * `timeoutMs` is ignored after connect.\n * - Handshake in progress: returns the same in-flight promise. Options from\n * the first connecting call apply until the handshake completes.\n *\n * Opt-in proactive refresh can be enabled with\n * `authTokenAutoRefresh: true` (optional `authTokenBufferMs`).\n *\n * @param options - Handshake timeout and optional auth-token auto-refresh.\n * @throws {InvalidOriginError} When `tenantUrl` is missing or invalid.\n * @throws When not embedded in a parent frame, destroyed, or the handshake\n * times out / returns an invalid payload.\n */\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n\n /**\n * Tears down listeners, proactive refresh timers, and cached state.\n * Rejects any in-flight requests. The instance cannot be reused after destroy.\n * When created via {@link createExtensionSDK}, clears the singleton so a\n * later create call can return a fresh instance.\n */\n destroy(): void;\n\n /** Whether a successful handshake has completed and not been destroyed. */\n readonly isConnected: boolean;\n\n /** Last auth token from handshake or refresh; `null` until connected. */\n getAuthToken(): AuthToken | null;\n\n /** Extension context from handshake; `null` until connected. */\n getExtensionDetails(): ExtensionDetails | null;\n\n /** Design tokens from handshake; `null` until connected. */\n getDesignTokens(): DesignTokens | null;\n\n /**\n * Latest page settings from handshake or `LOAD_PAGE_SETTINGS`;\n * `null` until set.\n */\n getPageSettings(): PageSettings | null;\n\n /**\n * Validated parent `postMessage` origin from `tenantUrl`;\n * `null` until `connect` resolves it.\n */\n getAllowedOrigin(): string | null;\n\n /**\n * Raw `tenantUrl` search-param value used for the allowed origin;\n * `null` until `connect` reads it.\n */\n getTenantUrl(): string | null;\n\n /**\n * Platform API origin derived from handshake `baseUrl`;\n * `null` until connected.\n */\n getApiUrl(): string | null;\n\n /**\n * Calls the Kadanza Platform API with the current connection credentials.\n *\n * The API origin is derived from the handshake `baseUrl`. Authorization and\n * tenant headers are managed by the SDK and cannot be overridden.\n *\n * @typeParam T - Expected JSON response body.\n * @param endpoint - Root-relative API path.\n * @param options - Standard fetch options.\n * @throws When not connected, the endpoint is invalid, the request fails,\n * or the response is not successful JSON.\n */\n apiCall<T>(endpoint: string, options?: RequestInit): Promise<T>;\n\n /**\n * Asks the parent for a new auth token and waits for `TOKEN_REFRESH`.\n *\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, a refresh is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitRequestAuthTokenRefresh(\n options?: RequestOptions,\n ): Promise<AuthToken>;\n\n /**\n * Pushes page settings to the parent and waits for `PAGE_SETTINGS_UPDATED`.\n *\n * @param payload - Update payload containing the settings object.\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, an update is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n /**\n * Registers a handler for parent-initiated `LOAD_PAGE_SETTINGS`.\n *\n * @returns Unsubscribe function.\n */\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n\n /**\n * Registers a handler for auth-token updates (requested or parent-pushed).\n *\n * @returns Unsubscribe function.\n */\n onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void;\n\n /**\n * Notifies the parent that the extension's route changed.\n *\n * Wire: `NAVIGATION_CHANGE` (fire-and-forget).\n *\n * @param payload - Navigation change payload (`path` within the extension).\n * @throws When not connected or destroyed.\n */\n emitNavigationChange(payload: NavigationChangePayload): void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** Default {@link IExtensionSDK} implementation. Prefer {@link createExtensionSDK}. */\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #tenantUrl: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: AuthToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #authTokenAutoRefresh = false;\n #authTokenBufferMs = DEFAULT_AUTH_TOKEN_BUFFER_MS;\n #authTokenRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n #authTokenRefreshRetryUsed = false;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #handshakePromise: Promise<HandshakePayload> | null = null;\n #pendingAuthTokenRefresh: Pending<AuthToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #authTokenRefreshHandlers = new Set<(authToken: AuthToken) => 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 this.#configureAuthTokenAutoRefresh(options);\n this.#scheduleProactiveAuthTokenRefresh();\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#handshakePromise) {\n return this.#handshakePromise;\n }\n\n this.#configureAuthTokenAutoRefresh(options);\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n this.#tenantUrl = 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 this.#handshakePromise = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n this.#handshakePromise = handshakePromise;\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n this.#clearProactiveAuthTokenRefreshTimer();\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#clearProactiveAuthTokenRefreshTimer();\n this.#authTokenAutoRefresh = false;\n this.#authTokenRefreshRetryUsed = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingAuthTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#pendingAuthTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#authTokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): AuthToken | 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 getAllowedOrigin(): string | null {\n return this.#allowedOrigin;\n }\n\n getTenantUrl(): string | null {\n return this.#tenantUrl;\n }\n\n getApiUrl(): string | null {\n if (!this.#extensionDetails) {\n return null;\n }\n\n return deriveApiUrl(this.#extensionDetails.baseUrl);\n }\n\n async apiCall<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n this.#assertConnected();\n\n return callApi<T>(\n endpoint,\n {\n baseUrl: this.#extensionDetails!.baseUrl,\n tenantDomain: this.#extensionDetails!.tenantDomain,\n authTokenJwt: this.#authToken!.jwt,\n },\n options,\n );\n }\n\n async emitRequestAuthTokenRefresh(\n options: RequestOptions = {},\n ): Promise<AuthToken> {\n this.#assertConnected();\n\n if (this.#pendingAuthTokenRefresh) {\n throw new Error(\"Auth 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<AuthToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingAuthTokenRefresh = null;\n reject(new Error(`Auth token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingAuthTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestAuthTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\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, payload);\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 onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void {\n this.#authTokenRefreshHandlers.add(handler);\n return () => {\n this.#authTokenRefreshHandlers.delete(handler);\n };\n }\n\n emitNavigationChange(payload: NavigationChangePayload): void {\n this.#assertConnected();\n postToParent(\n CONNECTION_EVENTS.navigationChange,\n this.#allowedOrigin!,\n payload,\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.authTokenRefresh:\n this.#handleAuthTokenRefresh(payload as AuthTokenRefreshPayload | 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 this.#handshakePromise = 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.#handshakePromise = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\n }\n\n #handleAuthTokenRefresh(payload: AuthTokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingAuthTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingAuthTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingAuthTokenRefresh;\n this.#pendingAuthTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#authTokenRefreshHandlers) {\n handler(this.#authToken);\n }\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\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 #configureAuthTokenAutoRefresh(options: ConnectOptions): void {\n if (options.authTokenBufferMs !== undefined) {\n if (\n !Number.isFinite(options.authTokenBufferMs) ||\n options.authTokenBufferMs < 0\n ) {\n throw new Error(\n \"authTokenBufferMs must be a finite non-negative number.\",\n );\n }\n }\n\n this.#authTokenAutoRefresh = options.authTokenAutoRefresh === true;\n this.#authTokenBufferMs =\n options.authTokenBufferMs ?? DEFAULT_AUTH_TOKEN_BUFFER_MS;\n this.#authTokenRefreshRetryUsed = false;\n this.#clearProactiveAuthTokenRefreshTimer();\n }\n\n #parseExpiresAtMs(authToken: AuthToken): number | null {\n const expiresSec = Number(authToken.expires);\n if (!Number.isFinite(expiresSec)) {\n return null;\n }\n return expiresSec * 1000;\n }\n\n #getProactiveRefreshDelayMs(authToken: AuthToken): number | null {\n const expiresAtMs = this.#parseExpiresAtMs(authToken);\n if (expiresAtMs === null) {\n return null;\n }\n return Math.max(0, expiresAtMs - this.#authTokenBufferMs - Date.now());\n }\n\n #clearProactiveAuthTokenRefreshTimer(): void {\n if (this.#authTokenRefreshTimer !== null) {\n clearTimeout(this.#authTokenRefreshTimer);\n this.#authTokenRefreshTimer = null;\n }\n }\n\n #scheduleProactiveAuthTokenRefresh(): void {\n this.#clearProactiveAuthTokenRefreshTimer();\n\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const delayMs = this.#getProactiveRefreshDelayMs(this.#authToken);\n if (delayMs === null) {\n return;\n }\n\n const clampedDelay = Math.min(delayMs, MAX_TIMEOUT_MS);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, clampedDelay);\n }\n\n async #runProactiveAuthTokenRefresh(): Promise<void> {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const authTokenAtRequest = this.#authToken;\n const delayMs = this.#getProactiveRefreshDelayMs(authTokenAtRequest);\n if (delayMs === null) {\n return;\n }\n\n if (delayMs > 0) {\n this.#scheduleProactiveAuthTokenRefresh();\n return;\n }\n\n if (this.#pendingAuthTokenRefresh) {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n return;\n }\n\n try {\n await this.emitRequestAuthTokenRefresh();\n } catch {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n }\n }\n\n #handleProactiveAuthTokenRefreshFailure(authTokenAtRequest: AuthToken): void {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n this.#authToken?.jwt !== authTokenAtRequest.jwt\n ) {\n return;\n }\n\n if (this.#authTokenRefreshRetryUsed) {\n return;\n }\n\n const expiresAtMs = this.#parseExpiresAtMs(authTokenAtRequest);\n if (expiresAtMs === null) {\n return;\n }\n\n const remainingMs = expiresAtMs - Date.now();\n if (remainingMs <= 0) {\n return;\n }\n\n this.#authTokenRefreshRetryUsed = true;\n this.#clearProactiveAuthTokenRefreshTimer();\n\n const retryDelay = Math.min(AUTO_REFRESH_RETRY_DELAY_MS, remainingMs);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, retryDelay);\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 AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nlet instance: IExtensionSDK | null = null;\nlet hasWarnedDuplicateCreate = false;\n\n/** Clears the factory singleton when the managed instance is destroyed. */\nclass ManagedExtensionSDK extends ExtensionSDK {\n destroy(): void {\n super.destroy();\n if (instance === this) {\n instance = null;\n hasWarnedDuplicateCreate = false;\n }\n }\n}\n\n/**\n * Returns the shared {@link IExtensionSDK} for the current extension frame.\n *\n * Creates the instance on first call. Later calls return the same instance and\n * log a console warning — use the SDK once per app. After {@link IExtensionSDK.destroy},\n * the next call creates a fresh instance.\n */\nexport const createExtensionSDK = (): IExtensionSDK => {\n if (instance) {\n if (!hasWarnedDuplicateCreate) {\n console.warn(\n `[${__PACKAGE_NAME__}] createExtensionSDK() was called more than once. ` +\n \"The SDK is a singleton — reuse the same instance for the whole app.\",\n );\n hasWarnedDuplicateCreate = true;\n }\n return instance;\n }\n\n instance = new ManagedExtensionSDK();\n return instance;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension-sdk.cjs","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#configureAuthTokenAutoRefresh","#scheduleProactiveAuthTokenRefresh","#pageSettings","#handshakePromise","#allowedOrigin","#tenantUrl","#unsubscribe","#onMessage","#pendingHandshake","#clearPending","#clearProactiveAuthTokenRefreshTimer","#destroyed","#authTokenAutoRefresh","#authTokenRefreshRetryUsed","#rejectPending","#pendingAuthTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#authTokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleAuthTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending","#authTokenBufferMs","#parseExpiresAtMs","#authTokenRefreshTimer","#getProactiveRefreshDelayMs","#runProactiveAuthTokenRefresh","#handleProactiveAuthTokenRefreshFailure","#options","#destroyed","#started","#unsubscribe","#onMessage","#assertNotDestroyed","#post","#handleHandshakeInit","#handleAuthTokenRefresh","#handleUpdatePageSettings","#postPageSettingsUpdated"],"sources":["../src/api.ts","../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/hostMessaging.ts","../src/ExtensionSDKHost.ts","../src/hostUrl.ts","../src/index.ts"],"sourcesContent":["interface ApiRequestContext {\n baseUrl: string;\n tenantDomain: string;\n authTokenJwt: string;\n}\n\n/** Derives the Platform API origin from handshake `baseUrl`. */\nexport function deriveApiUrl(baseUrl: string): string {\n if (baseUrl.startsWith(\"/\")) {\n return baseUrl;\n }\n\n const url = new URL(baseUrl);\n url.hostname = `api.${url.hostname}`;\n return url.origin;\n}\n\nfunction resolveApiEndpoint(baseUrl: string, endpoint: string): string {\n if (!endpoint.startsWith(\"/\") || endpoint.startsWith(\"//\")) {\n throw new Error(\n 'API endpoint must be a root-relative path starting with a single \"/\".',\n );\n }\n\n return `${deriveApiUrl(baseUrl).replace(/\\/+$/, \"\")}${endpoint}`;\n}\n\nexport async function callApi<T>(\n endpoint: string,\n context: ApiRequestContext,\n options: RequestInit = {},\n): Promise<T> {\n const headers = new Headers(options.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Authorization\", `Bearer ${context.authTokenJwt}`);\n headers.set(\"X-Tenant\", context.tenantDomain);\n\n const response = await fetch(resolveApiEndpoint(context.baseUrl, endpoint), {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const statusText = response.statusText ? ` ${response.statusText}` : \"\";\n throw new Error(`API call failed: ${response.status}${statusText}.`);\n }\n\n return (await response.json()) as T;\n}\n","/**\n * Wire event type strings for parent/child `postMessage` communication.\n *\n * Keys are SDK-facing names; values are the strings sent on the wire.\n * Comments mark message direction relative to the extension (child).\n */\nexport const CONNECTION_EVENTS = {\n /** Child → parent: start handshake. */\n handshakeInit: \"HANDSHAKE_INIT\",\n /** Parent → child: handshake success with auth and context. */\n handshakeAck: \"HANDSHAKE_ACK\",\n /** Child → parent: request a new auth token. */\n requestAuthTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n /** Parent → child: new auth token (reply or push). */\n authTokenRefresh: \"TOKEN_REFRESH\",\n /** Parent → child: open/load page settings UI with values. */\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n /** Child → parent: persist updated page settings. */\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n /** Parent → child: result of a page settings update. */\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n /** Child → parent: extension route changed. */\n navigationChange: \"NAVIGATION_CHANGE\",\n} as const;\n\n/** Union of all {@link CONNECTION_EVENTS} wire string values. */\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\n/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */\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 *\n * @param tenantUrl - Absolute URL from the `tenantUrl` search param.\n * @returns Origin string suitable for `postMessage` targetOrigin.\n * @throws {InvalidOriginError} When missing, unparsable, or disallowed scheme/host.\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\n/**\n * Reads the parent-supplied `tenantUrl` from a URL search string.\n *\n * The parent must pass `tenantUrl` on the iframe `src` so\n * {@link resolveAllowedOrigin} can whitelist the parent origin.\n *\n * @param search - Query string (defaults to `window.location.search`).\n */\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { callApi, deriveApiUrl } from \"./api\";\nimport { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_AUTH_TOKEN_BUFFER_MS = 120_000;\nconst AUTO_REFRESH_RETRY_DELAY_MS = 30_000;\n/** Largest delay `setTimeout` can reliably use in browsers. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Public contract for talking to the Kadanza parent frame over `postMessage`.\n *\n * Requires a `tenantUrl` search param on the extension URL so\n * {@link connect} can validate the parent origin.\n */\nexport interface IExtensionSDK {\n /**\n * Establishes the parent connection and waits for `HANDSHAKE_ACK`.\n *\n * Safe to call repeatedly (e.g. from a React `useEffect` with deps):\n * - Already connected: returns the cached handshake and re-applies\n * `authTokenAutoRefresh` / `authTokenBufferMs` without another init.\n * `timeoutMs` is ignored after connect.\n * - Handshake in progress: returns the same in-flight promise. Options from\n * the first connecting call apply until the handshake completes.\n *\n * Opt-in proactive refresh can be enabled with\n * `authTokenAutoRefresh: true` (optional `authTokenBufferMs`).\n *\n * @param options - Handshake timeout and optional auth-token auto-refresh.\n * @throws {InvalidOriginError} When `tenantUrl` is missing or invalid.\n * @throws When not embedded in a parent frame, destroyed, or the handshake\n * times out / returns an invalid payload.\n */\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n\n /**\n * Tears down listeners, proactive refresh timers, and cached state.\n * Rejects any in-flight requests. The instance cannot be reused after destroy.\n * When created via {@link createExtensionSDK}, clears the singleton so a\n * later create call can return a fresh instance.\n */\n destroy(): void;\n\n /** Whether a successful handshake has completed and not been destroyed. */\n readonly isConnected: boolean;\n\n /** Last auth token from handshake or refresh; `null` until connected. */\n getAuthToken(): AuthToken | null;\n\n /** Extension context from handshake; `null` until connected. */\n getExtensionDetails(): ExtensionDetails | null;\n\n /** Design tokens from handshake; `null` until connected. */\n getDesignTokens(): DesignTokens | null;\n\n /**\n * Latest page settings from handshake or `LOAD_PAGE_SETTINGS`;\n * `null` until set.\n */\n getPageSettings(): PageSettings | null;\n\n /**\n * Validated parent `postMessage` origin from `tenantUrl`;\n * `null` until `connect` resolves it.\n */\n getAllowedOrigin(): string | null;\n\n /**\n * Raw `tenantUrl` search-param value used for the allowed origin;\n * `null` until `connect` reads it.\n */\n getTenantUrl(): string | null;\n\n /**\n * Platform API origin derived from handshake `baseUrl`;\n * `null` until connected.\n */\n getApiUrl(): string | null;\n\n /**\n * Calls the Kadanza Platform API with the current connection credentials.\n *\n * The API origin is derived from the handshake `baseUrl`. Authorization and\n * tenant headers are managed by the SDK and cannot be overridden.\n *\n * @typeParam T - Expected JSON response body.\n * @param endpoint - Root-relative API path.\n * @param options - Standard fetch options.\n * @throws When not connected, the endpoint is invalid, the request fails,\n * or the response is not successful JSON.\n */\n apiCall<T>(endpoint: string, options?: RequestInit): Promise<T>;\n\n /**\n * Asks the parent for a new auth token and waits for `TOKEN_REFRESH`.\n *\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, a refresh is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitRequestAuthTokenRefresh(\n options?: RequestOptions,\n ): Promise<AuthToken>;\n\n /**\n * Pushes page settings to the parent and waits for `PAGE_SETTINGS_UPDATED`.\n *\n * @param payload - Update payload containing the settings object.\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, an update is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n /**\n * Registers a handler for parent-initiated `LOAD_PAGE_SETTINGS`.\n *\n * @returns Unsubscribe function.\n */\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n\n /**\n * Registers a handler for auth-token updates (requested or parent-pushed).\n *\n * @returns Unsubscribe function.\n */\n onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void;\n\n /**\n * Notifies the parent that the extension's route changed.\n *\n * Wire: `NAVIGATION_CHANGE` (fire-and-forget).\n *\n * @param payload - Navigation change payload (`path` within the extension).\n * @throws When not connected or destroyed.\n */\n emitNavigationChange(payload: NavigationChangePayload): void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** Default {@link IExtensionSDK} implementation. Prefer {@link createExtensionSDK}. */\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #tenantUrl: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: AuthToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #authTokenAutoRefresh = false;\n #authTokenBufferMs = DEFAULT_AUTH_TOKEN_BUFFER_MS;\n #authTokenRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n #authTokenRefreshRetryUsed = false;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #handshakePromise: Promise<HandshakePayload> | null = null;\n #pendingAuthTokenRefresh: Pending<AuthToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #authTokenRefreshHandlers = new Set<(authToken: AuthToken) => 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 this.#configureAuthTokenAutoRefresh(options);\n this.#scheduleProactiveAuthTokenRefresh();\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#handshakePromise) {\n return this.#handshakePromise;\n }\n\n this.#configureAuthTokenAutoRefresh(options);\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n this.#tenantUrl = 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 this.#handshakePromise = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n this.#handshakePromise = handshakePromise;\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n this.#clearProactiveAuthTokenRefreshTimer();\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#clearProactiveAuthTokenRefreshTimer();\n this.#authTokenAutoRefresh = false;\n this.#authTokenRefreshRetryUsed = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingAuthTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#pendingAuthTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#authTokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): AuthToken | 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 getAllowedOrigin(): string | null {\n return this.#allowedOrigin;\n }\n\n getTenantUrl(): string | null {\n return this.#tenantUrl;\n }\n\n getApiUrl(): string | null {\n if (!this.#extensionDetails) {\n return null;\n }\n\n return deriveApiUrl(this.#extensionDetails.baseUrl);\n }\n\n async apiCall<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n this.#assertConnected();\n\n return callApi<T>(\n endpoint,\n {\n baseUrl: this.#extensionDetails!.baseUrl,\n tenantDomain: this.#extensionDetails!.tenantDomain,\n authTokenJwt: this.#authToken!.jwt,\n },\n options,\n );\n }\n\n async emitRequestAuthTokenRefresh(\n options: RequestOptions = {},\n ): Promise<AuthToken> {\n this.#assertConnected();\n\n if (this.#pendingAuthTokenRefresh) {\n throw new Error(\"Auth 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<AuthToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingAuthTokenRefresh = null;\n reject(new Error(`Auth token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingAuthTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestAuthTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\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, payload);\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 onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void {\n this.#authTokenRefreshHandlers.add(handler);\n return () => {\n this.#authTokenRefreshHandlers.delete(handler);\n };\n }\n\n emitNavigationChange(payload: NavigationChangePayload): void {\n this.#assertConnected();\n postToParent(\n CONNECTION_EVENTS.navigationChange,\n this.#allowedOrigin!,\n payload,\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 }\n case CONNECTION_EVENTS.authTokenRefresh: {\n this.#handleAuthTokenRefresh(payload as AuthTokenRefreshPayload | undefined);\n break;\n }\n case CONNECTION_EVENTS.loadPageSettings: {\n this.#handleLoadPageSettings(payload as PageSettings | undefined);\n break;\n }\n case CONNECTION_EVENTS.pageSettingsUpdated: {\n this.#handlePageSettingsUpdated(\n payload as PageSettingsUpdatedPayload | undefined,\n );\n break;\n }\n default: {\n break;\n }\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 this.#handshakePromise = 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.#handshakePromise = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\n }\n\n #handleAuthTokenRefresh(payload: AuthTokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingAuthTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingAuthTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingAuthTokenRefresh;\n this.#pendingAuthTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#authTokenRefreshHandlers) {\n handler(this.#authToken);\n }\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\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 #configureAuthTokenAutoRefresh(options: ConnectOptions): void {\n if (options.authTokenBufferMs !== undefined) {\n if (\n !Number.isFinite(options.authTokenBufferMs) ||\n options.authTokenBufferMs < 0\n ) {\n throw new Error(\n \"authTokenBufferMs must be a finite non-negative number.\",\n );\n }\n }\n\n this.#authTokenAutoRefresh = options.authTokenAutoRefresh === true;\n this.#authTokenBufferMs =\n options.authTokenBufferMs ?? DEFAULT_AUTH_TOKEN_BUFFER_MS;\n this.#authTokenRefreshRetryUsed = false;\n this.#clearProactiveAuthTokenRefreshTimer();\n }\n\n #parseExpiresAtMs(authToken: AuthToken): number | null {\n const expiresSec = Number(authToken.expires);\n if (!Number.isFinite(expiresSec)) {\n return null;\n }\n return expiresSec * 1000;\n }\n\n #getProactiveRefreshDelayMs(authToken: AuthToken): number | null {\n const expiresAtMs = this.#parseExpiresAtMs(authToken);\n if (expiresAtMs === null) {\n return null;\n }\n return Math.max(0, expiresAtMs - this.#authTokenBufferMs - Date.now());\n }\n\n #clearProactiveAuthTokenRefreshTimer(): void {\n if (this.#authTokenRefreshTimer !== null) {\n clearTimeout(this.#authTokenRefreshTimer);\n this.#authTokenRefreshTimer = null;\n }\n }\n\n #scheduleProactiveAuthTokenRefresh(): void {\n this.#clearProactiveAuthTokenRefreshTimer();\n\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const delayMs = this.#getProactiveRefreshDelayMs(this.#authToken);\n if (delayMs === null) {\n return;\n }\n\n const clampedDelay = Math.min(delayMs, MAX_TIMEOUT_MS);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, clampedDelay);\n }\n\n async #runProactiveAuthTokenRefresh(): Promise<void> {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const authTokenAtRequest = this.#authToken;\n const delayMs = this.#getProactiveRefreshDelayMs(authTokenAtRequest);\n if (delayMs === null) {\n return;\n }\n\n if (delayMs > 0) {\n this.#scheduleProactiveAuthTokenRefresh();\n return;\n }\n\n if (this.#pendingAuthTokenRefresh) {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n return;\n }\n\n try {\n await this.emitRequestAuthTokenRefresh();\n } catch {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n }\n }\n\n #handleProactiveAuthTokenRefreshFailure(authTokenAtRequest: AuthToken): void {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n this.#authToken?.jwt !== authTokenAtRequest.jwt\n ) {\n return;\n }\n\n if (this.#authTokenRefreshRetryUsed) {\n return;\n }\n\n const expiresAtMs = this.#parseExpiresAtMs(authTokenAtRequest);\n if (expiresAtMs === null) {\n return;\n }\n\n const remainingMs = expiresAtMs - Date.now();\n if (remainingMs <= 0) {\n return;\n }\n\n this.#authTokenRefreshRetryUsed = true;\n this.#clearProactiveAuthTokenRefreshTimer();\n\n const retryDelay = Math.min(AUTO_REFRESH_RETRY_DELAY_MS, remainingMs);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, retryDelay);\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 type { ConnectionEvent } from \"./events\";\nimport type { ExtensionMessage } from \"./types\";\n\nexport type HostMessageHandler = (\n event: MessageEvent<ExtensionMessage>,\n) => void;\n\n/**\n * Posts a typed extension message to an embedded child frame.\n *\n * @param contentWindow - The iframe's `contentWindow`.\n * @param origin - Target origin for `postMessage` (extension origin).\n * @param type - Wire event type from {@link CONNECTION_EVENTS}.\n * @param payload - Optional message payload.\n */\nexport function postToChild(\n contentWindow: Window,\n origin: string,\n type: ConnectionEvent,\n payload?: unknown,\n): void {\n const message: ExtensionMessage =\n payload === undefined ? { type } : { type, payload };\n contentWindow.postMessage(message, origin);\n}\n\n/**\n * Subscribes to `message` events from a child frame at `origin`.\n *\n * Ignores events from other origins and payloads without a `type` field.\n *\n * @returns Unsubscribe function.\n */\nexport function subscribeToChildMessages(\n origin: string,\n handler: HostMessageHandler,\n): () => void {\n const listener = (event: MessageEvent) => {\n if (event.origin !== origin) {\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","import { CONNECTION_EVENTS } from \"./events\";\nimport { postToChild, subscribeToChildMessages } from \"./hostMessaging\";\nimport type {\n AuthToken,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\n/**\n * Options for {@link ExtensionSDKHost}.\n *\n * Platform code supplies token minting and page-settings persistence via\n * callbacks; the host owns only the postMessage protocol.\n */\nexport interface ExtensionSDKHostOptions {\n /** Returns the iframe `contentWindow`, or `null` if not ready. */\n getContentWindow: () => Window | null;\n /** Extension origin used as `postMessage` targetOrigin / source filter. */\n origin: string;\n /**\n * Builds the handshake payload (including auth token) when the child sends\n * `HANDSHAKE_INIT`.\n */\n resolveHandshakePayload: () => HandshakePayload | Promise<HandshakePayload>;\n /**\n * Resolves a fresh auth token when the child sends `REQUEST_TOKEN_REFRESH`.\n */\n resolveAuthToken: () => AuthToken | Promise<AuthToken>;\n /**\n * Persists page settings from `UPDATE_PAGE_SETTINGS`.\n * Return `true` on success; the host emits `PAGE_SETTINGS_UPDATED`.\n */\n onUpdatePageSettings?: (\n settings: PageSettings,\n ) => boolean | Promise<boolean>;\n}\n\n/**\n * Parent-frame counterpart to {@link IExtensionSDK}.\n *\n * Listens for child messages and replies with the shared wire protocol.\n * Create one instance per iframe.\n */\nexport interface IExtensionSDKHost {\n /**\n * Attaches the child message listener. Safe to call once; no-ops if already\n * started or destroyed.\n */\n start(): void;\n\n /**\n * Tears down the listener. The instance cannot be restarted after destroy.\n */\n destroy(): void;\n\n /**\n * Asks the child to open page settings UI with the given values.\n *\n * Wire: `LOAD_PAGE_SETTINGS` (fire-and-forget).\n */\n emitLoadPageSettings(settings: PageSettings | null): void;\n}\n\n/** Default {@link IExtensionSDKHost} implementation. */\nexport class ExtensionSDKHost implements IExtensionSDKHost {\n #options: ExtensionSDKHostOptions;\n #unsubscribe: (() => void) | null = null;\n #started = false;\n #destroyed = false;\n\n constructor(options: ExtensionSDKHostOptions) {\n this.#options = options;\n }\n\n start(): void {\n if (this.#destroyed || this.#started) {\n return;\n }\n\n this.#unsubscribe = subscribeToChildMessages(\n this.#options.origin,\n (event) => {\n void this.#onMessage(event);\n },\n );\n this.#started = true;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#started = false;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n }\n\n emitLoadPageSettings(settings: PageSettings | null): void {\n this.#assertNotDestroyed();\n this.#post(CONNECTION_EVENTS.loadPageSettings, settings);\n }\n\n async #onMessage(event: MessageEvent<ExtensionMessage>): Promise<void> {\n if (this.#destroyed) {\n return;\n }\n\n const { type, payload } = event.data;\n\n switch (type) {\n case CONNECTION_EVENTS.handshakeInit: {\n await this.#handleHandshakeInit();\n break;\n }\n case CONNECTION_EVENTS.requestAuthTokenRefresh: {\n await this.#handleAuthTokenRefresh();\n break;\n }\n case CONNECTION_EVENTS.updatePageSettings: {\n await this.#handleUpdatePageSettings(\n payload as UpdatePageSettingsPayload | undefined,\n );\n break;\n }\n default: {\n break;\n }\n }\n }\n\n async #handleHandshakeInit(): Promise<void> {\n try {\n const handshakePayload = await this.#options.resolveHandshakePayload();\n if (this.#destroyed) {\n return;\n }\n this.#post(CONNECTION_EVENTS.handshakeAck, handshakePayload);\n } catch {\n // Platform owns error UI / retry; do not reply with a partial ACK.\n }\n }\n\n async #handleAuthTokenRefresh(): Promise<void> {\n try {\n const authToken = await this.#options.resolveAuthToken();\n if (this.#destroyed) {\n return;\n }\n this.#post(CONNECTION_EVENTS.authTokenRefresh, { authToken });\n } catch {\n // Platform owns error UI / retry; do not reply with an invalid token.\n }\n }\n\n async #handleUpdatePageSettings(\n payload: UpdatePageSettingsPayload | undefined,\n ): Promise<void> {\n const settings = payload?.settings;\n if (!settings || typeof settings !== \"object\") {\n this.#postPageSettingsUpdated(false);\n return;\n }\n\n const handler = this.#options.onUpdatePageSettings;\n if (!handler) {\n this.#postPageSettingsUpdated(false);\n return;\n }\n\n try {\n const success = await handler(settings);\n if (this.#destroyed) {\n return;\n }\n this.#postPageSettingsUpdated(Boolean(success));\n } catch {\n if (this.#destroyed) {\n return;\n }\n this.#postPageSettingsUpdated(false);\n }\n }\n\n #postPageSettingsUpdated(success: boolean): void {\n const payload: PageSettingsUpdatedPayload = { success };\n this.#post(CONNECTION_EVENTS.pageSettingsUpdated, payload);\n }\n\n #post(type: (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS], payload?: unknown): void {\n const contentWindow = this.#options.getContentWindow();\n if (!contentWindow) {\n return;\n }\n postToChild(contentWindow, this.#options.origin, type, payload);\n }\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new Error(\"ExtensionSDKHost has been destroyed.\");\n }\n }\n}\n","/**\n * Returns whether `urlRaw` is a valid HTTPS extension URL.\n *\n * When `checkOrigin` is provided, the URL's origin must also pass that predicate\n * (e.g. hostname ends with `.kadanza.app`).\n */\nexport function isValidExtensionUrl(\n urlRaw: string | null | undefined,\n checkOrigin?: (origin: string) => boolean,\n): boolean {\n if (typeof urlRaw !== \"string\") {\n return false;\n }\n\n try {\n const url = new URL(urlRaw);\n const allowedProtocol = url.protocol === \"https:\";\n const allowedOrigin = checkOrigin ? checkOrigin(url.origin) : true;\n return allowedProtocol && allowedOrigin;\n } catch {\n return false;\n }\n}\n\n/**\n * Clones `urlRaw` and sets the `tenantUrl` search param (parent origin).\n *\n * @param urlRaw - Absolute extension URL string.\n * @param tenantUrl - Parent origin to embed (defaults to `window.location.origin`).\n * @returns Enriched `URL`, or `null` when `urlRaw` is missing or unparsable.\n */\nexport function enrichExtensionUrl(\n urlRaw: string | null | undefined,\n tenantUrl: string = window.location.origin,\n): URL | null {\n if (typeof urlRaw !== \"string\") {\n return null;\n }\n\n try {\n const enrichedUrl = new URL(urlRaw);\n\n if (typeof tenantUrl === \"string\") {\n enrichedUrl.searchParams.set(\"tenantUrl\", tenantUrl);\n }\n\n return enrichedUrl;\n } catch {\n return null;\n }\n}\n","import { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nexport { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\nexport {\n ExtensionSDKHost,\n type ExtensionSDKHostOptions,\n type IExtensionSDKHost,\n} from \"./ExtensionSDKHost\";\nexport { CONNECTION_EVENTS, type ConnectionEvent } from \"./events\";\nexport {\n postToChild,\n subscribeToChildMessages,\n type HostMessageHandler,\n} from \"./hostMessaging\";\nexport { enrichExtensionUrl, isValidExtensionUrl } from \"./hostUrl\";\nexport {\n InvalidOriginError,\n readTenantUrlFromLocation,\n resolveAllowedOrigin,\n} from \"./origin\";\nexport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nlet instance: IExtensionSDK | null = null;\nlet hasWarnedDuplicateCreate = false;\n\n/** Clears the factory singleton when the managed instance is destroyed. */\nclass ManagedExtensionSDK extends ExtensionSDK {\n destroy(): void {\n super.destroy();\n if (instance === this) {\n instance = null;\n hasWarnedDuplicateCreate = false;\n }\n }\n}\n\n/**\n * Returns the shared {@link IExtensionSDK} for the current extension frame.\n *\n * Creates the instance on first call. Later calls return the same instance and\n * log a console warning — use the SDK once per app. After {@link IExtensionSDK.destroy},\n * the next call creates a fresh instance.\n */\nexport const createExtensionSDK = (): IExtensionSDK => {\n if (instance) {\n if (!hasWarnedDuplicateCreate) {\n console.warn(\n `[${__PACKAGE_NAME__}] createExtensionSDK() was called more than once. ` +\n \"The SDK is a singleton — reuse the same instance for the whole app.\",\n );\n hasWarnedDuplicateCreate = true;\n }\n return instance;\n }\n\n instance = new ManagedExtensionSDK();\n return instance;\n};\n"],"mappings":"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"}
|
package/dist/extension-sdk.js
CHANGED
|
@@ -327,12 +327,133 @@ var u = 1e4, d = 12e4, f = 3e4, p = 2147483647, m = class {
|
|
|
327
327
|
#F(e, t) {
|
|
328
328
|
e && (clearTimeout(e.timer), e.reject(t));
|
|
329
329
|
}
|
|
330
|
-
}
|
|
330
|
+
};
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/hostMessaging.ts
|
|
333
|
+
function h(e, t, n, r) {
|
|
334
|
+
let i = r === void 0 ? { type: n } : {
|
|
335
|
+
type: n,
|
|
336
|
+
payload: r
|
|
337
|
+
};
|
|
338
|
+
e.postMessage(i, t);
|
|
339
|
+
}
|
|
340
|
+
function g(e, t) {
|
|
341
|
+
let n = (n) => {
|
|
342
|
+
n.origin === e && (!n.data || typeof n.data != "object" || !("type" in n.data) || t(n));
|
|
343
|
+
};
|
|
344
|
+
return window.addEventListener("message", n), () => window.removeEventListener("message", n);
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/ExtensionSDKHost.ts
|
|
348
|
+
var _ = class {
|
|
349
|
+
#e;
|
|
350
|
+
#t = null;
|
|
351
|
+
#n = !1;
|
|
352
|
+
#r = !1;
|
|
353
|
+
constructor(e) {
|
|
354
|
+
this.#e = e;
|
|
355
|
+
}
|
|
356
|
+
start() {
|
|
357
|
+
this.#r || this.#n || (this.#t = g(this.#e.origin, (e) => {
|
|
358
|
+
this.#i(e);
|
|
359
|
+
}), this.#n = !0);
|
|
360
|
+
}
|
|
361
|
+
destroy() {
|
|
362
|
+
this.#r = !0, this.#n = !1, this.#t?.(), this.#t = null;
|
|
363
|
+
}
|
|
364
|
+
emitLoadPageSettings(e) {
|
|
365
|
+
this.#u(), this.#l(r.loadPageSettings, e);
|
|
366
|
+
}
|
|
367
|
+
async #i(e) {
|
|
368
|
+
if (this.#r) return;
|
|
369
|
+
let { type: t, payload: n } = e.data;
|
|
370
|
+
switch (t) {
|
|
371
|
+
case r.handshakeInit:
|
|
372
|
+
await this.#a();
|
|
373
|
+
break;
|
|
374
|
+
case r.requestAuthTokenRefresh:
|
|
375
|
+
await this.#o();
|
|
376
|
+
break;
|
|
377
|
+
case r.updatePageSettings:
|
|
378
|
+
await this.#s(n);
|
|
379
|
+
break;
|
|
380
|
+
default: break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async #a() {
|
|
384
|
+
try {
|
|
385
|
+
let e = await this.#e.resolveHandshakePayload();
|
|
386
|
+
if (this.#r) return;
|
|
387
|
+
this.#l(r.handshakeAck, e);
|
|
388
|
+
} catch {}
|
|
389
|
+
}
|
|
390
|
+
async #o() {
|
|
391
|
+
try {
|
|
392
|
+
let e = await this.#e.resolveAuthToken();
|
|
393
|
+
if (this.#r) return;
|
|
394
|
+
this.#l(r.authTokenRefresh, { authToken: e });
|
|
395
|
+
} catch {}
|
|
396
|
+
}
|
|
397
|
+
async #s(e) {
|
|
398
|
+
let t = e?.settings;
|
|
399
|
+
if (!t || typeof t != "object") {
|
|
400
|
+
this.#c(!1);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
let n = this.#e.onUpdatePageSettings;
|
|
404
|
+
if (!n) {
|
|
405
|
+
this.#c(!1);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
let e = await n(t);
|
|
410
|
+
if (this.#r) return;
|
|
411
|
+
this.#c(!!e);
|
|
412
|
+
} catch {
|
|
413
|
+
if (this.#r) return;
|
|
414
|
+
this.#c(!1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
#c(e) {
|
|
418
|
+
let t = { success: e };
|
|
419
|
+
this.#l(r.pageSettingsUpdated, t);
|
|
420
|
+
}
|
|
421
|
+
#l(e, t) {
|
|
422
|
+
let n = this.#e.getContentWindow();
|
|
423
|
+
n && h(n, this.#e.origin, e, t);
|
|
424
|
+
}
|
|
425
|
+
#u() {
|
|
426
|
+
if (this.#r) throw Error("ExtensionSDKHost has been destroyed.");
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/hostUrl.ts
|
|
431
|
+
function v(e, t) {
|
|
432
|
+
if (typeof e != "string") return !1;
|
|
433
|
+
try {
|
|
434
|
+
let n = new URL(e), r = n.protocol === "https:", i = !t || t(n.origin);
|
|
435
|
+
return r && i;
|
|
436
|
+
} catch {
|
|
437
|
+
return !1;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function y(e, t = window.location.origin) {
|
|
441
|
+
if (typeof e != "string") return null;
|
|
442
|
+
try {
|
|
443
|
+
let n = new URL(e);
|
|
444
|
+
return typeof t == "string" && n.searchParams.set("tenantUrl", t), n;
|
|
445
|
+
} catch {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region src/index.ts
|
|
451
|
+
var b = null, x = !1, S = class extends m {
|
|
331
452
|
destroy() {
|
|
332
|
-
super.destroy(),
|
|
453
|
+
super.destroy(), b === this && (b = null, x = !1);
|
|
333
454
|
}
|
|
334
|
-
},
|
|
455
|
+
}, C = () => b ? (x ||= (console.warn("[@kadanza/extension-sdk] createExtensionSDK() was called more than once. The SDK is a singleton — reuse the same instance for the whole app."), !0), b) : (b = new S(), b);
|
|
335
456
|
//#endregion
|
|
336
|
-
export { r as CONNECTION_EVENTS, m as ExtensionSDK, s as InvalidOriginError,
|
|
457
|
+
export { r as CONNECTION_EVENTS, m as ExtensionSDK, _ as ExtensionSDKHost, s as InvalidOriginError, C as createExtensionSDK, y as enrichExtensionUrl, v as isValidExtensionUrl, h as postToChild, l as readTenantUrlFromLocation, c as resolveAllowedOrigin, g as subscribeToChildMessages };
|
|
337
458
|
|
|
338
459
|
//# sourceMappingURL=extension-sdk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-sdk.js","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#configureAuthTokenAutoRefresh","#scheduleProactiveAuthTokenRefresh","#pageSettings","#handshakePromise","#allowedOrigin","#tenantUrl","#unsubscribe","#onMessage","#pendingHandshake","#clearPending","#clearProactiveAuthTokenRefreshTimer","#destroyed","#authTokenAutoRefresh","#authTokenRefreshRetryUsed","#rejectPending","#pendingAuthTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#authTokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleAuthTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending","#authTokenBufferMs","#parseExpiresAtMs","#authTokenRefreshTimer","#getProactiveRefreshDelayMs","#runProactiveAuthTokenRefresh","#handleProactiveAuthTokenRefreshFailure"],"sources":["../src/api.ts","../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/index.ts"],"sourcesContent":["interface ApiRequestContext {\n baseUrl: string;\n tenantDomain: string;\n authTokenJwt: string;\n}\n\n/** Derives the Platform API origin from handshake `baseUrl`. */\nexport function deriveApiUrl(baseUrl: string): string {\n if (baseUrl.startsWith(\"/\")) {\n return baseUrl;\n }\n\n const url = new URL(baseUrl);\n url.hostname = `api.${url.hostname}`;\n return url.origin;\n}\n\nfunction resolveApiEndpoint(baseUrl: string, endpoint: string): string {\n if (!endpoint.startsWith(\"/\") || endpoint.startsWith(\"//\")) {\n throw new Error(\n 'API endpoint must be a root-relative path starting with a single \"/\".',\n );\n }\n\n return `${deriveApiUrl(baseUrl).replace(/\\/+$/, \"\")}${endpoint}`;\n}\n\nexport async function callApi<T>(\n endpoint: string,\n context: ApiRequestContext,\n options: RequestInit = {},\n): Promise<T> {\n const headers = new Headers(options.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Authorization\", `Bearer ${context.authTokenJwt}`);\n headers.set(\"X-Tenant\", context.tenantDomain);\n\n const response = await fetch(resolveApiEndpoint(context.baseUrl, endpoint), {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const statusText = response.statusText ? ` ${response.statusText}` : \"\";\n throw new Error(`API call failed: ${response.status}${statusText}.`);\n }\n\n return (await response.json()) as T;\n}\n","/**\n * Wire event type strings for parent/child `postMessage` communication.\n *\n * Keys are SDK-facing names; values are the strings sent on the wire.\n * Comments mark message direction relative to the extension (child).\n */\nexport const CONNECTION_EVENTS = {\n /** Child → parent: start handshake. */\n handshakeInit: \"HANDSHAKE_INIT\",\n /** Parent → child: handshake success with auth and context. */\n handshakeAck: \"HANDSHAKE_ACK\",\n /** Child → parent: request a new auth token. */\n requestAuthTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n /** Parent → child: new auth token (reply or push). */\n authTokenRefresh: \"TOKEN_REFRESH\",\n /** Parent → child: open/load page settings UI with values. */\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n /** Child → parent: persist updated page settings. */\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n /** Parent → child: result of a page settings update. */\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n /** Child → parent: extension route changed. */\n navigationChange: \"NAVIGATION_CHANGE\",\n} as const;\n\n/** Union of all {@link CONNECTION_EVENTS} wire string values. */\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\n/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */\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 *\n * @param tenantUrl - Absolute URL from the `tenantUrl` search param.\n * @returns Origin string suitable for `postMessage` targetOrigin.\n * @throws {InvalidOriginError} When missing, unparsable, or disallowed scheme/host.\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\n/**\n * Reads the parent-supplied `tenantUrl` from a URL search string.\n *\n * The parent must pass `tenantUrl` on the iframe `src` so\n * {@link resolveAllowedOrigin} can whitelist the parent origin.\n *\n * @param search - Query string (defaults to `window.location.search`).\n */\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { callApi, deriveApiUrl } from \"./api\";\nimport { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_AUTH_TOKEN_BUFFER_MS = 120_000;\nconst AUTO_REFRESH_RETRY_DELAY_MS = 30_000;\n/** Largest delay `setTimeout` can reliably use in browsers. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Public contract for talking to the Kadanza parent frame over `postMessage`.\n *\n * Requires a `tenantUrl` search param on the extension URL so\n * {@link connect} can validate the parent origin.\n */\nexport interface IExtensionSDK {\n /**\n * Establishes the parent connection and waits for `HANDSHAKE_ACK`.\n *\n * Safe to call repeatedly (e.g. from a React `useEffect` with deps):\n * - Already connected: returns the cached handshake and re-applies\n * `authTokenAutoRefresh` / `authTokenBufferMs` without another init.\n * `timeoutMs` is ignored after connect.\n * - Handshake in progress: returns the same in-flight promise. Options from\n * the first connecting call apply until the handshake completes.\n *\n * Opt-in proactive refresh can be enabled with\n * `authTokenAutoRefresh: true` (optional `authTokenBufferMs`).\n *\n * @param options - Handshake timeout and optional auth-token auto-refresh.\n * @throws {InvalidOriginError} When `tenantUrl` is missing or invalid.\n * @throws When not embedded in a parent frame, destroyed, or the handshake\n * times out / returns an invalid payload.\n */\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n\n /**\n * Tears down listeners, proactive refresh timers, and cached state.\n * Rejects any in-flight requests. The instance cannot be reused after destroy.\n * When created via {@link createExtensionSDK}, clears the singleton so a\n * later create call can return a fresh instance.\n */\n destroy(): void;\n\n /** Whether a successful handshake has completed and not been destroyed. */\n readonly isConnected: boolean;\n\n /** Last auth token from handshake or refresh; `null` until connected. */\n getAuthToken(): AuthToken | null;\n\n /** Extension context from handshake; `null` until connected. */\n getExtensionDetails(): ExtensionDetails | null;\n\n /** Design tokens from handshake; `null` until connected. */\n getDesignTokens(): DesignTokens | null;\n\n /**\n * Latest page settings from handshake or `LOAD_PAGE_SETTINGS`;\n * `null` until set.\n */\n getPageSettings(): PageSettings | null;\n\n /**\n * Validated parent `postMessage` origin from `tenantUrl`;\n * `null` until `connect` resolves it.\n */\n getAllowedOrigin(): string | null;\n\n /**\n * Raw `tenantUrl` search-param value used for the allowed origin;\n * `null` until `connect` reads it.\n */\n getTenantUrl(): string | null;\n\n /**\n * Platform API origin derived from handshake `baseUrl`;\n * `null` until connected.\n */\n getApiUrl(): string | null;\n\n /**\n * Calls the Kadanza Platform API with the current connection credentials.\n *\n * The API origin is derived from the handshake `baseUrl`. Authorization and\n * tenant headers are managed by the SDK and cannot be overridden.\n *\n * @typeParam T - Expected JSON response body.\n * @param endpoint - Root-relative API path.\n * @param options - Standard fetch options.\n * @throws When not connected, the endpoint is invalid, the request fails,\n * or the response is not successful JSON.\n */\n apiCall<T>(endpoint: string, options?: RequestInit): Promise<T>;\n\n /**\n * Asks the parent for a new auth token and waits for `TOKEN_REFRESH`.\n *\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, a refresh is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitRequestAuthTokenRefresh(\n options?: RequestOptions,\n ): Promise<AuthToken>;\n\n /**\n * Pushes page settings to the parent and waits for `PAGE_SETTINGS_UPDATED`.\n *\n * @param payload - Update payload containing the settings object.\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, an update is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n /**\n * Registers a handler for parent-initiated `LOAD_PAGE_SETTINGS`.\n *\n * @returns Unsubscribe function.\n */\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n\n /**\n * Registers a handler for auth-token updates (requested or parent-pushed).\n *\n * @returns Unsubscribe function.\n */\n onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void;\n\n /**\n * Notifies the parent that the extension's route changed.\n *\n * Wire: `NAVIGATION_CHANGE` (fire-and-forget).\n *\n * @param payload - Navigation change payload (`path` within the extension).\n * @throws When not connected or destroyed.\n */\n emitNavigationChange(payload: NavigationChangePayload): void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** Default {@link IExtensionSDK} implementation. Prefer {@link createExtensionSDK}. */\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #tenantUrl: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: AuthToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #authTokenAutoRefresh = false;\n #authTokenBufferMs = DEFAULT_AUTH_TOKEN_BUFFER_MS;\n #authTokenRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n #authTokenRefreshRetryUsed = false;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #handshakePromise: Promise<HandshakePayload> | null = null;\n #pendingAuthTokenRefresh: Pending<AuthToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #authTokenRefreshHandlers = new Set<(authToken: AuthToken) => 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 this.#configureAuthTokenAutoRefresh(options);\n this.#scheduleProactiveAuthTokenRefresh();\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#handshakePromise) {\n return this.#handshakePromise;\n }\n\n this.#configureAuthTokenAutoRefresh(options);\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n this.#tenantUrl = 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 this.#handshakePromise = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n this.#handshakePromise = handshakePromise;\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n this.#clearProactiveAuthTokenRefreshTimer();\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#clearProactiveAuthTokenRefreshTimer();\n this.#authTokenAutoRefresh = false;\n this.#authTokenRefreshRetryUsed = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingAuthTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#pendingAuthTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#authTokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): AuthToken | 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 getAllowedOrigin(): string | null {\n return this.#allowedOrigin;\n }\n\n getTenantUrl(): string | null {\n return this.#tenantUrl;\n }\n\n getApiUrl(): string | null {\n if (!this.#extensionDetails) {\n return null;\n }\n\n return deriveApiUrl(this.#extensionDetails.baseUrl);\n }\n\n async apiCall<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n this.#assertConnected();\n\n return callApi<T>(\n endpoint,\n {\n baseUrl: this.#extensionDetails!.baseUrl,\n tenantDomain: this.#extensionDetails!.tenantDomain,\n authTokenJwt: this.#authToken!.jwt,\n },\n options,\n );\n }\n\n async emitRequestAuthTokenRefresh(\n options: RequestOptions = {},\n ): Promise<AuthToken> {\n this.#assertConnected();\n\n if (this.#pendingAuthTokenRefresh) {\n throw new Error(\"Auth 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<AuthToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingAuthTokenRefresh = null;\n reject(new Error(`Auth token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingAuthTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestAuthTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\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, payload);\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 onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void {\n this.#authTokenRefreshHandlers.add(handler);\n return () => {\n this.#authTokenRefreshHandlers.delete(handler);\n };\n }\n\n emitNavigationChange(payload: NavigationChangePayload): void {\n this.#assertConnected();\n postToParent(\n CONNECTION_EVENTS.navigationChange,\n this.#allowedOrigin!,\n payload,\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.authTokenRefresh:\n this.#handleAuthTokenRefresh(payload as AuthTokenRefreshPayload | 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 this.#handshakePromise = 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.#handshakePromise = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\n }\n\n #handleAuthTokenRefresh(payload: AuthTokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingAuthTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingAuthTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingAuthTokenRefresh;\n this.#pendingAuthTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#authTokenRefreshHandlers) {\n handler(this.#authToken);\n }\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\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 #configureAuthTokenAutoRefresh(options: ConnectOptions): void {\n if (options.authTokenBufferMs !== undefined) {\n if (\n !Number.isFinite(options.authTokenBufferMs) ||\n options.authTokenBufferMs < 0\n ) {\n throw new Error(\n \"authTokenBufferMs must be a finite non-negative number.\",\n );\n }\n }\n\n this.#authTokenAutoRefresh = options.authTokenAutoRefresh === true;\n this.#authTokenBufferMs =\n options.authTokenBufferMs ?? DEFAULT_AUTH_TOKEN_BUFFER_MS;\n this.#authTokenRefreshRetryUsed = false;\n this.#clearProactiveAuthTokenRefreshTimer();\n }\n\n #parseExpiresAtMs(authToken: AuthToken): number | null {\n const expiresSec = Number(authToken.expires);\n if (!Number.isFinite(expiresSec)) {\n return null;\n }\n return expiresSec * 1000;\n }\n\n #getProactiveRefreshDelayMs(authToken: AuthToken): number | null {\n const expiresAtMs = this.#parseExpiresAtMs(authToken);\n if (expiresAtMs === null) {\n return null;\n }\n return Math.max(0, expiresAtMs - this.#authTokenBufferMs - Date.now());\n }\n\n #clearProactiveAuthTokenRefreshTimer(): void {\n if (this.#authTokenRefreshTimer !== null) {\n clearTimeout(this.#authTokenRefreshTimer);\n this.#authTokenRefreshTimer = null;\n }\n }\n\n #scheduleProactiveAuthTokenRefresh(): void {\n this.#clearProactiveAuthTokenRefreshTimer();\n\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const delayMs = this.#getProactiveRefreshDelayMs(this.#authToken);\n if (delayMs === null) {\n return;\n }\n\n const clampedDelay = Math.min(delayMs, MAX_TIMEOUT_MS);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, clampedDelay);\n }\n\n async #runProactiveAuthTokenRefresh(): Promise<void> {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const authTokenAtRequest = this.#authToken;\n const delayMs = this.#getProactiveRefreshDelayMs(authTokenAtRequest);\n if (delayMs === null) {\n return;\n }\n\n if (delayMs > 0) {\n this.#scheduleProactiveAuthTokenRefresh();\n return;\n }\n\n if (this.#pendingAuthTokenRefresh) {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n return;\n }\n\n try {\n await this.emitRequestAuthTokenRefresh();\n } catch {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n }\n }\n\n #handleProactiveAuthTokenRefreshFailure(authTokenAtRequest: AuthToken): void {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n this.#authToken?.jwt !== authTokenAtRequest.jwt\n ) {\n return;\n }\n\n if (this.#authTokenRefreshRetryUsed) {\n return;\n }\n\n const expiresAtMs = this.#parseExpiresAtMs(authTokenAtRequest);\n if (expiresAtMs === null) {\n return;\n }\n\n const remainingMs = expiresAtMs - Date.now();\n if (remainingMs <= 0) {\n return;\n }\n\n this.#authTokenRefreshRetryUsed = true;\n this.#clearProactiveAuthTokenRefreshTimer();\n\n const retryDelay = Math.min(AUTO_REFRESH_RETRY_DELAY_MS, remainingMs);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, retryDelay);\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 AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nlet instance: IExtensionSDK | null = null;\nlet hasWarnedDuplicateCreate = false;\n\n/** Clears the factory singleton when the managed instance is destroyed. */\nclass ManagedExtensionSDK extends ExtensionSDK {\n destroy(): void {\n super.destroy();\n if (instance === this) {\n instance = null;\n hasWarnedDuplicateCreate = false;\n }\n }\n}\n\n/**\n * Returns the shared {@link IExtensionSDK} for the current extension frame.\n *\n * Creates the instance on first call. Later calls return the same instance and\n * log a console warning — use the SDK once per app. After {@link IExtensionSDK.destroy},\n * the next call creates a fresh instance.\n */\nexport const createExtensionSDK = (): IExtensionSDK => {\n if (instance) {\n if (!hasWarnedDuplicateCreate) {\n console.warn(\n `[${__PACKAGE_NAME__}] createExtensionSDK() was called more than once. ` +\n \"The SDK is a singleton — reuse the same instance for the whole app.\",\n );\n hasWarnedDuplicateCreate = true;\n }\n return instance;\n }\n\n instance = new ManagedExtensionSDK();\n return instance;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension-sdk.js","names":["#connected","#assertNotDestroyed","#authToken","#extensionDetails","#designTokens","#configureAuthTokenAutoRefresh","#scheduleProactiveAuthTokenRefresh","#pageSettings","#handshakePromise","#allowedOrigin","#tenantUrl","#unsubscribe","#onMessage","#pendingHandshake","#clearPending","#clearProactiveAuthTokenRefreshTimer","#destroyed","#authTokenAutoRefresh","#authTokenRefreshRetryUsed","#rejectPending","#pendingAuthTokenRefresh","#pendingPageSettingsUpdate","#loadPageSettingsHandlers","#authTokenRefreshHandlers","#assertConnected","#handleHandshakeAck","#handleAuthTokenRefresh","#handleLoadPageSettings","#handlePageSettingsUpdated","#resolvePending","#authTokenBufferMs","#parseExpiresAtMs","#authTokenRefreshTimer","#getProactiveRefreshDelayMs","#runProactiveAuthTokenRefresh","#handleProactiveAuthTokenRefreshFailure","#options","#destroyed","#started","#unsubscribe","#onMessage","#assertNotDestroyed","#post","#handleHandshakeInit","#handleAuthTokenRefresh","#handleUpdatePageSettings","#postPageSettingsUpdated"],"sources":["../src/api.ts","../src/events.ts","../src/messaging.ts","../src/origin.ts","../src/ExtensionSDK.ts","../src/hostMessaging.ts","../src/ExtensionSDKHost.ts","../src/hostUrl.ts","../src/index.ts"],"sourcesContent":["interface ApiRequestContext {\n baseUrl: string;\n tenantDomain: string;\n authTokenJwt: string;\n}\n\n/** Derives the Platform API origin from handshake `baseUrl`. */\nexport function deriveApiUrl(baseUrl: string): string {\n if (baseUrl.startsWith(\"/\")) {\n return baseUrl;\n }\n\n const url = new URL(baseUrl);\n url.hostname = `api.${url.hostname}`;\n return url.origin;\n}\n\nfunction resolveApiEndpoint(baseUrl: string, endpoint: string): string {\n if (!endpoint.startsWith(\"/\") || endpoint.startsWith(\"//\")) {\n throw new Error(\n 'API endpoint must be a root-relative path starting with a single \"/\".',\n );\n }\n\n return `${deriveApiUrl(baseUrl).replace(/\\/+$/, \"\")}${endpoint}`;\n}\n\nexport async function callApi<T>(\n endpoint: string,\n context: ApiRequestContext,\n options: RequestInit = {},\n): Promise<T> {\n const headers = new Headers(options.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Authorization\", `Bearer ${context.authTokenJwt}`);\n headers.set(\"X-Tenant\", context.tenantDomain);\n\n const response = await fetch(resolveApiEndpoint(context.baseUrl, endpoint), {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const statusText = response.statusText ? ` ${response.statusText}` : \"\";\n throw new Error(`API call failed: ${response.status}${statusText}.`);\n }\n\n return (await response.json()) as T;\n}\n","/**\n * Wire event type strings for parent/child `postMessage` communication.\n *\n * Keys are SDK-facing names; values are the strings sent on the wire.\n * Comments mark message direction relative to the extension (child).\n */\nexport const CONNECTION_EVENTS = {\n /** Child → parent: start handshake. */\n handshakeInit: \"HANDSHAKE_INIT\",\n /** Parent → child: handshake success with auth and context. */\n handshakeAck: \"HANDSHAKE_ACK\",\n /** Child → parent: request a new auth token. */\n requestAuthTokenRefresh: \"REQUEST_TOKEN_REFRESH\",\n /** Parent → child: new auth token (reply or push). */\n authTokenRefresh: \"TOKEN_REFRESH\",\n /** Parent → child: open/load page settings UI with values. */\n loadPageSettings: \"LOAD_PAGE_SETTINGS\",\n /** Child → parent: persist updated page settings. */\n updatePageSettings: \"UPDATE_PAGE_SETTINGS\",\n /** Parent → child: result of a page settings update. */\n pageSettingsUpdated: \"PAGE_SETTINGS_UPDATED\",\n /** Child → parent: extension route changed. */\n navigationChange: \"NAVIGATION_CHANGE\",\n} as const;\n\n/** Union of all {@link CONNECTION_EVENTS} wire string values. */\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\n/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */\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 *\n * @param tenantUrl - Absolute URL from the `tenantUrl` search param.\n * @returns Origin string suitable for `postMessage` targetOrigin.\n * @throws {InvalidOriginError} When missing, unparsable, or disallowed scheme/host.\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\n/**\n * Reads the parent-supplied `tenantUrl` from a URL search string.\n *\n * The parent must pass `tenantUrl` on the iframe `src` so\n * {@link resolveAllowedOrigin} can whitelist the parent origin.\n *\n * @param search - Query string (defaults to `window.location.search`).\n */\nexport function readTenantUrlFromLocation(\n search: string = window.location.search,\n): string | null {\n return new URLSearchParams(search).get(\"tenantUrl\");\n}\n","import { callApi, deriveApiUrl } from \"./api\";\nimport { CONNECTION_EVENTS } from \"./events\";\nimport { postToParent, subscribeToParentMessages } from \"./messaging\";\nimport { readTenantUrlFromLocation, resolveAllowedOrigin } from \"./origin\";\nimport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_AUTH_TOKEN_BUFFER_MS = 120_000;\nconst AUTO_REFRESH_RETRY_DELAY_MS = 30_000;\n/** Largest delay `setTimeout` can reliably use in browsers. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Public contract for talking to the Kadanza parent frame over `postMessage`.\n *\n * Requires a `tenantUrl` search param on the extension URL so\n * {@link connect} can validate the parent origin.\n */\nexport interface IExtensionSDK {\n /**\n * Establishes the parent connection and waits for `HANDSHAKE_ACK`.\n *\n * Safe to call repeatedly (e.g. from a React `useEffect` with deps):\n * - Already connected: returns the cached handshake and re-applies\n * `authTokenAutoRefresh` / `authTokenBufferMs` without another init.\n * `timeoutMs` is ignored after connect.\n * - Handshake in progress: returns the same in-flight promise. Options from\n * the first connecting call apply until the handshake completes.\n *\n * Opt-in proactive refresh can be enabled with\n * `authTokenAutoRefresh: true` (optional `authTokenBufferMs`).\n *\n * @param options - Handshake timeout and optional auth-token auto-refresh.\n * @throws {InvalidOriginError} When `tenantUrl` is missing or invalid.\n * @throws When not embedded in a parent frame, destroyed, or the handshake\n * times out / returns an invalid payload.\n */\n connect(options?: ConnectOptions): Promise<HandshakePayload>;\n\n /**\n * Tears down listeners, proactive refresh timers, and cached state.\n * Rejects any in-flight requests. The instance cannot be reused after destroy.\n * When created via {@link createExtensionSDK}, clears the singleton so a\n * later create call can return a fresh instance.\n */\n destroy(): void;\n\n /** Whether a successful handshake has completed and not been destroyed. */\n readonly isConnected: boolean;\n\n /** Last auth token from handshake or refresh; `null` until connected. */\n getAuthToken(): AuthToken | null;\n\n /** Extension context from handshake; `null` until connected. */\n getExtensionDetails(): ExtensionDetails | null;\n\n /** Design tokens from handshake; `null` until connected. */\n getDesignTokens(): DesignTokens | null;\n\n /**\n * Latest page settings from handshake or `LOAD_PAGE_SETTINGS`;\n * `null` until set.\n */\n getPageSettings(): PageSettings | null;\n\n /**\n * Validated parent `postMessage` origin from `tenantUrl`;\n * `null` until `connect` resolves it.\n */\n getAllowedOrigin(): string | null;\n\n /**\n * Raw `tenantUrl` search-param value used for the allowed origin;\n * `null` until `connect` reads it.\n */\n getTenantUrl(): string | null;\n\n /**\n * Platform API origin derived from handshake `baseUrl`;\n * `null` until connected.\n */\n getApiUrl(): string | null;\n\n /**\n * Calls the Kadanza Platform API with the current connection credentials.\n *\n * The API origin is derived from the handshake `baseUrl`. Authorization and\n * tenant headers are managed by the SDK and cannot be overridden.\n *\n * @typeParam T - Expected JSON response body.\n * @param endpoint - Root-relative API path.\n * @param options - Standard fetch options.\n * @throws When not connected, the endpoint is invalid, the request fails,\n * or the response is not successful JSON.\n */\n apiCall<T>(endpoint: string, options?: RequestInit): Promise<T>;\n\n /**\n * Asks the parent for a new auth token and waits for `TOKEN_REFRESH`.\n *\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, a refresh is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitRequestAuthTokenRefresh(\n options?: RequestOptions,\n ): Promise<AuthToken>;\n\n /**\n * Pushes page settings to the parent and waits for `PAGE_SETTINGS_UPDATED`.\n *\n * @param payload - Update payload containing the settings object.\n * @param options - Optional request timeout (default 10s).\n * @throws When not connected, an update is already in progress,\n * or the request times out / returns an invalid payload.\n */\n emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\n options?: RequestOptions,\n ): Promise<PageSettingsUpdatedPayload>;\n\n /**\n * Registers a handler for parent-initiated `LOAD_PAGE_SETTINGS`.\n *\n * @returns Unsubscribe function.\n */\n onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;\n\n /**\n * Registers a handler for auth-token updates (requested or parent-pushed).\n *\n * @returns Unsubscribe function.\n */\n onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void;\n\n /**\n * Notifies the parent that the extension's route changed.\n *\n * Wire: `NAVIGATION_CHANGE` (fire-and-forget).\n *\n * @param payload - Navigation change payload (`path` within the extension).\n * @throws When not connected or destroyed.\n */\n emitNavigationChange(payload: NavigationChangePayload): void;\n}\n\ntype Pending<T> = {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** Default {@link IExtensionSDK} implementation. Prefer {@link createExtensionSDK}. */\nexport class ExtensionSDK implements IExtensionSDK {\n #allowedOrigin: string | null = null;\n #tenantUrl: string | null = null;\n #unsubscribe: (() => void) | null = null;\n #connected = false;\n #destroyed = false;\n\n #authToken: AuthToken | null = null;\n #extensionDetails: ExtensionDetails | null = null;\n #designTokens: DesignTokens | null = null;\n #pageSettings: PageSettings | null = null;\n\n #authTokenAutoRefresh = false;\n #authTokenBufferMs = DEFAULT_AUTH_TOKEN_BUFFER_MS;\n #authTokenRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n #authTokenRefreshRetryUsed = false;\n\n #pendingHandshake: Pending<HandshakePayload> | null = null;\n #handshakePromise: Promise<HandshakePayload> | null = null;\n #pendingAuthTokenRefresh: Pending<AuthToken> | null = null;\n #pendingPageSettingsUpdate: Pending<PageSettingsUpdatedPayload> | null = null;\n\n #loadPageSettingsHandlers = new Set<(settings: PageSettings) => void>();\n #authTokenRefreshHandlers = new Set<(authToken: AuthToken) => 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 this.#configureAuthTokenAutoRefresh(options);\n this.#scheduleProactiveAuthTokenRefresh();\n return {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n };\n }\n\n if (this.#handshakePromise) {\n return this.#handshakePromise;\n }\n\n this.#configureAuthTokenAutoRefresh(options);\n\n const tenantUrl = readTenantUrlFromLocation();\n this.#allowedOrigin = resolveAllowedOrigin(tenantUrl);\n this.#tenantUrl = 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 this.#handshakePromise = null;\n reject(new Error(`Handshake timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingHandshake = { resolve, reject, timer };\n });\n this.#handshakePromise = handshakePromise;\n\n try {\n postToParent(CONNECTION_EVENTS.handshakeInit, this.#allowedOrigin);\n } catch (error) {\n this.#clearPending(this.#pendingHandshake);\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n this.#clearProactiveAuthTokenRefreshTimer();\n throw error;\n }\n\n return handshakePromise;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#connected = false;\n\n this.#clearProactiveAuthTokenRefreshTimer();\n this.#authTokenAutoRefresh = false;\n this.#authTokenRefreshRetryUsed = false;\n\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n this.#allowedOrigin = null;\n this.#tenantUrl = null;\n\n this.#rejectPending(this.#pendingHandshake, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingAuthTokenRefresh, new Error(\"SDK destroyed.\"));\n this.#rejectPending(this.#pendingPageSettingsUpdate, new Error(\"SDK destroyed.\"));\n this.#pendingHandshake = null;\n this.#handshakePromise = null;\n this.#pendingAuthTokenRefresh = null;\n this.#pendingPageSettingsUpdate = null;\n\n this.#loadPageSettingsHandlers.clear();\n this.#authTokenRefreshHandlers.clear();\n\n this.#authToken = null;\n this.#extensionDetails = null;\n this.#designTokens = null;\n this.#pageSettings = null;\n }\n\n getAuthToken(): AuthToken | 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 getAllowedOrigin(): string | null {\n return this.#allowedOrigin;\n }\n\n getTenantUrl(): string | null {\n return this.#tenantUrl;\n }\n\n getApiUrl(): string | null {\n if (!this.#extensionDetails) {\n return null;\n }\n\n return deriveApiUrl(this.#extensionDetails.baseUrl);\n }\n\n async apiCall<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n this.#assertConnected();\n\n return callApi<T>(\n endpoint,\n {\n baseUrl: this.#extensionDetails!.baseUrl,\n tenantDomain: this.#extensionDetails!.tenantDomain,\n authTokenJwt: this.#authToken!.jwt,\n },\n options,\n );\n }\n\n async emitRequestAuthTokenRefresh(\n options: RequestOptions = {},\n ): Promise<AuthToken> {\n this.#assertConnected();\n\n if (this.#pendingAuthTokenRefresh) {\n throw new Error(\"Auth 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<AuthToken>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pendingAuthTokenRefresh = null;\n reject(new Error(`Auth token refresh timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.#pendingAuthTokenRefresh = { resolve, reject, timer };\n });\n\n postToParent(CONNECTION_EVENTS.requestAuthTokenRefresh, allowedOrigin);\n return promise;\n }\n\n async emitUpdatePageSettings(\n payload: UpdatePageSettingsPayload,\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, payload);\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 onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void {\n this.#authTokenRefreshHandlers.add(handler);\n return () => {\n this.#authTokenRefreshHandlers.delete(handler);\n };\n }\n\n emitNavigationChange(payload: NavigationChangePayload): void {\n this.#assertConnected();\n postToParent(\n CONNECTION_EVENTS.navigationChange,\n this.#allowedOrigin!,\n payload,\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 }\n case CONNECTION_EVENTS.authTokenRefresh: {\n this.#handleAuthTokenRefresh(payload as AuthTokenRefreshPayload | undefined);\n break;\n }\n case CONNECTION_EVENTS.loadPageSettings: {\n this.#handleLoadPageSettings(payload as PageSettings | undefined);\n break;\n }\n case CONNECTION_EVENTS.pageSettingsUpdated: {\n this.#handlePageSettingsUpdated(\n payload as PageSettingsUpdatedPayload | undefined,\n );\n break;\n }\n default: {\n break;\n }\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 this.#handshakePromise = 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.#handshakePromise = null;\n this.#resolvePending(pending, {\n authToken: this.#authToken,\n extensionDetails: this.#extensionDetails,\n designTokens: this.#designTokens,\n pageSettings: this.#pageSettings,\n });\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\n }\n\n #handleAuthTokenRefresh(payload: AuthTokenRefreshPayload | undefined): void {\n if (!payload?.authToken) {\n this.#rejectPending(\n this.#pendingAuthTokenRefresh,\n new Error(\"Invalid TOKEN_REFRESH payload.\"),\n );\n this.#pendingAuthTokenRefresh = null;\n return;\n }\n\n this.#authToken = payload.authToken;\n\n const pending = this.#pendingAuthTokenRefresh;\n this.#pendingAuthTokenRefresh = null;\n this.#resolvePending(pending, this.#authToken);\n\n for (const handler of this.#authTokenRefreshHandlers) {\n handler(this.#authToken);\n }\n\n this.#authTokenRefreshRetryUsed = false;\n this.#scheduleProactiveAuthTokenRefresh();\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 #configureAuthTokenAutoRefresh(options: ConnectOptions): void {\n if (options.authTokenBufferMs !== undefined) {\n if (\n !Number.isFinite(options.authTokenBufferMs) ||\n options.authTokenBufferMs < 0\n ) {\n throw new Error(\n \"authTokenBufferMs must be a finite non-negative number.\",\n );\n }\n }\n\n this.#authTokenAutoRefresh = options.authTokenAutoRefresh === true;\n this.#authTokenBufferMs =\n options.authTokenBufferMs ?? DEFAULT_AUTH_TOKEN_BUFFER_MS;\n this.#authTokenRefreshRetryUsed = false;\n this.#clearProactiveAuthTokenRefreshTimer();\n }\n\n #parseExpiresAtMs(authToken: AuthToken): number | null {\n const expiresSec = Number(authToken.expires);\n if (!Number.isFinite(expiresSec)) {\n return null;\n }\n return expiresSec * 1000;\n }\n\n #getProactiveRefreshDelayMs(authToken: AuthToken): number | null {\n const expiresAtMs = this.#parseExpiresAtMs(authToken);\n if (expiresAtMs === null) {\n return null;\n }\n return Math.max(0, expiresAtMs - this.#authTokenBufferMs - Date.now());\n }\n\n #clearProactiveAuthTokenRefreshTimer(): void {\n if (this.#authTokenRefreshTimer !== null) {\n clearTimeout(this.#authTokenRefreshTimer);\n this.#authTokenRefreshTimer = null;\n }\n }\n\n #scheduleProactiveAuthTokenRefresh(): void {\n this.#clearProactiveAuthTokenRefreshTimer();\n\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const delayMs = this.#getProactiveRefreshDelayMs(this.#authToken);\n if (delayMs === null) {\n return;\n }\n\n const clampedDelay = Math.min(delayMs, MAX_TIMEOUT_MS);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, clampedDelay);\n }\n\n async #runProactiveAuthTokenRefresh(): Promise<void> {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n !this.#authToken\n ) {\n return;\n }\n\n const authTokenAtRequest = this.#authToken;\n const delayMs = this.#getProactiveRefreshDelayMs(authTokenAtRequest);\n if (delayMs === null) {\n return;\n }\n\n if (delayMs > 0) {\n this.#scheduleProactiveAuthTokenRefresh();\n return;\n }\n\n if (this.#pendingAuthTokenRefresh) {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n return;\n }\n\n try {\n await this.emitRequestAuthTokenRefresh();\n } catch {\n this.#handleProactiveAuthTokenRefreshFailure(authTokenAtRequest);\n }\n }\n\n #handleProactiveAuthTokenRefreshFailure(authTokenAtRequest: AuthToken): void {\n if (\n !this.#authTokenAutoRefresh ||\n !this.#connected ||\n this.#destroyed ||\n this.#authToken?.jwt !== authTokenAtRequest.jwt\n ) {\n return;\n }\n\n if (this.#authTokenRefreshRetryUsed) {\n return;\n }\n\n const expiresAtMs = this.#parseExpiresAtMs(authTokenAtRequest);\n if (expiresAtMs === null) {\n return;\n }\n\n const remainingMs = expiresAtMs - Date.now();\n if (remainingMs <= 0) {\n return;\n }\n\n this.#authTokenRefreshRetryUsed = true;\n this.#clearProactiveAuthTokenRefreshTimer();\n\n const retryDelay = Math.min(AUTO_REFRESH_RETRY_DELAY_MS, remainingMs);\n this.#authTokenRefreshTimer = setTimeout(() => {\n this.#authTokenRefreshTimer = null;\n void this.#runProactiveAuthTokenRefresh();\n }, retryDelay);\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 type { ConnectionEvent } from \"./events\";\nimport type { ExtensionMessage } from \"./types\";\n\nexport type HostMessageHandler = (\n event: MessageEvent<ExtensionMessage>,\n) => void;\n\n/**\n * Posts a typed extension message to an embedded child frame.\n *\n * @param contentWindow - The iframe's `contentWindow`.\n * @param origin - Target origin for `postMessage` (extension origin).\n * @param type - Wire event type from {@link CONNECTION_EVENTS}.\n * @param payload - Optional message payload.\n */\nexport function postToChild(\n contentWindow: Window,\n origin: string,\n type: ConnectionEvent,\n payload?: unknown,\n): void {\n const message: ExtensionMessage =\n payload === undefined ? { type } : { type, payload };\n contentWindow.postMessage(message, origin);\n}\n\n/**\n * Subscribes to `message` events from a child frame at `origin`.\n *\n * Ignores events from other origins and payloads without a `type` field.\n *\n * @returns Unsubscribe function.\n */\nexport function subscribeToChildMessages(\n origin: string,\n handler: HostMessageHandler,\n): () => void {\n const listener = (event: MessageEvent) => {\n if (event.origin !== origin) {\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","import { CONNECTION_EVENTS } from \"./events\";\nimport { postToChild, subscribeToChildMessages } from \"./hostMessaging\";\nimport type {\n AuthToken,\n ExtensionMessage,\n HandshakePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\n/**\n * Options for {@link ExtensionSDKHost}.\n *\n * Platform code supplies token minting and page-settings persistence via\n * callbacks; the host owns only the postMessage protocol.\n */\nexport interface ExtensionSDKHostOptions {\n /** Returns the iframe `contentWindow`, or `null` if not ready. */\n getContentWindow: () => Window | null;\n /** Extension origin used as `postMessage` targetOrigin / source filter. */\n origin: string;\n /**\n * Builds the handshake payload (including auth token) when the child sends\n * `HANDSHAKE_INIT`.\n */\n resolveHandshakePayload: () => HandshakePayload | Promise<HandshakePayload>;\n /**\n * Resolves a fresh auth token when the child sends `REQUEST_TOKEN_REFRESH`.\n */\n resolveAuthToken: () => AuthToken | Promise<AuthToken>;\n /**\n * Persists page settings from `UPDATE_PAGE_SETTINGS`.\n * Return `true` on success; the host emits `PAGE_SETTINGS_UPDATED`.\n */\n onUpdatePageSettings?: (\n settings: PageSettings,\n ) => boolean | Promise<boolean>;\n}\n\n/**\n * Parent-frame counterpart to {@link IExtensionSDK}.\n *\n * Listens for child messages and replies with the shared wire protocol.\n * Create one instance per iframe.\n */\nexport interface IExtensionSDKHost {\n /**\n * Attaches the child message listener. Safe to call once; no-ops if already\n * started or destroyed.\n */\n start(): void;\n\n /**\n * Tears down the listener. The instance cannot be restarted after destroy.\n */\n destroy(): void;\n\n /**\n * Asks the child to open page settings UI with the given values.\n *\n * Wire: `LOAD_PAGE_SETTINGS` (fire-and-forget).\n */\n emitLoadPageSettings(settings: PageSettings | null): void;\n}\n\n/** Default {@link IExtensionSDKHost} implementation. */\nexport class ExtensionSDKHost implements IExtensionSDKHost {\n #options: ExtensionSDKHostOptions;\n #unsubscribe: (() => void) | null = null;\n #started = false;\n #destroyed = false;\n\n constructor(options: ExtensionSDKHostOptions) {\n this.#options = options;\n }\n\n start(): void {\n if (this.#destroyed || this.#started) {\n return;\n }\n\n this.#unsubscribe = subscribeToChildMessages(\n this.#options.origin,\n (event) => {\n void this.#onMessage(event);\n },\n );\n this.#started = true;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#started = false;\n this.#unsubscribe?.();\n this.#unsubscribe = null;\n }\n\n emitLoadPageSettings(settings: PageSettings | null): void {\n this.#assertNotDestroyed();\n this.#post(CONNECTION_EVENTS.loadPageSettings, settings);\n }\n\n async #onMessage(event: MessageEvent<ExtensionMessage>): Promise<void> {\n if (this.#destroyed) {\n return;\n }\n\n const { type, payload } = event.data;\n\n switch (type) {\n case CONNECTION_EVENTS.handshakeInit: {\n await this.#handleHandshakeInit();\n break;\n }\n case CONNECTION_EVENTS.requestAuthTokenRefresh: {\n await this.#handleAuthTokenRefresh();\n break;\n }\n case CONNECTION_EVENTS.updatePageSettings: {\n await this.#handleUpdatePageSettings(\n payload as UpdatePageSettingsPayload | undefined,\n );\n break;\n }\n default: {\n break;\n }\n }\n }\n\n async #handleHandshakeInit(): Promise<void> {\n try {\n const handshakePayload = await this.#options.resolveHandshakePayload();\n if (this.#destroyed) {\n return;\n }\n this.#post(CONNECTION_EVENTS.handshakeAck, handshakePayload);\n } catch {\n // Platform owns error UI / retry; do not reply with a partial ACK.\n }\n }\n\n async #handleAuthTokenRefresh(): Promise<void> {\n try {\n const authToken = await this.#options.resolveAuthToken();\n if (this.#destroyed) {\n return;\n }\n this.#post(CONNECTION_EVENTS.authTokenRefresh, { authToken });\n } catch {\n // Platform owns error UI / retry; do not reply with an invalid token.\n }\n }\n\n async #handleUpdatePageSettings(\n payload: UpdatePageSettingsPayload | undefined,\n ): Promise<void> {\n const settings = payload?.settings;\n if (!settings || typeof settings !== \"object\") {\n this.#postPageSettingsUpdated(false);\n return;\n }\n\n const handler = this.#options.onUpdatePageSettings;\n if (!handler) {\n this.#postPageSettingsUpdated(false);\n return;\n }\n\n try {\n const success = await handler(settings);\n if (this.#destroyed) {\n return;\n }\n this.#postPageSettingsUpdated(Boolean(success));\n } catch {\n if (this.#destroyed) {\n return;\n }\n this.#postPageSettingsUpdated(false);\n }\n }\n\n #postPageSettingsUpdated(success: boolean): void {\n const payload: PageSettingsUpdatedPayload = { success };\n this.#post(CONNECTION_EVENTS.pageSettingsUpdated, payload);\n }\n\n #post(type: (typeof CONNECTION_EVENTS)[keyof typeof CONNECTION_EVENTS], payload?: unknown): void {\n const contentWindow = this.#options.getContentWindow();\n if (!contentWindow) {\n return;\n }\n postToChild(contentWindow, this.#options.origin, type, payload);\n }\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new Error(\"ExtensionSDKHost has been destroyed.\");\n }\n }\n}\n","/**\n * Returns whether `urlRaw` is a valid HTTPS extension URL.\n *\n * When `checkOrigin` is provided, the URL's origin must also pass that predicate\n * (e.g. hostname ends with `.kadanza.app`).\n */\nexport function isValidExtensionUrl(\n urlRaw: string | null | undefined,\n checkOrigin?: (origin: string) => boolean,\n): boolean {\n if (typeof urlRaw !== \"string\") {\n return false;\n }\n\n try {\n const url = new URL(urlRaw);\n const allowedProtocol = url.protocol === \"https:\";\n const allowedOrigin = checkOrigin ? checkOrigin(url.origin) : true;\n return allowedProtocol && allowedOrigin;\n } catch {\n return false;\n }\n}\n\n/**\n * Clones `urlRaw` and sets the `tenantUrl` search param (parent origin).\n *\n * @param urlRaw - Absolute extension URL string.\n * @param tenantUrl - Parent origin to embed (defaults to `window.location.origin`).\n * @returns Enriched `URL`, or `null` when `urlRaw` is missing or unparsable.\n */\nexport function enrichExtensionUrl(\n urlRaw: string | null | undefined,\n tenantUrl: string = window.location.origin,\n): URL | null {\n if (typeof urlRaw !== \"string\") {\n return null;\n }\n\n try {\n const enrichedUrl = new URL(urlRaw);\n\n if (typeof tenantUrl === \"string\") {\n enrichedUrl.searchParams.set(\"tenantUrl\", tenantUrl);\n }\n\n return enrichedUrl;\n } catch {\n return null;\n }\n}\n","import { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\n\nexport { ExtensionSDK, type IExtensionSDK } from \"./ExtensionSDK\";\nexport {\n ExtensionSDKHost,\n type ExtensionSDKHostOptions,\n type IExtensionSDKHost,\n} from \"./ExtensionSDKHost\";\nexport { CONNECTION_EVENTS, type ConnectionEvent } from \"./events\";\nexport {\n postToChild,\n subscribeToChildMessages,\n type HostMessageHandler,\n} from \"./hostMessaging\";\nexport { enrichExtensionUrl, isValidExtensionUrl } from \"./hostUrl\";\nexport {\n InvalidOriginError,\n readTenantUrlFromLocation,\n resolveAllowedOrigin,\n} from \"./origin\";\nexport type {\n AuthToken,\n AuthTokenRefreshPayload,\n ConnectOptions,\n DesignTokens,\n ExtensionDetails,\n ExtensionMessage,\n HandshakePayload,\n NavigationChangePayload,\n PageSettings,\n PageSettingsUpdatedPayload,\n RequestOptions,\n UpdatePageSettingsPayload,\n} from \"./types\";\n\nlet instance: IExtensionSDK | null = null;\nlet hasWarnedDuplicateCreate = false;\n\n/** Clears the factory singleton when the managed instance is destroyed. */\nclass ManagedExtensionSDK extends ExtensionSDK {\n destroy(): void {\n super.destroy();\n if (instance === this) {\n instance = null;\n hasWarnedDuplicateCreate = false;\n }\n }\n}\n\n/**\n * Returns the shared {@link IExtensionSDK} for the current extension frame.\n *\n * Creates the instance on first call. Later calls return the same instance and\n * log a console warning — use the SDK once per app. After {@link IExtensionSDK.destroy},\n * the next call creates a fresh instance.\n */\nexport const createExtensionSDK = (): IExtensionSDK => {\n if (instance) {\n if (!hasWarnedDuplicateCreate) {\n console.warn(\n `[${__PACKAGE_NAME__}] createExtensionSDK() was called more than once. ` +\n \"The SDK is a singleton — reuse the same instance for the whole app.\",\n );\n hasWarnedDuplicateCreate = true;\n }\n return instance;\n }\n\n instance = new ManagedExtensionSDK();\n return instance;\n};\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -85,6 +85,15 @@ export declare interface DesignTokens {
|
|
|
85
85
|
borderRadius: string;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Clones `urlRaw` and sets the `tenantUrl` search param (parent origin).
|
|
90
|
+
*
|
|
91
|
+
* @param urlRaw - Absolute extension URL string.
|
|
92
|
+
* @param tenantUrl - Parent origin to embed (defaults to `window.location.origin`).
|
|
93
|
+
* @returns Enriched `URL`, or `null` when `urlRaw` is missing or unparsable.
|
|
94
|
+
*/
|
|
95
|
+
export declare function enrichExtensionUrl(urlRaw: string | null | undefined, tenantUrl?: string): URL | null;
|
|
96
|
+
|
|
88
97
|
/** Context about the embedded extension and its host page from handshake. */
|
|
89
98
|
export declare interface ExtensionDetails {
|
|
90
99
|
extensionId: string;
|
|
@@ -125,6 +134,42 @@ export declare class ExtensionSDK implements IExtensionSDK {
|
|
|
125
134
|
emitNavigationChange(payload: NavigationChangePayload): void;
|
|
126
135
|
}
|
|
127
136
|
|
|
137
|
+
/** Default {@link IExtensionSDKHost} implementation. */
|
|
138
|
+
export declare class ExtensionSDKHost implements IExtensionSDKHost {
|
|
139
|
+
#private;
|
|
140
|
+
constructor(options: ExtensionSDKHostOptions);
|
|
141
|
+
start(): void;
|
|
142
|
+
destroy(): void;
|
|
143
|
+
emitLoadPageSettings(settings: PageSettings | null): void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Options for {@link ExtensionSDKHost}.
|
|
148
|
+
*
|
|
149
|
+
* Platform code supplies token minting and page-settings persistence via
|
|
150
|
+
* callbacks; the host owns only the postMessage protocol.
|
|
151
|
+
*/
|
|
152
|
+
export declare interface ExtensionSDKHostOptions {
|
|
153
|
+
/** Returns the iframe `contentWindow`, or `null` if not ready. */
|
|
154
|
+
getContentWindow: () => Window | null;
|
|
155
|
+
/** Extension origin used as `postMessage` targetOrigin / source filter. */
|
|
156
|
+
origin: string;
|
|
157
|
+
/**
|
|
158
|
+
* Builds the handshake payload (including auth token) when the child sends
|
|
159
|
+
* `HANDSHAKE_INIT`.
|
|
160
|
+
*/
|
|
161
|
+
resolveHandshakePayload: () => HandshakePayload | Promise<HandshakePayload>;
|
|
162
|
+
/**
|
|
163
|
+
* Resolves a fresh auth token when the child sends `REQUEST_TOKEN_REFRESH`.
|
|
164
|
+
*/
|
|
165
|
+
resolveAuthToken: () => AuthToken | Promise<AuthToken>;
|
|
166
|
+
/**
|
|
167
|
+
* Persists page settings from `UPDATE_PAGE_SETTINGS`.
|
|
168
|
+
* Return `true` on success; the host emits `PAGE_SETTINGS_UPDATED`.
|
|
169
|
+
*/
|
|
170
|
+
onUpdatePageSettings?: (settings: PageSettings) => boolean | Promise<boolean>;
|
|
171
|
+
}
|
|
172
|
+
|
|
128
173
|
/** Payload delivered with a successful `HANDSHAKE_ACK`. */
|
|
129
174
|
export declare interface HandshakePayload {
|
|
130
175
|
authToken: AuthToken;
|
|
@@ -133,6 +178,8 @@ export declare interface HandshakePayload {
|
|
|
133
178
|
pageSettings: PageSettings | null;
|
|
134
179
|
}
|
|
135
180
|
|
|
181
|
+
export declare type HostMessageHandler = (event: MessageEvent<ExtensionMessage>) => void;
|
|
182
|
+
|
|
136
183
|
/**
|
|
137
184
|
* Public contract for talking to the Kadanza parent frame over `postMessage`.
|
|
138
185
|
*
|
|
@@ -247,11 +294,43 @@ export declare interface IExtensionSDK {
|
|
|
247
294
|
emitNavigationChange(payload: NavigationChangePayload): void;
|
|
248
295
|
}
|
|
249
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Parent-frame counterpart to {@link IExtensionSDK}.
|
|
299
|
+
*
|
|
300
|
+
* Listens for child messages and replies with the shared wire protocol.
|
|
301
|
+
* Create one instance per iframe.
|
|
302
|
+
*/
|
|
303
|
+
export declare interface IExtensionSDKHost {
|
|
304
|
+
/**
|
|
305
|
+
* Attaches the child message listener. Safe to call once; no-ops if already
|
|
306
|
+
* started or destroyed.
|
|
307
|
+
*/
|
|
308
|
+
start(): void;
|
|
309
|
+
/**
|
|
310
|
+
* Tears down the listener. The instance cannot be restarted after destroy.
|
|
311
|
+
*/
|
|
312
|
+
destroy(): void;
|
|
313
|
+
/**
|
|
314
|
+
* Asks the child to open page settings UI with the given values.
|
|
315
|
+
*
|
|
316
|
+
* Wire: `LOAD_PAGE_SETTINGS` (fire-and-forget).
|
|
317
|
+
*/
|
|
318
|
+
emitLoadPageSettings(settings: PageSettings | null): void;
|
|
319
|
+
}
|
|
320
|
+
|
|
250
321
|
/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */
|
|
251
322
|
export declare class InvalidOriginError extends Error {
|
|
252
323
|
constructor(message: string);
|
|
253
324
|
}
|
|
254
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Returns whether `urlRaw` is a valid HTTPS extension URL.
|
|
328
|
+
*
|
|
329
|
+
* When `checkOrigin` is provided, the URL's origin must also pass that predicate
|
|
330
|
+
* (e.g. hostname ends with `.kadanza.app`).
|
|
331
|
+
*/
|
|
332
|
+
export declare function isValidExtensionUrl(urlRaw: string | null | undefined, checkOrigin?: (origin: string) => boolean): boolean;
|
|
333
|
+
|
|
255
334
|
/** Payload for `NAVIGATION_CHANGE` (child → parent). */
|
|
256
335
|
export declare interface NavigationChangePayload {
|
|
257
336
|
path: string;
|
|
@@ -265,6 +344,16 @@ export declare interface PageSettingsUpdatedPayload {
|
|
|
265
344
|
success: boolean;
|
|
266
345
|
}
|
|
267
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Posts a typed extension message to an embedded child frame.
|
|
349
|
+
*
|
|
350
|
+
* @param contentWindow - The iframe's `contentWindow`.
|
|
351
|
+
* @param origin - Target origin for `postMessage` (extension origin).
|
|
352
|
+
* @param type - Wire event type from {@link CONNECTION_EVENTS}.
|
|
353
|
+
* @param payload - Optional message payload.
|
|
354
|
+
*/
|
|
355
|
+
export declare function postToChild(contentWindow: Window, origin: string, type: ConnectionEvent, payload?: unknown): void;
|
|
356
|
+
|
|
268
357
|
/**
|
|
269
358
|
* Reads the parent-supplied `tenantUrl` from a URL search string.
|
|
270
359
|
*
|
|
@@ -291,6 +380,15 @@ export declare interface RequestOptions {
|
|
|
291
380
|
*/
|
|
292
381
|
export declare function resolveAllowedOrigin(tenantUrl: string | null): string;
|
|
293
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Subscribes to `message` events from a child frame at `origin`.
|
|
385
|
+
*
|
|
386
|
+
* Ignores events from other origins and payloads without a `type` field.
|
|
387
|
+
*
|
|
388
|
+
* @returns Unsubscribe function.
|
|
389
|
+
*/
|
|
390
|
+
export declare function subscribeToChildMessages(origin: string, handler: HostMessageHandler): () => void;
|
|
391
|
+
|
|
294
392
|
/** Payload for `UPDATE_PAGE_SETTINGS` (child → parent). */
|
|
295
393
|
export declare interface UpdatePageSettingsPayload {
|
|
296
394
|
settings: PageSettings;
|
package/dist/index.d.ts
CHANGED
|
@@ -85,6 +85,15 @@ export declare interface DesignTokens {
|
|
|
85
85
|
borderRadius: string;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Clones `urlRaw` and sets the `tenantUrl` search param (parent origin).
|
|
90
|
+
*
|
|
91
|
+
* @param urlRaw - Absolute extension URL string.
|
|
92
|
+
* @param tenantUrl - Parent origin to embed (defaults to `window.location.origin`).
|
|
93
|
+
* @returns Enriched `URL`, or `null` when `urlRaw` is missing or unparsable.
|
|
94
|
+
*/
|
|
95
|
+
export declare function enrichExtensionUrl(urlRaw: string | null | undefined, tenantUrl?: string): URL | null;
|
|
96
|
+
|
|
88
97
|
/** Context about the embedded extension and its host page from handshake. */
|
|
89
98
|
export declare interface ExtensionDetails {
|
|
90
99
|
extensionId: string;
|
|
@@ -125,6 +134,42 @@ export declare class ExtensionSDK implements IExtensionSDK {
|
|
|
125
134
|
emitNavigationChange(payload: NavigationChangePayload): void;
|
|
126
135
|
}
|
|
127
136
|
|
|
137
|
+
/** Default {@link IExtensionSDKHost} implementation. */
|
|
138
|
+
export declare class ExtensionSDKHost implements IExtensionSDKHost {
|
|
139
|
+
#private;
|
|
140
|
+
constructor(options: ExtensionSDKHostOptions);
|
|
141
|
+
start(): void;
|
|
142
|
+
destroy(): void;
|
|
143
|
+
emitLoadPageSettings(settings: PageSettings | null): void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Options for {@link ExtensionSDKHost}.
|
|
148
|
+
*
|
|
149
|
+
* Platform code supplies token minting and page-settings persistence via
|
|
150
|
+
* callbacks; the host owns only the postMessage protocol.
|
|
151
|
+
*/
|
|
152
|
+
export declare interface ExtensionSDKHostOptions {
|
|
153
|
+
/** Returns the iframe `contentWindow`, or `null` if not ready. */
|
|
154
|
+
getContentWindow: () => Window | null;
|
|
155
|
+
/** Extension origin used as `postMessage` targetOrigin / source filter. */
|
|
156
|
+
origin: string;
|
|
157
|
+
/**
|
|
158
|
+
* Builds the handshake payload (including auth token) when the child sends
|
|
159
|
+
* `HANDSHAKE_INIT`.
|
|
160
|
+
*/
|
|
161
|
+
resolveHandshakePayload: () => HandshakePayload | Promise<HandshakePayload>;
|
|
162
|
+
/**
|
|
163
|
+
* Resolves a fresh auth token when the child sends `REQUEST_TOKEN_REFRESH`.
|
|
164
|
+
*/
|
|
165
|
+
resolveAuthToken: () => AuthToken | Promise<AuthToken>;
|
|
166
|
+
/**
|
|
167
|
+
* Persists page settings from `UPDATE_PAGE_SETTINGS`.
|
|
168
|
+
* Return `true` on success; the host emits `PAGE_SETTINGS_UPDATED`.
|
|
169
|
+
*/
|
|
170
|
+
onUpdatePageSettings?: (settings: PageSettings) => boolean | Promise<boolean>;
|
|
171
|
+
}
|
|
172
|
+
|
|
128
173
|
/** Payload delivered with a successful `HANDSHAKE_ACK`. */
|
|
129
174
|
export declare interface HandshakePayload {
|
|
130
175
|
authToken: AuthToken;
|
|
@@ -133,6 +178,8 @@ export declare interface HandshakePayload {
|
|
|
133
178
|
pageSettings: PageSettings | null;
|
|
134
179
|
}
|
|
135
180
|
|
|
181
|
+
export declare type HostMessageHandler = (event: MessageEvent<ExtensionMessage>) => void;
|
|
182
|
+
|
|
136
183
|
/**
|
|
137
184
|
* Public contract for talking to the Kadanza parent frame over `postMessage`.
|
|
138
185
|
*
|
|
@@ -247,11 +294,43 @@ export declare interface IExtensionSDK {
|
|
|
247
294
|
emitNavigationChange(payload: NavigationChangePayload): void;
|
|
248
295
|
}
|
|
249
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Parent-frame counterpart to {@link IExtensionSDK}.
|
|
299
|
+
*
|
|
300
|
+
* Listens for child messages and replies with the shared wire protocol.
|
|
301
|
+
* Create one instance per iframe.
|
|
302
|
+
*/
|
|
303
|
+
export declare interface IExtensionSDKHost {
|
|
304
|
+
/**
|
|
305
|
+
* Attaches the child message listener. Safe to call once; no-ops if already
|
|
306
|
+
* started or destroyed.
|
|
307
|
+
*/
|
|
308
|
+
start(): void;
|
|
309
|
+
/**
|
|
310
|
+
* Tears down the listener. The instance cannot be restarted after destroy.
|
|
311
|
+
*/
|
|
312
|
+
destroy(): void;
|
|
313
|
+
/**
|
|
314
|
+
* Asks the child to open page settings UI with the given values.
|
|
315
|
+
*
|
|
316
|
+
* Wire: `LOAD_PAGE_SETTINGS` (fire-and-forget).
|
|
317
|
+
*/
|
|
318
|
+
emitLoadPageSettings(settings: PageSettings | null): void;
|
|
319
|
+
}
|
|
320
|
+
|
|
250
321
|
/** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */
|
|
251
322
|
export declare class InvalidOriginError extends Error {
|
|
252
323
|
constructor(message: string);
|
|
253
324
|
}
|
|
254
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Returns whether `urlRaw` is a valid HTTPS extension URL.
|
|
328
|
+
*
|
|
329
|
+
* When `checkOrigin` is provided, the URL's origin must also pass that predicate
|
|
330
|
+
* (e.g. hostname ends with `.kadanza.app`).
|
|
331
|
+
*/
|
|
332
|
+
export declare function isValidExtensionUrl(urlRaw: string | null | undefined, checkOrigin?: (origin: string) => boolean): boolean;
|
|
333
|
+
|
|
255
334
|
/** Payload for `NAVIGATION_CHANGE` (child → parent). */
|
|
256
335
|
export declare interface NavigationChangePayload {
|
|
257
336
|
path: string;
|
|
@@ -265,6 +344,16 @@ export declare interface PageSettingsUpdatedPayload {
|
|
|
265
344
|
success: boolean;
|
|
266
345
|
}
|
|
267
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Posts a typed extension message to an embedded child frame.
|
|
349
|
+
*
|
|
350
|
+
* @param contentWindow - The iframe's `contentWindow`.
|
|
351
|
+
* @param origin - Target origin for `postMessage` (extension origin).
|
|
352
|
+
* @param type - Wire event type from {@link CONNECTION_EVENTS}.
|
|
353
|
+
* @param payload - Optional message payload.
|
|
354
|
+
*/
|
|
355
|
+
export declare function postToChild(contentWindow: Window, origin: string, type: ConnectionEvent, payload?: unknown): void;
|
|
356
|
+
|
|
268
357
|
/**
|
|
269
358
|
* Reads the parent-supplied `tenantUrl` from a URL search string.
|
|
270
359
|
*
|
|
@@ -291,6 +380,15 @@ export declare interface RequestOptions {
|
|
|
291
380
|
*/
|
|
292
381
|
export declare function resolveAllowedOrigin(tenantUrl: string | null): string;
|
|
293
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Subscribes to `message` events from a child frame at `origin`.
|
|
385
|
+
*
|
|
386
|
+
* Ignores events from other origins and payloads without a `type` field.
|
|
387
|
+
*
|
|
388
|
+
* @returns Unsubscribe function.
|
|
389
|
+
*/
|
|
390
|
+
export declare function subscribeToChildMessages(origin: string, handler: HostMessageHandler): () => void;
|
|
391
|
+
|
|
294
392
|
/** Payload for `UPDATE_PAGE_SETTINGS` (child → parent). */
|
|
295
393
|
export declare interface UpdatePageSettingsPayload {
|
|
296
394
|
settings: PageSettings;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadanza/extension-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Helpers for building extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kadanza",
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
"files": [
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
39
42
|
"scripts": {
|
|
40
43
|
"dev": "vite",
|
|
41
44
|
"build": "tsc -p tsconfig.lib.json --noEmit && vite build",
|
|
@@ -43,9 +46,17 @@
|
|
|
43
46
|
"docs": "typedoc",
|
|
44
47
|
"docs:check": "typedoc --emit none",
|
|
45
48
|
"check:types": "npx --yes @arethetypeswrong/cli --pack .",
|
|
49
|
+
"changeset": "changeset",
|
|
50
|
+
"version-packages": "changeset version",
|
|
51
|
+
"version-packages:snapshot": "changeset version --snapshot canary",
|
|
52
|
+
"release": "changeset publish",
|
|
53
|
+
"release:canary": "changeset publish --tag canary --no-git-tag",
|
|
54
|
+
"release:rc:enter": "changeset pre enter rc && node scripts/sync-rc-banner.mjs enter",
|
|
55
|
+
"release:rc:exit": "changeset pre exit && node scripts/sync-rc-banner.mjs exit",
|
|
46
56
|
"prepublishOnly": "npm run build && npm run check:types"
|
|
47
57
|
},
|
|
48
58
|
"devDependencies": {
|
|
59
|
+
"@changesets/cli": "^2.31.1",
|
|
49
60
|
"@vitejs/plugin-basic-ssl": "^2.3.0",
|
|
50
61
|
"typedoc": "^0.28.20",
|
|
51
62
|
"typescript": "~6.0.2",
|