Skip to content

Commit cab271d

Browse files
committed
fix: expose SDK instance for plugin API access via singleton
- Make ExperienceRuntime.sdk public (not readonly) for Proxy access - Add default export for IIFE build to expose singleton correctly - Update singleton Proxy to dynamically access defaultInstance.sdk - Remove manual window assignment (handled by IIFE build) - Fixes playground integration where experiences.modal was undefined This enables script tag users to access plugin APIs via global: experiences.modal.show(), experiences.inline.show(), etc.
1 parent 476c3cc commit cab271d

File tree

3 files changed

+20
-21
lines changed

3 files changed

+20
-21
lines changed

packages/core/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@ export {
1515
evaluateExperience,
1616
evaluateUrlRule,
1717
} from './runtime';
18-
1918
// Export singleton API
19+
// Default export for IIFE builds (script tag)
20+
// This is what gets exposed as window.experiences
2021
export {
2122
createInstance,
2223
destroy,
2324
evaluate,
2425
evaluateAll,
26+
experiences, // Named exportexperiences as default,
2527
explain,
2628
getState,
2729
init,
2830
on,
2931
register,
3032
} from './singleton';
33+
3134
// Export all types
3235
export type {
3336
BannerContent,

packages/core/src/runtime.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import type {
3333
* - Explainability-first (every decision has reasons)
3434
*/
3535
export class ExperienceRuntime {
36-
private sdk: SDK;
36+
public sdk: SDK; // Public for plugin API access via Proxy (readonly would prevent reinit)
3737
private experiences: Map<string, Experience> = new Map();
3838
private decisions: Decision[] = [];
3939
private initialized = false;

packages/core/src/singleton.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,10 @@ export async function destroy(): Promise<void> {
177177
* import experiences from '@prosdevlab/experience-sdk';
178178
* await experiences.init();
179179
* ```
180+
*
181+
* For IIFE builds (script tag), this becomes window.experiences via default export
180182
*/
181-
export const experiences = {
183+
const experiencesObject = {
182184
createInstance,
183185
init,
184186
register,
@@ -188,23 +190,17 @@ export const experiences = {
188190
getState,
189191
on,
190192
destroy,
191-
// Expose plugin APIs from the default instance
192-
get modal() {
193-
return (defaultInstance as any).modal;
194-
},
195-
get inline() {
196-
return (defaultInstance as any).inline;
197-
},
198-
get banner() {
199-
return (defaultInstance as any).banner;
200-
},
201193
};
202194

203-
/**
204-
* Global singleton instance for IIFE builds
205-
*
206-
* When loaded via script tag, this object is available as `window.experiences`
207-
*/
208-
if (typeof window !== 'undefined') {
209-
(window as unknown as Record<string, unknown>).experiences = experiences;
210-
}
195+
const experiencesProxy = new Proxy(experiencesObject, {
196+
get(target, prop) {
197+
// Check wrapper functions first
198+
if (prop in target && target[prop as keyof typeof target]) {
199+
return target[prop as keyof typeof target];
200+
}
201+
// Fall back to SDK instance for plugin APIs (modal, inline, banner, etc.)
202+
return (defaultInstance.sdk as any)[prop];
203+
},
204+
});
205+
206+
export { experiencesProxy as experiences };

0 commit comments

Comments
 (0)