@whatalo/plugin-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/adapters/express.cjs +87 -0
  2. package/dist/adapters/express.cjs.map +1 -0
  3. package/dist/adapters/express.d.cts +17 -0
  4. package/dist/adapters/express.d.ts +17 -0
  5. package/dist/adapters/express.mjs +60 -0
  6. package/dist/adapters/express.mjs.map +1 -0
  7. package/dist/adapters/hono.cjs +79 -0
  8. package/dist/adapters/hono.cjs.map +1 -0
  9. package/dist/adapters/hono.d.cts +15 -0
  10. package/dist/adapters/hono.d.ts +15 -0
  11. package/dist/adapters/hono.mjs +52 -0
  12. package/dist/adapters/hono.mjs.map +1 -0
  13. package/dist/adapters/nextjs.cjs +79 -0
  14. package/dist/adapters/nextjs.cjs.map +1 -0
  15. package/dist/adapters/nextjs.d.cts +7 -0
  16. package/dist/adapters/nextjs.d.ts +7 -0
  17. package/dist/adapters/nextjs.mjs +52 -0
  18. package/dist/adapters/nextjs.mjs.map +1 -0
  19. package/dist/bridge/index.cjs +290 -0
  20. package/dist/bridge/index.cjs.map +1 -0
  21. package/dist/bridge/index.d.cts +236 -0
  22. package/dist/bridge/index.d.ts +236 -0
  23. package/dist/bridge/index.mjs +260 -0
  24. package/dist/bridge/index.mjs.map +1 -0
  25. package/dist/client/index.cjs +423 -0
  26. package/dist/client/index.cjs.map +1 -0
  27. package/dist/client/index.d.cts +131 -0
  28. package/dist/client/index.d.ts +131 -0
  29. package/dist/client/index.mjs +396 -0
  30. package/dist/client/index.mjs.map +1 -0
  31. package/dist/index.cjs +843 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +57 -0
  34. package/dist/index.d.ts +57 -0
  35. package/dist/index.mjs +801 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/manifest/index.cjs +145 -0
  38. package/dist/manifest/index.cjs.map +1 -0
  39. package/dist/manifest/index.d.cts +78 -0
  40. package/dist/manifest/index.d.ts +78 -0
  41. package/dist/manifest/index.mjs +117 -0
  42. package/dist/manifest/index.mjs.map +1 -0
  43. package/dist/types-D2Efg3EG.d.ts +19 -0
  44. package/dist/types-DZ659i6f.d.ts +68 -0
  45. package/dist/types-Db_BeRCj.d.cts +19 -0
  46. package/dist/types-DdqKKyqX.d.cts +68 -0
  47. package/dist/types-M1eLMz6w.d.cts +279 -0
  48. package/dist/types-M1eLMz6w.d.ts +279 -0
  49. package/dist/webhooks/index.cjs +50 -0
  50. package/dist/webhooks/index.cjs.map +1 -0
  51. package/dist/webhooks/index.d.cts +18 -0
  52. package/dist/webhooks/index.d.ts +18 -0
  53. package/dist/webhooks/index.mjs +23 -0
  54. package/dist/webhooks/index.mjs.map +1 -0
  55. package/package.json +94 -0
