@pulse-editor/shared-utils 0.0.1 → 0.1.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.
- package/dist/imc/inter-module-communication.d.ts +18 -0
- package/dist/imc/inter-module-communication.js +1 -0
- package/dist/imc/message-receiver.d.ts +11 -0
- package/dist/imc/message-receiver.js +1 -0
- package/dist/imc/message-sender.d.ts +12 -0
- package/dist/imc/message-sender.js +1 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.js +1 -0
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/constants.js +1 -0
- package/dist/types/types.d.ts +119 -0
- package/dist/types/types.js +1 -0
- package/package.json +30 -13
- package/src/inter-module-communication.ts +0 -137
- package/src/main.ts +0 -5
- package/src/message-receiver.ts +0 -110
- package/src/message-sender.ts +0 -100
- package/tsconfig.json +0 -28
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IMCMessageTypeEnum, ReceiverHandlerMap } from "../types/types";
|
|
2
|
+
export declare class InterModuleCommunication {
|
|
3
|
+
private thisWindow;
|
|
4
|
+
private otherWindow;
|
|
5
|
+
private thisPendingTasks;
|
|
6
|
+
private otherPendingMessages;
|
|
7
|
+
private receiver;
|
|
8
|
+
private sender;
|
|
9
|
+
private moduleName;
|
|
10
|
+
private receiverHandlerMap;
|
|
11
|
+
private listener;
|
|
12
|
+
constructor(moduleName: string);
|
|
13
|
+
initThisWindow(window: Window): void;
|
|
14
|
+
initOtherWindow(window: Window): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
sendMessage(type: IMCMessageTypeEnum, payload?: any, abortSignal?: AbortSignal): Promise<any>;
|
|
17
|
+
updateReceiverHandlerMap(receiverHandlerMap: ReceiverHandlerMap): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{IMCMessageTypeEnum as e}from"../types/types.js";import{messageTimeout as s}from"../types/constants.js";import{MessageReceiver as i}from"./message-receiver.js";import{MessageSender as t}from"./message-sender.js";class r{constructor(e){this.moduleName=e}initThisWindow(e){this.thisWindow=e,this.receiverHandlerMap=new Map,this.thisPendingTasks=new Map;const s=new i(this.receiverHandlerMap,this.thisPendingTasks,this.moduleName);this.receiver=s,this.listener=e=>{if(!s)throw new Error("Receiver not initialized at module "+this.moduleName);const i=e.data,t=e.source;s.receiveMessage(t,i)},e.addEventListener("message",this.listener),console.log("Adding IMC listener in "+this.moduleName)}initOtherWindow(i){this.otherWindow=i,this.otherPendingMessages=new Map;const r=new t(i,s,this.otherPendingMessages,this.moduleName);if(this.sender=r,!this.receiverHandlerMap)throw new Error("You must initialize the current window first.");this.receiverHandlerMap.set(e.Acknowledge,(async(e,s)=>{const i=this.otherPendingMessages?.get(s.id);i&&(i.resolve(s.payload),this.otherPendingMessages?.delete(s.id))}))}close(){this.listener&&window.removeEventListener("message",this.listener)}async sendMessage(e,s,i){if(!this.sender)throw new Error("Sender not initialized");return await this.sender.sendMessage(e,s,i)}updateReceiverHandlerMap(e){if(!this.receiver)throw new Error("Receiver not initialized");this.receiverHandlerMap?.clear(),e.forEach(((e,s)=>{this.receiverHandlerMap?.set(s,e)}))}}export{r as InterModuleCommunication};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IMCMessage, ReceiverHandlerMap } from "../types/types";
|
|
2
|
+
export declare class MessageReceiver {
|
|
3
|
+
private handlerMap;
|
|
4
|
+
private pendingTasks;
|
|
5
|
+
private moduleName;
|
|
6
|
+
constructor(listenerMap: ReceiverHandlerMap, pendingTasks: Map<string, {
|
|
7
|
+
controller: AbortController;
|
|
8
|
+
}>, moduleInfo: string);
|
|
9
|
+
receiveMessage(senderWindow: Window, message: IMCMessage): void;
|
|
10
|
+
private acknowledgeSender;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{IMCMessageTypeEnum as e}from"../types/types.js";class s{constructor(e,s,o){this.handlerMap=e,this.pendingTasks=s,this.moduleName=o}receiveMessage(s,o){if(this.moduleName===o.from)return;if("development"===process.env.NODE_ENV&&console.log(`Module ${this.moduleName} received message from module ${o.from}:\n ${JSON.stringify(o)}`),o.type===e.Abort){const e=o.id,s=this.pendingTasks.get(e);return void(s&&(console.log("Aborting task",e),s.controller.abort(),this.pendingTasks.delete(e)))}const t=this.handlerMap.get(o.type);if(t){const n=new AbortController,i=n.signal;this.pendingTasks.set(o.id,{controller:n});t(s,o,i).then((t=>{i.aborted||o.type!==e.Acknowledge&&this.acknowledgeSender(s,o.id,t)})).catch((t=>{const n={id:o.id,type:e.Error,payload:t.message,from:this.moduleName};s.postMessage(n,"*")})).finally((()=>{this.pendingTasks.delete(o.id)}))}}acknowledgeSender(s,o,t){const n={id:o,type:e.Acknowledge,payload:t,from:this.moduleName};s.postMessage(n,"*")}}export{s as MessageReceiver};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IMCMessageTypeEnum } from "../types/types";
|
|
2
|
+
export declare class MessageSender {
|
|
3
|
+
private targetWindow;
|
|
4
|
+
private timeout;
|
|
5
|
+
private pendingMessages;
|
|
6
|
+
private moduleName;
|
|
7
|
+
constructor(targetWindow: Window, timeout: number, pendingMessages: Map<string, {
|
|
8
|
+
resolve: (result: any) => void;
|
|
9
|
+
reject: () => void;
|
|
10
|
+
}>, moduleInfo: string);
|
|
11
|
+
sendMessage(handlingType: IMCMessageTypeEnum, payload?: any, abortSignal?: AbortSignal): Promise<any>;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{IMCMessageTypeEnum as e}from"../types/types.js";class t{constructor(e,t,s,r){this.targetWindow=e,this.timeout=t,this.pendingMessages=s,this.moduleName=r}async sendMessage(t,s,r){const o=(new Date).getTime().toString(),i={id:o,type:t,payload:s,from:this.moduleName};return new Promise(((t,s)=>{if(r?.aborted)return s(new Error("Request aborted"));const n=()=>{this.pendingMessages.delete(o),this.targetWindow.postMessage({id:o,type:e.Abort,payload:JSON.stringify({status:"Task aborted",data:null})},"*"),s(new Error("Request aborted"))};r?.addEventListener("abort",n),this.pendingMessages.set(o,{resolve:t,reject:s}),this.targetWindow.postMessage(i,"*");const a=setTimeout((()=>{this.pendingMessages.delete(o),r?.removeEventListener("abort",n),s(new Error("Communication with Pulse Editor timeout."))}),this.timeout),d=this.pendingMessages.get(o);d&&(d.resolve=e=>{clearTimeout(a),r?.removeEventListener("abort",n),t(e)},d.reject=()=>{clearTimeout(a),r?.removeEventListener("abort",n),s()})}))}}export{t as MessageSender};
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{InterModuleCommunication}from"./imc/inter-module-communication.js";export{MessageSender}from"./imc/message-sender.js";export{MessageReceiver}from"./imc/message-receiver.js";export{AccessEnum,ExtensionTypeEnum,IMCMessageTypeEnum,NotificationTypeEnum}from"./types/types.js";export{messageTimeout}from"./types/constants.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const messageTimeout = 300000;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=3e5;export{e as messageTimeout};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export declare enum IMCMessageTypeEnum {
|
|
2
|
+
WriteViewFile = "write-view-file",
|
|
3
|
+
RequestViewFile = "request-view-file",
|
|
4
|
+
Fetch = "fetch",
|
|
5
|
+
Notification = "notification",
|
|
6
|
+
ThemeChange = "theme-change",
|
|
7
|
+
InstallAgent = "install-agent",
|
|
8
|
+
RunAgentMethod = "run-agent-method",
|
|
9
|
+
InstallAgentTool = "install-agent-tool",
|
|
10
|
+
OCR = "ocr",
|
|
11
|
+
RequestTerminal = "request-terminal",
|
|
12
|
+
Ready = "ready",
|
|
13
|
+
Loaded = "loaded",
|
|
14
|
+
Acknowledge = "acknowledge",
|
|
15
|
+
Abort = "abort",
|
|
16
|
+
Error = "error"
|
|
17
|
+
}
|
|
18
|
+
export type IMCMessage = {
|
|
19
|
+
id: string;
|
|
20
|
+
from: string;
|
|
21
|
+
type: IMCMessageTypeEnum;
|
|
22
|
+
payload?: any;
|
|
23
|
+
};
|
|
24
|
+
export type ReceiverHandlerMap = Map<IMCMessageTypeEnum, {
|
|
25
|
+
(senderWindow: Window, message: IMCMessage, abortSignal?: AbortSignal): Promise<any>;
|
|
26
|
+
}>;
|
|
27
|
+
export type TextFileSelection = {
|
|
28
|
+
lineStart: number;
|
|
29
|
+
lineEnd: number;
|
|
30
|
+
text: string;
|
|
31
|
+
};
|
|
32
|
+
export type FileViewModel = {
|
|
33
|
+
fileContent: string;
|
|
34
|
+
filePath: string;
|
|
35
|
+
selections?: TextFileSelection[];
|
|
36
|
+
isActive: boolean;
|
|
37
|
+
};
|
|
38
|
+
export type FetchPayload = {
|
|
39
|
+
uri: string;
|
|
40
|
+
options?: RequestInit;
|
|
41
|
+
};
|
|
42
|
+
export declare enum NotificationTypeEnum {
|
|
43
|
+
Success = "success",
|
|
44
|
+
Error = "error",
|
|
45
|
+
Info = "info",
|
|
46
|
+
Warning = "warning"
|
|
47
|
+
}
|
|
48
|
+
export declare enum ExtensionTypeEnum {
|
|
49
|
+
Generic = "generic",
|
|
50
|
+
FileView = "file-view",
|
|
51
|
+
ConsoleView = "console-view"
|
|
52
|
+
}
|
|
53
|
+
export type ExtensionConfig = {
|
|
54
|
+
id: string;
|
|
55
|
+
version: string;
|
|
56
|
+
author?: string;
|
|
57
|
+
displayName?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
materialIcon?: string;
|
|
60
|
+
extensionType?: ExtensionTypeEnum;
|
|
61
|
+
fileTypes?: string[];
|
|
62
|
+
preview?: string;
|
|
63
|
+
enabledPlatforms?: Record<string, boolean>;
|
|
64
|
+
};
|
|
65
|
+
export type Agent = {
|
|
66
|
+
name: string;
|
|
67
|
+
version: string;
|
|
68
|
+
systemPrompt: string;
|
|
69
|
+
availableMethods: AgentMethod[];
|
|
70
|
+
LLMConfig: LLMConfig;
|
|
71
|
+
description: string;
|
|
72
|
+
tools?: AgentTool[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* An agent method is a sub task that an agent can perform.
|
|
76
|
+
*/
|
|
77
|
+
export type AgentMethod = {
|
|
78
|
+
access: AccessEnum;
|
|
79
|
+
name: string;
|
|
80
|
+
parameters: Record<string, AgentVariable>;
|
|
81
|
+
prompt: string;
|
|
82
|
+
returns: Record<string, AgentVariable>;
|
|
83
|
+
LLMConfig?: LLMConfig;
|
|
84
|
+
};
|
|
85
|
+
export type AgentVariable = {
|
|
86
|
+
type: AgentVariableType;
|
|
87
|
+
description: string;
|
|
88
|
+
};
|
|
89
|
+
export type AgentVariableType = "string" | "number" | "boolean" | AgentVariableTypeArray;
|
|
90
|
+
type AgentVariableTypeArray = {
|
|
91
|
+
size: number;
|
|
92
|
+
elementType: AgentVariableType;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* A tool that agent can use during method execution.
|
|
96
|
+
*
|
|
97
|
+
* This is linked to a callback function created by user,
|
|
98
|
+
* tool developer, or extension.
|
|
99
|
+
*
|
|
100
|
+
* The tool may optionally return a value to running
|
|
101
|
+
* agent method.
|
|
102
|
+
*/
|
|
103
|
+
export type AgentTool = {
|
|
104
|
+
access: AccessEnum;
|
|
105
|
+
name: string;
|
|
106
|
+
description: string;
|
|
107
|
+
parameters: Record<string, AgentVariable>;
|
|
108
|
+
returns: Record<string, AgentVariable>;
|
|
109
|
+
};
|
|
110
|
+
export type LLMConfig = {
|
|
111
|
+
provider: string;
|
|
112
|
+
modelName: string;
|
|
113
|
+
temperature: number;
|
|
114
|
+
};
|
|
115
|
+
export declare enum AccessEnum {
|
|
116
|
+
public = "public",
|
|
117
|
+
private = "private"
|
|
118
|
+
}
|
|
119
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e,i,n,t;!function(e){e.WriteViewFile="write-view-file",e.RequestViewFile="request-view-file",e.Fetch="fetch",e.Notification="notification",e.ThemeChange="theme-change",e.InstallAgent="install-agent",e.RunAgentMethod="run-agent-method",e.InstallAgentTool="install-agent-tool",e.OCR="ocr",e.RequestTerminal="request-terminal",e.Ready="ready",e.Loaded="loaded",e.Acknowledge="acknowledge",e.Abort="abort",e.Error="error"}(e||(e={})),function(e){e.Success="success",e.Error="error",e.Info="info",e.Warning="warning"}(i||(i={})),function(e){e.Generic="generic",e.FileView="file-view",e.ConsoleView="console-view"}(n||(n={})),function(e){e.public="public",e.private="private"}(t||(t={}));export{t as AccessEnum,n as ExtensionTypeEnum,e as IMCMessageTypeEnum,i as NotificationTypeEnum};
|
package/package.json
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@pulse-editor/shared-utils",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@pulse-editor/shared-utils",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/main.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "rollup -c",
|
|
14
|
+
"lint": "eslint ."
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@babel/core": "^7.26.10",
|
|
18
|
+
"@babel/preset-env": "^7.26.9",
|
|
19
|
+
"@eslint/js": "^9.25.0",
|
|
20
|
+
"@rollup/plugin-babel": "^6.0.4",
|
|
21
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
22
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
23
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
24
|
+
"@types/node": "^22.13.1",
|
|
25
|
+
"eslint": "^9.25.0",
|
|
26
|
+
"rollup": "^4.40.0",
|
|
27
|
+
"typescript": "^5.8.3",
|
|
28
|
+
"typescript-eslint": "^8.30.1"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
messageTimeout,
|
|
3
|
-
IMCMessage,
|
|
4
|
-
IMCMessageTypeEnum,
|
|
5
|
-
ReceiverHandlerMap,
|
|
6
|
-
} from "@pulse-editor/types";
|
|
7
|
-
import { MessageReceiver } from "./message-receiver";
|
|
8
|
-
import { MessageSender } from "./message-sender";
|
|
9
|
-
|
|
10
|
-
export class InterModuleCommunication {
|
|
11
|
-
private thisWindow: Window | undefined;
|
|
12
|
-
private otherWindow: Window | undefined;
|
|
13
|
-
|
|
14
|
-
/* Wait current module to finish tasks to return a response or acknowledgement. */
|
|
15
|
-
private thisPendingTasks:
|
|
16
|
-
| Map<
|
|
17
|
-
string,
|
|
18
|
-
{
|
|
19
|
-
controller: AbortController;
|
|
20
|
-
}
|
|
21
|
-
>
|
|
22
|
-
| undefined;
|
|
23
|
-
|
|
24
|
-
/* Wait the other module to return a response or acknowledgement. */
|
|
25
|
-
private otherPendingMessages:
|
|
26
|
-
| Map<
|
|
27
|
-
string,
|
|
28
|
-
{
|
|
29
|
-
resolve: (result: any) => void;
|
|
30
|
-
reject: () => void;
|
|
31
|
-
}
|
|
32
|
-
>
|
|
33
|
-
| undefined;
|
|
34
|
-
|
|
35
|
-
private receiver: MessageReceiver | undefined;
|
|
36
|
-
private sender: MessageSender | undefined;
|
|
37
|
-
|
|
38
|
-
private moduleName: string;
|
|
39
|
-
|
|
40
|
-
private receiverHandlerMap: ReceiverHandlerMap | undefined;
|
|
41
|
-
|
|
42
|
-
private listener: ((event: MessageEvent) => void) | undefined;
|
|
43
|
-
|
|
44
|
-
constructor(moduleName: string) {
|
|
45
|
-
this.moduleName = moduleName;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/* Initialize a receiver to receive message. */
|
|
49
|
-
public initThisWindow(window: Window) {
|
|
50
|
-
this.thisWindow = window;
|
|
51
|
-
this.receiverHandlerMap = new Map();
|
|
52
|
-
this.thisPendingTasks = new Map();
|
|
53
|
-
|
|
54
|
-
const receiver = new MessageReceiver(
|
|
55
|
-
this.receiverHandlerMap,
|
|
56
|
-
this.thisPendingTasks,
|
|
57
|
-
this.moduleName
|
|
58
|
-
);
|
|
59
|
-
this.receiver = receiver;
|
|
60
|
-
|
|
61
|
-
this.listener = (event: MessageEvent<IMCMessage>) => {
|
|
62
|
-
if (!receiver) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
"Receiver not initialized at module " + this.moduleName
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const message = event.data;
|
|
69
|
-
const win = event.source as Window;
|
|
70
|
-
receiver.receiveMessage(win, message);
|
|
71
|
-
};
|
|
72
|
-
window.addEventListener("message", this.listener);
|
|
73
|
-
console.log("Adding IMC listener in " + this.moduleName);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/* Initialize a sender to send message ot the other window. */
|
|
77
|
-
public initOtherWindow(window: Window) {
|
|
78
|
-
this.otherWindow = window;
|
|
79
|
-
this.otherPendingMessages = new Map();
|
|
80
|
-
|
|
81
|
-
const sender = new MessageSender(
|
|
82
|
-
window,
|
|
83
|
-
messageTimeout,
|
|
84
|
-
this.otherPendingMessages,
|
|
85
|
-
this.moduleName
|
|
86
|
-
);
|
|
87
|
-
this.sender = sender;
|
|
88
|
-
|
|
89
|
-
if (!this.receiverHandlerMap) {
|
|
90
|
-
throw new Error("You must initialize the current window first.");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Add an acknowledgement handler in current window's receiver for results of sent messages.
|
|
94
|
-
// The current window must be initialized first. i.e. call initThisWindow() before initOtherWindow().
|
|
95
|
-
this.receiverHandlerMap.set(
|
|
96
|
-
IMCMessageTypeEnum.Acknowledge,
|
|
97
|
-
async (senderWindow: Window, message: IMCMessage) => {
|
|
98
|
-
const pendingMessage = this.otherPendingMessages?.get(message.id);
|
|
99
|
-
if (pendingMessage) {
|
|
100
|
-
pendingMessage.resolve(message.payload);
|
|
101
|
-
this.otherPendingMessages?.delete(message.id);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
public close() {
|
|
108
|
-
if (this.listener) {
|
|
109
|
-
window.removeEventListener("message", this.listener);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
public async sendMessage(
|
|
114
|
-
type: IMCMessageTypeEnum,
|
|
115
|
-
payload?: any,
|
|
116
|
-
abortSignal?: AbortSignal
|
|
117
|
-
): Promise<any> {
|
|
118
|
-
if (!this.sender) {
|
|
119
|
-
throw new Error("Sender not initialized");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return await this.sender.sendMessage(type, payload, abortSignal);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
public updateReceiverHandlerMap(
|
|
126
|
-
receiverHandlerMap: ReceiverHandlerMap
|
|
127
|
-
): void {
|
|
128
|
-
if (!this.receiver) {
|
|
129
|
-
throw new Error("Receiver not initialized");
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
this.receiverHandlerMap?.clear();
|
|
133
|
-
receiverHandlerMap.forEach((value, key) => {
|
|
134
|
-
this.receiverHandlerMap?.set(key, value);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
package/src/main.ts
DELETED
package/src/message-receiver.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IMCMessage,
|
|
3
|
-
IMCMessageTypeEnum,
|
|
4
|
-
ReceiverHandlerMap,
|
|
5
|
-
} from "@pulse-editor/types";
|
|
6
|
-
|
|
7
|
-
export class MessageReceiver {
|
|
8
|
-
private handlerMap: ReceiverHandlerMap;
|
|
9
|
-
private pendingTasks: Map<
|
|
10
|
-
string,
|
|
11
|
-
{
|
|
12
|
-
controller: AbortController;
|
|
13
|
-
}
|
|
14
|
-
>;
|
|
15
|
-
private moduleName: string;
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
listenerMap: ReceiverHandlerMap,
|
|
19
|
-
pendingTasks: Map<
|
|
20
|
-
string,
|
|
21
|
-
{
|
|
22
|
-
controller: AbortController;
|
|
23
|
-
}
|
|
24
|
-
>,
|
|
25
|
-
moduleInfo: string
|
|
26
|
-
) {
|
|
27
|
-
this.handlerMap = listenerMap;
|
|
28
|
-
this.pendingTasks = pendingTasks;
|
|
29
|
-
this.moduleName = moduleInfo;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public receiveMessage(senderWindow: Window, message: IMCMessage) {
|
|
33
|
-
// Not handling messages from self
|
|
34
|
-
if (this.moduleName === message.from) return;
|
|
35
|
-
|
|
36
|
-
// Log the message in dev mode
|
|
37
|
-
if (process.env.NODE_ENV === "development") {
|
|
38
|
-
console.log(
|
|
39
|
-
`Module ${this.moduleName} received message from module ${message.from}:\n ${JSON.stringify(
|
|
40
|
-
message
|
|
41
|
-
)}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Abort the task if the message type is Abort
|
|
46
|
-
if (message.type === IMCMessageTypeEnum.Abort) {
|
|
47
|
-
const id = message.id;
|
|
48
|
-
const pendingTask = this.pendingTasks.get(id);
|
|
49
|
-
|
|
50
|
-
if (pendingTask) {
|
|
51
|
-
console.log("Aborting task", id);
|
|
52
|
-
pendingTask.controller.abort();
|
|
53
|
-
this.pendingTasks.delete(id);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const handler = this.handlerMap.get(message.type);
|
|
60
|
-
if (handler) {
|
|
61
|
-
// Create abort controller to listen for abort signal from sender.
|
|
62
|
-
// Then save the message id and abort controller to the pending tasks.
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const signal = controller.signal;
|
|
65
|
-
this.pendingTasks.set(message.id, {
|
|
66
|
-
controller,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const promise = handler(senderWindow, message, signal);
|
|
70
|
-
promise
|
|
71
|
-
.then((result) => {
|
|
72
|
-
// Don't send the result if the task has been aborted
|
|
73
|
-
if (signal.aborted) return;
|
|
74
|
-
|
|
75
|
-
// Acknowledge the sender with the result if the message type is not Acknowledge
|
|
76
|
-
if (message.type !== IMCMessageTypeEnum.Acknowledge) {
|
|
77
|
-
this.acknowledgeSender(senderWindow, message.id, result);
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
.catch((error) => {
|
|
81
|
-
// Send the error message to the sender
|
|
82
|
-
const errMsg: IMCMessage = {
|
|
83
|
-
id: message.id,
|
|
84
|
-
type: IMCMessageTypeEnum.Error,
|
|
85
|
-
payload: error.message,
|
|
86
|
-
from: this.moduleName,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
senderWindow.postMessage(errMsg, "*");
|
|
90
|
-
})
|
|
91
|
-
.finally(() => {
|
|
92
|
-
this.pendingTasks.delete(message.id);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private acknowledgeSender(
|
|
98
|
-
senderWindow: Window,
|
|
99
|
-
id: string,
|
|
100
|
-
payload: any
|
|
101
|
-
): void {
|
|
102
|
-
const message: IMCMessage = {
|
|
103
|
-
id,
|
|
104
|
-
type: IMCMessageTypeEnum.Acknowledge,
|
|
105
|
-
payload: payload,
|
|
106
|
-
from: this.moduleName,
|
|
107
|
-
};
|
|
108
|
-
senderWindow.postMessage(message, "*");
|
|
109
|
-
}
|
|
110
|
-
}
|
package/src/message-sender.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { IMCMessage, IMCMessageTypeEnum } from "@pulse-editor/types";
|
|
2
|
-
|
|
3
|
-
export class MessageSender {
|
|
4
|
-
private targetWindow: Window;
|
|
5
|
-
private timeout: number;
|
|
6
|
-
|
|
7
|
-
private pendingMessages: Map<
|
|
8
|
-
string,
|
|
9
|
-
{ resolve: (result: any) => void; reject: () => void }
|
|
10
|
-
>;
|
|
11
|
-
|
|
12
|
-
private moduleName: string;
|
|
13
|
-
|
|
14
|
-
constructor(
|
|
15
|
-
targetWindow: Window,
|
|
16
|
-
timeout: number,
|
|
17
|
-
pendingMessages: Map<
|
|
18
|
-
string,
|
|
19
|
-
{ resolve: (result: any) => void; reject: () => void }
|
|
20
|
-
>,
|
|
21
|
-
moduleInfo: string
|
|
22
|
-
) {
|
|
23
|
-
this.targetWindow = targetWindow;
|
|
24
|
-
this.timeout = timeout;
|
|
25
|
-
|
|
26
|
-
this.pendingMessages = pendingMessages;
|
|
27
|
-
this.moduleName = moduleInfo;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public async sendMessage(
|
|
31
|
-
handlingType: IMCMessageTypeEnum,
|
|
32
|
-
payload?: any,
|
|
33
|
-
abortSignal?: AbortSignal
|
|
34
|
-
): Promise<any> {
|
|
35
|
-
// Generate a unique id for the message using timestamp
|
|
36
|
-
const id = new Date().getTime().toString();
|
|
37
|
-
const message: IMCMessage = {
|
|
38
|
-
id,
|
|
39
|
-
type: handlingType,
|
|
40
|
-
payload: payload,
|
|
41
|
-
from: this.moduleName,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
// If the signal is already aborted, reject immediately
|
|
46
|
-
if (abortSignal?.aborted) {
|
|
47
|
-
return reject(new Error("Request aborted"));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const abortHandler = () => {
|
|
51
|
-
this.pendingMessages.delete(id);
|
|
52
|
-
// Notify the target window that the request has been aborted
|
|
53
|
-
this.targetWindow.postMessage(
|
|
54
|
-
{
|
|
55
|
-
id,
|
|
56
|
-
type: IMCMessageTypeEnum.Abort,
|
|
57
|
-
payload: JSON.stringify({
|
|
58
|
-
status: "Task aborted",
|
|
59
|
-
data: null,
|
|
60
|
-
}),
|
|
61
|
-
},
|
|
62
|
-
"*"
|
|
63
|
-
);
|
|
64
|
-
reject(new Error("Request aborted"));
|
|
65
|
-
};
|
|
66
|
-
// Attach abort listener
|
|
67
|
-
abortSignal?.addEventListener("abort", abortHandler);
|
|
68
|
-
|
|
69
|
-
// Send message
|
|
70
|
-
this.pendingMessages.set(id, {
|
|
71
|
-
resolve,
|
|
72
|
-
reject,
|
|
73
|
-
});
|
|
74
|
-
this.targetWindow.postMessage(message, "*");
|
|
75
|
-
|
|
76
|
-
// Check timeout
|
|
77
|
-
const timeoutId = setTimeout(() => {
|
|
78
|
-
this.pendingMessages.delete(id);
|
|
79
|
-
abortSignal?.removeEventListener("abort", abortHandler);
|
|
80
|
-
reject(new Error("Communication with Pulse Editor timeout."));
|
|
81
|
-
}, this.timeout);
|
|
82
|
-
|
|
83
|
-
// Ensure cleanup on resolution
|
|
84
|
-
const currentMessage = this.pendingMessages.get(id);
|
|
85
|
-
if (currentMessage) {
|
|
86
|
-
currentMessage.resolve = (result) => {
|
|
87
|
-
clearTimeout(timeoutId);
|
|
88
|
-
abortSignal?.removeEventListener("abort", abortHandler);
|
|
89
|
-
resolve(result);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
currentMessage.reject = () => {
|
|
93
|
-
clearTimeout(timeoutId);
|
|
94
|
-
abortSignal?.removeEventListener("abort", abortHandler);
|
|
95
|
-
reject();
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"strict": true,
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"types": [
|
|
10
|
-
"react",
|
|
11
|
-
"node"
|
|
12
|
-
],
|
|
13
|
-
"baseUrl": ".",
|
|
14
|
-
"paths": {
|
|
15
|
-
"*": [
|
|
16
|
-
"node_modules/*",
|
|
17
|
-
"dist/*"
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"include": [
|
|
22
|
-
"src/**/*"
|
|
23
|
-
],
|
|
24
|
-
"exclude": [
|
|
25
|
-
"node_modules",
|
|
26
|
-
"dist",
|
|
27
|
-
]
|
|
28
|
-
}
|