@final-commerce/command-frame 0.1.61 → 0.1.63

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.
package/README.md CHANGED
@@ -6,6 +6,8 @@ A TypeScript library for type-safe communication between iframes and their paren
6
6
 
7
7
  Command Frame provides a structured way to build integrations that run inside Final Commerce applications (like Render POS or Manage Dashboard). It handles the underlying `postMessage` communication while enforcing strict type safety for both the host application (Provider) and the embedded app (Client).
8
8
 
9
+ `RenderClient` and `ManageClient` extend `CommandFrameClient`: dynamic methods such as `getProducts()` map to `postMessage` actions named after the method (camelCase), with typed params and responses per project.
10
+
9
11
  The library provides three main capabilities:
10
12
 
11
13
  | Capability | Purpose | Scope |
@@ -15,6 +17,8 @@ The library provides three main capabilities:
15
17
  | **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
16
18
  | **Host → iframe refunds** | Render asks the extension to reverse redeem / gift-card payments before completing a POS refund | Parent `postMessage` + `requestId` (see below) |
17
19
 
20
+ Domain models (orders, cart, customers, products, and related types) are documented in **[Types reference](./src/types/README.md)**.
21
+
18
22
  ## Installation
19
23
 
20
24
  ### From npm (public registry)
@@ -75,7 +79,7 @@ const context = await client.getContext();
75
79
  The pub/sub system allows iframe extensions to subscribe to topics and receive real-time events published by the host (Render). Subscriptions are **page-scoped** -- they fire only while the iframe is mounted on the current page.
76
80
 
77
81
  - **[Pub/Sub Documentation](./src/pubsub/README.md)**
78
- - **Topics:** Cart (9), Customers (8), Orders (4), Payments (2), Products (4), Refunds (4), Print (3), Custom Tables (3), Outlet (2), Station (2), Session (2), Users (2).
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).
79
83
 
80
84
  ```typescript
81
85
  import { topics } from '@final-commerce/command-frame';
@@ -104,6 +104,7 @@ export interface CFInventory {
104
104
  _id?: string;
105
105
  }
106
106
  export interface CFCustomerNote {
107
+ _id?: string;
107
108
  createdAt: string;
108
109
  message: string;
109
110
  }
@@ -8,6 +8,7 @@ export const mockAddCustomerNote = async (params) => {
8
8
  customer.notes = [];
9
9
  }
10
10
  customer.notes.push({
11
+ _id: `mock_note_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
11
12
  message: params.note,
12
13
  createdAt: new Date().toISOString()
13
14
  });
