@final-commerce/command-frame 0.1.73 → 0.1.75

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 (100) hide show
  1. package/README.md +50 -28
  2. package/dist/CommonTypes.d.ts +124 -7
  3. package/dist/CommonTypes.js +112 -0
  4. package/dist/actions/add-cart-discount/mock.js +6 -3
  5. package/dist/actions/add-cart-fee/mock.js +6 -6
  6. package/dist/actions/add-custom-sale/mock.js +7 -2
  7. package/dist/actions/add-order-note/mock.js +3 -1
  8. package/dist/actions/add-product-discount/mock.js +10 -3
  9. package/dist/actions/add-product-fee/mock.js +10 -1
  10. package/dist/actions/apply-transition/action.d.ts +2 -0
  11. package/dist/actions/apply-transition/action.js +4 -0
  12. package/dist/actions/apply-transition/mock.d.ts +6 -0
  13. package/dist/actions/apply-transition/mock.js +33 -0
  14. package/dist/actions/apply-transition/types.d.ts +17 -0
  15. package/dist/actions/apply-transition/types.js +1 -0
  16. package/dist/actions/can-transition/action.d.ts +2 -0
  17. package/dist/actions/can-transition/action.js +4 -0
  18. package/dist/actions/can-transition/mock.d.ts +6 -0
  19. package/dist/actions/can-transition/mock.js +28 -0
  20. package/dist/actions/can-transition/types.d.ts +11 -0
  21. package/dist/actions/can-transition/types.js +1 -0
  22. package/dist/actions/cash-payment/mock.js +12 -18
  23. package/dist/actions/cash-payment/types.d.ts +5 -0
  24. package/dist/actions/delete-parked-order/mock.js +3 -1
  25. package/dist/actions/delete-parked-order/types.d.ts +3 -0
  26. package/dist/actions/extension-payment/types.d.ts +5 -0
  27. package/dist/actions/get-available-transitions/action.d.ts +2 -0
  28. package/dist/actions/get-available-transitions/action.js +4 -0
  29. package/dist/actions/get-available-transitions/mock.d.ts +6 -0
  30. package/dist/actions/get-available-transitions/mock.js +26 -0
  31. package/dist/actions/get-available-transitions/types.d.ts +9 -0
  32. package/dist/actions/get-available-transitions/types.js +1 -0
  33. package/dist/actions/get-context/mock.js +1 -1
  34. package/dist/actions/get-orders/mock.js +3 -3
  35. package/dist/actions/initiate-refund/types.d.ts +3 -0
  36. package/dist/actions/open-extension-overlay/mock.d.ts +2 -0
  37. package/dist/actions/open-extension-overlay/mock.js +1 -0
  38. package/dist/actions/open-extension-overlay/types.d.ts +9 -0
  39. package/dist/actions/open-extension-overlay/types.js +1 -0
  40. package/dist/actions/park-order/mock.js +4 -1
  41. package/dist/actions/park-order/types.d.ts +3 -0
  42. package/dist/actions/partial-payment/mock.js +21 -3
  43. package/dist/actions/partial-payment/types.d.ts +5 -0
  44. package/dist/actions/process-partial-refund/types.d.ts +3 -0
  45. package/dist/actions/remove-custom-sale/mock.d.ts +2 -0
  46. package/dist/actions/remove-custom-sale/mock.js +29 -0
  47. package/dist/actions/remove-customer-from-cart/mock.js +3 -1
  48. package/dist/actions/remove-order-note/mock.d.ts +2 -0
  49. package/dist/actions/remove-order-note/mock.js +12 -0
  50. package/dist/actions/remove-product-discount/mock.d.ts +2 -0
  51. package/dist/actions/remove-product-discount/mock.js +17 -0
  52. package/dist/actions/remove-product-fee/mock.d.ts +2 -0
  53. package/dist/actions/remove-product-fee/mock.js +17 -0
  54. package/dist/actions/remove-product-from-cart/mock.js +5 -5
  55. package/dist/actions/resolve-extension-overlay/mock.d.ts +2 -0
  56. package/dist/actions/resolve-extension-overlay/mock.js +1 -0
  57. package/dist/actions/resolve-extension-overlay/types.d.ts +9 -0
  58. package/dist/actions/resolve-extension-overlay/types.js +1 -0
  59. package/dist/actions/resume-parked-order/mock.js +4 -1
  60. package/dist/actions/resume-parked-order/types.d.ts +3 -0
  61. package/dist/actions/tap-to-pay-payment/mock.js +5 -3
  62. package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
  63. package/dist/actions/terminal-payment/mock.js +4 -5
  64. package/dist/actions/terminal-payment/types.d.ts +5 -0
  65. package/dist/actions/update-cart-item-quantity/mock.js +9 -9
  66. package/dist/actions/vendara-payment/types.d.ts +5 -0
  67. package/dist/common-types/index.d.ts +1 -0
  68. package/dist/common-types/index.js +1 -0
  69. package/dist/common-types/order-state.d.ts +32 -0
  70. package/dist/common-types/order-state.js +6 -0
  71. package/dist/common-types/state-fragment.d.ts +77 -0
  72. package/dist/common-types/state-fragment.js +10 -0
  73. package/dist/demo/database.d.ts +10 -0
  74. package/dist/demo/database.js +169 -3
  75. package/dist/fragments/index.d.ts +1 -0
  76. package/dist/fragments/index.js +1 -0
  77. package/dist/fragments/preorder-no-deposit.d.ts +26 -0
  78. package/dist/fragments/preorder-no-deposit.js +166 -0
  79. package/dist/index.d.ts +14 -4
  80. package/dist/index.js +13 -3
  81. package/dist/interceptors/index.d.ts +21 -0
  82. package/dist/interceptors/index.js +53 -0
  83. package/dist/interceptors/types.d.ts +26 -0
  84. package/dist/interceptors/types.js +1 -0
  85. package/dist/projects/render/mocks.js +16 -4
  86. package/dist/projects/render/types.d.ts +7 -1
  87. package/dist/pubsub/topics/orders/index.js +10 -0
  88. package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
  89. package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
  90. package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
  91. package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
  92. package/dist/pubsub/topics/orders/types.d.ts +6 -2
  93. package/dist/pubsub/topics/orders/types.js +2 -0
  94. package/package.json +1 -1
  95. package/dist/actions/extension-refund/constants.d.ts +0 -2
  96. package/dist/actions/extension-refund/constants.js +0 -2
  97. package/dist/actions/extension-refund/extension-refund-listener.d.ts +0 -11
  98. package/dist/actions/extension-refund/extension-refund-listener.js +0 -60
  99. package/dist/actions/extension-refund/types.d.ts +0 -32
  100. package/dist/actions/extension-refund/types.js +0 -5
