@final-commerce/command-frame 0.1.63 → 0.2.0-beta.1

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 (50) hide show
  1. package/README.md +26 -28
  2. package/dist/CommonTypes.d.ts +21 -17
  3. package/dist/actions/add-customer-note/mock.js +3 -3
  4. package/dist/actions/can-transition/action.d.ts +2 -0
  5. package/dist/actions/can-transition/action.js +4 -0
  6. package/dist/actions/can-transition/mock.d.ts +6 -0
  7. package/dist/actions/can-transition/mock.js +28 -0
  8. package/dist/actions/can-transition/types.d.ts +11 -0
  9. package/dist/actions/can-transition/types.js +1 -0
  10. package/dist/actions/cash-payment/types.d.ts +5 -0
  11. package/dist/actions/delete-parked-order/types.d.ts +3 -0
  12. package/dist/actions/extension-payment/types.d.ts +5 -0
  13. package/dist/actions/get-available-transitions/action.d.ts +2 -0
  14. package/dist/actions/get-available-transitions/action.js +4 -0
  15. package/dist/actions/get-available-transitions/mock.d.ts +6 -0
  16. package/dist/actions/get-available-transitions/mock.js +26 -0
  17. package/dist/actions/get-available-transitions/types.d.ts +9 -0
  18. package/dist/actions/get-available-transitions/types.js +1 -0
  19. package/dist/actions/initiate-refund/types.d.ts +3 -0
  20. package/dist/actions/park-order/types.d.ts +3 -0
  21. package/dist/actions/partial-payment/types.d.ts +5 -0
  22. package/dist/actions/process-partial-refund/types.d.ts +3 -0
  23. package/dist/actions/remove-customer-note/mock.js +4 -4
  24. package/dist/actions/resume-parked-order/types.d.ts +3 -0
  25. package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
  26. package/dist/actions/terminal-payment/types.d.ts +5 -0
  27. package/dist/actions/vendara-payment/types.d.ts +5 -0
  28. package/dist/common-types/index.d.ts +1 -0
  29. package/dist/common-types/index.js +1 -0
  30. package/dist/common-types/order-state.d.ts +32 -0
  31. package/dist/common-types/order-state.js +6 -0
  32. package/dist/common-types/state-fragment.d.ts +77 -0
  33. package/dist/common-types/state-fragment.js +10 -0
  34. package/dist/demo/database.js +9 -0
  35. package/dist/fragments/index.d.ts +1 -0
  36. package/dist/fragments/index.js +1 -0
  37. package/dist/fragments/preorder-no-deposit.d.ts +26 -0
  38. package/dist/fragments/preorder-no-deposit.js +166 -0
  39. package/dist/index.d.ts +8 -1
  40. package/dist/index.js +8 -0
  41. package/dist/projects/render/mocks.js +5 -1
  42. package/dist/projects/render/types.d.ts +3 -1
  43. package/dist/pubsub/topics/orders/index.js +10 -0
  44. package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
  45. package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
  46. package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
  47. package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
  48. package/dist/pubsub/topics/orders/types.d.ts +6 -2
  49. package/dist/pubsub/topics/orders/types.js +2 -0
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -10,11 +10,11 @@ Command Frame provides a structured way to build integrations that run inside Fi
10
10
 
11
11
  The library provides three main capabilities:
12
12
 
13
- | Capability | Purpose | Scope |
14
- |-----------|---------|-------|
15
- | **Commands** | Call host functions from the iframe (e.g. get products, open cash drawer) | Request/response per call |
16
- | **Pub/Sub** | Subscribe to real-time events from the host (e.g. cart changes, payments) | Page-scoped (while iframe is mounted) |
17
- | **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
13
+ | Capability | Purpose | Scope |
14
+ | ------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------- |
15
+ | **Commands** | Call host functions from the iframe (e.g. get products, open cash drawer) | Request/response per call |
16
+ | **Pub/Sub** | Subscribe to real-time events from the host (e.g. cart changes, payments) | Page-scoped (while iframe is mounted) |
17
+ | **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
18
18
  | **Host → iframe refunds** | Render asks the extension to reverse redeem / gift-card payments before completing a POS refund | Parent `postMessage` + `requestId` (see below) |
19
19
 
20
20
  Domain models (orders, cart, customers, products, and related types) are documented in **[Types reference](./src/types/README.md)**.
@@ -54,7 +54,7 @@ For building applications that run inside the Render Point of Sale interface.
54
54
  - **Features:** Order management, Product catalog, Customer management, Payments, Hardware integration (Cash drawer, Printer), Custom tables, Secrets storage.
55
55
 
56
56
  ```typescript
57
- import { RenderClient } from '@final-commerce/command-frame';
57
+ import { RenderClient } from "@final-commerce/command-frame";
58
58
 
59
59
  const client = new RenderClient();
60
60
  const products = await client.getProducts();
@@ -68,7 +68,7 @@ For building applications that run inside the Final Commerce Management Dashboar
68
68
  - **Features:** Context, catalog, entities, custom tables, secrets, and optional host-specific commands (navigation, media, tax, branding, notifications) when the dashboard implements them.
69
69
 
70
70
  ```typescript
71
- import { ManageClient } from '@final-commerce/command-frame';
71
+ import { ManageClient } from "@final-commerce/command-frame";
72
72
 
73
73
  const client = new ManageClient();
74
74
  const context = await client.getContext();
@@ -82,14 +82,14 @@ The pub/sub system allows iframe extensions to subscribe to topics and receive r
82
82
  - **Topics:** Cart (16), Customers (8), Orders (4), Payments (2), Products (4), Refunds (4), Print (3), Custom Tables (3), Outlet (2), Station (2), Session (2), Users (2).