@@ -0,0 +1,2 @@
1
+ import type { RemoveCartFee } from "./types";
2
+ export declare const mockRemoveCartFee: RemoveCartFee;
@@ -0,0 +1,35 @@
1
+ import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
+ function feeContributionToTotal(fee, subtotal) {
3
+ if (fee.isPercent) {
4
+ return subtotal * (fee.amount / 100);
5
+ }
6
+ return fee.amount;
7
+ }
8
+ export const mockRemoveCartFee = (params) => {
9
+ console.log("[Mock] removeCartFee called", params);
10
+ const { index } = params;
11
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) {
12
+ throw new Error("removeCartFee requires a non-negative integer index");
13
+ }
14
+ const fees = MOCK_CART.customFee;
15
+ if (!fees?.length) {
16
+ throw new Error("Cart has no fees to remove");
17
+ }
18
+ if (index >= fees.length) {
19
+ throw new Error(`Cart fee index ${index} is out of range (0–${fees.length - 1})`);
20
+ }
21
+ const removed = fees[index];
22
+ const delta = feeContributionToTotal(removed, MOCK_CART.subtotal);
23
+ fees.splice(index, 1);
24
+ if (fees.length === 0) {
25
+ MOCK_CART.customFee = undefined;
26
+ }
27
+ MOCK_CART.total -= delta;
28
+ MOCK_CART.amountToBeCharged = MOCK_CART.total;
29
+ MOCK_CART.remainingBalance = MOCK_CART.total;
30
+ mockPublishEvent("cart", "cart-fee-removed", { feeIndex: index });
31
+ return Promise.resolve({
32
+ success: true,
33
+ timestamp: new Date().toISOString()
34
+ });
35
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Remove customer note action
3
+ * Calls the removeCustomerNote action on the parent window
4
+ */
5
+ import type { RemoveCustomerNote } from "./types";
6
+ export declare const removeCustomerNote: RemoveCustomerNote;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove customer note action
3
+ * Calls the removeCustomerNote action on the parent window
4
+ */
5
+ import { commandFrameClient } from "../../client";
6
+ export const removeCustomerNote = async (params) => {
7
+ return await commandFrameClient.call("removeCustomerNote", params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { RemoveCustomerNote } from "./types";
2
+ export declare const mockRemoveCustomerNote: RemoveCustomerNote;
@@ -0,0 +1,13 @@
1
+ export const mockRemoveCustomerNote = async (params) => {
2
+ console.log("[Mock] removeCustomerNote called", params);
3
+ const noteId = params?.noteId ?? "";
4
+ const timestamp = new Date().toISOString();
5
+ if (!noteId) {
6
+ return { success: false, noteId, timestamp };
7
+ }
8
+ return {
9
+ success: true,
10
+ noteId,
11
+ timestamp
12
+ };
13
+ };
@@ -0,0 +1,9 @@
1
+ export interface RemoveCustomerNoteParams {
2
+ noteId: string;
3
+ }
4
+ export interface RemoveCustomerNoteResponse {
5
+ success: boolean;
6
+ noteId: string;
7
+ timestamp: string;
8
+ }
9
+ export type RemoveCustomerNote = (params?: RemoveCustomerNoteParams) => Promise<RemoveCustomerNoteResponse>;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -39,6 +39,7 @@ export declare const command: {
39
39
  readonly extensionPayment: import("./actions/extension-payment/types").ExtensionPayment;
40
40
  readonly redeemPayment: import("./actions/redeem-payment/types").RedeemPayment;
41
41
  readonly addCustomerNote: import("./actions/add-customer-note/types").AddCustomerNote;
42
+ readonly removeCustomerNote: import("./actions/remove-customer-note/types").RemoveCustomerNote;
42
43
  readonly removeCustomerFromCart: import("./actions/remove-customer-from-cart/types").RemoveCustomerFromCart;
43
44
  readonly removeCartDiscount: import("./actions/remove-cart-discount/types").RemoveCartDiscount;
44
45
  readonly goToStationHome: import("./actions/go-to-station-home/types").GoToStationHome;
@@ -160,6 +161,7 @@ export { EXTENSION_REFUND_REQUEST_ACTION } from "./actions/extension-refund/cons
160
161
  export { installExtensionRefundListener } from "./actions/extension-refund/extension-refund-listener";
161
162
  export type { ExtensionRefundParams, ExtensionRefundResponse } from "./actions/extension-refund/types";
162
163
  export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse } from "./actions/add-customer-note/types";
164
+ export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse } from "./actions/remove-customer-note/types";
163
165
  export type { RemoveCustomerFromCart, RemoveCustomerFromCartResponse } from "./actions/remove-customer-from-cart/types";
164
166
  export type { RemoveCartDiscount, RemoveCartDiscountResponse } from "./actions/remove-cart-discount/types";
165
167
  export type { GoToStationHome, GoToStationHomeResponse } from "./actions/go-to-station-home/types";
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ import { extensionPayment } from "./actions/extension-payment/action";
40
40
  import { redeemPayment } from "./actions/redeem-payment/action";
41
41
  // Customer Actions
42
42
  import { addCustomerNote } from "./actions/add-customer-note/action";
43
+ import { removeCustomerNote } from "./actions/remove-customer-note/action";
43
44
  import { removeCustomerFromCart } from "./actions/remove-customer-from-cart/action";
44
45
  import { removeCartDiscount } from "./actions/remove-cart-discount/action";
45
46
  // System Actions
@@ -161,6 +162,7 @@ export const command = {
161
162
  redeemPayment,
162
163
  // Customer Actions
163
164
  addCustomerNote,
165
+ removeCustomerNote,
164
166
  removeCustomerFromCart,
165
167
  removeCartDiscount,
166
168
  // System Actions
@@ -1,8 +1,10 @@
1
1
  import { mockAddCartDiscount } from "../../actions/add-cart-discount/mock";
2
2
  import { mockAddCartFee } from "../../actions/add-cart-fee/mock";
3
+ import { mockRemoveCartFee } from "../../actions/remove-cart-fee/mock";
3
4
  import { mockAddCustomSale } from "../../actions/add-custom-sale/mock";
4
5
  import { mockAddCustomer } from "../../actions/add-customer/mock";
5
6
  import { mockAddCustomerNote } from "../../actions/add-customer-note/mock";
7
+ import { mockRemoveCustomerNote } from "../../actions/remove-customer-note/mock";
6
8
  import { mockEditCustomer } from "../../actions/edit-customer/mock";
7
9
  import { mockAddOrderNote } from "../../actions/add-order-note/mock";
8
10
  import { mockAddProductDiscount } from "../../actions/add-product-discount/mock";
@@ -31,6 +33,7 @@ import { mockGetCustomers } from "../../actions/get-customers/mock";
31
33
  import { mockGetOrders } from "../../actions/get-orders/mock";
32
34
  import { mockGetProducts } from "../../actions/get-products/mock";
33
35
  import { mockGetRefunds } from "../../actions/get-refunds/mock";
36
+ import { mockGetTaxTables } from "../../actions/get-tax-tables/mock";
34
37
  import { mockGetRemainingRefundableQuantities } from "../../actions/get-remaining-refundable-quantities/mock";
35
38
  import { mockGoToStationHome } from "../../actions/go-to-station-home/mock";
36
39
  import { mockInitiateRefund } from "../../actions/initiate-refund/mock";
@@ -89,6 +92,7 @@ export const RENDER_MOCKS = {
89
92
  addCustomer: mockAddCustomer,
90
93
  editCustomer: mockEditCustomer,
91
94
  addCustomerNote: mockAddCustomerNote,
95
+ removeCustomerNote: mockRemoveCustomerNote,
92
96
  addOrderNote: mockAddOrderNote,
93
97
  addProductDiscount: mockAddProductDiscount,
94
98
  addProductFee: mockAddProductFee,
@@ -115,6 +119,7 @@ export const RENDER_MOCKS = {
115
119
  getOrders: mockGetOrders,
116
120
  getProducts: mockGetProducts,
117
121
  getRefunds: mockGetRefunds,
122
+ getTaxTables: mockGetTaxTables,
118
123
  getRemainingRefundableQuantities: mockGetRemainingRefundableQuantities,
119
124
  goToStationHome: mockGoToStationHome,
120
125
  initiateRefund: mockInitiateRefund,
@@ -167,11 +172,11 @@ export const RENDER_MOCKS = {
167
172
  getActiveUser: mockGetActiveUser,
168
173
  setActiveUser: mockSetActiveUser,
169
174
  setActiveRefund: mockSetActiveRefund,
170
- removeProductDiscount: async () => ({ success: true, timestamp: new Date().toISOString() }),
171
- removeProductFee: async () => ({ success: true, timestamp: new Date().toISOString() }),
172
- removeProductNote: async () => ({ success: true, timestamp: new Date().toISOString() }),
173
- removeCartFee: async () => ({ success: true, timestamp: new Date().toISOString() }),
174
- removeOrderNote: async () => ({ success: true, timestamp: new Date().toISOString() }),
175
- removeCustomSale: async (params) => ({ success: true, id: params.id, timestamp: new Date().toISOString() }),
176
- removeNonRevenueItem: async (params) => ({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() })
175
+ removeProductDiscount: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
176
+ removeProductFee: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
177
+ removeProductNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
178
+ removeCartFee: mockRemoveCartFee,
179
+ removeOrderNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
180
+ removeCustomSale: params => Promise.resolve({ success: true, id: params.id, timestamp: new Date().toISOString() }),
181
+ removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() })
177
182
  };
@@ -1,4 +1,4 @@
1
- import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, 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, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem } from "../../index";
1
+ import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem } from "../../index";
2
2
  export interface RenderProviderActions {
3
3
  exampleFunction: ExampleFunction;
4
4
  getProducts: GetProducts;
@@ -10,6 +10,7 @@ export interface RenderProviderActions {
10
10
  getCategories: GetCategories;
11
11
  getOrders: GetOrders;
12
12
  getRefunds: GetRefunds;
13
+ getTaxTables: GetTaxTables;
13
14
  addProductDiscount: AddProductDiscount;
14
15
  addProductToCart: AddProductToCart;
15
16
  removeProductFromCart: RemoveProductFromCart;
@@ -39,6 +40,7 @@ export interface RenderProviderActions {
39
40
  redeemPayment: RedeemPayment;
40
41
  addNonRevenueItem: AddNonRevenueItem;
41
42
  addCustomerNote: AddCustomerNote;
43
+ removeCustomerNote: RemoveCustomerNote;
42
44
  removeCustomerFromCart: RemoveCustomerFromCart;
43
45
  removeCartDiscount: RemoveCartDiscount;
44
46
  goToStationHome: GoToStationHome;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.1.61",
3
+ "version": "0.1.63",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,7 +17,8 @@
17
17
  "test": "vitest",
18
18
  "test:run": "vitest run",
19
19
  "prepublishOnly": "npm run build",
20
- "prepare": "npm run build",
20
+ "prepare": "husky && npm run build",
21
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives",
21
22
  "format": "prettier --write .",
22
23
  "format:check": "prettier --check .",
23
24
  "publish:npm": "npm publish --registry=https://registry.npmjs.org",
@@ -41,9 +42,30 @@
41
42
  "type": "git",
42
43
  "url": "git+https://github.com/Final-Commerce/command-frame.git"
43
44
  },
45
+ "lint-staged": {
46
+ "**/*.{ts,tsx,js,json,md}": [
47
+ "prettier --write"
48
+ ],
49
+ "**/*.{ts,tsx}": [
50
+ "eslint --fix"
51
+ ]
52
+ },
53
+ "jira-prepare-commit-msg": {
54
+ "messagePattern": "$M [$J]",
55
+ "jiraTicketPattern": "([A-Z]+-\\d+)",
56
+ "ignoredBranchesPattern": "^(main|master|develop|release-staging.*|pre-prod)$",
57
+ "ignoreBranchesMissingTickets": true
58
+ },
44
59
  "devDependencies": {
45
60
  "@typescript-eslint/eslint-plugin": "^8.48.0",
46
61
  "@typescript-eslint/parser": "^8.48.0",
62
+ "eslint": "^8.57.0",
63
+ "eslint-plugin-react": "^7.37.5",
64
+ "eslint-plugin-react-hooks": "^7.1.1",
65
+ "eslint-plugin-react-refresh": "^0.4.26",
66
+ "husky": "^9.1.7",
67
+ "jira-prepare-commit-msg": "^1.7.2",
68
+ "lint-staged": "^16.2.7",
47
69
  "prettier": "^3.7.1",
48
70
  "typescript": "^5.0.0",
49
71
  "vitest": "^3.0.5"