Skip to content

Commit 9a03469

Browse files
committed
Add ignore signal and improve multi-channel polyimc handling
1 parent a9092e5 commit 9a03469

File tree

18 files changed

+264
-116
lines changed

18 files changed

+264
-116
lines changed

.changeset/cuddly-books-send.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@pulse-editor/shared-utils": patch
3+
"@pulse-editor/react-api": patch
4+
---
5+
6+
Add ignore signal and improve multi-channel polyimc handling

.changeset/pre.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"crisp-impalas-search",
2626
"cruel-waves-double",
2727
"cruel-zoos-play",
28+
"cuddly-books-send",
2829
"curvy-places-wash",
2930
"cute-foxes-wink",
3031
"dirty-swans-rescue",

npm-packages/react-api/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# @pulse-editor/react-api
22

3+
## 0.1.1-alpha.52
4+
5+
### Patch Changes
6+
7+
- Add ignore signal and improve multi-channel polyimc handling
8+
- Updated dependencies
9+
- @pulse-editor/shared-utils@0.1.1-alpha.52
10+
311
## 0.1.1-alpha.51
412

513
### Patch Changes

npm-packages/react-api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pulse-editor/react-api",
3-
"version": "0.1.1-alpha.51",
3+
"version": "0.1.1-alpha.52",
44
"main": "dist/main.js",
55
"files": [
66
"dist"
@@ -37,7 +37,7 @@
3737
"typescript-eslint": "^8.30.1"
3838
},
3939
"peerDependencies": {
40-
"@pulse-editor/shared-utils": "0.1.1-alpha.51",
40+
"@pulse-editor/shared-utils": "0.1.1-alpha.52",
4141
"react": "^19.0.0",
4242
"react-dom": "^19.0.0"
4343
}

npm-packages/react-api/src/hooks/editor/use-register-action.ts

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -118,42 +118,43 @@ export default function useRegisterAction(
118118
const { name: requestedName, args }: { name: string; args: any } =
119119
message.payload;
120120

121-
if (actionInfo.name === requestedName) {
122-
// Validate parameters
123-
const actionParams = actionInfo.parameters ?? {};
124-
if (Object.keys(args).length !== Object.keys(actionParams).length) {
125-
throw new Error(
126-
`Invalid number of parameters: expected ${
127-
Object.keys(actionParams).length
128-
}, got ${Object.keys(args).length}`
129-
);
130-
}
121+
if (actionInfo.name !== requestedName) {
122+
throw new Error("Message ignored by receiver");
123+
}
124+
// Validate parameters
125+
const actionParams = actionInfo.parameters ?? {};
126+
if (Object.keys(args).length !== Object.keys(actionParams).length) {
127+
throw new Error(
128+
`Invalid number of parameters: expected ${
129+
Object.keys(actionParams).length
130+
}, got ${Object.keys(args).length}`
131+
);
132+
}
131133

132-
// Check types
133-
for (const [key, value] of Object.entries(args)) {
134-
if (actionParams[key] === undefined) {
135-
throw new Error(`Invalid parameter: ${key}`);
136-
}
137-
if (typeof value !== actionParams[key].type) {
138-
throw new Error(
139-
`Invalid type for parameter ${key}: expected ${
140-
actionParams[key].type
141-
}, got ${typeof value}. Value received: ${value}`
142-
);
143-
}
134+
// Check types
135+
for (const [key, value] of Object.entries(args)) {
136+
if (actionParams[key] === undefined) {
137+
throw new Error(`Invalid parameter: ${key}`);
144138
}
145-
146-
// If extension is ready, execute immediately
147-
if (isExtReady) {
148-
const result = await executeAction(args);
149-
return result;
139+
if (typeof value !== actionParams[key].type) {
140+
throw new Error(
141+
`Invalid type for parameter ${key}: expected ${
142+
actionParams[key].type
143+
}, got ${typeof value}. Value received: ${value}`
144+
);
150145
}
146+
}
151147

152-
// Otherwise, queue the command and return when executed
153-
return new Promise((resolve) => {
154-
commandQueue.current.push({ args, resolve });
155-
});
148+
// If extension is ready, execute immediately
149+
if (isExtReady) {
150+
const result = await executeAction(args);
151+
return result;
156152
}
153+
154+
// Otherwise, queue the command and return when executed
155+
return new Promise((resolve) => {
156+
commandQueue.current.push({ args, resolve });
157+
});
157158
},
158159
],
159160
]);