83
83
 
84
84
  ```typescript
85
- import { topics } from '@final-commerce/command-frame';
85
+ import { topics } from "@final-commerce/command-frame";
86
86
 
87
- const subscriptionId = topics.subscribe('cart', (event) => {
88
- console.log('Cart event:', event.type, event.data);
87
+ const subscriptionId = topics.subscribe("cart", event => {
88
+ console.log("Cart event:", event.type, event.data);
89
89
  });
90
90
 
91
91
  // Unsubscribe when done
92
- topics.unsubscribe('cart', subscriptionId);
92
+ topics.unsubscribe("cart", subscriptionId);
93
93
  ```
94
94
 
95
95
  ## Hooks
@@ -101,17 +101,21 @@ Hooks are **session-scoped** event callbacks that run in the host (Render) conte
101
101
  - A stable `hookId` is required for deduplication (safe on iframe reload).
102
102
 
103
103
  ```typescript
104
- import { hooks } from '@final-commerce/command-frame';
105
-
106
- hooks.register('cart', async (event, hostCommands) => {
107
- await hostCommands.upsertCustomTableData({
108
- tableName: 'cart-events-log',
109
- data: { eventType: event.type, payload: event.data, timestamp: event.timestamp },
110
- });
111
- }, { hookId: 'my-extension:cart-log' });
104
+ import { hooks } from "@final-commerce/command-frame";
105
+
106
+ hooks.register(
107
+ "cart",
108
+ async (event, hostCommands) => {
109
+ await hostCommands.upsertCustomTableData({
110
+ tableName: "cart-events-log",
111
+ data: { eventType: event.type, payload: event.data, timestamp: event.timestamp }
112
+ });
113
+ },
114
+ { hookId: "my-extension:cart-log" }
115
+ );
112
116
 
113
117
  // Unregister when no longer needed
114
- hooks.unregister('my-extension:cart-log');
118
+ hooks.unregister("my-extension:cart-log");
115
119
  ```
116
120
 
117
121
  ## Host-initiated extension refunds (redeem / gift card)
@@ -127,18 +131,12 @@ hooks.unregister('my-extension:cart-log');
127
131
  Exported APIs: `installExtensionRefundListener`, `EXTENSION_REFUND_REQUEST_ACTION`, types **`ExtensionRefundParams`** / **`ExtensionRefundResponse`**.
128
132
 