@@ -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
@@ -107,6 +107,9 @@ export declare const command: {
107
107
  readonly uploadMedia: import("./actions/upload-media/types").UploadMedia;
108
108
  readonly getTaxTables: import("./actions/get-tax-tables/types").GetTaxTables;
109
109
  readonly getBranding: import("./actions/get-branding/types").GetBranding;
110
+ readonly canTransition: import("./actions/can-transition/types").CanTransition;
111
+ readonly getAvailableTransitions: import("./actions/get-available-transitions/types").GetAvailableTransitions;
112
+ readonly applyTransition: import("./actions/apply-transition/types").ApplyTransition;
110
113
  };
111
114
  export type { ExampleFunction, ExampleFunctionParams, ExampleFunctionResponse } from "./actions/example-function/types";
112
115
  export type { GenerateAPIKey, GenerateAPIKeyParams, GenerateAPIKeyResponse } from "./actions/generate-api-key/types";
@@ -135,6 +138,8 @@ export type { CalculateRefundTotal, CalculateRefundTotalParams, CalculateRefundT
135
138
  export type { GetRemainingRefundableQuantities, GetRemainingRefundableQuantitiesParams, GetRemainingRefundableQuantitiesResponse } from "./actions/get-remaining-refundable-quantities/types";
136
139
  export type { ProcessPartialRefund, ProcessPartialRefundParams, ProcessPartialRefundResponse } from "./actions/process-partial-refund/types";
137
140
  export type { InitiateRefund, InitiateRefundParams, InitiateRefundResponse } from "./actions/initiate-refund/types";
141
+ export type { OpenExtensionOverlay, OpenExtensionOverlayParams, OpenExtensionOverlayResponse } from "./actions/open-extension-overlay/types";
142
+ export type { ResolveExtensionOverlay, ResolveExtensionOverlayParams, ResolveExtensionOverlayResponse } from "./actions/resolve-extension-overlay/types";
138
143
  export type { GetCurrentCart, GetCurrentCartResponse } from "./actions/get-current-cart/types";
139
144
  export type { AddProductDiscount, AddProductDiscountParams, AddProductDiscountResponse } from "./actions/add-product-discount/types";
140
145
  export type { AddProductToCart, AddProductToCartParams, AddProductToCartResponse } from "./actions/add-product-to-cart/types";
@@ -163,9 +168,6 @@ export type { VendaraPayment, VendaraPaymentParams, VendaraPaymentResponse } fro
163
168
  export type { ExtensionPayment, ExtensionPaymentParams, ExtensionPaymentResponse } from "./actions/extension-payment/types";
164
169
  export type { RedeemPayment, RedeemPaymentParams, RedeemPaymentResponse } from "./actions/redeem-payment/types";
165
170
  export type { IntegrationPayment, IntegrationPaymentParams, IntegrationPaymentResponse, IntegrationEmvData } from "./actions/integration-payment/types";
166
- export { EXTENSION_REFUND_REQUEST_ACTION } from "./actions/extension-refund/constants";
167
- export { installExtensionRefundListener } from "./actions/extension-refund/extension-refund-listener";
168
- export type { ExtensionRefundParams, ExtensionRefundResponse } from "./actions/extension-refund/types";
169
171
  export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse } from "./actions/add-customer-note/types";
