forgeframe 1.0.0

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.
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Function bridge for cross-domain function calls.
4
+ *
5
+ * @remarks
6
+ * This module enables passing functions as props across domain boundaries
7
+ * by converting them to references that can be called via postMessage.
8
+ */
9
+ import type { FunctionRef } from '../types';
10
+ import type { Messenger } from './messenger';
11
+ /**
12
+ * Generic function type for cross-domain callable functions.
13
+ * @internal
14
+ */
15
+ type CallableFunction = (...args: unknown[]) => unknown | Promise<unknown>;
16
+ /**
17
+ * Handles serialization and deserialization of functions for cross-domain calls.
18
+ *
19
+ * @remarks
20
+ * When you pass a function as a prop, it gets converted to a reference
21
+ * that can be called across domains via postMessage. The bridge maintains
22
+ * mappings of local and remote functions.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const bridge = new FunctionBridge(messenger);
27
+ *
28
+ * // Serialize a local function
29
+ * const ref = bridge.serialize(() => console.log('Hello'));
30
+ *
31
+ * // Deserialize a remote reference
32
+ * const remoteFn = bridge.deserialize(ref, targetWin, targetDomain);
33
+ * await remoteFn(); // Calls the original function cross-domain
34
+ * ```
35
+ *
36
+ * @public
37
+ */
38
+ export declare class FunctionBridge {
39
+ private messenger;
40
+ /** @internal */
41
+ private localFunctions;
42
+ /** @internal */
43
+ private remoteFunctions;
44
+ /**
45
+ * Tracks function IDs from the current serialization batch.
46
+ * Used for cleanup of stale references when props are updated.
47
+ * @internal
48
+ */
49
+ private currentBatchIds;
50
+ /**
51
+ * Creates a new FunctionBridge instance.
52
+ *
53
+ * @param messenger - The messenger to use for cross-domain calls
54
+ */
55
+ constructor(messenger: Messenger);
56
+ /**
57
+ * Serializes a local function to a transferable reference.
58
+ *
59
+ * @param fn - The function to serialize
60
+ * @param name - Optional name for debugging
61
+ * @returns A function reference that can be sent across domains
62
+ */
63
+ serialize(fn: CallableFunction, name?: string): FunctionRef;
64
+ /**
65
+ * Deserializes a function reference to a callable wrapper.
66
+ *
67
+ * @remarks
68
+ * The returned function, when called, will invoke the original function
69
+ * in the remote window via postMessage and return the result.
70
+ *
71
+ * @param ref - The function reference to deserialize
72
+ * @param targetWin - The window containing the original function
73
+ * @param targetDomain - The origin of the target window
74
+ * @returns A callable wrapper function
75
+ */
76
+ deserialize(ref: FunctionRef, targetWin: Window, targetDomain: string): CallableFunction;
77
+ /**
78
+ * Type guard to check if a value is a function reference.
79
+ *
80
+ * @param value - The value to check
81
+ * @returns True if the value is a FunctionRef
82
+ */
83
+ static isFunctionRef(value: unknown): value is FunctionRef;
84
+ /**
85
+ * Sets up the handler for incoming function call messages.
86
+ * @internal
87
+ */
88
+ private setupCallHandler;
89
+ /**
90
+ * Removes a local function reference.
91
+ *
92
+ * @param id - The function reference ID to remove
93
+ */
94
+ removeLocal(id: string): void;
95
+ /**
96
+ * Starts a new serialization batch.
97
+ *
98
+ * @remarks
99
+ * Call this before serializing a new set of props. After serialization,
100
+ * call {@link finishBatch} to clean up functions from previous batches.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * bridge.startBatch();
105
+ * const serialized = serializeFunctions(props, bridge);
106
+ * bridge.finishBatch();
107
+ * ```
108
+ */
109
+ startBatch(): void;
110
+ /**
111
+ * Finishes the current batch and removes functions not in this batch.
112
+ *
113
+ * @remarks
114
+ * This cleans up function references from previous prop updates that
115
+ * are no longer needed, preventing memory leaks.
116
+ *
117
+ * @param keepPrevious - If true, keeps previous batch functions (default: false)
118
+ */
119
+ finishBatch(keepPrevious?: boolean): void;
120
+ /**
121
+ * Clears all remote function references.
122
+ *
123
+ * @remarks
124
+ * Call this when the remote window is no longer accessible
125
+ * (e.g., closed or navigated away).
126
+ */
127
+ clearRemote(): void;
128
+ /**
129
+ * Returns the current number of registered local functions.
130
+ * Useful for debugging and monitoring.
131
+ */
132
+ get localFunctionCount(): number;
133
+ /**
134
+ * Returns the current number of cached remote functions.
135
+ * Useful for debugging and monitoring.
136
+ */
137
+ get remoteFunctionCount(): number;
138
+ /**
139
+ * Cleans up all function references.
140
+ */
141
+ destroy(): void;
142
+ }
143
+ /**
144
+ * Recursively serializes all functions in an object.
145
+ *
146
+ * @param obj - The object to process
147
+ * @param bridge - The function bridge to use for serialization
148
+ * @param seen - Internal set for cycle detection (do not pass manually)
149
+ * @returns A new object with all functions replaced by references
150
+ *
151
+ * @public
152
+ */
153
+ export declare function serializeFunctions(obj: unknown, bridge: FunctionBridge, seen?: WeakSet<object>): unknown;
154
+ /**
155
+ * Recursively deserializes all function references in an object.
156
+ *
157
+ * @param obj - The object to process
158
+ * @param bridge - The function bridge to use for deserialization
159
+ * @param targetWin - The window containing the original functions
160
+ * @param targetDomain - The origin of the target window
161
+ * @param seen - Internal set for cycle detection (do not pass manually)
162
+ * @returns A new object with all references replaced by callable wrappers
163
+ *
164
+ * @public
165
+ */
166
+ export declare function deserializeFunctions(obj: unknown, bridge: FunctionBridge, targetWin: Window, targetDomain: string, seen?: WeakSet<object>): unknown;
167
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Cross-domain communication module for ForgeFrame.
4
+ *
5
+ * @remarks
6
+ * This module provides the postMessage-based communication layer for
7
+ * cross-domain parent-child component interaction. It includes message
8
+ * serialization, function bridging, and request/response handling.
9
+ */
10
+ export { Messenger, type MessageHandler } from './messenger';
11
+ export { FunctionBridge, serializeFunctions, deserializeFunctions, } from './bridge';
12
+ export { PROTOCOL_PREFIX, serializeMessage, deserializeMessage, createRequestMessage, createResponseMessage, createAckMessage, } from './protocol';
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Cross-domain messenger implementation.
4
+ *
5
+ * @remarks
6
+ * Provides a simple request/response communication layer over postMessage,
7
+ * replacing post-robot with a minimal implementation.
8
+ */
9
+ import type { DomainMatcher } from '../types';
10
+ /**
11
+ * Handler function for incoming messages.
12
+ *
13
+ * @typeParam T - The expected data type of incoming messages
14
+ * @typeParam R - The return type of the handler
15
+ * @public
16
+ */
17
+ export type MessageHandler<T = unknown, R = unknown> = (data: T, source: {
18
+ uid: string;
19
+ domain: string;
20
+ }) => R | Promise<R>;
21
+ /**
22
+ * Cross-domain messenger using postMessage.
23
+ *
24
+ * @remarks
25
+ * This class provides a request/response communication layer over the
26
+ * browser's postMessage API. It handles message serialization, timeouts,
27
+ * and response correlation.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const messenger = new Messenger('component-123', window, window.location.origin);
32
+ *
33
+ * // Register handler
34
+ * messenger.on('greeting', (data) => {
35
+ * return { message: `Hello, ${data.name}!` };
36
+ * });
37
+ *
38
+ * // Send message
39
+ * const response = await messenger.send(targetWindow, targetOrigin, 'greeting', { name: 'World' });
40
+ * ```
41
+ *
42
+ * @public
43
+ */
44
+ export declare class Messenger {
45
+ private uid;
46
+ private win;
47
+ private domain;
48
+ /** @internal */
49
+ private pending;
50
+ /** @internal */
51
+ private handlers;
52
+ /** @internal */
53
+ private listener;
54
+ /** @internal */
55
+ private destroyed;
56
+ /** @internal */
57
+ private allowedOrigins;
58
+ /** @internal */
59
+ private allowedOriginPatterns;
60
+ /**
61
+ * Creates a new Messenger instance.
62
+ *
63
+ * @param uid - Unique identifier for this messenger
64
+ * @param win - The window to listen for messages on
65
+ * @param domain - The origin domain of this messenger
66
+ * @param trustedDomains - Optional domains to trust for incoming messages
67
+ */
68
+ constructor(uid: string, win?: Window, domain?: string, trustedDomains?: DomainMatcher);
69
+ /**
70
+ * Adds a trusted domain that can send messages to this messenger.
71
+ *
72
+ * @param domain - Domain pattern to trust (string, RegExp, or array)
73
+ */
74
+ addTrustedDomain(domain: DomainMatcher): void;
75
+ /**
76
+ * Checks if an origin is trusted.
77
+ *
78
+ * @param origin - The origin to check
79
+ * @returns True if the origin is trusted
80
+ * @internal
81
+ */
82
+ private isOriginTrusted;
83
+ /**
84
+ * Sends a message and waits for a response.
85
+ *
86
+ * @typeParam T - The data type being sent
87
+ * @typeParam R - The expected response type
88
+ * @param targetWin - The target window to send to
89
+ * @param targetDomain - The target origin domain
90
+ * @param name - The message name/type
91
+ * @param data - Optional data payload
92
+ * @param timeout - Timeout in milliseconds (default: 10000)
93
+ * @returns Promise resolving to the response data
94
+ * @throws Error if messenger is destroyed or timeout occurs
95
+ */
96
+ send<T = unknown, R = unknown>(targetWin: Window, targetDomain: string, name: string, data?: T, timeout?: number): Promise<R>;
97
+ /**
98
+ * Sends a one-way message without waiting for a response.
99
+ *
100
+ * @typeParam T - The data type being sent
101
+ * @param targetWin - The target window to send to
102
+ * @param targetDomain - The target origin domain
103
+ * @param name - The message name/type
104
+ * @param data - Optional data payload
105
+ * @throws Error if messenger is destroyed
106
+ */
107
+ post<T = unknown>(targetWin: Window, targetDomain: string, name: string, data?: T): void;
108
+ /**
109
+ * Registers a handler for incoming messages of a specific type.
110
+ *
111
+ * @typeParam T - The expected data type of incoming messages
112
+ * @typeParam R - The return type of the handler
113
+ * @param name - The message name/type to handle
114
+ * @param handler - The handler function
115
+ * @returns Function to unregister the handler
116
+ */
117
+ on<T = unknown, R = unknown>(name: string, handler: MessageHandler<T, R>): () => void;
118
+ /**
119
+ * Sets up the postMessage event listener.
120
+ * @internal
121
+ */
122
+ private setupListener;
123
+ /**
124
+ * Processes a received message.
125
+ * @internal
126
+ */
127
+ private handleMessage;
128
+ /**
129
+ * Cleans up the messenger and releases resources.
130
+ *
131
+ * @remarks
132
+ * Removes the message listener, rejects all pending requests,
133
+ * and clears all handlers.
134
+ */
135
+ destroy(): void;
136
+ /**
137
+ * Checks if the messenger has been destroyed.
138
+ *
139
+ * @returns True if destroy() has been called
140
+ */
141
+ isDestroyed(): boolean;
142
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Message protocol for ForgeFrame communication.
4
+ *
5
+ * @remarks
6
+ * This module defines the message format and serialization for cross-domain
7
+ * communication. Messages are prefixed to identify ForgeFrame traffic.
8
+ */
9
+ import type { Message } from '../types';
10
+ /**
11
+ * Protocol prefix to identify ForgeFrame messages.
12
+ * @public
13
+ */
14
+ export declare const PROTOCOL_PREFIX = "forgeframe:";
15
+ /**
16
+ * Serializes a message for postMessage transmission.
17
+ *
18
+ * @param message - The message to serialize
19
+ * @returns JSON string prefixed with the protocol identifier
20
+ *
21
+ * @public
22
+ */
23
+ export declare function serializeMessage(message: Message): string;
24
+ /**
25
+ * Deserializes a message from postMessage data.
26
+ *
27
+ * @param data - The raw data received from postMessage
28
+ * @returns The parsed message, or null if not a valid ForgeFrame message
29
+ *
30
+ * @public
31
+ */
32
+ export declare function deserializeMessage(data: unknown): Message | null;
33
+ /**
34
+ * Creates a request message.
35
+ *
36
+ * @param id - Unique message identifier
37
+ * @param name - Message name/type
38
+ * @param data - Message payload
39
+ * @param source - Sender identification
40
+ * @returns A formatted request message
41
+ *
42
+ * @public
43
+ */
44
+ export declare function createRequestMessage(id: string, name: string, data: unknown, source: {
45
+ uid: string;
46
+ domain: string;
47
+ }): Message;
48
+ /**
49
+ * Creates a response message.
50
+ *
51
+ * @param requestId - The ID of the request being responded to
52
+ * @param data - Response payload
53
+ * @param source - Sender identification
54
+ * @param error - Optional error if the request failed
55
+ * @returns A formatted response message
56
+ *
57
+ * @public
58
+ */
59
+ export declare function createResponseMessage(requestId: string, data: unknown, source: {
60
+ uid: string;
61
+ domain: string;
62
+ }, error?: Error): Message;
63
+ /**
64
+ * Creates an acknowledgement message.
65
+ *
66
+ * @param requestId - The ID of the request being acknowledged
67
+ * @param source - Sender identification
68
+ * @returns A formatted acknowledgement message
69
+ *
70
+ * @public
71
+ */
72
+ export declare function createAckMessage(requestId: string, source: {
73
+ uid: string;
74
+ domain: string;
75
+ }): Message;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Prop type constants for defining component props.
3
+ *
4
+ * @remarks
5
+ * These constants define the valid types for props passed between parent and child components.
6
+ * Use these when defining prop definitions in your component configuration.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const MyComponent = ForgeFrame.create({
11
+ * tag: 'my-component',
12
+ * url: '/component.html',
13
+ * props: {
14
+ * name: { type: PROP_TYPE.STRING },
15
+ * count: { type: PROP_TYPE.NUMBER },
16
+ * onSubmit: { type: PROP_TYPE.FUNCTION },
17
+ * },
18
+ * });
19
+ * ```
20
+ *
21
+ * @public
22
+ */
23
+ export declare const PROP_TYPE: {
24
+ /** String prop type */
25
+ readonly STRING: "string";
26
+ /** Object prop type */
27
+ readonly OBJECT: "object";
28
+ /** Function prop type - serialized for cross-domain calls */
29
+ readonly FUNCTION: "function";
30
+ /** Boolean prop type */
31
+ readonly BOOLEAN: "boolean";
32
+ /** Number prop type */
33
+ readonly NUMBER: "number";
34
+ /** Array prop type */
35
+ readonly ARRAY: "array";
36
+ };
37
+ /**
38
+ * Union type of all valid prop types.
39
+ * @public
40
+ */
41
+ export type PropType = (typeof PROP_TYPE)[keyof typeof PROP_TYPE];
42
+ /**
43
+ * Rendering context types for components.
44
+ *
45
+ * @remarks
46
+ * Components can be rendered as either iframes or popups.
47
+ * The context determines how the child window is created and managed.
48
+ *
49
+ * @public
50
+ */
51
+ export declare const CONTEXT: {
52
+ /** Render component in an iframe */
53
+ readonly IFRAME: "iframe";
54
+ /** Render component in a popup window */
55
+ readonly POPUP: "popup";
56
+ };
57
+ /**
58
+ * Union type of valid rendering contexts.
59
+ * @public
60
+ */
61
+ export type ContextType = (typeof CONTEXT)[keyof typeof CONTEXT];
62
+ /**
63
+ * Component lifecycle event names.
64
+ *
65
+ * @remarks
66
+ * These events are emitted during the component lifecycle and can be
67
+ * listened to via `instance.event.on(EVENT.RENDERED, handler)`.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const instance = MyComponent({ prop: 'value' });
72
+ * instance.event.on(EVENT.RENDERED, () => {
73
+ * console.log('Component is ready!');
74
+ * });
75
+ * instance.render('#container');
76
+ * ```
77
+ *
78
+ * @public
79
+ */
80
+ export declare const EVENT: {
81
+ /** Emitted when rendering starts */
82
+ readonly RENDER: "render";
83
+ /** Emitted when component is fully rendered and initialized */
84
+ readonly RENDERED: "rendered";
85
+ /** Emitted when prerender (loading) phase starts */
86
+ readonly PRERENDER: "prerender";
87
+ /** Emitted when prerender phase completes */
88
+ readonly PRERENDERED: "prerendered";
89
+ /** Emitted when component becomes visible */
90
+ readonly DISPLAY: "display";
91
+ /** Emitted when an error occurs */
92
+ readonly ERROR: "error";
93
+ /** Emitted when component is closing */
94
+ readonly CLOSE: "close";
95
+ /** Emitted when component is destroyed */
96
+ readonly DESTROY: "destroy";
97
+ /** Emitted when props are updated */
98
+ readonly PROPS: "props";
99
+ /** Emitted when component is resized */
100
+ readonly RESIZE: "resize";
101
+ /** Emitted when component receives focus */
102
+ readonly FOCUS: "focus";
103
+ };
104
+ /**
105
+ * Union type of valid event names.
106
+ * @public
107
+ */
108
+ export type EventType = (typeof EVENT)[keyof typeof EVENT];
109
+ /**
110
+ * Prop serialization strategies for cross-domain transfer.
111
+ *
112
+ * @remarks
113
+ * When props are passed from parent to child across domains, they need to be
114
+ * serialized. Different strategies offer different trade-offs.
115
+ *
116
+ * @public
117
+ */
118
+ export declare const PROP_SERIALIZATION: {
119
+ /** Default JSON serialization */
120
+ readonly JSON: "json";
121
+ /** Base64 encoding for binary or large data */
122
+ readonly BASE64: "base64";
123
+ /** Dot notation for nested objects (e.g., "a.b.c=value") */
124
+ readonly DOTIFY: "dotify";
125
+ };
126
+ /**
127
+ * Union type of valid serialization strategies.
128
+ * @public
129
+ */
130
+ export type SerializationType = (typeof PROP_SERIALIZATION)[keyof typeof PROP_SERIALIZATION];
131
+ /**
132
+ * Internal message types for the cross-domain communication protocol.
133
+ * @internal
134
+ */
135
+ export declare const MESSAGE_TYPE: {
136
+ /** Request message expecting a response */
137
+ readonly REQUEST: "request";
138
+ /** Response to a previous request */
139
+ readonly RESPONSE: "response";
140
+ /** Acknowledgment message */
141
+ readonly ACK: "ack";
142
+ };
143
+ /**
144
+ * Union type of valid message types.
145
+ * @internal
146
+ */
147
+ export type MessageType = (typeof MESSAGE_TYPE)[keyof typeof MESSAGE_TYPE];
148
+ /**
149
+ * Message names for the parent-child communication protocol.
150
+ *
151
+ * @remarks
152
+ * These are the internal message identifiers used by the postMessage protocol.
153
+ * Each message type corresponds to a specific action in the component lifecycle.
154
+ *
155
+ * @internal
156
+ */
157
+ export declare const MESSAGE_NAME: {
158
+ /** Child initialization complete */
159
+ readonly INIT: "forgeframe_init";
160
+ /** Props update from parent to child */
161
+ readonly PROPS: "forgeframe_props";
162
+ /** Close request from child */
163
+ readonly CLOSE: "forgeframe_close";
164
+ /** Resize request from child */
165
+ readonly RESIZE: "forgeframe_resize";
166
+ /** Focus request from child */
167
+ readonly FOCUS: "forgeframe_focus";
168
+ /** Show request from child */
169
+ readonly SHOW: "forgeframe_show";
170
+ /** Hide request from child */
171
+ readonly HIDE: "forgeframe_hide";
172
+ /** Error report from child */
173
+ readonly ERROR: "forgeframe_error";
174
+ /** Data export from child to parent */
175
+ readonly EXPORT: "forgeframe_export";
176
+ /** Cross-domain function call */
177
+ readonly CALL: "forgeframe_call";
178
+ /** Parent export from child context */
179
+ readonly PARENT_EXPORT: "forgeframe_parent_export";
180
+ /** Get sibling components request */
181
+ readonly GET_SIBLINGS: "forgeframe_get_siblings";
182
+ };
183
+ /**
184
+ * Union type of valid message names.
185
+ * @internal
186
+ */
187
+ export type MessageName = (typeof MESSAGE_NAME)[keyof typeof MESSAGE_NAME];
188
+ /**
189
+ * Window name prefix for identifying ForgeFrame child windows.
190
+ *
191
+ * @remarks
192
+ * This prefix is prepended to the base64-encoded payload in `window.name`
193
+ * to identify windows created by ForgeFrame.
194
+ *
195
+ * @internal
196
+ */
197
+ export declare const WINDOW_NAME_PREFIX = "__forgeframe__";
198
+ /**
199
+ * Current library version.
200
+ * @public
201
+ */
202
+ export declare const VERSION = "1.0.0";