@final-commerce/command-frame 0.3.0-beta.3 → 0.3.0-beta.6

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 (123) hide show
  1. package/README.md +50 -28
  2. package/dist/CommonTypes.d.ts +2 -1
  3. package/dist/actions/add-cart-discount/mock.js +6 -3
  4. package/dist/actions/add-cart-fee/mock.js +6 -6
  5. package/dist/actions/add-custom-sale/mock.js +7 -2
  6. package/dist/actions/add-order-note/mock.js +3 -1
  7. package/dist/actions/apply-transition/action.d.ts +2 -0
  8. package/dist/actions/apply-transition/action.js +4 -0
  9. package/dist/actions/apply-transition/mock.d.ts +6 -0
  10. package/dist/actions/apply-transition/mock.js +33 -0
  11. package/dist/actions/apply-transition/types.d.ts +17 -0
  12. package/dist/actions/can-transition/action.d.ts +2 -0
  13. package/dist/actions/can-transition/action.js +4 -0
  14. package/dist/actions/can-transition/mock.d.ts +6 -0
  15. package/dist/actions/can-transition/mock.js +28 -0
  16. package/dist/actions/can-transition/types.d.ts +11 -0
  17. package/dist/actions/cash-payment/mock.js +12 -18
  18. package/dist/actions/cash-payment/types.d.ts +5 -0
  19. package/dist/actions/delete-parked-order/mock.js +3 -1
  20. package/dist/actions/delete-parked-order/types.d.ts +3 -0
  21. package/dist/actions/extension-payment/types.d.ts +8 -3
  22. package/dist/actions/get-available-transitions/action.d.ts +2 -0
  23. package/dist/actions/get-available-transitions/action.js +4 -0
  24. package/dist/actions/get-available-transitions/mock.d.ts +6 -0
  25. package/dist/actions/get-available-transitions/mock.js +26 -0
  26. package/dist/actions/get-available-transitions/types.d.ts +9 -0
  27. package/dist/actions/get-context/mock.js +1 -1
  28. package/dist/actions/get-orders/mock.js +3 -3
  29. package/dist/actions/get-products/mock.js +6 -1
  30. package/dist/actions/get-products/types.d.ts +2 -0
  31. package/dist/actions/get-smart-grid-layout/mock.d.ts +1 -1
  32. package/dist/actions/get-smart-grid-layout/mock.js +3 -1
  33. package/dist/actions/get-smart-grid-layout/types.d.ts +1 -3
  34. package/dist/actions/initiate-refund/types.d.ts +3 -0
  35. package/dist/actions/integration-payment/types.d.ts +48 -8
  36. package/dist/actions/open-extension-overlay/mock.js +1 -1
  37. package/dist/actions/open-extension-overlay/types.d.ts +1 -1
  38. package/dist/actions/park-order/mock.js +4 -1
  39. package/dist/actions/park-order/types.d.ts +3 -0
  40. package/dist/actions/partial-payment/mock.js +21 -3
  41. package/dist/actions/partial-payment/types.d.ts +5 -0
  42. package/dist/actions/process-partial-refund/types.d.ts +3 -0
  43. package/dist/actions/redeem-payment/types.d.ts +9 -5
  44. package/dist/actions/remove-custom-sale/mock.d.ts +2 -0
  45. package/dist/actions/remove-custom-sale/mock.js +29 -0
  46. package/dist/actions/remove-customer-from-cart/mock.js +3 -1
  47. package/dist/actions/remove-order-note/mock.d.ts +2 -0
  48. package/dist/actions/remove-order-note/mock.js +12 -0
  49. package/dist/actions/remove-product-discount/mock.d.ts +2 -0
  50. package/dist/actions/remove-product-discount/mock.js +17 -0
  51. package/dist/actions/remove-product-fee/mock.d.ts +2 -0
  52. package/dist/actions/remove-product-fee/mock.js +17 -0
  53. package/dist/actions/remove-product-from-cart/mock.js +5 -5
  54. package/dist/actions/resolve-extension-overlay/mock.js +1 -1
  55. package/dist/actions/resume-parked-order/mock.js +4 -1
  56. package/dist/actions/resume-parked-order/types.d.ts +3 -0
  57. package/dist/actions/save-smart-grid-layout/mock.d.ts +1 -1
  58. package/dist/actions/save-smart-grid-layout/mock.js +3 -0
  59. package/dist/actions/save-smart-grid-layout/types.d.ts +1 -3
  60. package/dist/actions/tap-to-pay-payment/mock.js +5 -3
  61. package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
  62. package/dist/actions/terminal-payment/mock.js +4 -5
  63. package/dist/actions/terminal-payment/types.d.ts +5 -0
  64. package/dist/actions/update-cart-item-quantity/mock.js +9 -9
  65. package/dist/actions/vendara-payment/types.d.ts +5 -0
  66. package/dist/common-types/index.d.ts +1 -0
  67. package/dist/common-types/index.js +1 -0
  68. package/dist/common-types/order-state.d.ts +32 -0
  69. package/dist/common-types/order-state.js +6 -0
  70. package/dist/common-types/state-fragment.d.ts +77 -0
  71. package/dist/common-types/state-fragment.js +10 -0
  72. package/dist/demo/database.d.ts +12 -1
  73. package/dist/demo/database.js +173 -3
  74. package/dist/fragments/index.d.ts +1 -0
  75. package/dist/fragments/index.js +1 -0
  76. package/dist/fragments/preorder-no-deposit.d.ts +26 -0
  77. package/dist/fragments/preorder-no-deposit.js +166 -0
  78. package/dist/index.d.ts +21 -20
  79. package/dist/index.js +23 -21
  80. package/dist/interceptors/index.d.ts +21 -0
  81. package/dist/interceptors/index.js +53 -0
  82. package/dist/interceptors/types.d.ts +8 -16
  83. package/dist/projects/render/mocks.js +19 -17
  84. package/dist/projects/render/types.d.ts +7 -6
  85. package/dist/pubsub/topics/orders/index.js +10 -0
  86. package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
  87. package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
  88. package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
  89. package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
  90. package/dist/pubsub/topics/orders/types.d.ts +6 -2
  91. package/dist/pubsub/topics/orders/types.js +2 -0
  92. package/dist/pubsub/topics/split-payments/index.d.ts +12 -0
  93. package/dist/pubsub/topics/split-payments/index.js +22 -0
  94. package/dist/pubsub/topics/split-payments/split-payment-updated/types.d.ts +16 -0
  95. package/dist/pubsub/topics/split-payments/split-payment-updated/types.js +1 -0
  96. package/dist/pubsub/topics/split-payments/types.d.ts +10 -0
  97. package/dist/pubsub/topics/split-payments/types.js +5 -0
  98. package/dist/pubsub/topics/types.d.ts +2 -0
  99. package/package.json +3 -3
  100. package/dist/actions/extension-refund/constants.d.ts +0 -2
  101. package/dist/actions/extension-refund/constants.js +0 -2
  102. package/dist/actions/extension-refund/extension-refund-listener.d.ts +0 -11
  103. package/dist/actions/extension-refund/extension-refund-listener.js +0 -60
  104. package/dist/actions/extension-refund/types.d.ts +0 -32
  105. package/dist/actions/extension-refund/types.js +0 -5
  106. package/dist/actions/set-active-outlet/action.d.ts +0 -2
  107. package/dist/actions/set-active-outlet/action.js +0 -4
  108. package/dist/actions/set-active-outlet/mock.d.ts +0 -2
  109. package/dist/actions/set-active-outlet/mock.js +0 -16
  110. package/dist/actions/set-active-outlet/types.d.ts +0 -10
  111. package/dist/actions/set-active-session/action.d.ts +0 -2
  112. package/dist/actions/set-active-session/action.js +0 -4
  113. package/dist/actions/set-active-session/mock.d.ts +0 -2
  114. package/dist/actions/set-active-session/mock.js +0 -15
  115. package/dist/actions/set-active-session/types.d.ts +0 -10
  116. package/dist/actions/set-active-station/action.d.ts +0 -2
  117. package/dist/actions/set-active-station/action.js +0 -4
  118. package/dist/actions/set-active-station/mock.d.ts +0 -2
  119. package/dist/actions/set-active-station/mock.js +0 -16
  120. package/dist/actions/set-active-station/types.d.ts +0 -10
  121. /package/dist/actions/{set-active-outlet → apply-transition}/types.js +0 -0
  122. /package/dist/actions/{set-active-session → can-transition}/types.js +0 -0
  123. /package/dist/actions/{set-active-station → get-available-transitions}/types.js +0 -0