170
172
  export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse } from "./actions/remove-customer-note/types";
171
173
  export type { RemoveCustomerFromCart, RemoveCustomerFromCartResponse } from "./actions/remove-customer-from-cart/types";
@@ -220,6 +222,8 @@ export type { TopicDefinition, TopicEvent, TopicEventType, TopicSubscriptionCall
220
222
  export type { TopicEventPayloadMap } from "./pubsub/topics/types";
221
223
  export { hooks } from "./hooks";
222
224
  export type { HookFunction, HookRegisterOptions } from "./hooks";
225
+ export { interceptors } from "./interceptors";
226
+ export type { InterceptorFunction, InterceptorPoint, InterceptorRegisterOptions, InterceptorReturn, InterceptorOverlayContext, InterceptorHostCommands } from "./interceptors";
223
227
  export { customersTopic } from "./pubsub/topics/customers";
224
228
  export { ordersTopic } from "./pubsub/topics/orders";
225
229
  export { refundsTopic } from "./pubsub/topics/refunds";
@@ -238,7 +242,7 @@ export { transactionsTopic } from "./pubsub/topics/transactions";
238
242
  export { categoriesTopic } from "./pubsub/topics/categories";
239
243
  export { attributesTopic } from "./pubsub/topics/attributes";
240
244
  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";
241
- export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
245
+ export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrderStateTransitionCompletedPayload, OrderStateTransitionBlockedPayload, OrderStateTransitionCompletedEvent, OrderStateTransitionBlockedEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
242
246
  export type { RefundCreatedPayload, RefundUpdatedPayload, RefundActiveSetPayload, RefundActiveGetPayload, RefundCreatedEvent, RefundUpdatedEvent, RefundActiveSetEvent, RefundActiveGetEvent, RefundsEventType, RefundsEventPayload } from "./pubsub/topics/refunds/types";
243
247
  export type { ProductCreatedPayload, ProductUpdatedPayload, ProductSetActivePayload, ProductGetActivePayload, ProductCreatedEvent, ProductUpdatedEvent, ProductSetActiveEvent, ProductGetActiveEvent, ProductsEventType, ProductsEventPayload } from "./pubsub/topics/products/types";
244
248
  export type { OutletActiveSetPayload, OutletActiveGetPayload, OutletActiveSetEvent, OutletActiveGetEvent, OutletEventType, OutletEventPayload } from "./pubsub/topics/outlet/types";
@@ -269,3 +273,9 @@ export type { GetSecretVal, GetSecretValParams, GetSecretValResponse } from "./a
269
273
  export type { SetSecretVal, SetSecretValParams, SetSecretValResponse } from "./actions/set-secret-val/types";
270
274
  export type { GetUsers, GetUsersParams, GetUsersResponse } from "./actions/get-users/types";
271
275
  export type { GetRoles, GetRolesParams, GetRolesResponse } from "./actions/get-roles/types";
276
+ export type { CFBlockedBy, CFStatePair, CFTransitionResult, CFFailedCondition, CFConditionStatus, CFAvailableTransition } from "./common-types/order-state";
277
+ export type { CFConditionOperator, CFCondition, CFConditionGroup, CFTransitionConditionSet, CFPaymentTransitionPath, CFFulfillmentTransitionPath, CFCrossAxisRule, CFDisplayStateRule, CFStateConfigFragment } from "./common-types/state-fragment";
278
+ export type { CanTransition, CanTransitionParams, CanTransitionResponse } from "./actions/can-transition/types";
279
+ export type { GetAvailableTransitions, GetAvailableTransitionsParams, GetAvailableTransitionsResponse } from "./actions/get-available-transitions/types";
280
+ export type { ApplyTransition, ApplyTransitionParams, ApplyTransitionResponse } from "./actions/apply-transition/types";
281
+ export { preorderNoDepositFragment } from "./fragments";
package/dist/index.js CHANGED
@@ -103,6 +103,10 @@ import { getSecretsKeys } from "./actions/get-secrets-keys/action";
103
103
  import { getSecretVal } from "./actions/get-secret-val/action";
104
104
  import { setSecretVal } from "./actions/set-secret-val/action";
105
105
  import { generateAPIKey } from "./actions/generate-api-key/action";
106
+ // State Machine Query Actions
107
+ import { canTransition } from "./actions/can-transition/action";
108
+ import { getAvailableTransitions } from "./actions/get-available-transitions/action";
109
+ import { applyTransition } from "./actions/apply-transition/action";
106
110
  // Product CRUD Actions
107
111
  import { addProduct } from "./actions/add-product/action";
108
112
  import { editProduct } from "./actions/edit-product/action";
@@ -245,10 +249,12 @@ export const command = {
245
249
  getMedia,
246
250
  uploadMedia,
247
251
  getTaxTables,
248
- getBranding
252
+ getBranding,
253
+ // State Machine Queries
254
+ canTransition,
255
+ getAvailableTransitions,
256
+ applyTransition
249
257
  };
250
- export { EXTENSION_REFUND_REQUEST_ACTION } from "./actions/extension-refund/constants";
251
- export { installExtensionRefundListener } from "./actions/extension-refund/extension-refund-listener";
252
258
  // Export Common Types
253
259
  export * from "./CommonTypes";
254
260
  // Mock database override (standalone / extension dev)
@@ -265,6 +271,8 @@ export { commandFrameClient, CommandFrameClient } from "./client";
265
271
  export { topics } from "./pubsub/topics";
266
272
  // Export Hooks (extension iframe API for session-scoped event callbacks)
267
273
  export { hooks } from "./hooks";
274
+ // Export Interceptors (extension iframe API for gating host flows)
275
+ export { interceptors } from "./interceptors";
268
276
  // Export Pub/Sub Topics
269
277
  export { customersTopic } from "./pubsub/topics/customers";
270
278
  export { ordersTopic } from "./pubsub/topics/orders";
@@ -285,3 +293,5 @@ export { categoriesTopic } from "./pubsub/topics/categories";
285
293
  export { attributesTopic } from "./pubsub/topics/attributes";
286
294
  // Export Custom Tables Types
287
295
  export { AttributeType } from "./common-types/attribute-type";
296
+ // State Machine Fragments
297
+ export { preorderNoDepositFragment } from "./fragments";
@@ -0,0 +1,21 @@
1
+ import type { InterceptorFunction, InterceptorPoint, InterceptorRegisterOptions, InterceptorOverlayContext } from "./types";
2
+ /** Register a session-scoped interceptor for a named point. Callback is serialized to the host. */
3
+ declare function register(point: InterceptorPoint, callback: InterceptorFunction, options: InterceptorRegisterOptions): string;
4
+ declare function unregister(interceptorId: string): void;
5
+ /** Register a handler invoked when this iframe is opened as an overlay by the host. */
6
+ declare function onOverlay(handler: (ctx: InterceptorOverlayContext) => void): void;
7
+ /** Continue the gated flow, merging `data` into the flow payload. */
8
+ declare function resolve(data: Record<string, any>): Promise<void>;
9
+ /** Continue the gated flow with no data. */
10
+ declare function proceed(): Promise<void>;
11
+ /** Stop the gated flow. */
12
+ declare function cancel(): Promise<void>;
13
+ export declare const interceptors: {
14
+ register: typeof register;
15
+ unregister: typeof unregister;
16
+ onOverlay: typeof onOverlay;
17
+ resolve: typeof resolve;
18
+ proceed: typeof proceed;
19
+ cancel: typeof cancel;
20
+ };
21
+ export type { InterceptorFunction, InterceptorPoint, InterceptorRegisterOptions, InterceptorReturn, InterceptorOverlayContext, InterceptorHostCommands } from "./types";
@@ -0,0 +1,53 @@
1
+ import { commandFrameClient } from "../client";
2
+ const DEFAULT_ORIGIN = "*";
3
+ function getOrigin() {
4
+ return typeof window !== "undefined" ? window.__COMMAND_FRAME_ORIGIN__ ?? DEFAULT_ORIGIN : DEFAULT_ORIGIN;
5
+ }
6
+ let overlayId = null;
7
+ let overlayContext = null;
8
+ let overlayHandler = null;
9
+ if (typeof window !== "undefined") {
10
+ window.addEventListener("message", (event) => {
11
+ const data = event.data;
12
+ if (data && data.type === "interceptor-overlay-init" && typeof data.overlayId === "string") {
13
+ overlayId = data.overlayId;
14
+ overlayContext = { point: data.point, payload: data.payload };
15
+ if (overlayHandler)
16
+ overlayHandler(overlayContext);
17
+ }
18
+ });
19
+ }
20
+ /** Register a session-scoped interceptor for a named point. Callback is serialized to the host. */
21
+ function register(point, callback, options) {
22
+ const interceptorId = options.interceptorId;
23
+ const functionBody = callback.toString();
24
+ if (typeof window !== "undefined" && window.top && window.top !== window) {
25
+ window.top.postMessage({ type: "interceptor-register", point, functionBody, interceptorId, timeoutMs: options.timeoutMs }, getOrigin());
26
+ }
27
+ return interceptorId;
28
+ }
29
+ function unregister(interceptorId) {
30
+ if (typeof window !== "undefined" && window.top && window.top !== window) {
31
+ window.top.postMessage({ type: "interceptor-unregister", interceptorId }, getOrigin());
32
+ }
33
+ }
34
+ /** Register a handler invoked when this iframe is opened as an overlay by the host. */
35
+ function onOverlay(handler) {
36
+ overlayHandler = handler;
37
+ if (overlayContext)
38
+ handler(overlayContext);
39
+ }
40
+ async function report(result) {
41
+ if (!overlayId) {
42
+ console.warn("[interceptors] report called outside an overlay context — ignored");
43
+ return;
44
+ }
45
+ await commandFrameClient.call("resolveExtensionOverlay", { overlayId, result });
46
+ }
47
+ /** Continue the gated flow, merging `data` into the flow payload. */
48
+ function resolve(data) { return report(data); }
49
+ /** Continue the gated flow with no data. */
50
+ function proceed() { return report(true); }
51
+ /** Stop the gated flow. */
52
+ function cancel() { return report(false); }
53
+ export const interceptors = { register, unregister, onOverlay, resolve, proceed, cancel };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Interceptors let an installed extension gate a host flow at a named point.
3
+ * Registration mirrors hooks: the callback is serialized and reconstructed on the host,
4
+ * so it must be self-contained (use only its payload arg and the injected host commands).
5
+ */
6
+ export type InterceptorPoint = "refund_start";
7
+ /** false = stop the flow; true = continue, no data; object = continue and merge into the flow payload. */
8
+ export type InterceptorReturn<T extends Record<string, any> = Record<string, any>> = boolean | T;
9
+ export interface InterceptorHostCommands {
10
+ openExtensionOverlay: (params: {
11
+ point: InterceptorPoint;
12
+ payload?: any;
13
+ }) => Promise<InterceptorReturn>;
14
+ [command: string]: (params?: any) => Promise<any>;
15
+ }
16
+ export type InterceptorFunction = (payload: any, cmds: InterceptorHostCommands, callCommand: (action: string, params?: any) => Promise<any>) => InterceptorReturn | Promise<InterceptorReturn>;
17
+ export interface InterceptorRegisterOptions {
18
+ /** Stable id; re-registering with the same id replaces the previous one. */
19
+ interceptorId: string;
20
+ /** Per-interceptor timeout override (ms). */
21
+ timeoutMs?: number;
22
+ }
23
+ export interface InterceptorOverlayContext {
24
+ point: InterceptorPoint;
25
+ payload: any;
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -2,13 +2,17 @@ import { mockAddCartDiscount } from "../../actions/add-cart-discount/mock";
2
2
  import { mockAddCartFee } from "../../actions/add-cart-fee/mock";
3
3
  import { mockRemoveCartFee } from "../../actions/remove-cart-fee/mock";
4
4
  import { mockAddCustomSale } from "../../actions/add-custom-sale/mock";
5
+ import { mockRemoveCustomSale } from "../../actions/remove-custom-sale/mock";
5
6
  import { mockAddCustomer } from "../../actions/add-customer/mock";
6
7
  import { mockAddCustomerNote } from "../../actions/add-customer-note/mock";
7
8
  import { mockRemoveCustomerNote } from "../../actions/remove-customer-note/mock";
8
9
  import { mockEditCustomer } from "../../actions/edit-customer/mock";
9
10
  import { mockAddOrderNote } from "../../actions/add-order-note/mock";
11
+ import { mockRemoveOrderNote } from "../../actions/remove-order-note/mock";
10
12
  import { mockAddProductDiscount } from "../../actions/add-product-discount/mock";
11
13
  import { mockAddProductFee } from "../../actions/add-product-fee/mock";
14
+ import { mockRemoveProductDiscount } from "../../actions/remove-product-discount/mock";
15
+ import { mockRemoveProductFee } from "../../actions/remove-product-fee/mock";
12
16
  import { mockSetActiveProductFee } from "../../actions/set-active-product-fee/mock";
13
17
  import { mockSetActiveProductDiscount } from "../../actions/set-active-product-discount/mock";
14
18
  import { mockGetActiveProduct } from "../../actions/get-active-product/mock";
@@ -37,6 +41,8 @@ import { mockGetTaxTables } from "../../actions/get-tax-tables/mock";
37
41
  import { mockGetRemainingRefundableQuantities } from "../../actions/get-remaining-refundable-quantities/mock";
38
42
  import { mockGoToStationHome } from "../../actions/go-to-station-home/mock";
39
43
  import { mockInitiateRefund } from "../../actions/initiate-refund/mock";
44
+ import { mockOpenExtensionOverlay } from "../../actions/open-extension-overlay/mock";
45
+ import { mockResolveExtensionOverlay } from "../../actions/resolve-extension-overlay/mock";
40
46
  import { mockOpenCashDrawer } from "../../actions/open-cash-drawer/mock";
41
47
  import { mockParkOrder } from "../../actions/park-order/mock";
42
48
  import { mockPartialPayment } from "../../actions/partial-payment/mock";
@@ -86,6 +92,8 @@ import { mockSetActiveSession } from "../../actions/set-active-session/mock";
86
92
  import { mockGetActiveUser } from "../../actions/get-active-user/mock";
87
93
  import { mockSetActiveUser } from "../../actions/set-active-user/mock";
88
94
  import { mockSetActiveRefund } from "../../actions/set-active-refund/mock";
95
+ import { canTransitionMock } from "../../actions/can-transition/mock";
96
+ import { getAvailableTransitionsMock } from "../../actions/get-available-transitions/mock";
89
97
  import { mockGetSmartGridLayout } from "../../actions/get-smart-grid-layout/mock";
90
98
  import { mockSaveSmartGridLayout } from "../../actions/save-smart-grid-layout/mock";
91
99
  export const RENDER_MOCKS = {
@@ -126,6 +134,8 @@ export const RENDER_MOCKS = {
126
134
  getRemainingRefundableQuantities: mockGetRemainingRefundableQuantities,
127
135
  goToStationHome: mockGoToStationHome,
128
136
  initiateRefund: mockInitiateRefund,
137
+ openExtensionOverlay: mockOpenExtensionOverlay,
138
+ resolveExtensionOverlay: mockResolveExtensionOverlay,
129
139
  openCashDrawer: mockOpenCashDrawer,
130
140
  parkOrder: mockParkOrder,
131
141
  partialPayment: mockPartialPayment,
@@ -176,13 +186,15 @@ export const RENDER_MOCKS = {
176
186
  getActiveUser: mockGetActiveUser,
177
187
  setActiveUser: mockSetActiveUser,
178
188
  setActiveRefund: mockSetActiveRefund,
179
- removeProductDiscount: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
180
- removeProductFee: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
189
+ removeProductDiscount: mockRemoveProductDiscount,
190
+ removeProductFee: mockRemoveProductFee,
181
191
  removeProductNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
182
192
  removeCartFee: mockRemoveCartFee,
183
- removeOrderNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
184
- removeCustomSale: params => Promise.resolve({ success: true, id: params.id, timestamp: new Date().toISOString() }),
193
+ removeOrderNote: mockRemoveOrderNote,
194
+ removeCustomSale: mockRemoveCustomSale,
185
195
  removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() }),
196
+ canTransition: canTransitionMock,
197
+ getAvailableTransitions: getAvailableTransitionsMock,
186
198
  getSmartGridLayout: mockGetSmartGridLayout,
187
199
  saveSmartGridLayout: mockSaveSmartGridLayout
188
200
  };
@@ -1,4 +1,6 @@
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, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout } 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, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout } from "../../index";
2
+ import type { OpenExtensionOverlay } from "../../actions/open-extension-overlay/types";
3
+ import type { ResolveExtensionOverlay } from "../../actions/resolve-extension-overlay/types";
2
4
  export interface RenderProviderActions {
3
5
  exampleFunction: ExampleFunction;
4
6
  getProducts: GetProducts;
@@ -32,6 +34,8 @@ export interface RenderProviderActions {
32
34
  resumeParkedOrder: ResumeParkedOrder;
33
35
  deleteParkedOrder: DeleteParkedOrder;
34
36
  initiateRefund: InitiateRefund;
37
+ openExtensionOverlay: OpenExtensionOverlay;
38
+ resolveExtensionOverlay: ResolveExtensionOverlay;
35
39
  cashPayment: CashPayment;
36
40
  tapToPayPayment: TapToPayPayment;
37
41
  terminalPayment: TerminalPayment;
@@ -94,6 +98,8 @@ export interface RenderProviderActions {
94
98
  removeOrderNote: RemoveOrderNote;
95
99
  removeCustomSale: RemoveCustomSale;
96
100
  removeNonRevenueItem: RemoveNonRevenueItem;
101
+ canTransition: CanTransition;
102
+ getAvailableTransitions: GetAvailableTransitions;
97
103
  getSmartGridLayout: GetSmartGridLayout;
98
104
  saveSmartGridLayout: SaveSmartGridLayout;
99
105
  }
@@ -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.73",
3
+ "version": "0.1.75",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,2 +0,0 @@
1
- /** postMessage `action` for host → iframe extension refund requests. */
2
- export declare const EXTENSION_REFUND_REQUEST_ACTION = "extensionRefundRequest";
@@ -1,2 +0,0 @@
1
- /** postMessage `action` for host → iframe extension refund requests. */
2
- export const EXTENSION_REFUND_REQUEST_ACTION = "extensionRefundRequest";
@@ -1,11 +0,0 @@
1
- import type { ExtensionRefundParams, ExtensionRefundResponse } from "./types";
2
- /**
3
- * Install a message listener in the **extension iframe** so the host can request refunds.
4
- * Validates `event.source === window.top` before handling.
5
- *
6
- * Replies with {@link PostMessageResponse}, using `event.origin` as the target origin when posting back to the host.
7
- *
8
- * @param handler - Perform the extension-side refund (API call, etc.)
9
- * @returns Unsubscribe function
10
- */
11
- export declare function installExtensionRefundListener(handler: (params: ExtensionRefundParams) => Promise<ExtensionRefundResponse>): () => void;
@@ -1,60 +0,0 @@
1
- import { EXTENSION_REFUND_REQUEST_ACTION } from "./constants";
2
- function isRecord(value) {
3
- return typeof value === "object" && value !== null;
4
- }
5
- /**
6
- * Install a message listener in the **extension iframe** so the host can request refunds.
7
- * Validates `event.source === window.top` before handling.
8
- *
9
- * Replies with {@link PostMessageResponse}, using `event.origin` as the target origin when posting back to the host.
10
- *
11
- * @param handler - Perform the extension-side refund (API call, etc.)
12
- * @returns Unsubscribe function
13
- */
14
- export function installExtensionRefundListener(handler) {
15
- const onMessage = async (event) => {
16
- const raw = event.data;
17
- if (!isRecord(raw) || raw.action !== EXTENSION_REFUND_REQUEST_ACTION) {
18
- return;
19
- }
20
- const requestId = raw.requestId;
21
- if (typeof requestId !== "string") {
22
- return;
23
- }
24
- if (event.source !== window.top || !event.source) {
25
- return;
26
- }
27
- const source = event.source;
28
- const replyOrigin = typeof event.origin === "string" ? event.origin : "*";
29
- const params = raw.params;
30
- try {
31
- if (!params || typeof params.paymentType !== "string" || typeof params.amount !== "number" || typeof params.saleId !== "string") {
32
- const errPayload = {
33
- requestId,
34
- success: false,
35
- error: "Invalid extension refund params"
36
- };
37
- source.postMessage(errPayload, replyOrigin);
38
- return;
39
- }
40
- const result = await handler(params);
41
- const payload = {
42
- requestId,
43
- success: true,
44
- data: result
45
- };
46
- source.postMessage(payload, replyOrigin);
47
- }
48
- catch (e) {
49
- const message = e instanceof Error ? e.message : String(e);
50
- const errPayload = {
51
- requestId,
52
- success: false,
53
- error: message
54
- };
55
- source.postMessage(errPayload, replyOrigin);
56
- }
57
- };
58
- window.addEventListener("message", onMessage);
59
- return () => window.removeEventListener("message", onMessage);
60
- }