@final-commerce/command-frame 0.1.15 → 0.1.17

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 (44) hide show
  1. package/README.md +42 -449
  2. package/dist/CommonTypes.d.ts +4 -7
  3. package/dist/actions/add-cart-discount/mock.js +5 -1
  4. package/dist/actions/add-product-to-cart/mock.js +3 -1
  5. package/dist/actions/assign-customer/mock.js +3 -1
  6. package/dist/actions/cash-payment/mock.js +3 -1
  7. package/dist/actions/clear-cart/mock.js +3 -1
  8. package/dist/actions/get-products/mock.js +3 -3
  9. package/dist/client.d.ts +4 -1
  10. package/dist/client.js +24 -4
  11. package/dist/demo/database.d.ts +10 -0
  12. package/dist/demo/database.js +77 -17
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +6 -0
  15. package/dist/projects/manage/client.d.ts +13 -0
  16. package/dist/projects/manage/client.js +13 -0
  17. package/dist/projects/manage/index.d.ts +4 -0
  18. package/dist/projects/manage/index.js +4 -0
  19. package/dist/projects/manage/mocks.d.ts +2 -0
  20. package/dist/projects/manage/mocks.js +6 -0
  21. package/dist/projects/manage/provider.d.ts +8 -0
  22. package/dist/projects/manage/provider.js +6 -0
  23. package/dist/projects/manage/types.d.ts +5 -0
  24. package/dist/projects/manage/types.js +1 -0
  25. package/dist/projects/render/client.d.ts +13 -0
  26. package/dist/projects/render/client.js +13 -0
  27. package/dist/projects/render/index.d.ts +4 -0
  28. package/dist/projects/render/index.js +4 -0
  29. package/dist/projects/render/mocks.d.ts +2 -0
  30. package/dist/projects/render/mocks.js +94 -0
  31. package/dist/projects/render/provider.d.ts +8 -0
  32. package/dist/projects/render/provider.js +6 -0
  33. package/dist/projects/render/types.d.ts +49 -0
  34. package/dist/projects/render/types.js +1 -0
  35. package/dist/provider.d.ts +19 -0
  36. package/dist/provider.js +178 -0
  37. package/dist/pubsub/subscriber.d.ts +4 -0
  38. package/dist/pubsub/subscriber.js +53 -5
  39. package/dist/pubsub/topics/types.d.ts +14 -0
  40. package/dist/pubsub/topics/types.js +1 -0
  41. package/dist/pubsub/types.d.ts +9 -0
  42. package/package.json +1 -1
  43. package/dist/demo/registry.d.ts +0 -5
  44. package/dist/demo/registry.js +0 -94