npm-packages/shared-utils/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @pulse-editor/shared-utils
22

3+
## 0.1.1-alpha.52
4+
5+
### Patch Changes
6+
7+
- Add ignore signal and improve multi-channel polyimc handling
8+
39
## 0.1.1-alpha.51
410

511
### Patch Changes

npm-packages/shared-utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pulse-editor/shared-utils",
3-
"version": "0.1.1-alpha.51",
3+
"version": "0.1.1-alpha.52",
44
"main": "dist/main.js",
55
"files": [
66
"dist"

npm-packages/shared-utils/src/imc/inter-module-communication.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,18 @@ export class InterModuleCommunication {
5050

5151
const receiver = new MessageReceiver(
5252
this.receiverHandlerMap,
53-
this.thisWindowId
53+
this.thisWindowId,
54+
this.intent
5455
);
5556
this.receiver = receiver;
5657

5758
this.messageRecords = new Map<string, IMCMessage>();
5859

5960
this.listener = (event: MessageEvent<IMCMessage>) => {
60-
const messageId = event.data.messageId;
61-
const channelId = event.data.channelId;
62-
const type = event.data.type;
61+
const message = event.data;
62+
const messageId = message.messageId;
63+
const channelId = message.channelId;
64+
const type = message.type;
6365

6466
// Return if the channel ID exists but does not match the current channel ID
6567
if (this.channelId !== undefined && channelId !== this.channelId) {
@@ -74,20 +76,19 @@ export class InterModuleCommunication {
7476
`[${
7577
this.thisWindowId
7678
}]: Duplicate message received with message ID: ${messageId}. Ignoring this message. Message: ${JSON.stringify(
77-
event.data
79+
message
7880
)}`
7981
);
8082
return;
8183
}
82-
this.messageRecords?.set(messageId, event.data);
84+
this.messageRecords?.set(messageId, message);
8385

8486
if (!receiver) {
8587
throw new Error(
8688
"Receiver not initialized at module " + this.thisWindowId
8789
);
8890
}
8991

90-
const message = event.data;
9192
if (message.from !== undefined) {
9293
console.log(
9394
`Module ${this.thisWindowId} received message from module ${
@@ -257,5 +258,21 @@ export class InterModuleCommunication {
257258
senderWindow.postMessage(msg, "*");
258259
}
259260
);
261+
262+
// Handle ignore signal
263+
this.receiverHandlerMap?.set(
264+
IMCMessageTypeEnum.SignalIgnore,
265+
async (senderWindow: Window, message: IMCMessage) => {
266+
console.warn(
267+
`Message ignored by receiver. Message ID: ${message.messageId}, Payload: ${message.payload}`
268+
);
269+
const pendingMessage = this.sender?.getPendingMessage(
270+
message.messageId
271+
);
272+
if (pendingMessage) {
273+
pendingMessage.reject(new Error("Message ignored by receiver"));
274+
}
275+
}
276+
);
260277
}
261278
}

npm-packages/shared-utils/src/imc/message-receiver.ts

Lines changed: 83 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ export class MessageReceiver {
1313
}
1414
>;
1515
private windowId: string;
16+
private intent: string | undefined;
1617

17-
constructor(listenerMap: ReceiverHandlerMap, windowId: string) {
18+
constructor(
19+
listenerMap: ReceiverHandlerMap,
20+
windowId: string,
21+
intent: string | undefined
22+
) {
1823
this.handlerMap = listenerMap;
1924
this.pendingTasks = new Map();
2025
this.windowId = windowId;
26+
this.intent = intent;
2127
}
2228

2329
public receiveMessage(senderWindow: Window, message: IMCMessage) {
@@ -39,49 +45,70 @@ export class MessageReceiver {
3945
}
4046

4147
const handler = this.handlerMap.get(message.type);
42-
if (handler) {
43-
// Create abort controller to listen for abort signal from sender.
44-
// Then save the message id and abort controller to the pending tasks.
45-
const controller = new AbortController();
46-
const signal = controller.signal;
47-
this.pendingTasks.set(message.messageId, {
48-
controller,
49-
});
5048

51-
const promise = handler(senderWindow, message, signal);
52-
promise
53-
.then((result) => {
54-
// Don't send the result if the task has been aborted
55-
if (signal.aborted) return;
56-
57-
// Acknowledge the sender with the result if the message type is not Acknowledge
58-
if (message.type !== IMCMessageTypeEnum.SignalAcknowledge) {
59-
this.acknowledgeSender(
60-
senderWindow,
61-
message.messageId,
62-
message.channelId,
63-
result
64-
);
65-
}
66-
})
67-
.catch((error) => {
68-
// Send the error message to the sender
69-
const errMsg: IMCMessage = {
70-
messageId: message.messageId,
71-
channelId: message.channelId,
72-
type: IMCMessageTypeEnum.SignalError,
73-
payload: error.message,
74-
from: this.windowId,
75-
};
76-
77-
console.error("Error handling message:", error);
78-
79-
senderWindow.postMessage(errMsg, "*");
80-
})
81-
.finally(() => {
82-
this.pendingTasks.delete(message.messageId);
83-
});
49+
if (!handler) {
50+
if (this.intent === "connection-listener") {
51+
// Ignore missing handler for connection listener,
52+
// as it handles connection related messages only.
53+
// There should be another channel created to handle other messages.
54+
return;
55+
}
56+
57+
console.warn(`No handler found for message type: ${message.type}`);
58+
59+
// Ignore the message if no handler is found
60+
this.ignoreSender(senderWindow, message);
61+
62+
return;
8463
}
64+
65+
// Create abort controller to listen for abort signal from sender.
66+
// Then save the message id and abort controller to the pending tasks.
67+
const controller = new AbortController();
68+
const signal = controller.signal;
69+
this.pendingTasks.set(message.messageId, {
70+
controller,
71+
});
72+
73+
const promise = handler(senderWindow, message, signal);
74+
promise
75+
.then((result) => {
76+
// Don't send the result if the task has been aborted
77+
if (signal.aborted) return;
78+
79+
// Acknowledge the sender with the result if the message type is not Acknowledge
80+
if (message.type !== IMCMessageTypeEnum.SignalAcknowledge) {
81+
this.acknowledgeSender(
82+
senderWindow,
83+
message.messageId,
84+
message.channelId,
85+
result
86+
);
87+
}
88+
})
89+
.catch((error) => {
90+
if (error.message === "Message ignored by receiver") {
91+
// Ignore the message if no handler is found
92+
this.ignoreSender(senderWindow, message);
93+
return;
94+
}
95+
96+
// Send the error message to the sender
97+
const errMsg: IMCMessage = {
98+
messageId: message.messageId,
99+
channelId: message.channelId,
100+
type: IMCMessageTypeEnum.SignalError,
101+
payload: error.message,
102+
from: this.windowId,
103+
};
104+
105+
console.error("Error handling message:", error);
106+
107+
senderWindow.postMessage(errMsg, "*");
108+
})
109+
.finally(() => {
110+
this.pendingTasks.delete(message.messageId);
111+
});
85112
}
86113

87114
private acknowledgeSender(
@@ -99,4 +126,18 @@ export class MessageReceiver {
99126
};
100127
senderWindow.postMessage(message, "*");
101128
}
129+
130+
private ignoreSender(senderWindow: Window, message: IMCMessage) {
131+
// Ignore the message if no handler is found
132+
senderWindow.postMessage(
133+
{
134+
messageId: message.messageId,
135+
channelId: message.channelId,
136+
type: IMCMessageTypeEnum.SignalIgnore,
137+
payload: `No handler for message type: ${message.type}`,
138+
from: this.windowId,
139+
} as IMCMessage,
140+
"*"
141+
);
142+
}
102143
}

npm-packages/shared-utils/src/imc/message-sender.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export class MessageSender {
8787
this.pendingMessages.delete(id);
8888
reject(reason);
8989
};
90+
9091
this.pendingMessages.set(id, {
9192
resolve: onResolve,
9293
reject: onReject,

0 commit comments

Comments
 (0)