forgeframe 0.0.4 → 0.0.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.
package/README.md CHANGED
@@ -832,7 +832,7 @@ interface ComponentOptions<P> {
832
832
  const instance = MyComponent(props);
833
833
 
834
834
  await instance.render(container?, context?) // Render
835
- await instance.renderTo(window, container?) // Render to another window
835
+ await instance.renderTo(window, container?) // Supports only current window; throws for other windows
836
836
  await instance.close() // Close and destroy
837
837
  await instance.focus() // Focus
838
838
  await instance.resize({ width, height }) // Resize
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Internal active component instance index.
4
+ *
5
+ * @remarks
6
+ * This module tracks active component instances by UID and tag so peer lookups
7
+ * can be performed without scanning the full component registry.
8
+ */
9
+ import type { ForgeFrameComponentInstance } from '../types';
10
+ export type IndexedComponentInstance = ForgeFrameComponentInstance<Record<string, unknown>, unknown>;
11
+ /**
12
+ * Adds an instance to the internal lookup index.
13
+ * @internal
14
+ */
15
+ export declare function indexComponentInstance<P extends Record<string, unknown>, X>(tag: string, instance: ForgeFrameComponentInstance<P, X>): void;
16
+ /**
17
+ * Removes an instance from the internal lookup index.
18
+ * @internal
19
+ */
20
+ export declare function removeIndexedComponentInstance(uid: string): void;
21
+ /**
22
+ * Removes all indexed instances for a specific component tag.
23
+ * @internal
24
+ */
25
+ export declare function clearIndexedInstancesByTag(tag: string): void;
26
+ /**
27
+ * Clears all active indexed instances.
28
+ * @internal
29
+ */
30
+ export declare function clearIndexedInstances(): void;
31
+ /**
32
+ * Returns active instances for a specific component tag.
33
+ * @internal
34
+ */
35
+ export declare function getComponentInstancesByTag(tag: string): IndexedComponentInstance[];
36
+ /**
37
+ * Returns all active indexed instances across tags.
38
+ * @internal
39
+ */
40
+ export declare function getIndexedComponentInstances(): Array<{
41
+ tag: string;
42
+ instance: IndexedComponentInstance;
43
+ }>;
@@ -8,6 +8,7 @@
8
8
  * creation, validation, and lifecycle management.
9
9
  */
10
10
  import type { ComponentOptions, ForgeFrameComponent, ForgeFrameComponentInstance } from '../types';
11
+ import { type IndexedComponentInstance } from './component-instance-index';
11
12
  /**
12
13
  * Creates a new ForgeFrame component definition.
13
14
  *
@@ -60,6 +61,27 @@ export declare function create<P extends Record<string, unknown> = Record<string
60
61
  * @public
61
62
  */
62
63
  export declare function getComponent<P extends Record<string, unknown> = Record<string, unknown>, X = unknown>(tag: string): ForgeFrameComponent<P, X> | undefined;
64
+ /**
65
+ * Returns all registered components as tag/component pairs.
66
+ * @internal
67
+ */
68
+ export declare function getRegisteredComponents(): Array<[
69
+ string,
70
+ ForgeFrameComponent<Record<string, unknown>>
71
+ ]>;
72
+ /**
73
+ * Returns active instances for a specific component tag using an internal index.
74
+ * @internal
75
+ */
76
+ export declare function getComponentInstancesByTag(tag: string): IndexedComponentInstance[];
77
+ /**
78
+ * Returns all active indexed instances across tags.
79
+ * @internal
80
+ */
81
+ export declare function getIndexedComponentInstances(): Array<{
82
+ tag: string;
83
+ instance: IndexedComponentInstance;
84
+ }>;
63
85
  /**
64
86
  * Returns the internal options metadata for a component factory.
65
87
  * @internal
@@ -0,0 +1,50 @@
1
+ import type { PropContext } from '../../types';
2
+ import type { NormalizedOptions } from './types';
3
+ /**
4
+ * Hooks used by the props pipeline to coordinate host synchronization behavior.
5
+ * @internal
6
+ */
7
+ export interface ConsumerPropsUpdateHooks<P extends Record<string, unknown>> {
8
+ resolveUrl: (props: P) => string;
9
+ resolveUrlOrigin: (url: string) => string | null;
10
+ assertStableRenderedOrigin: (nextHostOrigin: string | null) => void;
11
+ isRendered: () => boolean;
12
+ syncTrustedDomainForUrl: (url: string) => void;
13
+ shouldSendPropsToHost: () => boolean;
14
+ sendPropsUpdateToHost: (nextProps: P) => Promise<void>;
15
+ emitPropsUpdated: (nextProps: P) => void;
16
+ }
17
+ /**
18
+ * Owns consumer prop normalization, validation, and update queueing.
19
+ * @internal
20
+ */
21
+ export declare class ConsumerPropsPipeline<P extends Record<string, unknown>> {
22
+ private options;
23
+ private createPropContext;
24
+ /** Current normalized prop snapshot. */
25
+ props: P;
26
+ /** Last input props snapshot prior to normalization. */
27
+ inputProps: Partial<P>;
28
+ /** Active in-flight update chain when host synchronization is occurring. */
29
+ pendingPropsUpdate: Promise<void> | null;
30
+ constructor(options: NormalizedOptions<P>, initialInputProps: Partial<P>, createPropContext: (props: P) => PropContext<P>);
31
+ /**
32
+ * Builds and validates the next props snapshot.
33
+ */
34
+ buildNextProps(newProps: Partial<P>): {
35
+ nextInputProps: Partial<P>;
36
+ nextProps: P;
37
+ };
38
+ /**
39
+ * Applies a props update and synchronizes it to the host when connected.
40
+ */
41
+ updateProps(newProps: Partial<P>, hooks: ConsumerPropsUpdateHooks<P>): Promise<void>;
42
+ /**
43
+ * Queues prop updates when a previous host sync is in flight.
44
+ */
45
+ private queuePropsUpdate;
46
+ /**
47
+ * Tracks a promise as the active queued update and clears it when settled.
48
+ */
49
+ private trackPendingUpdate;
50
+ }
@@ -0,0 +1,83 @@
1
+ import type { ContextType } from '../../constants';
2
+ import type { Dimensions } from '../../types';
3
+ import type { NormalizedOptions } from './types';
4
+ /**
5
+ * Parameters required to open iframe/popup host content.
6
+ * @internal
7
+ */
8
+ export interface ConsumerOpenParams {
9
+ baseUrl: string;
10
+ buildUrl: (baseUrl: string) => string;
11
+ buildBodyParams: () => URLSearchParams;
12
+ buildWindowName: () => string;
13
+ submitBodyForm: (target: string, actionUrl: string, params: URLSearchParams) => void;
14
+ onPopupClose: () => void;
15
+ registerCleanup: (cleanupFn: () => void) => void;
16
+ }
17
+ /**
18
+ * Owns consumer rendering concerns (container resolution, prerender, iframe/popup lifecycle).
19
+ * @internal
20
+ */
21
+ export declare class ConsumerRenderer<P extends Record<string, unknown>> {
22
+ private options;
23
+ private uid;
24
+ private getProps;
25
+ private resolveDimensions;
26
+ private callbacks;
27
+ /** Active rendering context. */
28
+ context: ContextType;
29
+ /** Active iframe instance when rendering in iframe mode. */
30
+ iframe: HTMLIFrameElement | null;
31
+ /** Resolved container element. */
32
+ container: HTMLElement | null;
33
+ /** Prerender element currently displayed while host initializes. */
34
+ prerenderElement: HTMLElement | null;
35
+ constructor(options: NormalizedOptions<P>, uid: string, getProps: () => P, resolveDimensions: () => Dimensions, callbacks: {
36
+ close: () => Promise<void>;
37
+ focus: () => Promise<void>;
38
+ });
39
+ /**
40
+ * Resolves a container selector or element to an HTMLElement.
41
+ */
42
+ resolveContainer(container?: string | HTMLElement): HTMLElement;
43
+ /**
44
+ * Creates and displays prerender/loading content.
45
+ */
46
+ prerender(createIframeElement: (windowName: string) => HTMLIFrameElement, buildWindowName: () => string): Promise<void>;
47
+ /**
48
+ * Creates an iframe element without setting src (for prerender phase).
49
+ */
50
+ createIframeElement(windowName: string): HTMLIFrameElement;
51
+ /**
52
+ * Opens host content in iframe or popup context.
53
+ */
54
+ open(params: ConsumerOpenParams): Window | null;
55
+ /**
56
+ * Swaps prerender content with the live iframe after host initialization.
57
+ */
58
+ swapPrerenderContentIfNeeded(): Promise<void>;
59
+ /**
60
+ * Submits a hidden form to navigate a target window via POST.
61
+ */
62
+ submitBodyForm(target: string, actionUrl: string, params: URLSearchParams): void;
63
+ /**
64
+ * Focuses iframe/popup context.
65
+ */
66
+ focus(hostWindow: Window | null): void;
67
+ /**
68
+ * Resizes iframe/popup context.
69
+ */
70
+ resize(dimensions: Dimensions, hostWindow: Window | null): void;
71
+ /**
72
+ * Shows iframe context.
73
+ */
74
+ show(): void;
75
+ /**
76
+ * Hides iframe context.
77
+ */
78
+ hide(): void;
79
+ /**
80
+ * Destroys rendered iframe/popup DOM artifacts.
81
+ */
82
+ destroy(hostWindow: Window | null): void;
83
+ }
@@ -0,0 +1,106 @@
1
+ import type { ConsumerExports, Dimensions, GetPeerInstancesOptions, HostComponentRef, PropsDefinition, SerializedProps, SiblingInfo } from '../../types';
2
+ import { Messenger } from '../../communication/messenger';
3
+ import { FunctionBridge } from '../../communication/bridge';
4
+ import { createDeferred } from '../../utils/promise';
5
+ import type { ContextType } from '../../constants';
6
+ import type { NormalizedOptions } from './types';
7
+ interface PeerRequest {
8
+ uid: string;
9
+ tag: string;
10
+ options?: GetPeerInstancesOptions;
11
+ }
12
+ /**
13
+ * Callbacks used by transport to map inbound host messages to component behavior.
14
+ * @internal
15
+ */
16
+ export interface ConsumerTransportHandlers<X> {
17
+ onClose: () => Promise<void>;
18
+ onResize: (dimensions: Dimensions) => Promise<void>;
19
+ onFocus: () => Promise<void>;
20
+ onShow: () => Promise<void>;
21
+ onHide: () => Promise<void>;
22
+ onError: (error: Error) => void;
23
+ onExport: (exports: X) => void;
24
+ onConsumerExport: (data: unknown) => void;
25
+ onGetSiblings: (request: PeerRequest) => SiblingInfo[] | Promise<SiblingInfo[]>;
26
+ }
27
+ /**
28
+ * Owns consumer transport concerns (messenger, function bridge, trust management, handshake).
29
+ * @internal
30
+ */
31
+ export declare class ConsumerTransport<P extends Record<string, unknown>, X = unknown> {
32
+ private uid;
33
+ private options;
34
+ private resolveUrl;
35
+ private resolveUrlOrigin;
36
+ /** Messenger for host communication. */
37
+ messenger: Messenger;
38
+ /** Function bridge for serializing callable props across windows. */
39
+ bridge: FunctionBridge;
40
+ /** Connected host window reference. */
41
+ hostWindow: Window | null;
42
+ /** Origin of currently opened host content. */
43
+ openedHostDomain: string | null;
44
+ /** Dynamic origin currently trusted due to resolved URL. */
45
+ dynamicUrlTrustedOrigin: string | null;
46
+ /** Deferred host initialization handshake promise. */
47
+ initPromise: ReturnType<typeof createDeferred<void>> | null;
48
+ /** Whether host initialization handshake has completed. */
49
+ hostInitialized: boolean;
50
+ constructor(uid: string, options: NormalizedOptions<P>, resolveUrl: () => string, resolveUrlOrigin: (url: string) => string | null);
51
+ /**
52
+ * Builds trusted domains used to initialize messenger security checks.
53
+ */
54
+ private buildTrustedDomains;
55
+ /**
56
+ * Returns true when the domain option explicitly includes this origin.
57
+ */
58
+ isExplicitDomainTrust(origin: string): boolean;
59
+ /**
60
+ * Ensures the messenger trusts the origin for a resolved host URL.
61
+ */
62
+ syncTrustedDomainForUrl(url: string): void;
63
+ /**
64
+ * Returns the current host domain target used for messaging.
65
+ */
66
+ getHostDomain(): string;
67
+ /**
68
+ * Returns true when the host window is connected and not closed.
69
+ */
70
+ isHostConnected(): boolean;
71
+ /**
72
+ * Serializes host props while keeping function bridge references in sync.
73
+ */
74
+ serializePropsForHost(propsForHost: Record<string, unknown>, propDefinitions: PropsDefinition<Record<string, unknown>>, options?: {
75
+ finishBatch?: boolean;
76
+ }): SerializedProps;
77
+ /**
78
+ * Sends the current props snapshot to the host window when available.
79
+ */
80
+ sendPropsUpdateToHost(nextProps: P, propDefinitions: PropsDefinition<P>): Promise<void>;
81
+ /**
82
+ * Builds the window.name payload for host initialization.
83
+ */
84
+ buildWindowName(options: {
85
+ tag: string;
86
+ context: ContextType;
87
+ props: P;
88
+ propDefinitions: PropsDefinition<P>;
89
+ hostDomain?: string;
90
+ children?: Record<string, HostComponentRef>;
91
+ exports: ConsumerExports;
92
+ }): string;
93
+ /**
94
+ * Waits for the host to send the initialization handshake.
95
+ */
96
+ waitForHost(timeout: number, tag: string, onError: (error: Error) => void): Promise<void>;
97
+ /**
98
+ * Sets up host message handlers.
99
+ */
100
+ setupMessageHandlers(handlers: ConsumerTransportHandlers<X>): void;
101
+ /**
102
+ * Destroys transport resources.
103
+ */
104
+ destroy(): void;
105
+ }
106
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { ComponentOptions, Dimensions, PropsDefinition } from '../../types';
2
+ import type { ContextType } from '../../constants';
3
+ /**
4
+ * Normalized and validated component options.
5
+ * @internal
6
+ */
7
+ export interface NormalizedOptions<P extends Record<string, unknown>> {
8
+ tag: string;
9
+ url: string | ((props: P) => string);
10
+ props: PropsDefinition<P>;
11
+ defaultContext: ContextType;
12
+ dimensions: Dimensions | ((props: P) => Dimensions);
13
+ timeout: number;
14
+ domain?: ComponentOptions<P>['domain'];
15
+ allowedConsumerDomains?: ComponentOptions<P>['allowedConsumerDomains'];
16
+ containerTemplate?: ComponentOptions<P>['containerTemplate'];
17
+ prerenderTemplate?: ComponentOptions<P>['prerenderTemplate'];
18
+ eligible?: ComponentOptions<P>['eligible'];
19
+ validate?: ComponentOptions<P>['validate'];
20
+ attributes?: ComponentOptions<P>['attributes'];
21
+ style?: ComponentOptions<P>['style'];
22
+ autoResize?: ComponentOptions<P>['autoResize'];
23
+ children?: ComponentOptions<P>['children'];
24
+ }
@@ -14,9 +14,8 @@ import { EventEmitter } from '../events/emitter';
14
14
  * Consumer-side component implementation.
15
15
  *
16
16
  * @remarks
17
- * This class manages the lifecycle of a component from the consumer (embedding app) perspective.
18
- * It handles rendering the component into iframes or popups, communicating with
19
- * the host window via postMessage, and managing component state.
17
+ * This class coordinates focused subsystems for rendering, transport, and
18
+ * prop synchronization while preserving the existing public API.
20
19
  *
21
20
  * @typeParam P - The props type passed to the component
22
21
  * @typeParam X - The exports type that the host can expose to the consumer
@@ -48,35 +47,65 @@ export declare class ConsumerComponent<P extends Record<string, unknown>, X = un
48
47
  /** @internal */
49
48
  private options;
50
49
  /** @internal */
51
- private props;
50
+ private cleanup;
52
51
  /** @internal */
53
- private context;
52
+ private transport;
54
53
  /** @internal */
55
- private messenger;
54
+ private renderer;
56
55
  /** @internal */
57
- private bridge;
56
+ private propsPipeline;
58
57
  /** @internal */
59
- private cleanup;
58
+ private rendered;
60
59
  /** @internal */
61
- private hostWindow;
60
+ private destroyed;
62
61
  /** @internal */
63
- private openedHostDomain;
62
+ private get props();
64
63
  /** @internal */
65
- private dynamicUrlTrustedOrigin;
64
+ private set props(value);
66
65
  /** @internal */
67
- private iframe;
66
+ private get inputProps();
68
67
  /** @internal */
69
- private container;
68
+ private set inputProps(value);
70
69
  /** @internal */
71
- private prerenderElement;
70
+ private get pendingPropsUpdate();
72
71
  /** @internal */
73
- private initPromise;
72
+ private set pendingPropsUpdate(value);
74
73
  /** @internal */
75
- private hostInitialized;
74
+ private get context();
76
75
  /** @internal */
77
- private rendered;
76
+ private set context(value);
78
77
  /** @internal */
79
- private destroyed;
78
+ private get hostWindow();
79
+ /** @internal */
80
+ private set hostWindow(value);
81
+ /** @internal */
82
+ private get openedHostDomain();
83
+ /** @internal */
84
+ private set openedHostDomain(value);
85
+ /** @internal */
86
+ private get dynamicUrlTrustedOrigin();
87
+ /** @internal */
88
+ private set dynamicUrlTrustedOrigin(value);
89
+ /** @internal */
90
+ private get iframe();
91
+ /** @internal */
92
+ private set iframe(value);
93
+ /** @internal */
94
+ private get container();
95
+ /** @internal */
96
+ private set container(value);
97
+ /** @internal */
98
+ private get initPromise();
99
+ /** @internal */
100
+ private set initPromise(value);
101
+ /** @internal */
102
+ private get hostInitialized();
103
+ /** @internal */
104
+ private set hostInitialized(value);
105
+ /** @internal */
106
+ private get messenger();
107
+ /** @internal */
108
+ private get bridge();
80
109
  /**
81
110
  * Creates a new ConsumerComponent instance.
82
111
  *
@@ -84,11 +113,6 @@ export declare class ConsumerComponent<P extends Record<string, unknown>, X = un
84
113
  * @param props - Initial props to pass to the component
85
114
  */
86
115
  constructor(options: ComponentOptions<P>, props?: Partial<P>);
87
- /**
88
- * Builds the list of trusted domains for messenger communication.
89
- * @internal
90
- */
91
- private buildTrustedDomains;
92
116
  /**
93
117
  * Renders the component into a DOM container.
94
118
  *
@@ -112,14 +136,14 @@ export declare class ConsumerComponent<P extends Record<string, unknown>, X = un
112
136
  * Renders the component into a container in a different window.
113
137
  *
114
138
  * @remarks
115
- * Currently delegates to regular render. Full cross-window rendering
116
- * would require additional complexity.
139
+ * Only rendering into the current window is supported. Passing a
140
+ * different window throws explicitly to prevent silent misuse.
117
141
  *
118
- * @param _win - Target window (currently unused)
142
+ * @param win - Target window
119
143
  * @param container - CSS selector or HTMLElement to render into
120
144
  * @param context - Override the default rendering context
121
145
  */
122
- renderTo(_win: Window, container?: string | HTMLElement, context?: ContextType): Promise<void>;
146
+ renderTo(win: Window, container?: string | HTMLElement, context?: ContextType): Promise<void>;
123
147
  /**
124
148
  * Closes and destroys the component.
125
149
  *
@@ -163,6 +187,26 @@ export declare class ConsumerComponent<P extends Record<string, unknown>, X = un
163
187
  * @param newProps - Partial props object to merge with existing props
164
188
  */
165
189
  updateProps(newProps: Partial<P>): Promise<void>;
190
+ /**
191
+ * Applies a props update and synchronizes it to the host when connected.
192
+ * @internal
193
+ */
194
+ private applyPropsUpdate;
195
+ /**
196
+ * Prevents origin changes after render for security and routing consistency.
197
+ * @internal
198
+ */
199
+ private assertStableRenderedOrigin;
200
+ /**
201
+ * Sends the current props snapshot to the host window when available.
202
+ * @internal
203
+ */
204
+ private sendPropsUpdateToHost;
205
+ /**
206
+ * Emits prop update lifecycle hooks.
207
+ * @internal
208
+ */
209
+ private emitPropsUpdated;
166
210
  /**
167
211
  * Creates a clone of this instance with the same props.
168
212
  *
@@ -281,16 +325,6 @@ export declare class ConsumerComponent<P extends Record<string, unknown>, X = un
281
325
  * @internal
282
326
  */
283
327
  private setupMessageHandlers;
284
- /**
285
- * Sets up host lifecycle command handlers.
286
- * @internal
287
- */
288
- private setupHostLifecycleHandlers;
289
- /**
290
- * Sets up host data exchange handlers.
291
- * @internal
292
- */
293
- private setupHostDataHandlers;
294
328
  /**
295
329
  * Gets sibling component instances for a request.
296
330
  * @internal
@@ -180,6 +180,11 @@ export declare class HostComponent<P extends Record<string, unknown>> {
180
180
  * @internal
181
181
  */
182
182
  private setupMessageHandlers;
183
+ /**
184
+ * Removes stale prop keys that are no longer present in the latest consumer payload.
185
+ * @internal
186
+ */
187
+ private removeStaleHostProps;
183
188
  /**
184
189
  * Destroys the host component and cleans up resources.
185
190
  */