@@ -0,0 +1,19 @@
1
+ export type ActionHandler<TParams = any, TResponse = any> = (params: TParams) => Promise<TResponse> | TResponse;
2
+ export type ActionHandlers = Map<string, ActionHandler>;
3
+ export declare class CommandFrameProvider<TActions extends object = any> {
4
+ private handlers;
5
+ private origin;
6
+ private debug;
7
+ private destroyed;
8
+ private boundHandleMessage;
9
+ constructor(actions?: TActions, options?: {
10
+ origin?: string;
11
+ debug?: boolean;
12
+ });
13
+ register<TParams = any, TResponse = any>(action: string, handler: ActionHandler<TParams, TResponse>): void;
14
+ unregister(action: string): void;
15
+ private handleMessage;
16
+ private sendResponse;
17
+ destroy(): void;
18
+ isDestroyed(): boolean;
19
+ }
@@ -0,0 +1,178 @@
1
+ export class CommandFrameProvider {
2
+ constructor(actions, options = {}) {
3
+ this.handlers = new Map();
4
+ this.destroyed = false;
5
+ this.origin = options.origin || "*";
6
+ this.debug = options.debug || false;
7
+ this.boundHandleMessage = this.handleMessage.bind(this);
8
+ if (typeof window !== "undefined") {
9
+ window.addEventListener("message", this.boundHandleMessage);
10
+ }
11
+ if (actions) {
12
+ Object.keys(actions).forEach((actionName) => {
13
+ const handler = actions[actionName];
14
+ if (typeof handler === "function") {
15
+ this.register(actionName, handler);
16
+ }
17
+ });
18
+ }
19
+ if (this.debug) {
20
+ console.log("[CommandFrameProvider] Initialized", {
21
+ origin: this.origin,
22
+ debug: this.debug
23
+ });
24
+ }
25
+ }
26
+ register(action, handler) {
27
+ if (this.destroyed) {
28
+ if (this.debug) {
29
+ console.warn("[CommandFrameProvider] Cannot register action after destruction", { action });
30
+ }
31
+ return;
32
+ }
33
+ this.handlers.set(action, handler);
34
+ if (this.debug) {
35
+ console.log("[CommandFrameProvider] Registered action", { action });
36
+ }
37
+ }
38
+ unregister(action) {
39
+ if (this.destroyed) {
40
+ if (this.debug) {
41
+ console.warn("[CommandFrameProvider] Cannot unregister action after destruction", { action });
42
+ }
43
+ return;
44
+ }
45
+ this.handlers.delete(action);
46
+ if (this.debug) {
47
+ console.log("[CommandFrameProvider] Unregistered action", { action });
48
+ }
49
+ }
50
+ async handleMessage(event) {
51
+ // Ignore messages after destruction
52
+ if (this.destroyed) {
53
+ return;
54
+ }
55
+ const request = event.data;
56
+ if (!request || typeof request !== "object" || !("action" in request)) {
57
+ return;
58
+ }
59
+ if (this.origin !== "*" && event.origin !== this.origin) {
60
+ if (this.debug) {
61
+ console.warn("[CommandFrameProvider] Origin mismatch", {
62
+ expected: this.origin,
63
+ received: event.origin
64
+ });
65
+ }
66
+ return;
67
+ }
68
+ if (!request.action || !request.requestId) {
69
+ if (this.debug) {
70
+ console.warn("[CommandFrameProvider] Invalid request format", {
71
+ data: event.data,
72
+ origin: event.origin
73
+ });
74
+ }
75
+ return;
76
+ }
77
+ if (this.debug) {
78
+ console.log("[CommandFrameProvider] Received request", {
79
+ origin: event.origin,
80
+ action: request.action,
81
+ requestId: request.requestId,
82
+ timestamp: new Date().toISOString()
83
+ });
84
+ }
85
+ const handler = this.handlers.get(request.action);
86
+ if (!handler) {
87
+ if (this.debug) {
88
+ console.warn("[CommandFrameProvider] Unknown action", { action: request.action });
89
+ }
90
+ // Only send response if source window is still available
91
+ if (event.source && event.source !== window) {
92
+ this.sendResponse(event.source, request.requestId, {
93
+ success: false,
94
+ error: `Unknown action: ${request.action}`
95
+ });
96
+ }
97
+ return;
98
+ }
99
+ try {
100
+ const result = await handler(request.params);
101
+ // Only send response if source window is still available
102
+ if (event.source && event.source !== window) {
103
+ this.sendResponse(event.source, request.requestId, {
104
+ success: true,
105
+ data: result
106
+ });
107
+ if (this.debug) {
108
+ console.log("[CommandFrameProvider] Action executed", {
109
+ action: request.action,
110
+ requestId: request.requestId
111
+ });
112
+ }
113
+ }
114
+ else if (this.debug) {
115
+ console.warn("[CommandFrameProvider] Cannot send response - source window unavailable", {
116
+ action: request.action,
117
+ requestId: request.requestId
118
+ });
119
+ }
120
+ }
121
+ catch (error) {
122
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
123
+ // Only send response if source window is still available
124
+ if (event.source && event.source !== window) {
125
+ this.sendResponse(event.source, request.requestId, {
126
+ success: false,
127
+ error: errorMessage
128
+ });
129
+ if (this.debug) {
130
+ console.error("[CommandFrameProvider] Action failed", {
131
+ action: request.action,
132
+ requestId: request.requestId,
133
+ error: errorMessage
134
+ });
135
+ }
136
+ }
137
+ else if (this.debug) {
138
+ console.warn("[CommandFrameProvider] Cannot send error response - source window unavailable", {
139
+ action: request.action,
140
+ requestId: request.requestId,
141
+ error: errorMessage
142
+ });
143
+ }
144
+ }
145
+ }
146
+ sendResponse(target, requestId, response) {
147
+ const message = {
148
+ requestId,
149
+ ...response
150
+ };
151
+ if (this.debug) {
152
+ console.log("[CommandFrameProvider] Sending response", {
153
+ requestId,
154
+ success: response.success
155
+ });
156
+ }
157
+ target.postMessage(message, this.origin);
158
+ }
159
+ destroy() {
160
+ if (this.destroyed) {
161
+ if (this.debug) {
162
+ console.warn("[CommandFrameProvider] Already destroyed");
163
+ }
164
+ return;
165
+ }
166
+ this.destroyed = true;
167
+ this.handlers.clear();
168
+ if (typeof window !== "undefined") {
169
+ window.removeEventListener("message", this.boundHandleMessage);
170
+ }
171
+ if (this.debug) {
172
+ console.log("[CommandFrameProvider] Destroyed");
173
+ }
174
+ }
175
+ isDestroyed() {
176
+ return this.destroyed;
177
+ }
178
+ }
@@ -11,10 +11,14 @@ export declare class TopicSubscriber {
11
11
  private useGlobalDebug;
12
12
  private boundHandleMessage;
13
13
  private subscriptionIdCounter;
14
+ private mockMode;
15
+ private detectionTimeout;
14
16
  constructor(options?: {
15
17
  origin?: string;
16
18
  debug?: boolean;
17
19
  });
20
+ private initDetection;
21
+ private switchToMockMode;
18
22
  private isDebugEnabled;
19
23
  /**
20
24
  * Request the list of available topics from the host
@@ -2,28 +2,64 @@
2
2
  * Topic Subscriber for iframe communication
3
3
  * Manages subscriptions to topics and receives events from the host window
4
4
  */
5
+ import { mockSubscribeToTopic } from "../demo/database";
5
6
  export class TopicSubscriber {
6
7
  constructor(options = {}) {
7
8
  this.subscriptions = new Map();
8
9
  this.topics = [];
9
10
  this.subscriptionIdCounter = 0;
11
+ this.mockMode = false;
10
12
  this.origin = options.origin || "*";
11
13
  this.debug = options.debug ?? false;
12
14
  this.useGlobalDebug = options.debug === undefined;
15
+ // Detect standalone mode (no parent iframe)
16
+ if (typeof window !== 'undefined' && (!window.parent || window.parent === window)) {
17
+ this.mockMode = true;
18
+ if (this.isDebugEnabled()) {
19
+ console.log("[TopicSubscriber] Mock Mode enabled (standalone mode detected)");
20
+ }
21
+ }
13
22
  // Store bound handler for cleanup
14
23
  this.boundHandleMessage = this.handleMessage.bind(this);
15
24
  if (typeof window !== "undefined") {
16
25
  window.addEventListener("message", this.boundHandleMessage);
17
26
  }
18
- // Request topics list on initialization
19
- this.requestTopics();
27
+ // Initialize connection detection
28
+ if (!this.mockMode) {
29
+ this.initDetection();
30
+ this.requestTopics();
31
+ }
20
32
  if (this.isDebugEnabled()) {
21
33
  console.log("[TopicSubscriber] Initialized", {
22
34
  origin: this.origin,
23
- debug: this.isDebugEnabled()
35
+ debug: this.isDebugEnabled(),
36
+ mockMode: this.mockMode
24
37
  });
25
38
  }
26
39
  }
40
+ initDetection() {
41
+ // If we don't receive any message from parent within 2 seconds, switch to mock mode
42
+ this.detectionTimeout = setTimeout(() => {
43
+ if (this.isDebugEnabled()) {
44
+ console.warn("[TopicSubscriber] Connection timeout. Switching to Mock Mode.");
45
+ }
46
+ this.switchToMockMode();
47
+ }, 2000);
48
+ }
49
+ switchToMockMode() {
50
+ if (this.mockMode)
51
+ return;
52
+ this.mockMode = true;
53
+ // Migrate existing subscriptions to mock system
54
+ this.subscriptions.forEach((subs, topic) => {
55
+ subs.forEach(sub => {
56
+ if (this.isDebugEnabled()) {
57
+ console.log("[TopicSubscriber] Migrating subscription to mock system", { topic, id: sub.id });
58
+ }
59
+ mockSubscribeToTopic(topic, sub.callback);
60
+ });
61
+ });
62
+ }
27
63
  isDebugEnabled() {
28
64
  if (!this.useGlobalDebug) {
29
65
  return this.debug;
@@ -60,12 +96,19 @@ export class TopicSubscriber {
60
96
  callback: callback
61
97
  };
62
98
  this.subscriptions.get(topic).push(subscription);
63
- // Notify host about the subscription
64
- this.notifySubscription(topic, true);
99
+ // In mock mode, also register with the mock event system
100
+ if (this.mockMode) {
101
+ mockSubscribeToTopic(topic, callback);
102
+ }
103
+ else {
104
+ // Notify host about the subscription (only in real mode)
105
+ this.notifySubscription(topic, true);
106
+ }
65
107
  if (this.isDebugEnabled()) {
66
108
  console.log("[TopicSubscriber] Subscribed to topic", {
67
109
  topic,
68
110
  subscriptionId,
111
+ mockMode: this.mockMode,
69
112
  totalSubscriptions: this.subscriptions.get(topic).length
70
113
  });
71
114
  }
@@ -160,6 +203,11 @@ export class TopicSubscriber {
160
203
  return;
161
204
  }
162
205
  const data = event.data;
206
+ // Clear detection timeout on first valid message from parent
207
+ if (this.detectionTimeout && data && (data.type === "pubsub-event" || data.type === "pubsub-topics-list")) {
208
+ clearTimeout(this.detectionTimeout);
209
+ this.detectionTimeout = undefined;
210
+ }
163
211
  // Handle topic event
164
212
  if (data && data.type === "pubsub-event") {
165
213
  const eventMessage = data;
@@ -0,0 +1,14 @@
1
+ import { CustomersEventType, CustomersEventPayload } from "./customers/types";
2
+ import { OrdersEventType, OrdersEventPayload } from "./orders/types";
3
+ import { RefundsEventType, RefundsEventPayload } from "./refunds/types";
4
+ import { ProductsEventType, ProductsEventPayload } from "./products/types";
5
+ import { CartEventType, CartEventPayload } from "./cart/types";
6
+ import { PaymentsEventType, PaymentsEventPayload } from "./payments/types";
7
+ export interface TopicEventPayloadMap {
8
+ customers: Record<CustomersEventType, CustomersEventPayload>;
9
+ orders: Record<OrdersEventType, OrdersEventPayload>;
10
+ refunds: Record<RefundsEventType, RefundsEventPayload>;
11
+ products: Record<ProductsEventType, ProductsEventPayload>;
12
+ cart: Record<CartEventType, CartEventPayload>;
13
+ payments: Record<PaymentsEventType, PaymentsEventPayload>;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -40,3 +40,12 @@ export interface TopicSubscription {
40
40
  topic: string;
41
41
  callback: TopicSubscriptionCallback;
42
42
  }
43
+ export interface TopicRegistration {
44
+ definition: TopicDefinition;
45
+ subscribers: {
46
+ iframeWindow: Window;
47
+ topic: string;
48
+ subscribedAt: number;
49
+ subscriptionCount: number;
50
+ }[];
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,5 +0,0 @@
1
- import { command } from "../index";
2
- type MockHandler = (params?: any) => Promise<any>;
3
- export type CommandName = keyof typeof command;
4
- export declare const MOCK_REGISTRY: Record<CommandName, MockHandler>;
5
- export {};
@@ -1,94 +0,0 @@
1
- import { mockAddCartDiscount } from "../actions/add-cart-discount/mock";
2
- import { mockAddCartFee } from "../actions/add-cart-fee/mock";
3
- import { mockAddCustomSale } from "../actions/add-custom-sale/mock";
4
- import { mockAddCustomer } from "../actions/add-customer/mock";
5
- import { mockAddCustomerNote } from "../actions/add-customer-note/mock";
6
- import { mockAddOrderNote } from "../actions/add-order-note/mock";
7
- import { mockAddProductDiscount } from "../actions/add-product-discount/mock";
8
- import { mockAddProductFee } from "../actions/add-product-fee/mock";
9
- import { mockAddProductNote } from "../actions/add-product-note/mock";
10
- import { mockAddProductToCart } from "../actions/add-product-to-cart/mock";
11
- import { mockAdjustInventory } from "../actions/adjust-inventory/mock";
12
- import { mockAssignCustomer } from "../actions/assign-customer/mock";
13
- import { mockAuthenticateUser } from "../actions/authenticate-user/mock";
14
- import { mockCalculateRefundTotal } from "../actions/calculate-refund-total/mock";
15
- import { mockCashPayment } from "../actions/cash-payment/mock";
16
- import { mockClearCart } from "../actions/clear-cart/mock";
17
- import { mockDeleteParkedOrder } from "../actions/delete-parked-order/mock";
18
- import { mockExampleFunction } from "../actions/example-function/mock";
19
- import { mockGetCategories } from "../actions/get-categories/mock";
20
- import { mockGetContext } from "../actions/get-context/mock";
21
- import { mockGetCurrentCart } from "../actions/get-current-cart/mock";
22
- import { mockGetCustomers } from "../actions/get-customers/mock";
23
- import { mockGetOrders } from "../actions/get-orders/mock";
24
- import { mockGetProducts } from "../actions/get-products/mock";
25
- import { mockGetRefunds } from "../actions/get-refunds/mock";
26
- import { mockGetRemainingRefundableQuantities } from "../actions/get-remaining-refundable-quantities/mock";
27
- import { mockGoToStationHome } from "../actions/go-to-station-home/mock";
28
- import { mockInitiateRefund } from "../actions/initiate-refund/mock";
29
- import { mockOpenCashDrawer } from "../actions/open-cash-drawer/mock";
30
- import { mockParkOrder } from "../actions/park-order/mock";
31
- import { mockPartialPayment } from "../actions/partial-payment/mock";
32
- import { mockProcessPartialRefund } from "../actions/process-partial-refund/mock";
33
- import { mockRemoveCustomerFromCart } from "../actions/remove-customer-from-cart/mock";
34
- import { mockResetRefundDetails } from "../actions/reset-refund-details/mock";
35
- import { mockResumeParkedOrder } from "../actions/resume-parked-order/mock";
36
- import { mockSelectAllRefundItems } from "../actions/select-all-refund-items/mock";
37
- import { mockSetRefundStockAction } from "../actions/set-refund-stock-action/mock";
38
- import { mockShowConfirmation } from "../actions/show-confirmation/mock";
39
- import { mockShowNotification } from "../actions/show-notification/mock";
40
- import { mockSwitchUser } from "../actions/switch-user/mock";
41
- import { mockTapToPayPayment } from "../actions/tap-to-pay-payment/mock";
42
- import { mockTerminalPayment } from "../actions/terminal-payment/mock";
43
- import { mockTriggerWebhook } from "../actions/trigger-webhook/mock";
44
- import { mockTriggerZapierWebhook } from "../actions/trigger-zapier-webhook/mock";
45
- import { mockVendaraPayment } from "../actions/vendara-payment/mock";
46
- import { mockGetFinalContext } from "../actions/get-final-context/mock";
47
- export const MOCK_REGISTRY = {
48
- "addCartDiscount": mockAddCartDiscount,
49
- "addCartFee": mockAddCartFee,
50
- "addCustomSale": mockAddCustomSale,
51
- "addCustomer": mockAddCustomer,
52
- "addCustomerNote": mockAddCustomerNote,
53
- "addOrderNote": mockAddOrderNote,
54
- "addProductDiscount": mockAddProductDiscount,
55
- "addProductFee": mockAddProductFee,
56
- "addProductNote": mockAddProductNote,
57
- "addProductToCart": mockAddProductToCart,
58
- "adjustInventory": mockAdjustInventory,
59
- "assignCustomer": mockAssignCustomer,
60
- "authenticateUser": mockAuthenticateUser,
61
- "calculateRefundTotal": mockCalculateRefundTotal,
62
- "cashPayment": mockCashPayment,
63
- "clearCart": mockClearCart,
64
- "deleteParkedOrder": mockDeleteParkedOrder,
65
- "exampleFunction": mockExampleFunction,
66
- "getCategories": mockGetCategories,
67
- "getContext": mockGetContext,
68
- "getCurrentCart": mockGetCurrentCart,
69
- "getCustomers": mockGetCustomers,
70
- "getOrders": mockGetOrders,
71
- "getProducts": mockGetProducts,
72
- "getRefunds": mockGetRefunds,
73
- "getRemainingRefundableQuantities": mockGetRemainingRefundableQuantities,
74
- "goToStationHome": mockGoToStationHome,
75
- "initiateRefund": mockInitiateRefund,
76
- "openCashDrawer": mockOpenCashDrawer,
77
- "parkOrder": mockParkOrder,
78
- "partialPayment": mockPartialPayment,
79
- "processPartialRefund": mockProcessPartialRefund,
80
- "removeCustomerFromCart": mockRemoveCustomerFromCart,
81
- "resetRefundDetails": mockResetRefundDetails,
82
- "resumeParkedOrder": mockResumeParkedOrder,
83
- "selectAllRefundItems": mockSelectAllRefundItems,
84
- "setRefundStockAction": mockSetRefundStockAction,
85
- "showConfirmation": mockShowConfirmation,
86
- "showNotification": mockShowNotification,
87
- "switchUser": mockSwitchUser,
88
- "tapToPayPayment": mockTapToPayPayment,
89
- "terminalPayment": mockTerminalPayment,
90
- "triggerWebhook": mockTriggerWebhook,
91
- "triggerZapierWebhook": mockTriggerZapierWebhook,
92
- "vendaraPayment": mockVendaraPayment,
93
- "getFinalContext": mockGetFinalContext,
94
- };