@@ -1,11 +1,9 @@
1
- import type { CFSmartGridLayout } from "../../CommonTypes";
2
- /** Fetch a builder SmartGrid layout by `gridId`. */
1
+ import { CFSmartGridLayout } from "../../CommonTypes";
3
2
  export interface GetSmartGridLayoutParams {
4
3
  gridId: string;
5
4
  }
6
5
  export interface GetSmartGridLayoutResponse {
7
6
  success: boolean;
8
- /** The stored layout, or `null` when no layout exists for the grid. */
9
7
  layout: CFSmartGridLayout | null;
10
8
  timestamp: string;
11
9
  }
@@ -1,3 +1,4 @@
1
+ import type { CFTransitionResult } from "../../common-types/order-state";
1
2
  export interface InitiateRefundParams {
2
3
  /** The ID of the order to refund. If not provided, uses the currently active order. */
3
4
  orderId?: string;
@@ -6,5 +7,7 @@ export interface InitiateRefundResponse {
6
7
  success: boolean;
7
8
  orderId: string;
8
9
  timestamp: string;
10
+ /** Present when the state machine blocked or forced the transition. */
11
+ transitionResult?: CFTransitionResult;
9
12
  }
10
13
  export type InitiateRefund = (params?: InitiateRefundParams) => Promise<InitiateRefundResponse>;
@@ -1,16 +1,56 @@
1
1
  import type { ExtensionPaymentResponse } from "../extension-payment/types";
2
- /** Integration (e.g. Stripe-style terminal) payment — always uses paymentType `"integration"` on the wire. */
2
+ /**
3
+ * Card display fields for an integration payment.
4
+ *
5
+ * Render JSON-serializes this object onto the order's `paymentMethod.emv` field;
6
+ * the transactions list, split-payment refund modal, and receipt mapper parse it
7
+ * back. Field names are translated to the platform's canonical EMV keys ("Brand",
8
+ * "Cardholder Name", "Card Number", etc.) on the host before serialization — your
9
+ * extension passes camelCase, the order's persisted shape is unchanged from the
10
+ * native card flow.
11
+ *
12
+ * All fields are optional; the more you supply, the richer the receipt and the
13
+ * transactions row will be. At minimum, supplying `brand` and `cardNumberLast4`
14
+ * produces the typical "Visa **** **** **** 4242" row.
15
+ */
16
+ export interface IntegrationEmvData {
17
+ /** Card brand, e.g. "Visa", "MasterCard". → "Brand" */
18
+ brand?: string;
19
+ /** Cardholder name as printed on the card. → "Cardholder Name" */
20
+ cardholderName?: string;
21
+ /** Issuing country (ISO 3166-1 alpha-2), e.g. "US". → "Country" */
22
+ country?: string;
23
+ /** Display expiry, e.g. "12/26". → "Expiry date" */
24
+ expiryDate?: string;
25
+ /** Issuer / bank name. → "Issuer" */
26
+ issuer?: string;
27
+ /** Last 4 digits of the card. Render masks to "**** **** **** XXXX" before display. → "Card Number" */
28
+ cardNumberLast4?: string;
29
+ }
30
+ /**
31
+ * Integration payment (e.g. Stripe-like) — always uses paymentType `"integration"` on the wire.
32
+ * The extension processes the payment with its own provider, then reports the result here so
33
+ * Render can record the transaction + order.
34
+ *
35
+ * Required fields (compile-time enforced by TS, runtime-enforced by the host handler):
36
+ * - `amount` — minor units of the captured amount
37
+ * - `emvData` — typed card display fields; the host maps + JSON-serializes to `paymentMethod.emv`
38
+ * (same persisted shape as the native card flow). Required: if the integration
39
+ * doesn't produce card data, use redeemPayment instead.
40
+ */
3
41
  export interface IntegrationPaymentParams {
4
- amount?: number;
5
- processor?: string;
42
+ amount: number;
43
+ emvData: IntegrationEmvData;
44
+ /** Human-readable label (e.g. "Visa ****4242"); shown in the transaction list. */
6
45
  label?: string;
7
- referenceId?: string;
46
+ /** Identifier of the extension making the call; audit/debug link. */
8
47
  extensionId?: string;
48
+ /** Label/name of the integration (e.g. "Stripe", "AMP"). Stored on the order's paymentMethod.processor. */
49
+ processor?: string;
50
+ referenceId?: string;
9
51
  metadata?: Record<string, unknown>;
10
- /** EMV tag string from the integration terminal; forwarded to the order PaymentMethod's `emv`. */
11
- emvData?: string;
12
- /** Processor fee for the integration payment; forwarded to the order PaymentMethod's `processorFee`. */
52
+ /** Provider fee in minor units stored on paymentMethod.processorFee. */
13
53
  processorFee?: number;
14
54
  }
15
55
  export type IntegrationPaymentResponse = ExtensionPaymentResponse;
16
- export type IntegrationPayment = (params?: IntegrationPaymentParams) => Promise<IntegrationPaymentResponse>;
56
+ export type IntegrationPayment = (params: IntegrationPaymentParams) => Promise<IntegrationPaymentResponse>;
@@ -1 +1 @@
1
- export const mockOpenExtensionOverlay = () => Promise.resolve(true);
1
+ export const mockOpenExtensionOverlay = async (_params) => true;
@@ -1,7 +1,7 @@
1
1
  import type { InterceptorPoint, InterceptorReturn } from "../../interceptors/types";
2
2
  export interface OpenExtensionOverlayParams {
3
3
  point: InterceptorPoint;
4
- payload?: unknown;
4
+ payload?: any;
5
5
  /** Owning extension id. Injected by the host for interceptor-originated calls; optional for direct calls. */
6
6
  extensionId?: string;
7
7
  }
@@ -1,4 +1,4 @@
1
- import { MOCK_PARKED_ORDERS, createOrderFromCart, MOCK_ORDERS } from "../../demo/database";
1
+ import { MOCK_PARKED_ORDERS, createOrderFromCart, MOCK_ORDERS, mockPublishEvent } from "../../demo/database";
2
2
  export const mockParkOrder = async () => {
3
3
  console.log("[Mock] parkOrder called");
4
4
  // Create a temporary order to capture cart state
@@ -13,6 +13,9 @@ export const mockParkOrder = async () => {
13
13
  MOCK_ORDERS.splice(foundIndex, 1);
14
14
  }
15
15
  MOCK_PARKED_ORDERS.push(tempOrder);
16
+ // Refresh order lists so the new parked order appears (createOrderFromCart
17
+ // already published the cart-created event that clears the cart).
18
+ mockPublishEvent("orders", "order-parked", { order: tempOrder });
16
19
  return {
17
20
  success: true,
18
21
  order: tempOrder,
@@ -1,7 +1,10 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface ParkOrderResponse {
3
4
  success: boolean;
4
5
  order: CFOrder;
5
6
  timestamp: string;
7
+ /** Present when the state machine blocked or forced the transition. */
8
+ transitionResult?: CFTransitionResult;
6
9
  }
7
10
  export type ParkOrder = () => Promise<ParkOrderResponse>;
@@ -1,17 +1,35 @@
1
- import { MOCK_ORDERS } from "../../demo/database";
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
2
  export const mockPartialPayment = async (params) => {
3
3
  console.log("[Mock] partialPayment called", params);
4
4
  const openUI = params?.openUI ?? true;
5
5
  if (openUI) {
6
- // Simulate UI opening
6
+ // Split-payment UI is host-owned; nothing to simulate here.
7
7
  window.alert("Demo: Split Payment UI would open here.");
8
+ return {
9
+ success: true,
10
+ amount: params?.amount,
11
+ isPercent: params?.isPercent || false,
12
+ openUI,
13
+ order: null,
14
+ timestamp: new Date().toISOString()
15
+ };
8
16
  }
17
+ // Queue a partial amount as the next tender. The remaining balance is left
18
+ // untouched until the payment is actually taken (see applyMockPayment).
19
+ const remaining = MOCK_CART.remainingBalance ?? MOCK_CART.total;
20
+ const raw = params?.amount ?? 0;
21
+ // Mirror render: fixed amount is raw dollars (render does toMinorUnits), so
22
+ // convert to minor units here; percent is a percentage of the remaining total.
23
+ const minorFactor = 10 ** (MOCK_CART.minorUnits ?? 2);
24
+ const charge = params?.isPercent ? Math.round((remaining * raw) / 100) : Math.round(raw * minorFactor);
25
+ MOCK_CART.amountToBeCharged = Math.min(Math.max(0, charge), remaining);
26
+ mockPublishEvent("cart", "partial-payment-set", { amountToBeCharged: MOCK_CART.amountToBeCharged });
9
27
  return {
10
28
  success: true,
11
29
  amount: params?.amount,
12
30
  isPercent: params?.isPercent || false,
13
31
  openUI,
14
- order: openUI ? null : MOCK_ORDERS[0],
32
+ order: null,
15
33
  timestamp: new Date().toISOString()
16
34
  };
17
35
  };
@@ -1,4 +1,5 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface PartialPaymentParams {
3
4
  /** The payment amount (required if openUI is false). */
4
5
  amount?: number;
@@ -6,6 +7,8 @@ export interface PartialPaymentParams {
6
7
  isPercent?: boolean;
7
8
  /** If true, opens the split payment UI. */
8
9
  openUI?: boolean;
10
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
11
+ checkoutFulfillmentTarget?: string;
9
12
  }
10
13
  export interface PartialPaymentResponse {
11
14
  success: boolean;
@@ -14,5 +17,7 @@ export interface PartialPaymentResponse {
14
17
  openUI: boolean;
15
18
  order: CFOrder | null;
16
19
  timestamp: string;
20
+ /** Present when the state machine blocked or forced the transition. */
21
+ transitionResult?: CFTransitionResult;
17
22
  }
18
23
  export type PartialPayment = (params?: PartialPaymentParams) => Promise<PartialPaymentResponse>;
@@ -1,3 +1,4 @@
1
+ import type { CFTransitionResult } from "../../common-types/order-state";
1
2
  export interface ProcessPartialRefundParams {
2
3
  /** Optional refund reason. */
3
4
  reason?: string;
@@ -15,5 +16,7 @@ export interface ProcessPartialRefundResponse {
15
16
  success: boolean;
16
17
  refundId: string;
17
18
  timestamp: string;
19
+ /** Present when the state machine blocked or forced the transition. */
20
+ transitionResult?: CFTransitionResult;
18
21
  }
19
22
  export type ProcessPartialRefund = (params?: ProcessPartialRefundParams) => Promise<ProcessPartialRefundResponse>;
@@ -1,12 +1,16 @@
1
1
  import type { ExtensionPaymentResponse } from "../extension-payment/types";
2
- /** Redeem (e.g. gift card) — always uses paymentType `"redeem"` on the wire. */
2
+ /**
3
+ * Redeem (e.g. gift card) — always uses paymentType `"redeem"` on the wire.
4
+ * `amount` is required so the persisted payment never silently falls back to the cart
5
+ * total. The host handler also re-validates this at runtime to catch raw-postMessage callers.
6
+ */
3
7
  export interface RedeemPaymentParams {
4
- amount?: number;
5
- processor?: string;
8
+ amount: number;
6
9
  label?: string;
7
- referenceId?: string;
8
10
  extensionId?: string;
11
+ processor?: string;
12
+ referenceId?: string;
9
13
  metadata?: Record<string, unknown>;
10
14
  }
11
15
  export type RedeemPaymentResponse = ExtensionPaymentResponse;
12
- export type RedeemPayment = (params?: RedeemPaymentParams) => Promise<RedeemPaymentResponse>;
16
+ export type RedeemPayment = (params: RedeemPaymentParams) => Promise<RedeemPaymentResponse>;
@@ -0,0 +1,2 @@
1
+ import { RemoveCustomSale } from "./types";
2
+ export declare const mockRemoveCustomSale: RemoveCustomSale;
@@ -0,0 +1,29 @@
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
+ export const mockRemoveCustomSale = (params) => {
3
+ console.log("[Mock] removeCustomSale called", params);
4
+ if (!params?.id) {
5
+ throw new Error("id is required");
6
+ }
7
+ const { id } = params;
8
+ const sales = MOCK_CART.customSales ?? [];
9
+ const index = sales.findIndex(s => s.id === id);
10
+ if (index === -1) {
11
+ throw new Error(`Custom sale with id ${id} not found`);
12
+ }
13
+ const sale = sales[index];
14
+ // Remove from cart
15
+ sales.splice(index, 1);
16
+ // Recalculate totals
17
+ const lineTotal = sale.price * sale.quantity;
18
+ MOCK_CART.subtotal -= lineTotal;
19
+ MOCK_CART.total -= lineTotal;
20
+ MOCK_CART.amountToBeCharged = MOCK_CART.total;
21
+ MOCK_CART.remainingBalance = MOCK_CART.total;
22
+ // Publish custom-sale-removed event so cart subscribers refresh
23
+ mockPublishEvent("cart", "custom-sale-removed", { customSale: sale, id });
24
+ return Promise.resolve({
25
+ success: true,
26
+ id,
27
+ timestamp: new Date().toISOString()
28
+ });
29
+ };
@@ -1,8 +1,10 @@
1
- import { MOCK_CART } from "../../demo/database";
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
2
  export const mockRemoveCustomerFromCart = async () => {
3
3
  console.log("[Mock] removeCustomerFromCart called");
4
4
  // Actually remove the customer from the mock cart
5
5
  MOCK_CART.customer = null;
6
+ // Publish customer-removed event so cart subscribers refresh
7
+ mockPublishEvent('cart', 'customer-removed', {});
6
8
  return {
7
9
  success: true,
8
10
  timestamp: new Date().toISOString()
@@ -0,0 +1,2 @@
1
+ import { RemoveOrderNote } from "./types";
2
+ export declare const mockRemoveOrderNote: RemoveOrderNote;
@@ -0,0 +1,12 @@
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
+ export const mockRemoveOrderNote = () => {
3
+ console.log("[Mock] removeOrderNote called");
4
+ // Clear the active cart's note.
5
+ MOCK_CART.orderNotes = undefined;
6
+ // Publish order-note-removed event so cart subscribers refresh.
7
+ mockPublishEvent("cart", "order-note-removed", {});
8
+ return Promise.resolve({
9
+ success: true,
10
+ timestamp: new Date().toISOString()
11
+ });
12
+ };
@@ -0,0 +1,2 @@
1
+ import { RemoveProductDiscount } from "./types";
2
+ export declare const mockRemoveProductDiscount: RemoveProductDiscount;
@@ -0,0 +1,17 @@
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
+ export const mockRemoveProductDiscount = (params) => {
3
+ console.log("[Mock] removeProductDiscount called", params);
4
+ const item = params?.internalId
5
+ ? MOCK_CART.products.find(p => p.internalId === params.internalId)
6
+ : MOCK_CART.products[MOCK_CART.products.length - 1];
7
+ if (item) {
8
+ item.discounts = [];
9
+ // Publish so cart subscribers refresh.
10
+ mockPublishEvent("cart", "product-discount-removed", { internalId: params?.internalId });
11
+ }
12
+ return Promise.resolve({
13
+ success: true,
14
+ internalId: params?.internalId,
15
+ timestamp: new Date().toISOString()
16
+ });
17
+ };
@@ -0,0 +1,2 @@
1
+ import { RemoveProductFee } from "./types";
2
+ export declare const mockRemoveProductFee: RemoveProductFee;
@@ -0,0 +1,17 @@
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
+ export const mockRemoveProductFee = (params) => {
3
+ console.log("[Mock] removeProductFee called", params);
4
+ const item = params?.internalId
5
+ ? MOCK_CART.products.find(p => p.internalId === params.internalId)
6
+ : MOCK_CART.products[MOCK_CART.products.length - 1];
7
+ if (item) {
8
+ item.fees = [];
9
+ // Publish so cart subscribers refresh.
10
+ mockPublishEvent("cart", "product-fee-removed", { internalId: params?.internalId });
11
+ }
12
+ return Promise.resolve({
13
+ success: true,
14
+ internalId: params?.internalId,
15
+ timestamp: new Date().toISOString()
16
+ });
17
+ };
@@ -1,8 +1,8 @@
1
1
  import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
- export const mockRemoveProductFromCart = async (params) => {
2
+ export const mockRemoveProductFromCart = (params) => {
3
3
  console.log("[Mock] removeProductFromCart called", params);
4
4
  if (!params?.internalId) {
5
- throw new Error('internalId is required');
5
+ throw new Error("internalId is required");
6
6
  }
7
7
  const { internalId } = params;
8
8
  // Find the product in the cart
@@ -20,13 +20,13 @@ export const mockRemoveProductFromCart = async (params) => {
20
20
  MOCK_CART.amountToBeCharged = MOCK_CART.total;
21
21
  MOCK_CART.remainingBalance = MOCK_CART.total;
22
22
  // Publish product-deleted event
23
- mockPublishEvent('cart', 'product-deleted', {
23
+ mockPublishEvent("cart", "product-deleted", {
24
24
  product: product,
25
25
  internalId: internalId
26
26
  });
27
- return {
27
+ return Promise.resolve({
28
28
  success: true,
29
29
  internalId: internalId,
30
30
  timestamp: new Date().toISOString()
31
- };
31
+ });
32
32
  };
@@ -1 +1 @@
1
- export const mockResolveExtensionOverlay = () => Promise.resolve({ success: true });
1
+ export const mockResolveExtensionOverlay = async (_params) => ({ success: true });
@@ -1,4 +1,4 @@
1
- import { MOCK_PARKED_ORDERS, MOCK_CART, resetMockCart } from "../../demo/database";
1
+ import { MOCK_PARKED_ORDERS, MOCK_CART, resetMockCart, mockPublishEvent } from "../../demo/database";
2
2
  export const mockResumeParkedOrder = async (params) => {
3
3
  console.log("[Mock] resumeParkedOrder called", params);
4
4
  const orderId = params?.orderId;
@@ -45,6 +45,9 @@ export const mockResumeParkedOrder = async (params) => {
45
45
  MOCK_CART.remainingBalance = MOCK_CART.total;
46
46
  // Remove from parked
47
47
  MOCK_PARKED_ORDERS.splice(index, 1);
48
+ // Refresh the restored cart and the order lists.
49
+ mockPublishEvent("cart", "parked-order-resumed", { orderId });
50
+ mockPublishEvent("orders", "parked-order-resumed", { orderId });
48
51
  return {
49
52
  success: true,
50
53
  order: orderToResume,
@@ -1,4 +1,5 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface ResumeParkedOrderParams {
3
4
  orderId: string;
4
5
  }
@@ -6,5 +7,7 @@ export interface ResumeParkedOrderResponse {
6
7
  success: boolean;
7
8
  order: CFOrder;
8
9
  timestamp: string;
10
+ /** Present when the state machine blocked or forced the transition. */
11
+ transitionResult?: CFTransitionResult;
9
12
  }
10
13
  export type ResumeParkedOrder = (params?: ResumeParkedOrderParams) => Promise<ResumeParkedOrderResponse>;
@@ -1,2 +1,2 @@
1
- import type { SaveSmartGridLayout } from "./types";
1
+ import { SaveSmartGridLayout } from "./types";
2
2
  export declare const mockSaveSmartGridLayout: SaveSmartGridLayout;
@@ -1,4 +1,7 @@
1
+ import { MOCK_SMART_GRID_LAYOUTS } from "../../demo/database";
1
2
  export const mockSaveSmartGridLayout = (params) => {
3
+ console.log("[Mock] saveSmartGridLayout called", params);
4
+ MOCK_SMART_GRID_LAYOUTS[params.layout.gridId] = params.layout;
2
5
  return Promise.resolve({
3
6
  success: true,
4
7
  gridId: params.layout.gridId,
@@ -1,11 +1,9 @@
1
- import type { CFSmartGridLayout } from "../../CommonTypes";
2
- /** Persist a builder SmartGrid layout. The `layout.gridId` identifies the grid. */
1
+ import { CFSmartGridLayout } from "../../CommonTypes";
3
2
  export interface SaveSmartGridLayoutParams {
4
3
  layout: CFSmartGridLayout;
5
4
  }
6
5
  export interface SaveSmartGridLayoutResponse {
7
6
  success: boolean;
8
- /** The gridId that was saved. */
9
7
  gridId: string;
10
8
  timestamp: string;
11
9
  }
@@ -1,13 +1,15 @@
1
- import { MOCK_ORDERS } from "../../demo/database";
1
+ import { applyMockPayment, MOCK_CART } from "../../demo/database";
2
2
  export const mockTapToPayPayment = async (params) => {
3
3
  console.log("[Mock] tapToPayPayment called", params);
4
4
  // Simulate Tap to Pay interaction
5
5
  window.alert("Demo: Processing Tap to Pay...\n(Please tap card or device on screen)");
6
+ const due = params?.amount ?? MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
7
+ const order = applyMockPayment(due, "card", "tapToPay");
6
8
  return {
7
9
  success: true,
8
- amount: params?.amount || null,
10
+ amount: due,
9
11
  paymentType: "tapToPay",
10
- order: MOCK_ORDERS[0],
12
+ order,
11
13
  timestamp: new Date().toISOString()
12
14
  };
13
15
  };
@@ -1,7 +1,10 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface TapToPayPaymentParams {
3
4
  /** If not provided, uses the cart total. */
4
5
  amount?: number;
6
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
7
+ checkoutFulfillmentTarget?: string;
5
8
  }
6
9
  export interface TapToPayPaymentResponse {
7
10
  success: boolean;
@@ -9,5 +12,7 @@ export interface TapToPayPaymentResponse {
9
12
  paymentType: string;
10
13
  order: CFOrder | null;
11
14
  timestamp: string;
15
+ /** Present when the state machine blocked or forced the transition. */
16
+ transitionResult?: CFTransitionResult;
12
17
  }
13
18
  export type TapToPayPayment = (params?: TapToPayPaymentParams) => Promise<TapToPayPaymentResponse>;
@@ -1,15 +1,14 @@
1
- import { createOrderFromCart, MOCK_CART } from "../../demo/database";
1
+ import { applyMockPayment, MOCK_CART } from "../../demo/database";
2
2
  export const mockTerminalPayment = async (params) => {
3
3
  console.log("[Mock] terminalPayment called", params);
4
4
  const connectionType = params?.paymentType || "Cloud";
5
5
  // Simulate terminal interaction
6
6
  window.alert(`Demo: Processing ${connectionType} Terminal Payment...\n(Please tap, insert, or swipe card on terminal)`);
7
- const amount = params?.amount || MOCK_CART.total;
8
- // Mocking terminal payment success immediately
9
- const order = createOrderFromCart("card", amount, "stripe_terminal");
7
+ const due = params?.amount ?? MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
8
+ const order = applyMockPayment(due, "card", "stripe_terminal");
10
9
  return {
11
10
  success: true,
12
- amount: amount,
11
+ amount: due,
13
12
  paymentType: "terminal",
14
13
  order,
15
14
  timestamp: new Date().toISOString()
@@ -1,9 +1,12 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface TerminalPaymentParams {
3
4
  /** If not provided, uses the cart total. */
4
5
  amount?: number;
5
6
  /** "Bluetooth" or "Cloud". Defaults to "Cloud". */
6
7
  paymentType?: 'Bluetooth' | 'Cloud';
8
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
9
+ checkoutFulfillmentTarget?: string;
7
10
  }
8
11
  export interface TerminalPaymentResponse {
9
12
  success: boolean;
@@ -11,5 +14,7 @@ export interface TerminalPaymentResponse {
11
14
  paymentType: string;
12
15
  order: CFOrder | null;
13
16
  timestamp: string;
17
+ /** Present when the state machine blocked or forced the transition. */
18
+ transitionResult?: CFTransitionResult;
14
19
  }
15
20
  export type TerminalPayment = (params?: TerminalPaymentParams) => Promise<TerminalPaymentResponse>;
@@ -1,11 +1,11 @@
1
1
  import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
- export const mockUpdateCartItemQuantity = async (params) => {
2
+ export const mockUpdateCartItemQuantity = (params) => {
3
3
  console.log("[Mock] updateCartItemQuantity called", params);
4
4
  if (!params?.internalId) {
5
- throw new Error('internalId is required');
5
+ throw new Error("internalId is required");
6
6
  }
7
7
  if (params.quantity === undefined || params.quantity === null) {
8
- throw new Error('quantity is required');
8
+ throw new Error("quantity is required");
9
9
  }
10
10
  const { internalId, quantity } = params;
11
11
  // Find the product in the cart
@@ -25,16 +25,16 @@ export const mockUpdateCartItemQuantity = async (params) => {
25
25
  MOCK_CART.amountToBeCharged = MOCK_CART.total;
26
26
  MOCK_CART.remainingBalance = MOCK_CART.total;
27
27
  // Publish product-deleted event
28
- mockPublishEvent('cart', 'product-deleted', {
28
+ mockPublishEvent("cart", "product-deleted", {
29
29
  product: product,
30
30
  internalId: internalId
31
31
  });
32
- return {
32
+ return Promise.resolve({
33
33
  success: true,
34
34
  internalId: internalId,
35
35
  quantity: 0,
36
36
  timestamp: new Date().toISOString()
37
- };
37
+ });
38
38
  }
39
39
  // Update quantity
40
40
  const quantityDelta = quantity - previousQuantity;
@@ -46,15 +46,15 @@ export const mockUpdateCartItemQuantity = async (params) => {
46
46
  MOCK_CART.amountToBeCharged = MOCK_CART.total;
47
47
  MOCK_CART.remainingBalance = MOCK_CART.total;
48
48
  // Publish product-updated event
49
- mockPublishEvent('cart', 'product-updated', {
49
+ mockPublishEvent("cart", "product-updated", {
50
50
  product: product,
51
51
  previousQuantity: previousQuantity,
52
52
  newQuantity: quantity
53
53
  });
54
- return {
54
+ return Promise.resolve({
55
55
  success: true,
56
56
  internalId: internalId,
57
57
  quantity: quantity,
58
58
  timestamp: new Date().toISOString()
59
- };
59
+ });
60
60
  };
@@ -1,7 +1,10 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface VendaraPaymentParams {
3
4
  /** If not provided, uses the cart total. */
4
5
  amount?: number;
6
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
7
+ checkoutFulfillmentTarget?: string;
5
8
  }
6
9
  export interface VendaraPaymentResponse {
7
10
  success: boolean;
@@ -9,5 +12,7 @@ export interface VendaraPaymentResponse {
9
12
  paymentType: string;
10
13
  order: CFOrder | null;
11
14
  timestamp: string;
15
+ /** Present when the state machine blocked or forced the transition. */
16
+ transitionResult?: CFTransitionResult;
12
17
  }
13
18
  export type VendaraPayment = (params?: VendaraPaymentParams) => Promise<VendaraPaymentResponse>;
@@ -1,2 +1,3 @@
1
1
  export * from "./custom-tables";
2
2
  export * from "./custom-extensions";
3
+ export * from "./order-state";
@@ -1,2 +1,3 @@
1
1
  export * from "./custom-tables";
2
2
  export * from "./custom-extensions";
3
+ export * from "./order-state";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * State machine protocol types for the command-frame boundary.
3
+ * These mirror @final-commerce/common shapes but carry no logic.
4
+ * command-frame must NOT depend on common.
5
+ */
6
+ export interface CFStatePair {
7
+ payment: string;
8
+ fulfillment: string;
9
+ }
10
+ export type CFBlockedBy = "financial_invariant" | "cross_axis_rule" | "path" | "condition";
11
+ export interface CFTransitionResult {
12
+ allowed: boolean;
13
+ blockedBy?: CFBlockedBy;
14
+ guard?: string;
15
+ reason?: string;
16
+ failedConditions?: CFFailedCondition[];
17
+ }
18
+ export interface CFFailedCondition {
19
+ field: string;
20
+ operator: string;
21
+ value: unknown;
22
+ reason?: string;
23
+ }
24
+ export interface CFConditionStatus {
25
+ met: boolean;
26
+ description: string;
27
+ }
28
+ export interface CFAvailableTransition {
29
+ to: CFStatePair;
30
+ displayLabel: string;
31
+ conditions: CFConditionStatus[];
32
+ }