@final-commerce/command-frame 0.1.63 → 0.2.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 (101) hide show
  1. package/README.md +26 -28
  2. package/dist/CommonTypes.d.ts +118 -19
  3. package/dist/actions/add-customer-note/mock.js +27 -18
  4. package/dist/actions/add-customer-note/types.d.ts +3 -2
  5. package/dist/actions/add-product/mock.js +11 -5
  6. package/dist/actions/can-transition/action.d.ts +2 -0
  7. package/dist/actions/can-transition/action.js +4 -0
  8. package/dist/actions/can-transition/mock.d.ts +6 -0
  9. package/dist/actions/can-transition/mock.js +28 -0
  10. package/dist/actions/can-transition/types.d.ts +11 -0
  11. package/dist/actions/can-transition/types.js +1 -0
  12. package/dist/actions/cash-payment/types.d.ts +5 -0
  13. package/dist/actions/delete-parked-order/types.d.ts +3 -0
  14. package/dist/actions/edit-product/mock.js +4 -2
  15. package/dist/actions/extension-payment/types.d.ts +5 -0
  16. package/dist/actions/get-available-transitions/action.d.ts +2 -0
  17. package/dist/actions/get-available-transitions/action.js +4 -0
  18. package/dist/actions/get-available-transitions/mock.d.ts +6 -0
  19. package/dist/actions/get-available-transitions/mock.js +26 -0
  20. package/dist/actions/get-available-transitions/types.d.ts +9 -0
  21. package/dist/actions/get-available-transitions/types.js +1 -0
  22. package/dist/actions/get-context/mock.js +12 -6
  23. package/dist/actions/initiate-refund/types.d.ts +3 -0
  24. package/dist/actions/park-order/types.d.ts +3 -0
  25. package/dist/actions/partial-payment/types.d.ts +5 -0
  26. package/dist/actions/print/mock.js +3 -6
  27. package/dist/actions/print/types.d.ts +1 -7
  28. package/dist/actions/process-partial-refund/types.d.ts +3 -0
  29. package/dist/actions/remove-customer-note/mock.js +16 -7
  30. package/dist/actions/resume-parked-order/types.d.ts +3 -0
  31. package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
  32. package/dist/actions/terminal-payment/types.d.ts +5 -0
  33. package/dist/actions/vendara-payment/types.d.ts +5 -0
  34. package/dist/common-types/index.d.ts +1 -0
  35. package/dist/common-types/index.js +1 -0
  36. package/dist/common-types/order-state.d.ts +32 -0
  37. package/dist/common-types/order-state.js +6 -0
  38. package/dist/common-types/state-fragment.d.ts +77 -0
  39. package/dist/common-types/state-fragment.js +10 -0
  40. package/dist/demo/database.js +18 -24
  41. package/dist/fragments/index.d.ts +1 -0
  42. package/dist/fragments/index.js +1 -0
  43. package/dist/fragments/preorder-no-deposit.d.ts +26 -0
  44. package/dist/fragments/preorder-no-deposit.js +166 -0
  45. package/dist/index.d.ts +17 -2
  46. package/dist/index.js +12 -0
  47. package/dist/projects/render/mocks.js +5 -1
  48. package/dist/projects/render/types.d.ts +3 -1
  49. package/dist/pubsub/topics/attributes/attribute-created/types.d.ts +6 -0
  50. package/dist/pubsub/topics/attributes/attribute-created/types.js +1 -0
  51. package/dist/pubsub/topics/attributes/attribute-updated/types.d.ts +6 -0
  52. package/dist/pubsub/topics/attributes/attribute-updated/types.js +1 -0
  53. package/dist/pubsub/topics/attributes/index.d.ts +3 -0
  54. package/dist/pubsub/topics/attributes/index.js +18 -0
  55. package/dist/pubsub/topics/attributes/types.d.ts +6 -0
  56. package/dist/pubsub/topics/attributes/types.js +2 -0
  57. package/dist/pubsub/topics/categories/category-created/types.d.ts +6 -0
  58. package/dist/pubsub/topics/categories/category-created/types.js +1 -0
  59. package/dist/pubsub/topics/categories/category-updated/types.d.ts +6 -0
  60. package/dist/pubsub/topics/categories/category-updated/types.js +1 -0
  61. package/dist/pubsub/topics/categories/index.d.ts +3 -0
  62. package/dist/pubsub/topics/categories/index.js +18 -0
  63. package/dist/pubsub/topics/categories/types.d.ts +6 -0
  64. package/dist/pubsub/topics/categories/types.js +2 -0
  65. package/dist/pubsub/topics/index.d.ts +4 -0
  66. package/dist/pubsub/topics/index.js +4 -0
  67. package/dist/pubsub/topics/orders/index.js +10 -0
  68. package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
  69. package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
  70. package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
  71. package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
  72. package/dist/pubsub/topics/orders/types.d.ts +6 -2
  73. package/dist/pubsub/topics/orders/types.js +2 -0
  74. package/dist/pubsub/topics/print/print-completed/types.d.ts +1 -1
  75. package/dist/pubsub/topics/print/print-error/types.d.ts +1 -1
  76. package/dist/pubsub/topics/print/print-started/types.d.ts +1 -1
  77. package/dist/pubsub/topics/transactions/index.d.ts +3 -0
  78. package/dist/pubsub/topics/transactions/index.js +18 -0
  79. package/dist/pubsub/topics/transactions/transaction-created/types.d.ts +6 -0
  80. package/dist/pubsub/topics/transactions/transaction-created/types.js +1 -0
  81. package/dist/pubsub/topics/transactions/transaction-updated/types.d.ts +6 -0
  82. package/dist/pubsub/topics/transactions/transaction-updated/types.js +1 -0
  83. package/dist/pubsub/topics/transactions/types.d.ts +6 -0
  84. package/dist/pubsub/topics/transactions/types.js +2 -0
  85. package/dist/pubsub/topics/types.d.ts +8 -0
  86. package/dist/pubsub/topics/users/index.js +10 -0
  87. package/dist/pubsub/topics/users/types.d.ts +6 -2
  88. package/dist/pubsub/topics/users/types.js +2 -0
  89. package/dist/pubsub/topics/users/user-created/types.d.ts +6 -0
  90. package/dist/pubsub/topics/users/user-created/types.js +1 -0
  91. package/dist/pubsub/topics/users/user-updated/types.d.ts +6 -0
  92. package/dist/pubsub/topics/users/user-updated/types.js +1 -0
  93. package/dist/pubsub/topics/variants/index.d.ts +3 -0
  94. package/dist/pubsub/topics/variants/index.js +18 -0
  95. package/dist/pubsub/topics/variants/types.d.ts +6 -0
  96. package/dist/pubsub/topics/variants/types.js +2 -0
  97. package/dist/pubsub/topics/variants/variant-created/types.d.ts +6 -0
  98. package/dist/pubsub/topics/variants/variant-created/types.js +1 -0
  99. package/dist/pubsub/topics/variants/variant-updated/types.d.ts +6 -0
  100. package/dist/pubsub/topics/variants/variant-updated/types.js +1 -0
  101. package/package.json +1 -1