129
133
  ```typescript
130
- import {
131
- installExtensionRefundListener,
132
- type ExtensionRefundParams,
133
- type ExtensionRefundResponse
134
- } from '@final-commerce/command-frame';
134
+ import { installExtensionRefundListener, type ExtensionRefundParams, type ExtensionRefundResponse } from "@final-commerce/command-frame";
135
135
 
136
136
  const unsubscribe = installExtensionRefundListener(async (params: ExtensionRefundParams): Promise<ExtensionRefundResponse> => {
137
137
  // params.paymentType === "redeem", params.amount in major currency units, params.saleId, params.processor, etc.
138
138
  const ok = await myGiftCardProvider.refund(params);
139
- return ok
140
- ? { success: true, extensionTransactionId: ok.providerRefundId }
141
- : { success: false, error: 'Refund declined' };
139
+ return ok ? { success: true, extensionTransactionId: ok.providerRefundId } : { success: false, error: "Refund declined" };
142
140
  });
143
141
 
144
142
  // on teardown (optional)
@@ -60,8 +60,7 @@ export declare enum CFUserTypes {
60
60
  CASHIER = "cashier",
61
61
  RESELLER = "reseller"
62
62
  }
63
- export interface CFActiveEntity {
64
- }
63
+ export type CFActiveEntity = object;
65
64
  export interface CFDiscount {
66
65
  value: number;
67
66
  label?: string;
@@ -199,7 +198,7 @@ export interface CFActiveProduct extends CFActiveEntity {
199
198
  }
200
199
  export interface CFCustomer {
201
200
  _id: string;
202
- companyId: any;
201
+ companyId: string;
203
202
  externalId?: string;
204
203
  email: string;
205
204
  firstName: string;
@@ -358,8 +357,7 @@ export interface CFRefundedLineItem {
358
357
  images?: string[];
359
358
  fee: CFFeeLineItem;
360
359
  }
361
- export interface CFRefundedCustomSale extends CFCustomSale {
362
- }
360
+ export type CFRefundedCustomSale = CFCustomSale;
363
361
  export interface CFRefundItem {
364
362
  lineItems: CFRefundedLineItem[];
365
363
  customSales: CFRefundedCustomSale[];
@@ -382,6 +380,12 @@ export interface CFOrder {
382
380
  companyId: string;
383
381
  externalId: string | null;
384
382
  status: string;
383
+ /** Financial state (state machine). Undefined on legacy orders pre-backfill. */
384
+ paymentState?: string;
385
+ /** Operational state (state machine). Undefined on legacy orders pre-backfill. */
386
+ fulfillmentState?: string;
387
+ /** Computed display label from paymentState + fulfillmentState matrix. */
388
+ displayState?: string;
385
389
  customer: Partial<CFActiveCustomer | null>;
386
390
  customerNote?: string;
387
391
  summary: CFSummary;
@@ -435,7 +439,7 @@ export interface CFActiveUser extends CFActiveEntity {
435
439
  _id: string;
436
440
  }[];
437
441
  type?: CFUserTypes;
438
- companies?: any;
442
+ companies?: unknown;
439
443
  }
440
444
  export interface CFActiveOutlet extends CFActiveEntity {
441
445
  address: string;
@@ -515,8 +519,8 @@ export interface CFActiveCustomSales {
515
519
  taxTableId?: string;
516
520
  quantity: number;
517
521
  price: number;
518
- discount?: any;
519
- fee?: any;
522
+ discount?: Record<string, unknown>;
523
+ fee?: Record<string, unknown>;
520
524
  }
521
525
  /** Non-revenue cart line (e.g. gift card load) — aligned with Render `NonRevenueItem.externalId` (order line id). */
522
526
  export interface CFNonRevenueItem {
@@ -550,7 +554,7 @@ export interface CFActiveCompany extends CFActiveEntity {
550
554
  id?: string;
551
555
  name?: string;
552
556
  logo?: string;
553
- settings?: any;
557
+ settings?: Record<string, unknown>;
554
558
  }
555
559
  export type CFProjectName = "Render" | "Manage";
556
560
  export interface CFContextRender {
@@ -568,10 +572,10 @@ export interface CFContextRender {
568
572
  buildSourceId: string | null;
569
573
  buildIsPremium: boolean;
570
574
  isOffline: boolean;
571
- user: Record<string, any> | null;
572
- company: Omit<Record<string, any>, 'settings'> | null;
573
- station: Record<string, any> | null;
574
- outlet: Record<string, any> | null;
575
+ user: Record<string, unknown> | null;
576
+ company: Omit<Record<string, unknown>, "settings"> | null;
577
+ station: Record<string, unknown> | null;
578
+ outlet: Record<string, unknown> | null;
575
579
  timestamp: string;
576
580
  }
577
581
  export interface CFOutletInfo {
@@ -591,11 +595,11 @@ export interface CFOutletInfo {
591
595
  country?: string;
592
596
  }
593
597
  export interface CFContextManage {
594
- user: any;
595
- company: any;
596
- menuItem?: any;
598
+ user: unknown;
599
+ company: unknown;
600
+ menuItem?: unknown;
597
601
  extensionId: string;
598
- outlets?: any[];
602
+ outlets?: unknown[];
599
603
  timestamp: string;
600
604
  }
601
605
  export type CFContext = CFContextRender;
@@ -1,5 +1,5 @@
1
1
  import { MOCK_CUSTOMERS } from "../../demo/database";
2
- export const mockAddCustomerNote = async (params) => {
2
+ export const mockAddCustomerNote = (params) => {
3
3
  console.log("[Mock] addCustomerNote called", params);
4
4
  if (params?.customerId && params.note) {
5
5
  const customer = MOCK_CUSTOMERS.find(c => c._id === params.customerId);
@@ -14,10 +14,10 @@ export const mockAddCustomerNote = async (params) => {
14
14
  });
15
15
  }
16
16
  }
17
- return {
17
+ return Promise.resolve({
18
18
  success: true,
19
19
  customerId: params?.customerId || "",
20
20
  note: params?.note || "",
21
21
  timestamp: new Date().toISOString()
22
- };
22
+ });
23
23
  };
@@ -0,0 +1,2 @@
1
+ import type { CanTransition } from "./types";
2
+ export declare const canTransition: CanTransition;
@@ -0,0 +1,4 @@
1
+ import { commandFrameClient } from "../../client";
2
+ export const canTransition = async (params) => {
3
+ return await commandFrameClient.call("canTransition", params);
4
+ };
@@ -0,0 +1,6 @@
1
+ import type { CanTransitionParams, CanTransitionResponse } from "./types";
2
+ /**
3
+ * Mock implementation: allows all transitions except a few known-invalid ones
4
+ * so the demo app can show both allowed and blocked responses.
5
+ */
6
+ export declare const canTransitionMock: (params: CanTransitionParams) => Promise<CanTransitionResponse>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Mock implementation: allows all transitions except a few known-invalid ones
3
+ * so the demo app can show both allowed and blocked responses.
4
+ */
5
+ export const canTransitionMock = async (params) => {
6
+ const to = params.to;
7
+ if (to.payment === "refunded" && to.fulfillment === "draft") {
8
+ return {
9
+ result: {
10
+ allowed: false,
11
+ blockedBy: "financial_invariant",
12
+ guard: "no-refund-in-draft",
13
+ reason: "Cannot refund an order that is still in draft",
14
+ },
15
+ };
16
+ }
17
+ if (to.payment === "paid" && to.fulfillment === "cancelled") {
18
+ return {
19
+ result: {
20
+ allowed: false,
21
+ blockedBy: "cross_axis_rule",
22
+ guard: "no-pay-cancelled",
23
+ reason: "Cannot mark a cancelled order as paid",
24
+ },
25
+ };
26
+ }
27
+ return { result: { allowed: true } };
28
+ };
@@ -0,0 +1,11 @@
1
+ import type { CFStatePair, CFTransitionResult } from "../../common-types/order-state";
2
+ export interface CanTransitionParams {
3
+ /** Order to evaluate. If omitted, evaluates against a new order (from = null). */
4
+ orderId?: string;
5
+ /** Target state pair to transition to. */
6
+ to: CFStatePair;
7
+ }
8
+ export interface CanTransitionResponse {
9
+ result: CFTransitionResult;
10
+ }
11
+ export type CanTransition = (params: CanTransitionParams) => Promise<CanTransitionResponse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,9 +1,12 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  export interface CashPaymentParams {
3
4
  /** If not provided, uses the cart total. */
4
5
  amount?: number;
5
6
  /** Defaults to false. */
6
7
  openChangeCalculator?: boolean;
8
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
9
+ checkoutFulfillmentTarget?: string;
7
10
  }
8
11
  export interface CashPaymentResponse {
9
12
  success: boolean;
@@ -12,5 +15,7 @@ export interface CashPaymentResponse {
12
15
  paymentType: string;
13
16
  order: CFOrder | null;
14
17
  timestamp: string;
18
+ /** Present when the state machine blocked or forced the transition. */
19
+ transitionResult?: CFTransitionResult;
15
20
  }
16
21
  export type CashPayment = (params?: CashPaymentParams) => Promise<CashPaymentResponse>;
@@ -1,3 +1,4 @@
1
+ import type { CFTransitionResult } from "../../common-types/order-state";
1
2
  export interface DeleteParkedOrderParams {
2
3
  orderId: string;
3
4
  }
@@ -5,5 +6,7 @@ export interface DeleteParkedOrderResponse {
5
6
  success: boolean;
6
7
  orderId: string;
7
8
  timestamp: string;
9
+ /** Present when the state machine blocked or forced the transition. */
10
+ transitionResult?: CFTransitionResult;
8
11
  }
9
12
  export type DeleteParkedOrder = (params?: DeleteParkedOrderParams) => Promise<DeleteParkedOrderResponse>;
@@ -1,4 +1,5 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
+ import type { CFTransitionResult } from "../../common-types/order-state";
2
3
  /** Params for extension-initiated payments; host routes by `paymentType`. */
3
4
  export interface ExtensionPaymentParams {
4
5
  paymentType: string;
@@ -8,6 +9,8 @@ export interface ExtensionPaymentParams {
8
9
  referenceId?: string;
9
10
  extensionId?: string;
10
11
  metadata?: Record<string, unknown>;
12
+ /** Override the fulfillment state after full payment. Render resolves the cascade. */
13
+ checkoutFulfillmentTarget?: string;
11
14
  }
12
15
  export interface ExtensionPaymentResponse {
13
16
  success: boolean;
@@ -15,5 +18,7 @@ export interface ExtensionPaymentResponse {
15
18
  paymentType: string;
16
19
  order: CFOrder | null;
17
20
  timestamp: string;
21
+ /** Present when the state machine blocked or forced the transition. */
22
+ transitionResult?: CFTransitionResult;
18
23
  }
19
24
  export type ExtensionPayment = (params?: ExtensionPaymentParams) => Promise<ExtensionPaymentResponse>;
@@ -0,0 +1,2 @@
1
+ import type { GetAvailableTransitions } from "./types";
2
+ export declare const getAvailableTransitions: GetAvailableTransitions;
@@ -0,0 +1,4 @@
1
+ import { commandFrameClient } from "../../client";
2
+ export const getAvailableTransitions = async (params) => {
3
+ return await commandFrameClient.call("getAvailableTransitions", params);
4
+ };
@@ -0,0 +1,6 @@
1
+ import type { GetAvailableTransitionsParams, GetAvailableTransitionsResponse } from "./types";
2
+ /**
3
+ * Mock implementation: returns a fixed set of plausible transitions
4
+ * so the demo app has data to render.
5
+ */
6
+ export declare const getAvailableTransitionsMock: (_params: GetAvailableTransitionsParams) => Promise<GetAvailableTransitionsResponse>;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Mock implementation: returns a fixed set of plausible transitions
3
+ * so the demo app has data to render.
4
+ */
5
+ export const getAvailableTransitionsMock = async (_params) => ({
6
+ transitions: [
7
+ {
8
+ to: { payment: "refunded", fulfillment: "returned" },
9
+ displayLabel: "Full Refund",
10
+ conditions: [{ met: true, description: "Order is paid" }],
11
+ },
12
+ {
13
+ to: { payment: "partially_refunded", fulfillment: "partially_returned" },
14
+ displayLabel: "Partial Refund",
15
+ conditions: [{ met: true, description: "Order is paid" }],
16
+ },
17
+ {
18
+ to: { payment: "voided", fulfillment: "cancelled" },
19
+ displayLabel: "Void Order",
20
+ conditions: [
21
+ { met: true, description: "Order exists" },
22
+ { met: false, description: "No payments captured (mock: skipped)" },
23
+ ],
24
+ },
25
+ ],
26
+ });
@@ -0,0 +1,9 @@
1
+ import type { CFAvailableTransition } from "../../common-types/order-state";
2
+ export interface GetAvailableTransitionsParams {
3
+ /** Order to evaluate. Required — we need the current state. */
4
+ orderId: string;
5
+ }
6
+ export interface GetAvailableTransitionsResponse {
7
+ transitions: CFAvailableTransition[];
8
+ }
9
+ export type GetAvailableTransitions = (params: GetAvailableTransitionsParams) => Promise<GetAvailableTransitionsResponse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -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,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,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,13 +1,13 @@
1
- export const mockRemoveCustomerNote = async (params) => {
1
+ export const mockRemoveCustomerNote = (params) => {
2
2
  console.log("[Mock] removeCustomerNote called", params);
3
3
  const noteId = params?.noteId ?? "";
4
4
  const timestamp = new Date().toISOString();
5
5
  if (!noteId) {
6
- return { success: false, noteId, timestamp };
6
+ return Promise.resolve({ success: false, noteId, timestamp });
7
7
  }
8
- return {
8
+ return Promise.resolve({
9
9
  success: true,
10
10
  noteId,
11
11
  timestamp
12
- };
12
+ });
13
13
  };
@@ -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,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,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,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
+ }
@@ -0,0 +1,6 @@
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 {};
@@ -0,0 +1,77 @@
1
+ /**
2
+ * State config fragment types for the command-frame boundary.
3
+ * These mirror @final-commerce/common's StateConfigFragment shapes but carry no logic.
4
+ * command-frame must NOT depend on common.
5
+ *
6
+ * Extensions pass a CFStateConfigFragment to Render, which merges it into the
7
+ * running StoredStateConfig via mergeFragment(). All contributed entries are tagged
8
+ * with the extension's id (_sourceExtensionId) so they can be cleaned up on unregister.
9
+ */
10
+ import type { CFStatePair } from "./order-state";
11
+ export type CFConditionOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "isEmpty" | "isNotEmpty";
12
+ export interface CFCondition {
13
+ /** Dot-path into the OrderContext, e.g. "order.balance" or "computed.totalPaid". */
14
+ field: string;
15
+ operator: CFConditionOperator;
16
+ value: unknown;
17
+ }
18
+ /** All conditions in a group are AND'd together. */
19
+ export interface CFConditionGroup {
20
+ conditions: CFCondition[];
21
+ }
22
+ /** Groups are OR'd — the first passing group satisfies the set. */
23
+ export interface CFTransitionConditionSet {
24
+ id: string;
25
+ label: string;
26
+ groups: CFConditionGroup[];
27
+ }
28
+ export interface CFPaymentTransitionPath {
29
+ from: string;
30
+ to: string;
31
+ /** References a CFTransitionConditionSet.id in the same fragment. */
32
+ conditionSetId?: string;
33
+ }
34
+ export interface CFFulfillmentTransitionPath {
35
+ from: string;
36
+ to: string;
37
+ conditionSetId?: string;
38
+ }
39
+ export interface CFCrossAxisRule {
40
+ id: string;
41
+ label: string;
42
+ description?: string;
43
+ enabled: boolean;
44
+ /** Which axis + target state(s) activate this rule. */
45
+ trigger: {
46
+ axis: "payment" | "fulfillment";
47
+ to: string[];
48
+ };
49
+ /** Which axis + states must be present when the trigger fires. */
50
+ requires: {
51
+ axis: "payment" | "fulfillment";
52
+ states: string[];
53
+ };
54
+ }
55
+ export interface CFDisplayStateRule {
56
+ paymentState?: string[];
57
+ fulfillmentState?: string[];
58
+ label: string;
59
+ color?: string;
60
+ icon?: string;
61
+ }
62
+ /**
63
+ * Partial config contributed by an extension. Render merges this into the
64
+ * running StoredStateConfig. All arrays are appended; display rules replace
65
+ * on key collision.
66
+ *
67
+ * IDs (crossAxisRules, transitionConditions) must be unique across the merged
68
+ * config — prefix them with your extension id to avoid collisions.
69
+ */
70
+ export interface CFStateConfigFragment {
71
+ paymentPaths?: CFPaymentTransitionPath[];
72
+ fulfillmentPaths?: CFFulfillmentTransitionPath[];
73
+ crossAxisRules?: CFCrossAxisRule[];
74
+ transitionConditions?: CFTransitionConditionSet[];
75
+ validInitialStates?: CFStatePair[];
76
+ displayStateMap?: CFDisplayStateRule[];
77
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * State config fragment types for the command-frame boundary.
3
+ * These mirror @final-commerce/common's StateConfigFragment shapes but carry no logic.
4
+ * command-frame must NOT depend on common.
5
+ *
6
+ * Extensions pass a CFStateConfigFragment to Render, which merges it into the
7
+ * running StoredStateConfig via mergeFragment(). All contributed entries are tagged
8
+ * with the extension's id (_sourceExtensionId) so they can be cleaned up on unregister.
9
+ */
10
+ export {};
@@ -335,6 +335,9 @@ export const MOCK_ORDER_1 = {
335
335
  companyId: MOCK_COMPANY.id,
336
336
  externalId: null,
337
337
  status: "completed",
338
+ paymentState: "paid",
339
+ fulfillmentState: "fulfilled",
340
+ displayState: "Completed",
338
341
  customer: MOCK_CUSTOMER_1,
339
342
  summary: {
340
343
  total: 2100,
@@ -385,6 +388,9 @@ export const MOCK_ORDER_2 = {
385
388
  companyId: MOCK_COMPANY.id,
386
389
  externalId: null,
387
390
  status: "completed",
391
+ paymentState: "paid",
392
+ fulfillmentState: "fulfilled",
393
+ displayState: "Completed",
388
394
  customer: MOCK_CUSTOMER_2,
389
395
  summary: {
390
396
  total: 3000,
@@ -618,6 +624,9 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
618
624
  companyId: MOCK_COMPANY.id,
619
625
  externalId: null,
620
626
  status: "completed",
627
+ paymentState: "paid",
628
+ fulfillmentState: "fulfilled",
629
+ displayState: "Completed",
621
630
  customer: MOCK_CART.customer ? MOCK_CART.customer : null,
622
631
  summary: {
623
632
  total: totalNum,
@@ -0,0 +1 @@
1
+ export { preorderNoDepositFragment } from "./preorder-no-deposit";
@@ -0,0 +1 @@
1
+ export { preorderNoDepositFragment } from "./preorder-no-deposit";
@@ -0,0 +1,26 @@
1
+ import type { CFStateConfigFragment } from "../common-types/state-fragment";
2
+ /**
3
+ * Pre-order fragment — no deposit required.
4
+ *
5
+ * Mirrors preorder-with-deposit but removes the cross-axis rule that gates
6
+ * `on_hold` on payment state. An order can be placed on hold while still
7
+ * `unpaid` — payment is collected later (pay-at-pickup, BOPIS, COD, etc.).
8
+ *
9
+ * State flow:
10
+ *
11
+ * unpaid|on_hold → (cashier collects payment) → paid|on_hold
12
+ * ↓
13
+ * (progress fulfillment)
14
+ * ↓
15
+ * paid|in_progress
16
+ *
17
+ * Key difference from preorder-with-deposit:
18
+ * - No cross-axis rule blocking `on_hold` when unpaid. The cashier can
19
+ * confirm the pre-order without collecting a deposit first.
20
+ * - `unpaid|on_hold` is whitelisted as a valid first-save state pair.
21
+ * - `in_progress` still requires at least a partial payment (you must pay
22
+ * before the merchant starts fulfilling).
23
+ *
24
+ * Prefix all IDs with your extension id before merging to avoid collisions.
25
+ */
26
+ export declare const preorderNoDepositFragment: CFStateConfigFragment;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Pre-order fragment — no deposit required.
3
+ *
4
+ * Mirrors preorder-with-deposit but removes the cross-axis rule that gates
5
+ * `on_hold` on payment state. An order can be placed on hold while still
6
+ * `unpaid` — payment is collected later (pay-at-pickup, BOPIS, COD, etc.).
7
+ *
8
+ * State flow:
9
+ *
10
+ * unpaid|on_hold → (cashier collects payment) → paid|on_hold
11
+ * ↓
12
+ * (progress fulfillment)
13
+ * ↓
14
+ * paid|in_progress
15
+ *
16
+ * Key difference from preorder-with-deposit:
17
+ * - No cross-axis rule blocking `on_hold` when unpaid. The cashier can
18
+ * confirm the pre-order without collecting a deposit first.
19
+ * - `unpaid|on_hold` is whitelisted as a valid first-save state pair.
20
+ * - `in_progress` still requires at least a partial payment (you must pay
21
+ * before the merchant starts fulfilling).
22
+ *
23
+ * Prefix all IDs with your extension id before merging to avoid collisions.
24
+ */
25
+ export const preorderNoDepositFragment = {
26
+ paymentPaths: [
27
+ {
28
+ from: "unpaid",
29
+ to: "partially_paid",
30
+ conditionSetId: "require-payment-recorded"
31
+ },
32
+ {
33
+ from: "unpaid",
34
+ to: "paid",
35
+ conditionSetId: "require-full-payment"
36
+ },
37
+ {
38
+ from: "partially_paid",
39
+ to: "paid",
40
+ conditionSetId: "require-full-payment"
41
+ }
42
+ ],
43
+ fulfillmentPaths: [
44
+ {
45
+ from: "draft",
46
+ to: "on_hold",
47
+ conditionSetId: "preorder-no-deposit-require-items"
48
+ },
49
+ {
50
+ from: "on_hold",
51
+ to: "cancelled",
52
+ conditionSetId: "preorder-no-deposit-cancel-before-progress"
53
+ }
54
+ ],
55
+ transitionConditions: [
56
+ {
57
+ id: "preorder-no-deposit-require-items",
58
+ label: "Require at least one item to place pre-order",
59
+ groups: [
60
+ {
61
+ conditions: [
62
+ {
63
+ field: "computed.itemCount",
64
+ operator: "gte",
65
+ value: 1
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ id: "preorder-no-deposit-cancel-before-progress",
73
+ label: "Can only cancel pre-order before fulfillment begins",
74
+ groups: [
75
+ {
76
+ conditions: [
77
+ {
78
+ field: "computed.fulfilledItemCount",
79
+ operator: "eq",
80
+ value: 0
81
+ }
82
+ ]
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ id: "require-payment-recorded",
88
+ label: "Require at least one payment before marking partially paid",
89
+ groups: [
90
+ {
91
+ conditions: [
92
+ {
93
+ field: "computed.totalPaid",
94
+ operator: "gt",
95
+ value: 0
96
+ }
97
+ ]
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ id: "require-full-payment",
103
+ label: "Require full payment before marking as paid",
104
+ groups: [
105
+ {
106
+ conditions: [
107
+ {
108
+ field: "computed.totalPaid",
109
+ operator: "gte",
110
+ value: 1
111
+ },
112
+ {
113
+ field: "computed.balance",
114
+ operator: "lte",
115
+ value: 0
116
+ }
117
+ ]
118
+ }
119
+ ]
120
+ }
121
+ ],
122
+ crossAxisRules: [
123
+ {
124
+ id: "preorder-no-deposit-require-payment-before-confirm",
125
+ label: "Require payment before starting fulfillment",
126
+ description: "Fulfillment cannot move to in_progress unless at least a partial payment has been made",
127
+ enabled: true,
128
+ trigger: {
129
+ axis: "fulfillment",
130
+ to: ["in_progress"]
131
+ },
132
+ requires: {
133
+ axis: "payment",
134
+ states: ["partially_paid", "paid"]
135
+ }
136
+ }
137
+ ],
138
+ validInitialStates: [
139
+ { payment: "unpaid", fulfillment: "on_hold" },
140
+ { payment: "partially_paid", fulfillment: "on_hold" },
141
+ { payment: "paid", fulfillment: "on_hold" }
142
+ ],
143
+ displayStateMap: [
144
+ {
145
+ paymentState: ["unpaid"],
146
+ fulfillmentState: ["on_hold"],
147
+ label: "Pre-Order",
148
+ color: "#6366f1",
149
+ icon: "clock"
150
+ },
151
+ {
152
+ paymentState: ["partially_paid"],
153
+ fulfillmentState: ["on_hold"],
154
+ label: "Pre-Order (Deposit Received)",
155
+ color: "#f59e0b",
156
+ icon: "clock"
157
+ },
158
+ {
159
+ paymentState: ["paid"],
160
+ fulfillmentState: ["on_hold"],
161
+ label: "Pre-Order (Fully Paid)",
162
+ color: "#22c55e",
163
+ icon: "check-circle"
164
+ }
165
+ ]
166
+ };
package/dist/index.d.ts CHANGED
@@ -104,6 +104,8 @@ export declare const command: {
104
104
  readonly uploadMedia: import("./actions/upload-media/types").UploadMedia;
105
105
  readonly getTaxTables: import("./actions/get-tax-tables/types").GetTaxTables;
106
106
  readonly getBranding: import("./actions/get-branding/types").GetBranding;
107
+ readonly canTransition: import("./actions/can-transition/types").CanTransition;
108
+ readonly getAvailableTransitions: import("./actions/get-available-transitions/types").GetAvailableTransitions;
107
109
  };
108
110
  export type { ExampleFunction, ExampleFunctionParams, ExampleFunctionResponse } from "./actions/example-function/types";
109
111
  export type { GenerateAPIKey, GenerateAPIKeyParams, GenerateAPIKeyResponse } from "./actions/generate-api-key/types";
@@ -227,7 +229,7 @@ export { stationTopic } from "./pubsub/topics/station";
227
229
  export { sessionTopic } from "./pubsub/topics/session";
228
230
  export { usersTopic } from "./pubsub/topics/users";
229
231
  export type { CustomerCreatedPayload, CustomerUpdatedPayload, CustomerNoteAddedPayload, CustomerNoteDeletedPayload, CustomerAssignedPayload, CustomerUnassignedPayload, CustomerActiveSetPayload, CustomerActiveGetPayload, CustomerCreatedEvent, CustomerUpdatedEvent, CustomerNoteAddedEvent, CustomerNoteDeletedEvent, CustomerAssignedEvent, CustomerUnassignedEvent, CustomerActiveSetEvent, CustomerActiveGetEvent, CustomersEventType, CustomersEventPayload } from "./pubsub/topics/customers/types";
230
- export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
232
+ export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrderStateTransitionCompletedPayload, OrderStateTransitionBlockedPayload, OrderStateTransitionCompletedEvent, OrderStateTransitionBlockedEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
231
233
  export type { RefundCreatedPayload, RefundUpdatedPayload, RefundActiveSetPayload, RefundActiveGetPayload, RefundCreatedEvent, RefundUpdatedEvent, RefundActiveSetEvent, RefundActiveGetEvent, RefundsEventType, RefundsEventPayload } from "./pubsub/topics/refunds/types";
232
234
  export type { ProductCreatedPayload, ProductUpdatedPayload, ProductSetActivePayload, ProductGetActivePayload, ProductCreatedEvent, ProductUpdatedEvent, ProductSetActiveEvent, ProductGetActiveEvent, ProductsEventType, ProductsEventPayload } from "./pubsub/topics/products/types";
233
235
  export type { OutletActiveSetPayload, OutletActiveGetPayload, OutletActiveSetEvent, OutletActiveGetEvent, OutletEventType, OutletEventPayload } from "./pubsub/topics/outlet/types";
@@ -253,3 +255,8 @@ export type { GetSecretVal, GetSecretValParams, GetSecretValResponse } from "./a
253
255
  export type { SetSecretVal, SetSecretValParams, SetSecretValResponse } from "./actions/set-secret-val/types";
254
256
  export type { GetUsers, GetUsersParams, GetUsersResponse } from "./actions/get-users/types";
255
257
  export type { GetRoles, GetRolesParams, GetRolesResponse } from "./actions/get-roles/types";
258
+ export type { CFBlockedBy, CFStatePair, CFTransitionResult, CFFailedCondition, CFConditionStatus, CFAvailableTransition } from "./common-types/order-state";
259
+ export type { CFConditionOperator, CFCondition, CFConditionGroup, CFTransitionConditionSet, CFPaymentTransitionPath, CFFulfillmentTransitionPath, CFCrossAxisRule, CFDisplayStateRule, CFStateConfigFragment } from "./common-types/state-fragment";
260
+ export type { CanTransition, CanTransitionParams, CanTransitionResponse } from "./actions/can-transition/types";
261
+ export type { GetAvailableTransitions, GetAvailableTransitionsParams, GetAvailableTransitionsResponse } from "./actions/get-available-transitions/types";
262
+ export { preorderNoDepositFragment } from "./fragments";
package/dist/index.js CHANGED
@@ -102,6 +102,9 @@ import { getSecretsKeys } from "./actions/get-secrets-keys/action";
102
102
  import { getSecretVal } from "./actions/get-secret-val/action";
103
103
  import { setSecretVal } from "./actions/set-secret-val/action";
104
104
  import { generateAPIKey } from "./actions/generate-api-key/action";
105
+ // State Machine Query Actions
106
+ import { canTransition } from "./actions/can-transition/action";
107
+ import { getAvailableTransitions } from "./actions/get-available-transitions/action";
105
108
  // Product CRUD Actions
106
109
  import { addProduct } from "./actions/add-product/action";
107
110
  import { editProduct } from "./actions/edit-product/action";
@@ -238,6 +241,9 @@ export const command = {
238
241
  uploadMedia,
239
242
  getTaxTables,
240
243
  getBranding,
244
+ // State Machine Queries
245
+ canTransition,
246
+ getAvailableTransitions
241
247
  };
242
248
  export { EXTENSION_REFUND_REQUEST_ACTION } from "./actions/extension-refund/constants";
243
249
  export { installExtensionRefundListener } from "./actions/extension-refund/extension-refund-listener";
@@ -272,3 +278,5 @@ export { sessionTopic } from "./pubsub/topics/session";
272
278
  export { usersTopic } from "./pubsub/topics/users";
273
279
  // Export Custom Tables Types
274
280
  export { AttributeType } from "./common-types/attribute-type";
281
+ // State Machine Fragments
282
+ export { preorderNoDepositFragment } from "./fragments";
@@ -85,6 +85,8 @@ import { mockSetActiveSession } from "../../actions/set-active-session/mock";
85
85
  import { mockGetActiveUser } from "../../actions/get-active-user/mock";
86
86
  import { mockSetActiveUser } from "../../actions/set-active-user/mock";
87
87
  import { mockSetActiveRefund } from "../../actions/set-active-refund/mock";
88
+ import { canTransitionMock } from "../../actions/can-transition/mock";
89
+ import { getAvailableTransitionsMock } from "../../actions/get-available-transitions/mock";
88
90
  export const RENDER_MOCKS = {
89
91
  addCartDiscount: mockAddCartDiscount,
90
92
  addCartFee: mockAddCartFee,
@@ -178,5 +180,7 @@ export const RENDER_MOCKS = {
178
180
  removeCartFee: mockRemoveCartFee,
179
181
  removeOrderNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
180
182
  removeCustomSale: params => Promise.resolve({ success: true, id: params.id, timestamp: new Date().toISOString() }),
181
- removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() })
183
+ removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() }),
184
+ canTransition: canTransitionMock,
185
+ getAvailableTransitions: getAvailableTransitionsMock
182
186
  };
@@ -1,4 +1,4 @@
1
- import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem } from "../../index";
1
+ import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions } from "../../index";
2
2
  export interface RenderProviderActions {
3
3
  exampleFunction: ExampleFunction;
4
4
  getProducts: GetProducts;
@@ -93,4 +93,6 @@ export interface RenderProviderActions {
93
93
  removeOrderNote: RemoveOrderNote;
94
94
  removeCustomSale: RemoveCustomSale;
95
95
  removeNonRevenueItem: RemoveNonRevenueItem;
96
+ canTransition: CanTransition;
97
+ getAvailableTransitions: GetAvailableTransitions;
96
98
  }
@@ -26,6 +26,16 @@ export const ordersTopic = {
26
26
  id: "get-active-order",
27
27
  name: "Get Active Order",
28
28
  description: "Published when the active order is retrieved for listeners"
29
+ },
30
+ {
31
+ id: "state-transition-completed",
32
+ name: "State Transition Completed",
33
+ description: "Published when an order state transition completes successfully"
34
+ },
35
+ {
36
+ id: "state-transition-blocked",
37
+ name: "State Transition Blocked",
38
+ description: "Published when an order state transition is blocked by the state machine"
29
39
  }
30
40
  ]
31
41
  };
@@ -0,0 +1,16 @@
1
+ import type { TopicEvent } from "../../../types";
2
+ import type { CFBlockedBy } from "../../../../common-types/order-state";
3
+ export interface OrderStateTransitionBlockedPayload {
4
+ orderId: string;
5
+ from: {
6
+ payment: string;
7
+ fulfillment: string;
8
+ };
9
+ to: {
10
+ payment: string;
11
+ fulfillment: string;
12
+ };
13
+ blockedBy: CFBlockedBy;
14
+ reason?: string;
15
+ }
16
+ export type OrderStateTransitionBlockedEvent = TopicEvent<OrderStateTransitionBlockedPayload>;
@@ -0,0 +1,15 @@
1
+ import type { TopicEvent } from "../../../types";
2
+ export interface OrderStateTransitionCompletedPayload {
3
+ orderId: string;
4
+ from: {
5
+ payment: string;
6
+ fulfillment: string;
7
+ };
8
+ to: {
9
+ payment: string;
10
+ fulfillment: string;
11
+ };
12
+ displayState: string;
13
+ forced?: boolean;
14
+ }
15
+ export type OrderStateTransitionCompletedEvent = TopicEvent<OrderStateTransitionCompletedPayload>;
@@ -6,9 +6,13 @@ export * from "./order-created/types";
6
6
  export * from "./order-updated/types";
7
7
  export * from "./set-active-order/types";
8
8
  export * from "./get-active-order/types";
9
+ export * from "./state-transition-completed/types";
10
+ export * from "./state-transition-blocked/types";
9
11
  import type { OrderCreatedPayload } from "./order-created/types";
10
12
  import type { OrderUpdatedPayload } from "./order-updated/types";
11
13
  import type { OrderActiveSetPayload } from "./set-active-order/types";
12
14
  import type { OrderActiveGetPayload } from "./get-active-order/types";
13
- export type OrdersEventPayload = OrderCreatedPayload | OrderUpdatedPayload | OrderActiveSetPayload | OrderActiveGetPayload;
14
- export type OrdersEventType = "order-created" | "order-updated" | "set-active-order" | "get-active-order";
15
+ import type { OrderStateTransitionCompletedPayload } from "./state-transition-completed/types";
16
+ import type { OrderStateTransitionBlockedPayload } from "./state-transition-blocked/types";
17
+ export type OrdersEventPayload = OrderCreatedPayload | OrderUpdatedPayload | OrderActiveSetPayload | OrderActiveGetPayload | OrderStateTransitionCompletedPayload | OrderStateTransitionBlockedPayload;
18
+ export type OrdersEventType = "order-created" | "order-updated" | "set-active-order" | "get-active-order" | "state-transition-completed" | "state-transition-blocked";
@@ -7,3 +7,5 @@ export * from "./order-created/types";
7
7
  export * from "./order-updated/types";
8
8
  export * from "./set-active-order/types";
9
9
  export * from "./get-active-order/types";
10
+ export * from "./state-transition-completed/types";
11
+ export * from "./state-transition-blocked/types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.1.63",
3
+ "version": "0.2.0-beta.1",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",