@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
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 = Record<string, unknown>;
65
64
  export interface CFDiscount {
66
65
  value: number;
67
66
  label?: string;
@@ -97,8 +96,26 @@ export interface CFTax {
97
96
  amount: number;
98
97
  taxTableName: string;
99
98
  taxTableId: string;
99
+ /** Whether this tax compounds on top of others. */
100
+ compounding?: boolean;
101
+ /** Application order; lower numbers apply first. */
102
+ priority?: number;
103
+ }
104
+ /** Per-rate tax entry on a cart fee (gst, hst, etc.). Same shape as `CFTax`; declared
105
+ * separately to match the host's distinct `CartFeeTaxEntry` type. */
106
+ export interface CFCartFeeTaxEntry {
107
+ id: string;
108
+ name: string;
109
+ percentage: number;
110
+ amount: number;
111
+ taxTableId: string;
112
+ taxTableName: string;
113
+ compounding?: boolean;
114
+ priority?: number;
100
115
  }
101
116
  export interface CFInventory {
117
+ /** Warehouse identifier the stock is held at. */
118
+ warehouse: string;
102
119
  outletId: string;
103
120
  stock?: number | null;
104
121
  _id?: string;
@@ -116,6 +133,29 @@ export interface CFOrderNote {
116
133
  addedByUser?: string;
117
134
  dateCreated?: string;
118
135
  }
136
+ export interface CFAttributeOption {
137
+ name: string;
138
+ order?: number;
139
+ }
140
+ export interface CFAttribute {
141
+ _id: string;
142
+ companyId?: string;
143
+ optionName: string;
144
+ sortingOrder: number;
145
+ options: CFAttributeOption[];
146
+ }
147
+ export interface CFTransaction {
148
+ _id: string;
149
+ companyId?: string;
150
+ orderId?: string;
151
+ amount: number;
152
+ currency?: string;
153
+ status?: string;
154
+ paymentMethod?: string;
155
+ createdAt?: string;
156
+ updatedAt?: string;
157
+ [key: string]: unknown;
158
+ }
119
159
  export interface CFCategory {
120
160
  _id: string;
121
161
  companyId: string;
@@ -132,7 +172,8 @@ export interface CFProductVariant {
132
172
  barcode?: string;
133
173
  costPrice?: number;
134
174
  manageStock: boolean;
135
- externalId?: string;
175
+ /** External variant identifier — required by host. */
176
+ externalId: string;
136
177
  inventory?: CFInventory[];
137
178
  allowBackorder?: boolean;
138
179
  images?: string[];
@@ -145,11 +186,15 @@ export interface CFProductVariant {
145
186
  value: string;
146
187
  }[];
147
188
  _id: string;
189
+ isDeleted?: boolean;
190
+ currency?: CurrencyCode;
191
+ minorUnits?: number;
148
192
  }
149
193
  export interface CFProduct {
150
194
  _id: string;
151
195
  companyId?: string;
152
- externalId?: string;
196
+ /** External product identifier — required by host. */
197
+ externalId: string;
153
198
  taxTable: string;
154
199
  name: string;
155
200
  description?: string;
@@ -169,8 +214,12 @@ export interface CFProduct {
169
214
  minorUnits: number;
170
215
  minPrice?: number;
171
216
  maxPrice?: number;
217
+ /** Catalog-level price (rare; usually only on simple products). */
218
+ price?: number;
172
219
  status?: string;
173
220
  isDeleted?: boolean;
221
+ createdAt?: string;
222
+ updatedAt?: string;
174
223
  }
175
224
  export interface CFActiveProduct extends CFActiveEntity {
176
225
  id: string;
@@ -196,10 +245,17 @@ export interface CFActiveProduct extends CFActiveEntity {
196
245
  isUnlimited?: boolean;
197
246
  attributes?: string;
198
247
  localQuantity?: number;
248
+ /** Mongo-style id when the active product retains the catalog `_id`. */
249
+ _id?: string;
250
+ productType?: CFProductType;
251
+ /** Currency code for the line price. */
252
+ currency?: CurrencyCode;
253
+ /** Number of minor units (decimal places) for the line currency. */
254
+ minorUnits?: number;
199
255
  }
200
256
  export interface CFCustomer {
201
257
  _id: string;
202
- companyId: any;
258
+ companyId: string;
203
259
  externalId?: string;
204
260
  email: string;
205
261
  firstName: string;
@@ -246,6 +302,8 @@ export interface CFCartFeeItem {
246
302
  taxTableId?: string;
247
303
  tax?: number;
248
304
  taxName: string;
305
+ /** Per-rate breakdown so summary/refund views can show correct amounts per rate. */
306
+ taxes?: CFCartFeeTaxEntry[];
249
307
  }
250
308
  export interface CFTipPayment {
251
309
  amount: number;
@@ -269,6 +327,8 @@ export interface CFPaymentMethod {
269
327
  tip?: CFTipPayment | null;
270
328
  cashRounding?: number;
271
329
  emv?: string | null;
330
+ /** Processor fee charged on this payment. */
331
+ processorFee?: number | null;
272
332
  }
273
333
  export interface CFPosDataItem {
274
334
  outlet: string;
@@ -358,8 +418,7 @@ export interface CFRefundedLineItem {
358
418
  images?: string[];
359
419
  fee: CFFeeLineItem;
360
420
  }
361
- export interface CFRefundedCustomSale extends CFCustomSale {
362
- }
421
+ export type CFRefundedCustomSale = CFCustomSale;
363
422
  export interface CFRefundItem {
364
423
  lineItems: CFRefundedLineItem[];
365
424
  customSales: CFRefundedCustomSale[];
@@ -382,6 +441,12 @@ export interface CFOrder {
382
441
  companyId: string;
383
442
  externalId: string | null;
384
443
  status: string;
444
+ /** Financial state (state machine). Undefined on legacy orders pre-backfill. */
445
+ paymentState?: string;
446
+ /** Operational state (state machine). Undefined on legacy orders pre-backfill. */
447
+ fulfillmentState?: string;
448
+ /** Computed display label from paymentState + fulfillmentState matrix. */
449
+ displayState?: string;
385
450
  customer: Partial<CFActiveCustomer | null>;
386
451
  customerNote?: string;
387
452
  summary: CFSummary;
@@ -404,6 +469,10 @@ export interface CFOrder {
404
469
  refund?: CFRefundItem[];
405
470
  balance: number;
406
471
  signature?: string | null;
472
+ /** ISO timestamp at which a parked order's stock reservation expires. */
473
+ parkExpiryDate?: string;
474
+ /** Park stock-reduction policy (host-defined string flag). */
475
+ parkReduceStock?: string;
407
476
  }
408
477
  export interface CFActiveUserRole {
409
478
  id?: string;
@@ -435,7 +504,7 @@ export interface CFActiveUser extends CFActiveEntity {
435
504
  _id: string;
436
505
  }[];
437
506
  type?: CFUserTypes;
438
- companies?: any;
507
+ companies?: unknown;
439
508
  }
440
509
  export interface CFActiveOutlet extends CFActiveEntity {
441
510
  address: string;
@@ -465,6 +534,12 @@ export interface CFActiveStation {
465
534
  createdAt?: string;
466
535
  updatedAt?: string;
467
536
  stripeTerminalId?: string;
537
+ /** Stripe serial-reader identifier when paired. */
538
+ serialReaderId?: string;
539
+ /** User id of whoever enrolled the station. */
540
+ enrolledBy?: string;
541
+ /** Whether the station is virtual (no physical terminal). */
542
+ isVirtual?: boolean;
468
543
  }
469
544
  /** Cash register session (station session), aligned with Render `Session`. */
470
545
  export interface CFSession {
@@ -515,8 +590,8 @@ export interface CFActiveCustomSales {
515
590
  taxTableId?: string;
516
591
  quantity: number;
517
592
  price: number;
518
- discount?: any;
519
- fee?: any;
593
+ discount?: Record<string, unknown>;
594
+ fee?: Record<string, unknown>;
520
595
  }
521
596
  /** Non-revenue cart line (e.g. gift card load) — aligned with Render `NonRevenueItem.externalId` (order line id). */
522
597
  export interface CFNonRevenueItem {
@@ -530,6 +605,8 @@ export interface CFNonRevenueItem {
530
605
  }
531
606
  export interface CFActiveCart extends CFActiveEntity {
532
607
  tax?: number;
608
+ /** Per-rate tax breakdown for the cart. Same shape as line item taxes. */
609
+ taxes?: CFTax[];
533
610
  total: number;
534
611
  subtotal: number;
535
612
  discount?: CFDiscount;
@@ -545,12 +622,28 @@ export interface CFActiveCart extends CFActiveEntity {
545
622
  cartTotal?: number;
546
623
  orderTotal?: number;
547
624
  orderId?: string;
625
+ /** Currency code for amounts on this cart. */
626
+ currency?: CurrencyCode;
627
+ /** Number of minor units (decimal places) for the cart's currency. */
628
+ minorUnits?: number;
629
+ }
630
+ /** A parked order. Extends `CFActiveCart` with parking-specific fields. */
631
+ export interface CFActivePark extends CFActiveCart {
632
+ /** Required on parked orders (overrides the optional `orderId` on the parent). */
633
+ orderId: string;
634
+ receiptId: string;
635
+ servedBy: string | {
636
+ _id?: string;
637
+ firstName: string;
638
+ lastName: string;
639
+ } | undefined;
640
+ createdAt: string | number;
548
641
  }
549
642
  export interface CFActiveCompany extends CFActiveEntity {
550
643
  id?: string;
551
644
  name?: string;
552
645
  logo?: string;
553
- settings?: any;
646
+ settings?: Record<string, unknown>;
554
647
  }
555
648
  export type CFProjectName = "Render" | "Manage";
556
649
  export interface CFContextRender {
@@ -568,10 +661,16 @@ export interface CFContextRender {
568
661
  buildSourceId: string | null;
569
662
  buildIsPremium: boolean;
570
663
  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;
664
+ currency: string | null;
665
+ currencySymbol: string | null;
666
+ currencyPrefix: string | null;
667
+ currencySuffix: string | null;
668
+ thousandSeparator: string | null;
669
+ decimalSeparator: string | null;
670
+ user: Record<string, unknown> | null;
671
+ company: Omit<Record<string, unknown>, "settings"> | null;
672
+ station: Record<string, unknown> | null;
673
+ outlet: Record<string, unknown> | null;
575
674
  timestamp: string;
576
675
  }
577
676
  export interface CFOutletInfo {
@@ -591,11 +690,11 @@ export interface CFOutletInfo {
591
690
  country?: string;
592
691
  }
593
692
  export interface CFContextManage {
594
- user: any;
595
- company: any;
596
- menuItem?: any;
693
+ user: unknown;
694
+ company: unknown;
695
+ menuItem?: unknown;
597
696
  extensionId: string;
598
- outlets?: any[];
697
+ outlets?: unknown[];
599
698
  timestamp: string;
600
699
  }
601
700
  export type CFContext = CFContextRender;
@@ -1,23 +1,32 @@
1
- import { MOCK_CUSTOMERS } from "../../demo/database";
2
- export const mockAddCustomerNote = async (params) => {
1
+ import { MOCK_CUSTOMERS, mockPublishEvent } from "../../demo/database";
2
+ function generateNoteId() {
3
+ return `mock_note_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
4
+ }
5
+ export const mockAddCustomerNote = (params) => {
3
6
  console.log("[Mock] addCustomerNote called", params);
4
- if (params?.customerId && params.note) {
5
- const customer = MOCK_CUSTOMERS.find(c => c._id === params.customerId);
6
- if (customer) {
7
- if (!customer.notes) {
8
- customer.notes = [];
9
- }
10
- customer.notes.push({
11
- _id: `mock_note_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
12
- message: params.note,
13
- createdAt: new Date().toISOString()
14
- });
15
- }
7
+ if (!params?.note) {
8
+ throw new Error("addCustomerNote: note is required");
16
9
  }
17
- return {
10
+ const target = params.customerId ? MOCK_CUSTOMERS.find(c => c._id === params.customerId) : MOCK_CUSTOMERS[0];
11
+ if (!target) {
12
+ throw new Error("addCustomerNote: no target customer (provide customerId or seed at least one mock customer)");
13
+ }
14
+ const noteId = generateNoteId();
15
+ const note = {
16
+ _id: noteId,
17
+ message: params.note,
18
+ createdAt: new Date().toISOString()
19
+ };
20
+ target.notes = [...(target.notes || []), note];
21
+ mockPublishEvent("customers", "customer-note-added", {
22
+ customer: target,
23
+ note
24
+ });
25
+ return Promise.resolve({
18
26
  success: true,
19
- customerId: params?.customerId || "",
20
- note: params?.note || "",
27
+ customerId: target._id,
28
+ noteId,
29
+ note: params.note,
21
30
  timestamp: new Date().toISOString()
22
- };
31
+ });
23
32
  };
@@ -1,11 +1,12 @@
1
1
  export interface AddCustomerNoteParams {
2
- customerId: string;
3
2
  note: string;
3
+ customerId?: string;
4
4
  }
5
5
  export interface AddCustomerNoteResponse {
6
6
  success: boolean;
7
7
  customerId: string;
8
+ noteId: string;
8
9
  note: string;
9
10
  timestamp: string;
10
11
  }
11
- export type AddCustomerNote = (params?: AddCustomerNoteParams) => Promise<AddCustomerNoteResponse>;
12
+ export type AddCustomerNote = (params: AddCustomerNoteParams) => Promise<AddCustomerNoteResponse>;
@@ -1,10 +1,13 @@
1
1
  import { CFProductType, CurrencyCode } from "../../CommonTypes";
2
+ // eslint-disable-next-line @typescript-eslint/require-await
2
3
  export const mockAddProduct = async (params) => {
3
4
  console.log("[Mock] addProduct called", params);
4
5
  const hasVariants = params.variants && params.variants.length > 0;
6
+ const productId = "mock_product_" + Date.now();
5
7
  return {
6
8
  product: {
7
- _id: "mock_product_" + Date.now(),
9
+ _id: productId,
10
+ externalId: `ext_${productId}`,
8
11
  currency: CurrencyCode.USD,
9
12
  minorUnits: 2,
10
13
  name: params.name,
@@ -20,16 +23,19 @@ export const mockAddProduct = async (params) => {
20
23
  maxPrice: params.price || 0,
21
24
  variants: hasVariants
22
25
  ? params.variants.map((v, i) => ({ ...v, _id: `mock_variant_${Date.now()}_${i}` }))
23
- : [{
26
+ : [
27
+ {
24
28
  _id: `mock_variant_${Date.now()}_0`,
29
+ externalId: `ext_variant_${Date.now()}_0`,
25
30
  sku: params.sku || "",
26
31
  price: params.price || 0,
27
32
  salePrice: 0,
28
33
  isOnSale: false,
29
34
  manageStock: params.manageStock || false,
30
- attributes: [],
31
- }],
35
+ attributes: []
36
+ }
37
+ ]
32
38
  },
33
- timestamp: new Date().toISOString(),
39
+ timestamp: new Date().toISOString()
34
40
  };
35
41
  };
@@ -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,9 +1,11 @@
1
1
  import { CFProductType, CurrencyCode } from "../../CommonTypes";
2
+ // eslint-disable-next-line @typescript-eslint/require-await
2
3
  export const mockEditProduct = async (params) => {
3
4
  console.log("[Mock] editProduct called", params);
4
5
  return {
5
6
  product: {
6
7
  _id: params.productId,
8
+ externalId: `ext_${params.productId}`,
7
9
  currency: CurrencyCode.USD,
8
10
  minorUnits: 2,
9
11
  name: params.changes.name || "Updated Product",
@@ -14,8 +16,8 @@ export const mockEditProduct = async (params) => {
14
16
  status: params.changes.status || "active",
15
17
  productType: CFProductType.SIMPLE,
16
18
  attributes: [],
17
- variants: [],
19
+ variants: []
18
20
  },
19
- timestamp: new Date().toISOString(),
21
+ timestamp: new Date().toISOString()
20
22
  };
21
23
  };
@@ -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>;