@final-commerce/command-frame 0.1.33 → 0.1.34
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 +54 -13
- package/dist/hooks/index.d.ts +17 -0
- package/dist/hooks/index.js +39 -0
- package/dist/hooks/types.d.ts +15 -0
- package/dist/hooks/types.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/pubsub/types.d.ts +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,43 +6,44 @@ 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
|
+
The library provides three main capabilities:
|
|
10
|
+
|
|
11
|
+
| Capability | Purpose | Scope |
|
|
12
|
+
|-----------|---------|-------|
|
|
13
|
+
| **Commands** | Call host functions from the iframe (e.g. get products, open cash drawer) | Request/response per call |
|
|
14
|
+
| **Pub/Sub** | Subscribe to real-time events from the host (e.g. cart changes, payments) | Page-scoped (while iframe is mounted) |
|
|
15
|
+
| **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
|
|
16
|
+
|
|
9
17
|
## Installation
|
|
10
18
|
|
|
11
19
|
```bash
|
|
12
20
|
npm install @final-commerce/command-frame
|
|
13
21
|
```
|
|
14
22
|
|
|
15
|
-
##
|
|
23
|
+
## Commands
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
Commands let the extension iframe call typed functions on the host. Each host environment (Render, Manage) exposes its own set of commands.
|
|
18
26
|
|
|
19
|
-
###
|
|
27
|
+
### Render (POS System)
|
|
20
28
|
|
|
21
29
|
For building applications that run inside the Render Point of Sale interface.
|
|
22
30
|
|
|
23
31
|
- **[Render Documentation](./src/projects/render/README.md)**
|
|
24
|
-
- **Features:** Order management, Product catalog, Customer management, Payments, Hardware integration (Cash drawer, Printer).
|
|
25
|
-
|
|
26
|
-
**Quick Start:**
|
|
32
|
+
- **Features:** Order management, Product catalog, Customer management, Payments, Hardware integration (Cash drawer, Printer), Custom tables, Secrets storage.
|
|
27
33
|
|
|
28
34
|
```typescript
|
|
29
35
|
import { RenderClient } from '@final-commerce/command-frame';
|
|
30
36
|
|
|
31
|
-
// Initialize the client
|
|
32
37
|
const client = new RenderClient();
|
|
33
|
-
|
|
34
|
-
// Use typed methods
|
|
35
38
|
const products = await client.getProducts();
|
|
36
39
|
```
|
|
37
40
|
|
|
38
|
-
###
|
|
41
|
+
### Manage (Dashboard)
|
|
39
42
|
|
|
40
43
|
For building applications that run inside the Final Commerce Management Dashboard.
|
|
41
44
|
|
|
42
45
|
- **[Manage Documentation](./src/projects/manage/README.md)**
|
|
43
|
-
- **Features:** Context information, Dashboard widgets (
|
|
44
|
-
|
|
45
|
-
**Quick Start:**
|
|
46
|
+
- **Features:** Context information, Dashboard widgets (more coming soon).
|
|
46
47
|
|
|
47
48
|
```typescript
|
|
48
49
|
import { ManageClient } from '@final-commerce/command-frame';
|
|
@@ -51,6 +52,46 @@ const client = new ManageClient();
|
|
|
51
52
|
const context = await client.getContext();
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
## Pub/Sub
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
- **[Pub/Sub Documentation](./src/pubsub/README.md)**
|
|
60
|
+
- **Topics:** Cart (8 events), Customers (6), Orders (2), Payments (2), Products (2), Refunds (2), Print (3).
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { topics } from '@final-commerce/command-frame';
|
|
64
|
+
|
|
65
|
+
const subscriptionId = topics.subscribe('cart', (event) => {
|
|
66
|
+
console.log('Cart event:', event.type, event.data);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Unsubscribe when done
|
|
70
|
+
topics.unsubscribe('cart', subscriptionId);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Hooks
|
|
74
|
+
|
|
75
|
+
Hooks are **session-scoped** event callbacks that run in the host (Render) context and persist across all page navigations -- even when the extension iframe is no longer on the current page. Use hooks for business logic that must run on every event (e.g. logging to custom tables, triggering webhooks).
|
|
76
|
+
|
|
77
|
+
- **[Hooks Documentation](./src/hooks/README.md)**
|
|
78
|
+
- The callback is serialized and sent to the host; it must be **self-contained** (no closures, no imports).
|
|
79
|
+
- A stable `hookId` is required for deduplication (safe on iframe reload).
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { hooks } from '@final-commerce/command-frame';
|
|
83
|
+
|
|
84
|
+
hooks.register('cart', async (event, hostCommands) => {
|
|
85
|
+
await hostCommands.upsertCustomTableData({
|
|
86
|
+
tableName: 'cart-events-log',
|
|
87
|
+
data: { eventType: event.type, payload: event.data, timestamp: event.timestamp },
|
|
88
|
+
});
|
|
89
|
+
}, { hookId: 'my-extension:cart-log' });
|
|
90
|
+
|
|
91
|
+
// Unregister when no longer needed
|
|
92
|
+
hooks.unregister('my-extension:cart-log');
|
|
93
|
+
```
|
|
94
|
+
|
|
54
95
|
## Development & Testing
|
|
55
96
|
|
|
56
97
|
### Demo Mode / Mocking
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HookFunction, HookRegisterOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Register a session-scoped hook. The callback is serialized and sent to the host (Render);
|
|
4
|
+
* once registered, it runs in the host context even when the extension iframe is not on the current page.
|
|
5
|
+
* The callback must be self-contained (only use event, hostCommands, callCommand).
|
|
6
|
+
* Uses options.hookId as a stable dedup key: re-registering with the same hookId replaces the previous hook.
|
|
7
|
+
*/
|
|
8
|
+
declare function register(topic: string, callback: HookFunction, options: HookRegisterOptions): string;
|
|
9
|
+
/**
|
|
10
|
+
* Unregister a hook by ID. Sends a message to the host to remove the hook.
|
|
11
|
+
*/
|
|
12
|
+
declare function unregister(hookId: string): void;
|
|
13
|
+
export declare const hooks: {
|
|
14
|
+
register: typeof register;
|
|
15
|
+
unregister: typeof unregister;
|
|
16
|
+
};
|
|
17
|
+
export type { HookFunction, HookRegisterOptions } from "./types";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const DEFAULT_ORIGIN = "*";
|
|
2
|
+
function getOrigin() {
|
|
3
|
+
return typeof window !== "undefined" ? window.__COMMAND_FRAME_ORIGIN__ ?? DEFAULT_ORIGIN : DEFAULT_ORIGIN;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Register a session-scoped hook. The callback is serialized and sent to the host (Render);
|
|
7
|
+
* once registered, it runs in the host context even when the extension iframe is not on the current page.
|
|
8
|
+
* The callback must be self-contained (only use event, hostCommands, callCommand).
|
|
9
|
+
* Uses options.hookId as a stable dedup key: re-registering with the same hookId replaces the previous hook.
|
|
10
|
+
*/
|
|
11
|
+
function register(topic, callback, options) {
|
|
12
|
+
const hookId = options.hookId;
|
|
13
|
+
const functionBody = callback.toString();
|
|
14
|
+
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
|
15
|
+
window.parent.postMessage({
|
|
16
|
+
type: "hook-register",
|
|
17
|
+
topic,
|
|
18
|
+
functionBody,
|
|
19
|
+
eventTypes: options.eventTypes,
|
|
20
|
+
hookId
|
|
21
|
+
}, getOrigin());
|
|
22
|
+
}
|
|
23
|
+
return hookId;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Unregister a hook by ID. Sends a message to the host to remove the hook.
|
|
27
|
+
*/
|
|
28
|
+
function unregister(hookId) {
|
|
29
|
+
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
|
30
|
+
window.parent.postMessage({
|
|
31
|
+
type: "hook-unregister",
|
|
32
|
+
hookId
|
|
33
|
+
}, getOrigin());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export const hooks = {
|
|
37
|
+
register,
|
|
38
|
+
unregister
|
|
39
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TopicEvent } from "../pubsub/types";
|
|
2
|
+
/**
|
|
3
|
+
* Function signature for extension hook callbacks.
|
|
4
|
+
* The function is serialized and sent to the host; it receives event and hostCommands when executed.
|
|
5
|
+
*/
|
|
6
|
+
export type HookFunction = (event: TopicEvent, hostCommands: Record<string, (params?: any) => Promise<any>>, callCommand: (action: string, params?: any) => Promise<any>) => void | Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Options when registering a hook from the extension iframe.
|
|
9
|
+
*/
|
|
10
|
+
export interface HookRegisterOptions {
|
|
11
|
+
/** Stable identifier for this hook. Used to deduplicate on reload. Must be unique per hook. */
|
|
12
|
+
hookId: string;
|
|
13
|
+
/** Only fire for these event types; omit to receive all events for the topic */
|
|
14
|
+
eventTypes?: string[];
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -123,7 +123,9 @@ export * from "./projects/manage";
|
|
|
123
123
|
export { commandFrameClient, CommandFrameClient } from "./client";
|
|
124
124
|
export type { PostMessageRequest, PostMessageResponse } from "./client";
|
|
125
125
|
export { topics } from "./pubsub/topics";
|
|
126
|
-
export type { TopicDefinition, TopicEvent, TopicEventType, TopicSubscriptionCallback, TopicSubscription } from "./pubsub/types";
|
|
126
|
+
export type { TopicDefinition, TopicEvent, TopicEventType, TopicSubscriptionCallback, TopicSubscription, HookCallback, HookOptions, HookRegistration } from "./pubsub/types";
|
|
127
|
+
export { hooks } from "./hooks";
|
|
128
|
+
export type { HookFunction, HookRegisterOptions } from "./hooks";
|
|
127
129
|
export { customersTopic } from "./pubsub/topics/customers";
|
|
128
130
|
export { ordersTopic } from "./pubsub/topics/orders";
|
|
129
131
|
export { refundsTopic } from "./pubsub/topics/refunds";
|
package/dist/index.js
CHANGED
|
@@ -161,6 +161,8 @@ export * from "./projects/manage";
|
|
|
161
161
|
export { commandFrameClient, CommandFrameClient } from "./client";
|
|
162
162
|
// Export Pub/Sub
|
|
163
163
|
export { topics } from "./pubsub/topics";
|
|
164
|
+
// Export Hooks (extension iframe API for session-scoped event callbacks)
|
|
165
|
+
export { hooks } from "./hooks";
|
|
164
166
|
// Export Pub/Sub Topics
|
|
165
167
|
export { customersTopic } from "./pubsub/topics/customers";
|
|
166
168
|
export { ordersTopic } from "./pubsub/topics/orders";
|
package/dist/pubsub/types.d.ts
CHANGED
|
@@ -49,3 +49,25 @@ export interface TopicRegistration {
|
|
|
49
49
|
subscriptionCount: number;
|
|
50
50
|
}[];
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Hook callback function type (host-side, session-scoped)
|
|
54
|
+
*/
|
|
55
|
+
export type HookCallback<T = any> = (event: TopicEvent<T>) => void | Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Options when registering a host-side hook
|
|
58
|
+
*/
|
|
59
|
+
export interface HookOptions {
|
|
60
|
+
/** Only fire for these event types; omit to receive all event types for the topic */
|
|
61
|
+
eventTypes?: string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Single hook registration (stored by TopicPublisher)
|
|
65
|
+
*/
|
|
66
|
+
export interface HookRegistration {
|
|
67
|
+
id: string;
|
|
68
|
+
topicId: string;
|
|
69
|
+
callback: HookCallback;
|
|
70
|
+
eventTypes?: string[];
|
|
71
|
+
/** Origin of the iframe that registered this hook (for debugging) */
|
|
72
|
+
sourceOrigin?: string;
|
|
73
|
+
}
|