@@ -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,8 +1,8 @@
1
1
  import { MOCK_COMPANY, MOCK_OUTLET, MOCK_STATION, MOCK_USER } from "../../demo/database";
2
2
  // Mock for Render context (POS terminal)
3
- export const mockGetContext = async () => {
3
+ export const mockGetContext = () => {
4
4
  console.log("[Mock] getContext called (Render)");
5
- return {
5
+ return Promise.resolve({
6
6
  userId: MOCK_USER.id || null,
7
7
  companyId: MOCK_COMPANY.id || null,
8
8
  companyName: MOCK_COMPANY.name || null,
@@ -17,17 +17,23 @@ export const mockGetContext = async () => {
17
17
  buildSourceId: "mock_source_id",
18
18
  buildIsPremium: true,
19
19
  isOffline: false,
20
+ currency: "USD",
21
+ currencySymbol: "$",
22
+ currencyPrefix: "$",
23
+ currencySuffix: "",
24
+ thousandSeparator: ",",
25
+ decimalSeparator: ".",
20
26
  user: null,
21
27
  company: null,
22
28
  station: null,
23
29
  outlet: null,
24
30
  timestamp: new Date().toISOString()
25
- };
31
+ });
26
32
  };
27
33
  // Mock for Manage context (Hub/BuilderHub)
28
- export const mockGetContextManage = async () => {
34
+ export const mockGetContextManage = () => {
29
35
  console.log("[Mock] getContext called (Manage)");
30
- return {
36
+ return Promise.resolve({
31
37
  user: {
32
38
  _id: MOCK_USER.id,
33
39
  id: MOCK_USER.id,
@@ -47,5 +53,5 @@ export const mockGetContextManage = async () => {
47
53
  extensionId: "mock_extension_id",
48
54
  outlets: [MOCK_OUTLET],
49
55
  timestamp: new Date().toISOString()
50
- };
56
+ });
51
57
  };
@@ -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,4 +1,4 @@
1
- export const mockPrint = async (params) => {
1
+ export const mockPrint = (params) => {
2
2
  console.log("[Mock] print called", params);
3
3
  if (!params) {
4
4
  throw new Error("Print parameters are required");
@@ -11,16 +11,13 @@ export const mockPrint = async (params) => {
11
11
  case "html":
12
12
  console.log("[Mock] Would print HTML:", params.data.html?.substring(0, 100) + "...");
13
13
  break;
14
- case "selector":
15
- console.log("[Mock] Would print element with selector:", params.data.selector);
16
- break;
17
14
  case "receipt":
18
15
  console.log("[Mock] Would print receipt for order:", params.data.order);
19
16
  break;
20
17
  }
21
- return {
18
+ return Promise.resolve({
22
19
  success: true,
23
20
  timestamp: new Date().toISOString(),
24
21
  type: params.type
25
- };
22
+ });
26
23
  };
@@ -1,5 +1,5 @@
1
1
  import type { CFActiveOrder } from "../../CommonTypes";
2
- export type PrintType = "image" | "html" | "selector" | "receipt";
2
+ export type PrintType = "image" | "html" | "receipt";
3
3
  export type PrintParams = {
4
4
  type: "image";
5
5
  data: {
@@ -12,12 +12,6 @@ export type PrintParams = {
12
12
  html: string;
13
13
  };
14
14
  options?: PrintOptions;
15
- } | {
16
- type: "selector";
17
- data: {
18
- selector: string;
19
- };
20
- options?: PrintOptions;
21
15
  } | {
22
16
  type: "receipt";
23
17
  data: {
@@ -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,22 @@
1
- export const mockRemoveCustomerNote = async (params) => {
1
+ import { MOCK_CUSTOMERS, mockPublishEvent } from "../../demo/database";
2
+ export const mockRemoveCustomerNote = (params) => {
2
3
  console.log("[Mock] removeCustomerNote called", params);
3
4
  const noteId = params?.noteId ?? "";
4
5
  const timestamp = new Date().toISOString();
5
6
  if (!noteId) {
6
- return { success: false, noteId, timestamp };
7
+ return Promise.resolve({ success: false, noteId, timestamp });
7
8
  }
8
- return {
9
- success: true,
10
- noteId,
11
- timestamp
12
- };
9
+ for (const customer of MOCK_CUSTOMERS) {
10
+ const before = customer.notes?.length ?? 0;
11
+ const removed = (customer.notes || []).find(n => n._id === noteId);
12
+ customer.notes = (customer.notes || []).filter(n => n._id !== noteId);
13
+ if ((customer.notes?.length ?? 0) !== before) {
14
+ mockPublishEvent("customers", "customer-note-deleted", {
15
+ customer,
16
+ note: removed
17
+ });
18
+ return Promise.resolve({ success: true, noteId, timestamp });
19
+ }
20
+ }
21
+ return Promise.resolve({ success: false, noteId, timestamp });
13
22
  };
@@ -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 {};
@@ -202,7 +202,7 @@ export const MOCK_CATEGORY_SPICY = {
202
202
  parentId: null
203
203
  };
204
204
  // --- PRODUCTS ---
205
- const createInventory = (stock) => [{ outletId: MOCK_OUTLET_MAIN.id, stock }];
205
+ const createInventory = (stock) => [{ warehouse: "main", outletId: MOCK_OUTLET_MAIN.id, stock }];
206
206
  // Helper for Simple Product
207
207
  const createSimpleProduct = (id, name, price, image, categories, description) => {
208
208
  const sku = `SKU-${id.toUpperCase()}`;
@@ -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,
@@ -366,10 +369,7 @@ export const MOCK_ORDER_1 = {
366
369
  metadata: [],
367
370
  billing: MOCK_CUSTOMER_1.billing || null,
368
371
  shipping: null,
369
- lineItems: [
370
- createLineItem(MOCK_PRODUCT_GARLIC, 0, 1),
371
- createLineItem(MOCK_PRODUCT_BASIL_ALMOND, 0, 1)
372
- ],
372
+ lineItems: [createLineItem(MOCK_PRODUCT_GARLIC, 0, 1), createLineItem(MOCK_PRODUCT_BASIL_ALMOND, 0, 1)],
373
373
  customSales: [],
374
374
  balance: 0,
375
375
  user: MOCK_USER_LUIGI,
@@ -385,6 +385,9 @@ export const MOCK_ORDER_2 = {
385
385
  companyId: MOCK_COMPANY.id,
386
386
  externalId: null,
387
387
  status: "completed",
388
+ paymentState: "paid",
389
+ fulfillmentState: "fulfilled",
390
+ displayState: "Completed",
388
391
  customer: MOCK_CUSTOMER_2,
389
392
  summary: {
390
393
  total: 3000,
@@ -416,9 +419,7 @@ export const MOCK_ORDER_2 = {
416
419
  metadata: [],
417
420
  billing: null,
418
421
  shipping: null,
419
- lineItems: [
420
- createLineItem(MOCK_PRODUCT_BEER, 0, 2)
421
- ],
422
+ lineItems: [createLineItem(MOCK_PRODUCT_BEER, 0, 2)],
422
423
  customSales: [],
423
424
  balance: 0,
424
425
  user: MOCK_USER_MARIO,
@@ -430,20 +431,8 @@ export const MOCK_ORDER_2 = {
430
431
  export const MOCK_USERS = [MOCK_USER_MARIO, MOCK_USER_LUIGI];
431
432
  export const MOCK_STATIONS = [MOCK_STATION_1, MOCK_STATION_2];
432
433
  export const MOCK_OUTLETS = [MOCK_OUTLET_MAIN];
433
- export const MOCK_CUSTOMERS = [
434
- MOCK_CUSTOMER_1,
435
- MOCK_CUSTOMER_2,
436
- MOCK_CUSTOMER_3,
437
- MOCK_CUSTOMER_4,
438
- MOCK_CUSTOMER_5
439
- ];
440
- export const MOCK_CATEGORIES = [
441
- MOCK_CATEGORY_PASTES,
442
- MOCK_CATEGORY_SPECIALTY,
443
- MOCK_CATEGORY_BASIC,
444
- MOCK_CATEGORY_VEGAN,
445
- MOCK_CATEGORY_SPICY
446
- ];
434
+ export const MOCK_CUSTOMERS = [MOCK_CUSTOMER_1, MOCK_CUSTOMER_2, MOCK_CUSTOMER_3, MOCK_CUSTOMER_4, MOCK_CUSTOMER_5];
435
+ export const MOCK_CATEGORIES = [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_SPECIALTY, MOCK_CATEGORY_BASIC, MOCK_CATEGORY_VEGAN, MOCK_CATEGORY_SPICY];
447
436
  export const MOCK_PRODUCTS = [
448
437
  MOCK_PRODUCT_BASIL_ALMOND,
449
438
  MOCK_PRODUCT_BEER,
@@ -494,7 +483,8 @@ export const resetMockCart = () => {
494
483
  };
495
484
  };
496
485
  function resolveMockOrderCurrency() {
497
- const raw = MOCK_COMPANY.settings?.currency;
486
+ const settings = MOCK_COMPANY.settings;
487
+ const raw = settings?.currency;
498
488
  if (typeof raw === "string" && Object.values(CurrencyCode).includes(raw)) {
499
489
  return raw;
500
490
  }
@@ -554,6 +544,7 @@ export const safeSerialize = (data) => {
554
544
  return JSON.parse(JSON.stringify(data));
555
545
  };
556
546
  const mockTopicSubscribers = {};
547
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
557
548
  export const mockPublishEvent = (topic, eventType, data) => {
558
549
  const subscribers = mockTopicSubscribers[topic] || [];
559
550
  const event = {
@@ -618,6 +609,9 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
618
609
  companyId: MOCK_COMPANY.id,
619
610
  externalId: null,
620
611
  status: "completed",
612
+ paymentState: "paid",
613
+ fulfillmentState: "fulfilled",
614
+ displayState: "Completed",
621
615
  customer: MOCK_CART.customer ? MOCK_CART.customer : null,
622
616
  summary: {
623
617
  total: totalNum,
@@ -660,6 +654,6 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
660
654
  MOCK_ORDERS.push(newOrder);
661
655
  resetMockCart();
662
656
  // Publish cart-created event after cart is reset (simulates new empty cart)
663
- mockPublishEvent('cart', 'cart-created', {});
657
+ mockPublishEvent("cart", "cart-created", {});
664
658
  return newOrder;
665
659
  };
@@ -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;