@@ -0,0 +1,236 @@
1
+ /** Context data sent from the admin host to a plugin iframe */
2
+ interface WhataloContext {
3
+ /** Store UUID */
4
+ storeId: string;
5
+ /** Store display name */
6
+ storeName: string;
7
+ /** Current order ID (set when plugin opens from order detail) */
8
+ orderId?: string;
9
+ /** Current order status */
10
+ orderStatus?: string;
11
+ /** Current product ID (set when plugin opens from product detail) */
12
+ productId?: string;
13
+ /** Authenticated user info */
14
+ user: WhataloUser;
15
+ /** Plugin ID from the manifest */
16
+ appId: string;
17
+ /** Current admin page path (e.g., "/integrations/my-plugin/settings") */
18
+ currentPage: string;
19
+ /** Locale code (e.g., "es", "en") */
20
+ locale: string;
21
+ /** Active color scheme */
22
+ theme: "light" | "dark";
23
+ /** Suggested initial iframe height in pixels */
24
+ initialHeight: number;
25
+ }
26
+ /**
27
+ * Authenticated user information provided to the plugin.
28
+ *
29
+ * `role` reflects the user's actual role in the store, not a hardcoded value.
30
+ * Current Whatalo stores are single-owner, so the role will always be "owner"
31
+ * for now. The union type is forward-compatible for future multi-member support.
32
+ */
33
+ interface WhataloUser {
34
+ id: string;
35
+ name: string;
36
+ email: string;
37
+ role: "owner" | "admin" | "editor" | "staff" | "viewer";
38
+ }
39
+ /** Available bridge actions the plugin can dispatch */
40
+ type BridgeAction = "navigate" | "toast" | "modal" | "resize" | "ready" | "billing";
41
+ /** All billing operations supported by the bridge. createUsageRecord is reserved for v2. */
42
+ declare const BILLING_OPERATION: {
43
+ readonly GET_PLANS: "getPlans";
44
+ readonly GET_SUBSCRIPTION: "getSubscription";
45
+ readonly REQUEST_SUBSCRIPTION: "requestSubscription";
46
+ readonly CANCEL_SUBSCRIPTION: "cancelSubscription";
47
+ readonly REACTIVATE_SUBSCRIPTION: "reactivateSubscription";
48
+ readonly SWITCH_PLAN: "switchPlan";
49
+ readonly CREATE_USAGE_RECORD: "createUsageRecord";
50
+ };
51
+ type BillingOperation = (typeof BILLING_OPERATION)[keyof typeof BILLING_OPERATION];
52
+ /** Payload sent from the plugin to the host for billing actions. */
53
+ interface BillingPayload {
54
+ operation: BillingOperation;
55
+ /** Required for requestSubscription */
56
+ planSlug?: string;
57
+ /** Human-readable reason for the subscription request */
58
+ description?: string;
59
+ /** Reserved for createUsageRecord (v2) */
60
+ amount?: number;
61
+ /** Idempotency key for safe retries (v2) */
62
+ idempotencyKey?: string;
63
+ }
64
+ /** A single pricing plan returned by getPlans. */
65
+ interface BillingPlanResponse {
66
+ slug: string;
67
+ name: string;
68
+ price: number;
69
+ currency: string;
70
+ /** "monthly" | "annual" | null (null for one-time or usage plans) */
71
+ interval: string | null;
72
+ trialDays: number;
73
+ features: string[];
74
+ isPopular: boolean;
75
+ }
76
+ /** The store's current subscription to this plugin, returned by getSubscription. */
77
+ interface BillingSubscriptionResponse {
78
+ planSlug: string;
79
+ planName: string;
80
+ /** "pending" | "trialing" | "active" | "past_due" | "canceled" */
81
+ status: string;
82
+ trialEndsAt: string | null;
83
+ currentPeriodEnd: string | null;
84
+ /** Whether the subscription is scheduled for cancellation at period end */
85
+ cancelAtPeriodEnd: boolean;
86
+ /** ISO timestamp of when the cancellation was initiated, or null */
87
+ canceledAt: string | null;
88
+ }
89
+ /** Message sent from admin host to plugin with context data */
90
+ interface ContextMessage {
91
+ type: "whatalo:context";
92
+ data: WhataloContext;
93
+ }
94
+ /** Action message sent from plugin to admin host */
95
+ interface ActionMessage {
96
+ type: "whatalo:action";
97
+ actionId: string;
98
+ action: BridgeAction;
99
+ payload: Record<string, unknown>;
100
+ }
101
+ /** Acknowledgement message sent from admin host back to plugin */
102
+ interface ActionAck {
103
+ type: "whatalo:ack";
104
+ actionId: string;
105
+ success: boolean;
106
+ error?: string;
107
+ }
108
+ /** Payload for toast notifications */
109
+ interface ToastPayload {
110
+ title: string;
111
+ description?: string;
112
+ variant?: "success" | "error" | "warning" | "info";
113
+ }
114
+ /** Payload for admin navigation */
115
+ interface NavigatePayload {
116
+ path: string;
117
+ }
118
+ /** Payload for modal operations */
119
+ interface ModalPayload {
120
+ operation: "open" | "close";
121
+ title?: string;
122
+ url?: string;
123
+ width?: number;
124
+ height?: number;
125
+ }
126
+ /** Payload for iframe resize */
127
+ interface ResizePayload {
128
+ height: number;
129
+ }
130
+
131
+ /** Billing namespace returned by useWhataloAction. */
132
+ interface BillingActions {
133
+ /** Fetch all active pricing plans for this plugin. */
134
+ getPlans(): Promise<BillingPlanResponse[]>;
135
+ /** Fetch the store's current subscription to this plugin, or null if none. */
136
+ getSubscription(): Promise<BillingSubscriptionResponse | null>;
137
+ /**
138
+ * Request a subscription to the given plan.
139
+ * The host will redirect the admin to an approval page.
140
+ */
141
+ requestSubscription(planSlug: string): Promise<void>;
142
+ /** Initiate a cancellation for the current subscription. */
143
+ cancelSubscription(): Promise<void>;
144
+ /** Reactivate a subscription that was scheduled for cancellation at period end. */
145
+ reactivateSubscription(): Promise<void>;
146
+ /** Switch the current subscription to a different plan with proration. */
147
+ switchPlan(newPlanSlug: string): Promise<void>;
148
+ }
149
+ interface UseWhataloActionReturn {
150
+ /** Send a raw bridge action to the admin host */
151
+ sendAction: (action: BridgeAction, payload: Record<string, unknown>) => Promise<ActionAck>;
152
+ /** Display a toast notification in the admin */
153
+ showToast: (options: ToastPayload) => Promise<ActionAck>;
154
+ /** Navigate the admin to a path */
155
+ navigate: (path: string) => Promise<ActionAck>;
156
+ /** Open a modal in the admin */
157
+ openModal: (options: Omit<ModalPayload, "operation">) => Promise<ActionAck>;
158
+ /** Close the currently open modal */
159
+ closeModal: () => Promise<ActionAck>;
160
+ /** Request an iframe resize */
161
+ resize: (height: number) => Promise<ActionAck>;
162
+ /** Billing actions — query plans, manage subscriptions. */
163
+ billing: BillingActions;
164
+ }
165
+ /**
166
+ * Sends actions to the admin host via postMessage and waits for acknowledgements.
167
+ * Each action has a unique ID and a timeout to prevent hanging promises.
168
+ *
169
+ * All postMessage calls target parentOrigin (set via whatalo:init handshake)
170
+ * instead of "*" to prevent message interception by malicious embedders.
171
+ * If the handshake has not completed yet, the message is queued and flushed
172
+ * once the origin is received.
173
+ */
174
+ declare function useWhataloAction(): UseWhataloActionReturn;
175
+
176
+ interface AppBridgeToast {
177
+ /** Show a toast notification in the admin host */
178
+ show: (title: string, options?: Omit<ToastPayload, "title">) => Promise<ActionAck>;
179
+ }
180
+ interface AppBridgeModal {
181
+ /** Open a modal in the admin host */
182
+ open: (options: Omit<ModalPayload, "operation">) => Promise<ActionAck>;
183
+ /** Close the currently open modal */
184
+ close: () => Promise<ActionAck>;
185
+ }
186
+
187
+ interface AppBridge {
188
+ /** Store UUID */
189
+ storeId: string;
190
+ /** Store display name */
191
+ storeName: string;
192
+ /** Locale code (e.g., "es", "en") */
193
+ locale: string;
194
+ /** Active color scheme */
195
+ theme: "light" | "dark";
196
+ /** Authenticated user info */
197
+ user: WhataloUser;
198
+ /** Plugin ID from the manifest */
199
+ appId: string;
200
+ /** Whether the plugin has received context from the admin host */
201
+ isReady: boolean;
202
+ /** Toast notification helpers */
203
+ toast: AppBridgeToast;
204
+ /** Navigate the admin to a path */
205
+ navigate: (path: string) => Promise<ActionAck>;
206
+ /** Modal helpers */
207
+ modal: AppBridgeModal;
208
+ /** Request an iframe resize */
209
+ resize: (height: number) => Promise<ActionAck>;
210
+ /** Billing actions — query plans and manage subscriptions */
211
+ billing: BillingActions;
212
+ }
213
+ /**
214
+ * Unified bridge hook that combines context and actions into a single API.
215
+ * This is the primary hook plugin developers should use.
216
+ *
217
+ * @example
218
+ * ```tsx
219
+ * const bridge = useAppBridge();
220
+ * bridge.toast.show("Saved!", { variant: "success" });
221
+ * bridge.navigate("/orders");
222
+ * ```
223
+ */
224
+ declare function useAppBridge(): AppBridge;
225
+
226
+ interface UseWhataloContextReturn extends WhataloContext {
227
+ /** Whether the plugin has received context from the admin host */
228
+ isReady: boolean;
229
+ }
230
+ /**
231
+ * Listens for context messages from the admin host and signals readiness.
232
+ * Automatically applies the theme attribute to the document root.
233
+ */
234
+ declare function useWhataloContext(): UseWhataloContextReturn;
235
+
236
+ export { type ActionAck, type ActionMessage, type AppBridge, type AppBridgeModal, type AppBridgeToast, BILLING_OPERATION, type BillingActions, type BillingOperation, type BillingPayload, type BillingPlanResponse, type BillingSubscriptionResponse, type BridgeAction, type ContextMessage, type ModalPayload, type NavigatePayload, type ResizePayload, type ToastPayload, type UseWhataloActionReturn, type UseWhataloContextReturn, type WhataloContext, type WhataloUser, useAppBridge, useWhataloAction, useWhataloContext };
@@ -0,0 +1,260 @@
1
+ // src/bridge/use-whatalo-context.ts
2
+ import { useState, useEffect as useEffect2, useCallback as useCallback2 } from "react";
3
+
4
+ // src/bridge/use-whatalo-action.ts
5
+ import { useCallback, useRef, useEffect } from "react";
6
+
7
+ // src/bridge/types.ts
8
+ var BILLING_OPERATION = {
9
+ GET_PLANS: "getPlans",
10
+ GET_SUBSCRIPTION: "getSubscription",
11
+ REQUEST_SUBSCRIPTION: "requestSubscription",
12
+ CANCEL_SUBSCRIPTION: "cancelSubscription",
13
+ REACTIVATE_SUBSCRIPTION: "reactivateSubscription",
14
+ SWITCH_PLAN: "switchPlan",
15
+ CREATE_USAGE_RECORD: "createUsageRecord"
16
+ };
17
+
18
+ // src/bridge/use-whatalo-action.ts
19
+ var ACK_TIMEOUT_MS = 5e3;
20
+ var parentOrigin = null;
21
+ var originResolvers = [];
22
+ function waitForParentOrigin() {
23
+ if (parentOrigin !== null) {
24
+ return Promise.resolve(parentOrigin);
25
+ }
26
+ return new Promise((resolve) => {
27
+ originResolvers.push(resolve);
28
+ });
29
+ }
30
+ var initListenerAttached = false;
31
+ function attachInitListener() {
32
+ if (initListenerAttached) return;
33
+ initListenerAttached = true;
34
+ window.addEventListener("message", (event) => {
35
+ if (event.source !== window.parent) return;
36
+ if (!event.data || typeof event.data !== "object") return;
37
+ if (event.data.type !== "whatalo:init") return;
38
+ const origin = event.data.origin;
39
+ if (typeof origin !== "string" || !origin) return;
40
+ parentOrigin = origin;
41
+ for (const resolve of originResolvers) {
42
+ resolve(origin);
43
+ }
44
+ originResolvers.length = 0;
45
+ });
46
+ }
47
+ function useWhataloAction() {
48
+ const pendingAcks = useRef(
49
+ /* @__PURE__ */ new Map()
50
+ );
51
+ useEffect(() => {
52
+ attachInitListener();
53
+ const handleMessage = (event) => {
54
+ if (!event.data || typeof event.data !== "object") return;
55
+ const msg = event.data;
56
+ if (msg.type !== "whatalo:ack") return;
57
+ const resolve = pendingAcks.current.get(msg.actionId);
58
+ if (resolve) {
59
+ resolve(msg);
60
+ pendingAcks.current.delete(msg.actionId);
61
+ }
62
+ };
63
+ window.addEventListener("message", handleMessage);
64
+ return () => window.removeEventListener("message", handleMessage);
65
+ }, []);
66
+ const sendAction = useCallback(
67
+ (action, payload) => {
68
+ return new Promise((resolve) => {
69
+ const actionId = crypto.randomUUID();
70
+ const timeout = setTimeout(() => {
71
+ pendingAcks.current.delete(actionId);
72
+ resolve({
73
+ type: "whatalo:ack",
74
+ actionId,
75
+ success: false,
76
+ error: "timeout"
77
+ });
78
+ }, ACK_TIMEOUT_MS);
79
+ pendingAcks.current.set(actionId, (ack) => {
80
+ clearTimeout(timeout);
81
+ resolve(ack);
82
+ });
83
+ waitForParentOrigin().then((origin) => {
84
+ window.parent.postMessage(
85
+ { type: "whatalo:action", actionId, action, payload },
86
+ origin
87
+ );
88
+ });
89
+ });
90
+ },
91
+ []
92
+ );
93
+ const showToast = useCallback(
94
+ (options) => sendAction("toast", options),
95
+ [sendAction]
96
+ );
97
+ const navigate = useCallback(
98
+ (path) => sendAction("navigate", { path }),
99
+ [sendAction]
100
+ );
101
+ const openModal = useCallback(
102
+ (options) => sendAction("modal", {
103
+ operation: "open",
104
+ ...options
105
+ }),
106
+ [sendAction]
107
+ );
108
+ const closeModal = useCallback(
109
+ () => sendAction("modal", { operation: "close" }),
110
+ [sendAction]
111
+ );
112
+ const resize = useCallback(
113
+ (height) => sendAction("resize", { height }),
114
+ [sendAction]
115
+ );
116
+ const sendBillingAction = useCallback(
117
+ (operation, extra) => sendAction("billing", {
118
+ operation,
119
+ ...extra
120
+ }),
121
+ [sendAction]
122
+ );
123
+ const billingGetPlans = useCallback(async () => {
124
+ const ack = await sendBillingAction(BILLING_OPERATION.GET_PLANS);
125
+ if (!ack.success) {
126
+ throw new Error(ack.error ?? "billing.getPlans failed");
127
+ }
128
+ return ack.data ?? [];
129
+ }, [sendBillingAction]);
130
+ const billingGetSubscription = useCallback(
131
+ async () => {
132
+ const ack = await sendBillingAction(BILLING_OPERATION.GET_SUBSCRIPTION);
133
+ if (!ack.success) {
134
+ throw new Error(ack.error ?? "billing.getSubscription failed");
135
+ }
136
+ return ack.data ?? null;
137
+ },
138
+ [sendBillingAction]
139
+ );
140
+ const billingRequestSubscription = useCallback(
141
+ async (planSlug) => {
142
+ const ack = await sendBillingAction(BILLING_OPERATION.REQUEST_SUBSCRIPTION, {
143
+ planSlug
144
+ });
145
+ if (!ack.success) {
146
+ throw new Error(ack.error ?? "billing.requestSubscription failed");
147
+ }
148
+ },
149
+ [sendBillingAction]
150
+ );
151
+ const billingCancelSubscription = useCallback(async () => {
152
+ const ack = await sendBillingAction(BILLING_OPERATION.CANCEL_SUBSCRIPTION);
153
+ if (!ack.success) {
154
+ throw new Error(ack.error ?? "billing.cancelSubscription failed");
155
+ }
156
+ }, [sendBillingAction]);
157
+ const billingReactivateSubscription = useCallback(async () => {
158
+ const ack = await sendBillingAction(BILLING_OPERATION.REACTIVATE_SUBSCRIPTION);
159
+ if (!ack.success) {
160
+ throw new Error(ack.error ?? "billing.reactivateSubscription failed");
161
+ }
162
+ }, [sendBillingAction]);
163
+ const billingSwitchPlan = useCallback(
164
+ async (newPlanSlug) => {
165
+ const ack = await sendBillingAction(BILLING_OPERATION.SWITCH_PLAN, {
166
+ planSlug: newPlanSlug
167
+ });
168
+ if (!ack.success) {
169
+ throw new Error(ack.error ?? "billing.switchPlan failed");
170
+ }
171
+ },
172
+ [sendBillingAction]
173
+ );
174
+ const billing = {
175
+ getPlans: billingGetPlans,
176
+ getSubscription: billingGetSubscription,
177
+ requestSubscription: billingRequestSubscription,
178
+ cancelSubscription: billingCancelSubscription,
179
+ reactivateSubscription: billingReactivateSubscription,
180
+ switchPlan: billingSwitchPlan
181
+ };
182
+ return { sendAction, showToast, navigate, openModal, closeModal, resize, billing };
183
+ }
184
+
185
+ // src/bridge/use-whatalo-context.ts
186
+ var DEFAULT_CONTEXT = {
187
+ storeId: "",
188
+ storeName: "",
189
+ user: { id: "", name: "", email: "", role: "owner" },
190
+ appId: "",
191
+ currentPage: "",
192
+ locale: "es",
193
+ theme: "light",
194
+ initialHeight: 200
195
+ };
196
+ function useWhataloContext() {
197
+ const [context, setContext] = useState(DEFAULT_CONTEXT);
198
+ const [isReady, setIsReady] = useState(false);
199
+ const handleMessage = useCallback2((event) => {
200
+ if (!event.data || typeof event.data !== "object") return;
201
+ const msg = event.data;
202
+ if (msg.type !== "whatalo:context") return;
203
+ const payload = msg.data ?? msg.context;
204
+ if (!payload || typeof payload.storeId !== "string") return;
205
+ setContext(payload);
206
+ setIsReady(true);
207
+ if (payload.theme) {
208
+ document.documentElement.setAttribute("data-theme", payload.theme);
209
+ }
210
+ }, []);
211
+ useEffect2(() => {
212
+ attachInitListener();
213
+ window.addEventListener("message", handleMessage);
214
+ waitForParentOrigin().then((origin) => {
215
+ window.parent.postMessage(
216
+ {
217
+ type: "whatalo:action",
218
+ actionId: crypto.randomUUID(),
219
+ action: "ready",
220
+ payload: {}
221
+ },
222
+ origin
223
+ );
224
+ });
225
+ return () => window.removeEventListener("message", handleMessage);
226
+ }, [handleMessage]);
227
+ return { ...context, isReady };
228
+ }
229
+
230
+ // src/bridge/use-app-bridge.ts
231
+ function useAppBridge() {
232
+ const ctx = useWhataloContext();
233
+ const actions = useWhataloAction();
234
+ return {
235
+ storeId: ctx.storeId,
236
+ storeName: ctx.storeName,
237
+ locale: ctx.locale,
238
+ theme: ctx.theme,
239
+ user: ctx.user,
240
+ appId: ctx.appId,
241
+ isReady: ctx.isReady,
242
+ toast: {
243
+ show: (title, options) => actions.showToast({ title, ...options })
244
+ },
245
+ navigate: actions.navigate,
246
+ modal: {
247
+ open: actions.openModal,
248
+ close: actions.closeModal
249
+ },
250
+ resize: actions.resize,
251
+ billing: actions.billing
252
+ };
253
+ }
254
+ export {
255
+ BILLING_OPERATION,
256
+ useAppBridge,
257
+ useWhataloAction,
258
+ useWhataloContext
259
+ };
260
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bridge/use-whatalo-context.ts","../../src/bridge/use-whatalo-action.ts","../../src/bridge/types.ts","../../src/bridge/use-app-bridge.ts"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport type { WhataloContext, ContextMessage } from \"./types.js\";\nimport { attachInitListener, waitForParentOrigin } from \"./use-whatalo-action.js\";\n\nexport interface UseWhataloContextReturn extends WhataloContext {\n /** Whether the plugin has received context from the admin host */\n isReady: boolean;\n}\n\nconst DEFAULT_CONTEXT: WhataloContext = {\n storeId: \"\",\n storeName: \"\",\n user: { id: \"\", name: \"\", email: \"\", role: \"owner\" },\n appId: \"\",\n currentPage: \"\",\n locale: \"es\",\n theme: \"light\",\n initialHeight: 200,\n};\n\n/**\n * Listens for context messages from the admin host and signals readiness.\n * Automatically applies the theme attribute to the document root.\n */\nexport function useWhataloContext(): UseWhataloContextReturn {\n const [context, setContext] = useState<WhataloContext>(DEFAULT_CONTEXT);\n const [isReady, setIsReady] = useState(false);\n\n const handleMessage = useCallback((event: MessageEvent) => {\n if (!event.data || typeof event.data !== \"object\") return;\n const msg = event.data as ContextMessage & { context?: WhataloContext };\n if (msg.type !== \"whatalo:context\") return;\n\n const payload = msg.data ?? msg.context;\n if (!payload || typeof payload.storeId !== \"string\") return;\n\n setContext(payload);\n setIsReady(true);\n\n if (payload.theme) {\n document.documentElement.setAttribute(\"data-theme\", payload.theme);\n }\n }, []);\n\n useEffect(() => {\n // Ensure the whatalo:init listener is attached before doing anything else.\n // This is idempotent — safe to call multiple times.\n attachInitListener();\n\n window.addEventListener(\"message\", handleMessage);\n\n // Send the ready signal to the admin host. We wait for parentOrigin from\n // the whatalo:init handshake before sending so we never broadcast to \"*\".\n // The admin sends whatalo:init on iframe onLoad, which fires before the\n // plugin's React tree hydrates — so the wait is typically sub-millisecond.\n waitForParentOrigin().then((origin) => {\n window.parent.postMessage(\n {\n type: \"whatalo:action\",\n actionId: crypto.randomUUID(),\n action: \"ready\",\n payload: {},\n },\n origin,\n );\n });\n\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [handleMessage]);\n\n return { ...context, isReady };\n}\n","import { useCallback, useRef, useEffect } from \"react\";\nimport type {\n BridgeAction,\n ActionAck,\n ToastPayload,\n ModalPayload,\n BillingOperation,\n BillingPayload,\n BillingPlanResponse,\n BillingSubscriptionResponse,\n} from \"./types.js\";\nimport { BILLING_OPERATION } from \"./types.js\";\n\nconst ACK_TIMEOUT_MS = 5_000;\n\n// ---------------------------------------------------------------------------\n// Module-level origin store (singleton shared across all hook instances)\n//\n// This module owns the handshake state. Both useWhataloAction and\n// useWhataloContext import from here so they target the same parentOrigin\n// once the admin sends the whatalo:init handshake.\n// ---------------------------------------------------------------------------\n\n/** Origin of the admin host, received via whatalo:init handshake. */\nlet parentOrigin: string | null = null;\n\n/** Callbacks waiting for parentOrigin to be resolved. */\nconst originResolvers: Array<(origin: string) => void> = [];\n\n/**\n * Returns a Promise that resolves with the parent origin.\n * If the handshake already completed, resolves immediately.\n */\nexport function waitForParentOrigin(): Promise<string> {\n if (parentOrigin !== null) {\n return Promise.resolve(parentOrigin);\n }\n return new Promise((resolve) => {\n originResolvers.push(resolve);\n });\n}\n\n/**\n * Initializes the origin listener exactly once (idempotent).\n * Listens for the whatalo:init message from the admin host and stores\n * the origin so all subsequent postMessage calls can target it explicitly.\n */\nlet initListenerAttached = false;\n\nexport function attachInitListener(): void {\n if (initListenerAttached) return;\n initListenerAttached = true;\n\n window.addEventListener(\"message\", (event: MessageEvent) => {\n // Only accept the init message from the direct parent window\n if (event.source !== window.parent) return;\n if (!event.data || typeof event.data !== \"object\") return;\n if (event.data.type !== \"whatalo:init\") return;\n\n const origin = event.data.origin;\n if (typeof origin !== \"string\" || !origin) return;\n\n // Store origin and flush any queued resolvers\n parentOrigin = origin;\n for (const resolve of originResolvers) {\n resolve(origin);\n }\n originResolvers.length = 0;\n });\n}\n\n/** Billing namespace returned by useWhataloAction. */\nexport interface BillingActions {\n /** Fetch all active pricing plans for this plugin. */\n getPlans(): Promise<BillingPlanResponse[]>;\n /** Fetch the store's current subscription to this plugin, or null if none. */\n getSubscription(): Promise<BillingSubscriptionResponse | null>;\n /**\n * Request a subscription to the given plan.\n * The host will redirect the admin to an approval page.\n */\n requestSubscription(planSlug: string): Promise<void>;\n /** Initiate a cancellation for the current subscription. */\n cancelSubscription(): Promise<void>;\n /** Reactivate a subscription that was scheduled for cancellation at period end. */\n reactivateSubscription(): Promise<void>;\n /** Switch the current subscription to a different plan with proration. */\n switchPlan(newPlanSlug: string): Promise<void>;\n}\n\nexport interface UseWhataloActionReturn {\n /** Send a raw bridge action to the admin host */\n sendAction: (\n action: BridgeAction,\n payload: Record<string, unknown>,\n ) => Promise<ActionAck>;\n /** Display a toast notification in the admin */\n showToast: (options: ToastPayload) => Promise<ActionAck>;\n /** Navigate the admin to a path */\n navigate: (path: string) => Promise<ActionAck>;\n /** Open a modal in the admin */\n openModal: (options: Omit<ModalPayload, \"operation\">) => Promise<ActionAck>;\n /** Close the currently open modal */\n closeModal: () => Promise<ActionAck>;\n /** Request an iframe resize */\n resize: (height: number) => Promise<ActionAck>;\n /** Billing actions — query plans, manage subscriptions. */\n billing: BillingActions;\n}\n\n/**\n * Sends actions to the admin host via postMessage and waits for acknowledgements.\n * Each action has a unique ID and a timeout to prevent hanging promises.\n *\n * All postMessage calls target parentOrigin (set via whatalo:init handshake)\n * instead of \"*\" to prevent message interception by malicious embedders.\n * If the handshake has not completed yet, the message is queued and flushed\n * once the origin is received.\n */\nexport function useWhataloAction(): UseWhataloActionReturn {\n const pendingAcks = useRef(\n new Map<string, (ack: ActionAck) => void>(),\n );\n\n useEffect(() => {\n // Ensure the init listener is running whenever this hook mounts\n attachInitListener();\n\n const handleMessage = (event: MessageEvent) => {\n if (!event.data || typeof event.data !== \"object\") return;\n const msg = event.data as ActionAck;\n if (msg.type !== \"whatalo:ack\") return;\n\n const resolve = pendingAcks.current.get(msg.actionId);\n if (resolve) {\n resolve(msg);\n pendingAcks.current.delete(msg.actionId);\n }\n };\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, []);\n\n const sendAction = useCallback(\n (\n action: BridgeAction,\n payload: Record<string, unknown>,\n ): Promise<ActionAck> => {\n return new Promise((resolve) => {\n const actionId = crypto.randomUUID();\n\n const timeout = setTimeout(() => {\n pendingAcks.current.delete(actionId);\n resolve({\n type: \"whatalo:ack\",\n actionId,\n success: false,\n error: \"timeout\",\n });\n }, ACK_TIMEOUT_MS);\n\n pendingAcks.current.set(actionId, (ack) => {\n clearTimeout(timeout);\n resolve(ack);\n });\n\n // Wait for the parent origin from the whatalo:init handshake before\n // sending. If the handshake already completed this resolves synchronously.\n waitForParentOrigin().then((origin) => {\n window.parent.postMessage(\n { type: \"whatalo:action\", actionId, action, payload },\n origin,\n );\n });\n });\n },\n [],\n );\n\n const showToast = useCallback(\n (options: ToastPayload) =>\n sendAction(\"toast\", options as unknown as Record<string, unknown>),\n [sendAction],\n );\n\n const navigate = useCallback(\n (path: string) => sendAction(\"navigate\", { path }),\n [sendAction],\n );\n\n const openModal = useCallback(\n (options: Omit<ModalPayload, \"operation\">) =>\n sendAction(\"modal\", {\n operation: \"open\",\n ...options,\n } as unknown as Record<string, unknown>),\n [sendAction],\n );\n\n const closeModal = useCallback(\n () => sendAction(\"modal\", { operation: \"close\" }),\n [sendAction],\n );\n\n const resize = useCallback(\n (height: number) => sendAction(\"resize\", { height }),\n [sendAction],\n );\n\n // ---------------------------------------------------------------------------\n // Billing helpers\n //\n // Each billing method sends a \"billing\" action with a typed operation field.\n // The host resolves the operation against the store's app_plans /\n // app_subscriptions records and returns data in the ack payload.\n // ---------------------------------------------------------------------------\n\n /**\n * Extracts typed data from a billing ack payload.\n * Throws on failure so callers can use try/catch for error handling.\n */\n const sendBillingAction = useCallback(\n (operation: BillingOperation, extra?: Omit<BillingPayload, \"operation\">) =>\n sendAction(\"billing\", {\n operation,\n ...extra,\n } as unknown as Record<string, unknown>),\n [sendAction],\n );\n\n const billingGetPlans = useCallback(async (): Promise<BillingPlanResponse[]> => {\n const ack = await sendBillingAction(BILLING_OPERATION.GET_PLANS);\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.getPlans failed\");\n }\n // The host embeds the plans array in the ack under a `data` key\n return (ack as ActionAck & { data: BillingPlanResponse[] }).data ?? [];\n }, [sendBillingAction]);\n\n const billingGetSubscription = useCallback(\n async (): Promise<BillingSubscriptionResponse | null> => {\n const ack = await sendBillingAction(BILLING_OPERATION.GET_SUBSCRIPTION);\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.getSubscription failed\");\n }\n return (\n (ack as ActionAck & { data: BillingSubscriptionResponse | null }).data ??\n null\n );\n },\n [sendBillingAction],\n );\n\n const billingRequestSubscription = useCallback(\n async (planSlug: string): Promise<void> => {\n const ack = await sendBillingAction(BILLING_OPERATION.REQUEST_SUBSCRIPTION, {\n planSlug,\n });\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.requestSubscription failed\");\n }\n },\n [sendBillingAction],\n );\n\n const billingCancelSubscription = useCallback(async (): Promise<void> => {\n const ack = await sendBillingAction(BILLING_OPERATION.CANCEL_SUBSCRIPTION);\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.cancelSubscription failed\");\n }\n }, [sendBillingAction]);\n\n const billingReactivateSubscription = useCallback(async (): Promise<void> => {\n const ack = await sendBillingAction(BILLING_OPERATION.REACTIVATE_SUBSCRIPTION);\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.reactivateSubscription failed\");\n }\n }, [sendBillingAction]);\n\n const billingSwitchPlan = useCallback(\n async (newPlanSlug: string): Promise<void> => {\n const ack = await sendBillingAction(BILLING_OPERATION.SWITCH_PLAN, {\n planSlug: newPlanSlug,\n });\n if (!ack.success) {\n throw new Error(ack.error ?? \"billing.switchPlan failed\");\n }\n },\n [sendBillingAction],\n );\n\n const billing: BillingActions = {\n getPlans: billingGetPlans,\n getSubscription: billingGetSubscription,\n requestSubscription: billingRequestSubscription,\n cancelSubscription: billingCancelSubscription,\n reactivateSubscription: billingReactivateSubscription,\n switchPlan: billingSwitchPlan,\n };\n\n return { sendAction, showToast, navigate, openModal, closeModal, resize, billing };\n}\n","/** Context data sent from the admin host to a plugin iframe */\nexport interface WhataloContext {\n /** Store UUID */\n storeId: string;\n /** Store display name */\n storeName: string;\n /** Current order ID (set when plugin opens from order detail) */\n orderId?: string;\n /** Current order status */\n orderStatus?: string;\n /** Current product ID (set when plugin opens from product detail) */\n productId?: string;\n /** Authenticated user info */\n user: WhataloUser;\n /** Plugin ID from the manifest */\n appId: string;\n /** Current admin page path (e.g., \"/integrations/my-plugin/settings\") */\n currentPage: string;\n /** Locale code (e.g., \"es\", \"en\") */\n locale: string;\n /** Active color scheme */\n theme: \"light\" | \"dark\";\n /** Suggested initial iframe height in pixels */\n initialHeight: number;\n}\n\n/**\n * Authenticated user information provided to the plugin.\n *\n * `role` reflects the user's actual role in the store, not a hardcoded value.\n * Current Whatalo stores are single-owner, so the role will always be \"owner\"\n * for now. The union type is forward-compatible for future multi-member support.\n */\nexport interface WhataloUser {\n id: string;\n name: string;\n email: string;\n role: \"owner\" | \"admin\" | \"editor\" | \"staff\" | \"viewer\";\n}\n\n/** Available bridge actions the plugin can dispatch */\nexport type BridgeAction =\n | \"navigate\"\n | \"toast\"\n | \"modal\"\n | \"resize\"\n | \"ready\"\n | \"billing\";\n\n// ---------------------------------------------------------------------------\n// Billing types\n// ---------------------------------------------------------------------------\n\n/** All billing operations supported by the bridge. createUsageRecord is reserved for v2. */\nexport const BILLING_OPERATION = {\n GET_PLANS: \"getPlans\",\n GET_SUBSCRIPTION: \"getSubscription\",\n REQUEST_SUBSCRIPTION: \"requestSubscription\",\n CANCEL_SUBSCRIPTION: \"cancelSubscription\",\n REACTIVATE_SUBSCRIPTION: \"reactivateSubscription\",\n SWITCH_PLAN: \"switchPlan\",\n CREATE_USAGE_RECORD: \"createUsageRecord\",\n} as const;\n\nexport type BillingOperation =\n (typeof BILLING_OPERATION)[keyof typeof BILLING_OPERATION];\n\n/** Payload sent from the plugin to the host for billing actions. */\nexport interface BillingPayload {\n operation: BillingOperation;\n /** Required for requestSubscription */\n planSlug?: string;\n /** Human-readable reason for the subscription request */\n description?: string;\n /** Reserved for createUsageRecord (v2) */\n amount?: number;\n /** Idempotency key for safe retries (v2) */\n idempotencyKey?: string;\n}\n\n/** A single pricing plan returned by getPlans. */\nexport interface BillingPlanResponse {\n slug: string;\n name: string;\n price: number;\n currency: string;\n /** \"monthly\" | \"annual\" | null (null for one-time or usage plans) */\n interval: string | null;\n trialDays: number;\n features: string[];\n isPopular: boolean;\n}\n\n/** The store's current subscription to this plugin, returned by getSubscription. */\nexport interface BillingSubscriptionResponse {\n planSlug: string;\n planName: string;\n /** \"pending\" | \"trialing\" | \"active\" | \"past_due\" | \"canceled\" */\n status: string;\n trialEndsAt: string | null;\n currentPeriodEnd: string | null;\n /** Whether the subscription is scheduled for cancellation at period end */\n cancelAtPeriodEnd: boolean;\n /** ISO timestamp of when the cancellation was initiated, or null */\n canceledAt: string | null;\n}\n\n/** Message sent from admin host to plugin with context data */\nexport interface ContextMessage {\n type: \"whatalo:context\";\n data: WhataloContext;\n}\n\n/** Action message sent from plugin to admin host */\nexport interface ActionMessage {\n type: \"whatalo:action\";\n actionId: string;\n action: BridgeAction;\n payload: Record<string, unknown>;\n}\n\n/** Acknowledgement message sent from admin host back to plugin */\nexport interface ActionAck {\n type: \"whatalo:ack\";\n actionId: string;\n success: boolean;\n error?: string;\n}\n\n/** Payload for toast notifications */\nexport interface ToastPayload {\n title: string;\n description?: string;\n variant?: \"success\" | \"error\" | \"warning\" | \"info\";\n}\n\n/** Payload for admin navigation */\nexport interface NavigatePayload {\n path: string;\n}\n\n/** Payload for modal operations */\nexport interface ModalPayload {\n operation: \"open\" | \"close\";\n title?: string;\n url?: string;\n width?: number;\n height?: number;\n}\n\n/** Payload for iframe resize */\nexport interface ResizePayload {\n height: number;\n}\n","import { useWhataloContext } from \"./use-whatalo-context.js\";\nimport { useWhataloAction } from \"./use-whatalo-action.js\";\nimport type {\n ActionAck,\n ToastPayload,\n ModalPayload,\n WhataloUser,\n BillingPlanResponse,\n BillingSubscriptionResponse,\n} from \"./types.js\";\nimport type { BillingActions } from \"./use-whatalo-action.js\";\n\nexport interface AppBridgeToast {\n /** Show a toast notification in the admin host */\n show: (\n title: string,\n options?: Omit<ToastPayload, \"title\">,\n ) => Promise<ActionAck>;\n}\n\nexport interface AppBridgeModal {\n /** Open a modal in the admin host */\n open: (options: Omit<ModalPayload, \"operation\">) => Promise<ActionAck>;\n /** Close the currently open modal */\n close: () => Promise<ActionAck>;\n}\n\n/** Re-export for consumers who import directly from use-app-bridge */\nexport type { BillingActions, BillingPlanResponse, BillingSubscriptionResponse };\n\nexport interface AppBridge {\n /** Store UUID */\n storeId: string;\n /** Store display name */\n storeName: string;\n /** Locale code (e.g., \"es\", \"en\") */\n locale: string;\n /** Active color scheme */\n theme: \"light\" | \"dark\";\n /** Authenticated user info */\n user: WhataloUser;\n /** Plugin ID from the manifest */\n appId: string;\n /** Whether the plugin has received context from the admin host */\n isReady: boolean;\n /** Toast notification helpers */\n toast: AppBridgeToast;\n /** Navigate the admin to a path */\n navigate: (path: string) => Promise<ActionAck>;\n /** Modal helpers */\n modal: AppBridgeModal;\n /** Request an iframe resize */\n resize: (height: number) => Promise<ActionAck>;\n /** Billing actions — query plans and manage subscriptions */\n billing: BillingActions;\n}\n\n/**\n * Unified bridge hook that combines context and actions into a single API.\n * This is the primary hook plugin developers should use.\n *\n * @example\n * ```tsx\n * const bridge = useAppBridge();\n * bridge.toast.show(\"Saved!\", { variant: \"success\" });\n * bridge.navigate(\"/orders\");\n * ```\n */\nexport function useAppBridge(): AppBridge {\n const ctx = useWhataloContext();\n const actions = useWhataloAction();\n\n return {\n storeId: ctx.storeId,\n storeName: ctx.storeName,\n locale: ctx.locale,\n theme: ctx.theme,\n user: ctx.user,\n appId: ctx.appId,\n isReady: ctx.isReady,\n toast: {\n show: (title, options) =>\n actions.showToast({ title, ...options }),\n },\n navigate: actions.navigate,\n modal: {\n open: actions.openModal,\n close: actions.closeModal,\n },\n resize: actions.resize,\n billing: actions.billing,\n };\n}\n"],"mappings":"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","names":["useEffect","useCallback","useCallback","useEffect"]}