@rimori/client 2.2.0-next.3 → 2.2.0-next.5
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.
|
@@ -92,18 +92,18 @@ jobs:
|
|
|
92
92
|
# Uses OIDC token automatically (no NODE_AUTH_TOKEN needed)
|
|
93
93
|
# Requires npm 11.5.1+ and id-token: write permission (already set)
|
|
94
94
|
|
|
95
|
-
- name: Commit version bump
|
|
96
|
-
run: |
|
|
97
|
-
git config --local user.email "action@github.com"
|
|
98
|
-
git config --local user.name "GitHub Action"
|
|
99
|
-
git add package.json
|
|
100
|
-
git commit -m "chore: bump @rimori/client to ${{ steps.version.outputs.new_version }} [skip ci]"
|
|
101
|
-
git push
|
|
102
|
-
|
|
103
95
|
- name: Output published version
|
|
104
96
|
run: |
|
|
105
97
|
echo "✅ Published @rimori/client@${{ steps.version.outputs.new_version }} to npm with @next tag"
|
|
106
98
|
|
|
99
|
+
- name: Create git tag
|
|
100
|
+
run: |
|
|
101
|
+
git config --local user.email "action@github.com"
|
|
102
|
+
git config --local user.name "GitHub Action"
|
|
103
|
+
git tag "v${{ steps.version.outputs.new_version }}" -m "Pre-release v${{ steps.version.outputs.new_version }}"
|
|
104
|
+
git push origin "v${{ steps.version.outputs.new_version }}"
|
|
105
|
+
echo "🏷️ Created and pushed tag v${{ steps.version.outputs.new_version }}"
|
|
106
|
+
|
|
107
107
|
- name: Notify Slack
|
|
108
108
|
if: always()
|
|
109
109
|
uses: slackapi/slack-github-action@v1.24.0
|
|
@@ -12,18 +12,18 @@ export class EventBusHandler {
|
|
|
12
12
|
this.listeners = new Map();
|
|
13
13
|
this.responseResolvers = new Map();
|
|
14
14
|
this.debugEnabled = false;
|
|
15
|
-
this.evName =
|
|
15
|
+
this.evName = '';
|
|
16
16
|
//private constructor
|
|
17
17
|
}
|
|
18
18
|
static getInstance(name) {
|
|
19
19
|
if (!EventBusHandler.instance) {
|
|
20
20
|
EventBusHandler.instance = new EventBusHandler();
|
|
21
|
-
EventBusHandler.instance.on(
|
|
21
|
+
EventBusHandler.instance.on('global.system.requestDebug', () => {
|
|
22
22
|
EventBusHandler.instance.debugEnabled = true;
|
|
23
23
|
console.log(`[${EventBusHandler.instance.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`);
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
if (name && EventBusHandler.instance.evName ===
|
|
26
|
+
if (name && EventBusHandler.instance.evName === '') {
|
|
27
27
|
EventBusHandler.instance.evName = name;
|
|
28
28
|
}
|
|
29
29
|
return EventBusHandler.instance;
|
|
@@ -79,9 +79,7 @@ export class EventBusHandler {
|
|
|
79
79
|
this.logAndThrowError(false, `No handlers found for topic: ` + topic);
|
|
80
80
|
}
|
|
81
81
|
// If it's a response to a request
|
|
82
|
-
if (eventId &&
|
|
83
|
-
this.responseResolvers.has(eventId) &&
|
|
84
|
-
!skipResponseTrigger) {
|
|
82
|
+
if (eventId && this.responseResolvers.has(eventId) && !skipResponseTrigger) {
|
|
85
83
|
// console.log("[Rimori] Resolving response to request: " + eventId, event.data);
|
|
86
84
|
this.responseResolvers.get(eventId)(event);
|
|
87
85
|
this.responseResolvers.delete(eventId);
|
|
@@ -108,7 +106,7 @@ export class EventBusHandler {
|
|
|
108
106
|
const blackListedEventIds = [];
|
|
109
107
|
const eventHandler = (data) => {
|
|
110
108
|
if (blackListedEventIds.some((item) => item.eventId === data.eventId && item.sender === data.sender)) {
|
|
111
|
-
console.log(
|
|
109
|
+
console.log('BLACKLISTED EVENT ID', data.eventId, data);
|
|
112
110
|
return;
|
|
113
111
|
}
|
|
114
112
|
blackListedEventIds.push({
|
|
@@ -120,9 +118,7 @@ export class EventBusHandler {
|
|
|
120
118
|
}
|
|
121
119
|
return handler(data);
|
|
122
120
|
};
|
|
123
|
-
this.listeners
|
|
124
|
-
.get(topic)
|
|
125
|
-
.add({ id, handler: eventHandler, ignoreSender });
|
|
121
|
+
this.listeners.get(topic).add({ id, handler: eventHandler, ignoreSender });
|
|
126
122
|
this.logIfDebug(`Subscribed to ` + topic, {
|
|
127
123
|
listenerId: id,
|
|
128
124
|
ignoreSender,
|
|
@@ -145,7 +141,7 @@ export class EventBusHandler {
|
|
|
145
141
|
const listeners = topics.map((topic) => {
|
|
146
142
|
const blackListedEventIds = [];
|
|
147
143
|
//To allow event communication inside the same plugin the sender needs to be ignored but the events still need to be checked for the same event just reaching the subscriber to prevent infinite loops
|
|
148
|
-
const finalIgnoreSender = !topic.startsWith(
|
|
144
|
+
const finalIgnoreSender = !topic.startsWith('self.') ? [sender] : [];
|
|
149
145
|
const listener = this.on(topic, (data) => __awaiter(this, void 0, void 0, function* () {
|
|
150
146
|
if (blackListedEventIds.includes(data.eventId)) {
|
|
151
147
|
// console.log("BLACKLISTED EVENT ID", data.eventId);
|
|
@@ -155,10 +151,10 @@ export class EventBusHandler {
|
|
|
155
151
|
if (blackListedEventIds.length > 100) {
|
|
156
152
|
blackListedEventIds.shift();
|
|
157
153
|
}
|
|
158
|
-
const response = typeof handler ===
|
|
154
|
+
const response = typeof handler === 'function' ? yield handler(data) : handler;
|
|
159
155
|
this.emit(sender, topic, response, data.eventId);
|
|
160
156
|
}), finalIgnoreSender);
|
|
161
|
-
this.logIfDebug(`Added respond listener ` + sender +
|
|
157
|
+
this.logIfDebug(`Added respond listener ` + sender + ' to topic ' + topic, { listener, sender });
|
|
162
158
|
return {
|
|
163
159
|
off: () => listener.off(),
|
|
164
160
|
};
|
|
@@ -177,7 +173,7 @@ export class EventBusHandler {
|
|
|
177
173
|
this.logAndThrowError(false, `Invalid topic: ` + topic);
|
|
178
174
|
return;
|
|
179
175
|
}
|
|
180
|
-
let listener;
|
|
176
|
+
let listener = undefined;
|
|
181
177
|
const wrapper = (event) => {
|
|
182
178
|
handler(event);
|
|
183
179
|
listener === null || listener === void 0 ? void 0 : listener.off();
|
|
@@ -236,7 +232,7 @@ export class EventBusHandler {
|
|
|
236
232
|
const exact = this.listeners.get(topic) || new Set();
|
|
237
233
|
// Find wildcard matches
|
|
238
234
|
const wildcard = [...this.listeners.entries()]
|
|
239
|
-
.filter(([key]) => key.endsWith(
|
|
235
|
+
.filter(([key]) => key.endsWith('*') && topic.startsWith(key.slice(0, -1)))
|
|
240
236
|
.flatMap(([_, handlers]) => [...handlers]);
|
|
241
237
|
return new Set([...exact, ...wildcard]);
|
|
242
238
|
}
|
|
@@ -247,30 +243,27 @@ export class EventBusHandler {
|
|
|
247
243
|
*/
|
|
248
244
|
validateTopic(topic) {
|
|
249
245
|
// Split event type into parts
|
|
250
|
-
const parts = topic.split(
|
|
246
|
+
const parts = topic.split('.');
|
|
251
247
|
const [plugin, area, action] = parts;
|
|
252
248
|
if (parts.length !== 3) {
|
|
253
|
-
if (parts.length === 1 && plugin ===
|
|
249
|
+
if (parts.length === 1 && plugin === '*') {
|
|
254
250
|
return true;
|
|
255
251
|
}
|
|
256
|
-
if (parts.length === 2 && plugin !==
|
|
252
|
+
if (parts.length === 2 && plugin !== '*' && area === '*') {
|
|
257
253
|
return true;
|
|
258
254
|
}
|
|
259
255
|
this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
|
|
260
256
|
return false;
|
|
261
257
|
}
|
|
262
|
-
if (action ===
|
|
258
|
+
if (action === '*') {
|
|
263
259
|
return true;
|
|
264
260
|
}
|
|
265
261
|
// Validate action part
|
|
266
|
-
const validActions = [
|
|
262
|
+
const validActions = ['request', 'create', 'update', 'delete', 'trigger'];
|
|
267
263
|
if (validActions.some((a) => action.startsWith(a))) {
|
|
268
264
|
return true;
|
|
269
265
|
}
|
|
270
|
-
this.logAndThrowError(false, `Invalid event topic name. The action: ` +
|
|
271
|
-
action +
|
|
272
|
-
". Must be or start with one of: " +
|
|
273
|
-
validActions.join(", "));
|
|
266
|
+
this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + '. Must be or start with one of: ' + validActions.join(', '));
|
|
274
267
|
return false;
|
|
275
268
|
}
|
|
276
269
|
logIfDebug(...args) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export type Plugin<T extends
|
|
1
|
+
export type Plugin<T extends object = object> = Omit<RimoriPluginConfig<T>, 'context_menu_actions'> & {
|
|
2
2
|
version: string;
|
|
3
3
|
endpoint: string;
|
|
4
4
|
assetEndpoint: string;
|
|
5
5
|
context_menu_actions: MenuEntry[];
|
|
6
|
-
release_channel:
|
|
6
|
+
release_channel: 'alpha' | 'beta' | 'stable';
|
|
7
7
|
};
|
|
8
8
|
export type ActivePlugin = Plugin<{
|
|
9
9
|
active?: boolean;
|
|
@@ -14,7 +14,7 @@ export interface PluginPage {
|
|
|
14
14
|
url: string;
|
|
15
15
|
show: boolean;
|
|
16
16
|
description: string;
|
|
17
|
-
root:
|
|
17
|
+
root: 'vocabulary' | 'grammar' | 'reading' | 'listening' | 'watching' | 'writing' | 'speaking' | 'other' | 'community';
|
|
18
18
|
action?: {
|
|
19
19
|
key: string;
|
|
20
20
|
parameters: ObjectTool;
|
|
@@ -46,7 +46,7 @@ export interface ContextMenuAction {
|
|
|
46
46
|
* Rimori plugin structure representing the complete configuration
|
|
47
47
|
* of a Rimori plugin with all metadata and configuration options.
|
|
48
48
|
*/
|
|
49
|
-
export interface RimoriPluginConfig<T extends
|
|
49
|
+
export interface RimoriPluginConfig<T extends object = object> {
|
|
50
50
|
id: string;
|
|
51
51
|
/**
|
|
52
52
|
* Basic information about the plugin including branding and core details.
|
|
@@ -79,7 +79,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
79
79
|
/**
|
|
80
80
|
* Context menu actions that the plugin registers to appear in right-click menus throughout the application.
|
|
81
81
|
*/
|
|
82
|
-
context_menu_actions: Omit<MenuEntry,
|
|
82
|
+
context_menu_actions: Omit<MenuEntry, 'plugin_id'>[];
|
|
83
83
|
/**
|
|
84
84
|
* Documentation paths for different types of plugin documentation.
|
|
85
85
|
*/
|
|
@@ -107,7 +107,7 @@ export interface Tool {
|
|
|
107
107
|
parameters: {
|
|
108
108
|
name: string;
|
|
109
109
|
description: string;
|
|
110
|
-
type:
|
|
110
|
+
type: 'string' | 'number' | 'boolean';
|
|
111
111
|
}[];
|
|
112
112
|
execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
|
|
113
113
|
}
|
package/package.json
CHANGED
|
@@ -23,9 +23,7 @@ export interface EventBusMessage<T = EventPayload> {
|
|
|
23
23
|
debug: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export type EventHandler<T = EventPayload> = (
|
|
27
|
-
event: EventBusMessage<T>
|
|
28
|
-
) => void | Promise<void>;
|
|
26
|
+
export type EventHandler<T = EventPayload> = (event: EventBusMessage<T>) => void | Promise<void>;
|
|
29
27
|
|
|
30
28
|
interface Listeners<T = EventPayload> {
|
|
31
29
|
id: number;
|
|
@@ -39,13 +37,10 @@ export interface EventListener {
|
|
|
39
37
|
|
|
40
38
|
export class EventBusHandler {
|
|
41
39
|
private listeners: Map<string, Set<Listeners<EventPayload>>> = new Map();
|
|
42
|
-
private responseResolvers: Map<
|
|
43
|
-
number,
|
|
44
|
-
(value: EventBusMessage<unknown>) => void
|
|
45
|
-
> = new Map();
|
|
40
|
+
private responseResolvers: Map<number, (value: EventBusMessage<unknown>) => void> = new Map();
|
|
46
41
|
private static instance: EventBusHandler | null = null;
|
|
47
|
-
private debugEnabled
|
|
48
|
-
private evName
|
|
42
|
+
private debugEnabled = false;
|
|
43
|
+
private evName = '';
|
|
49
44
|
|
|
50
45
|
private constructor() {
|
|
51
46
|
//private constructor
|
|
@@ -55,27 +50,22 @@ export class EventBusHandler {
|
|
|
55
50
|
if (!EventBusHandler.instance) {
|
|
56
51
|
EventBusHandler.instance = new EventBusHandler();
|
|
57
52
|
|
|
58
|
-
EventBusHandler.instance.on(
|
|
53
|
+
EventBusHandler.instance.on('global.system.requestDebug', () => {
|
|
59
54
|
EventBusHandler.instance!.debugEnabled = true;
|
|
60
55
|
console.log(
|
|
61
56
|
`[${
|
|
62
57
|
EventBusHandler.instance!.evName
|
|
63
|
-
}] Debug mode enabled. Make sure debugging messages are enabled in the browser console
|
|
58
|
+
}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`,
|
|
64
59
|
);
|
|
65
60
|
});
|
|
66
61
|
}
|
|
67
|
-
if (name && EventBusHandler.instance.evName ===
|
|
62
|
+
if (name && EventBusHandler.instance.evName === '') {
|
|
68
63
|
EventBusHandler.instance.evName = name;
|
|
69
64
|
}
|
|
70
65
|
return EventBusHandler.instance;
|
|
71
66
|
}
|
|
72
67
|
|
|
73
|
-
private createEvent(
|
|
74
|
-
sender: string,
|
|
75
|
-
topic: string,
|
|
76
|
-
data: EventPayload,
|
|
77
|
-
eventId?: number
|
|
78
|
-
): EventBusMessage {
|
|
68
|
+
private createEvent(sender: string, topic: string, data: EventPayload, eventId?: number): EventBusMessage {
|
|
79
69
|
const generatedEventId = eventId || Math.floor(Math.random() * 10000000000);
|
|
80
70
|
|
|
81
71
|
return {
|
|
@@ -106,12 +96,7 @@ export class EventBusHandler {
|
|
|
106
96
|
* - pl1234.card.delete
|
|
107
97
|
* - pl1234.card.triggerBackup
|
|
108
98
|
*/
|
|
109
|
-
public emit<T = EventPayload>(
|
|
110
|
-
sender: string,
|
|
111
|
-
topic: string,
|
|
112
|
-
data?: T,
|
|
113
|
-
eventId?: number
|
|
114
|
-
): void {
|
|
99
|
+
public emit<T = EventPayload>(sender: string, topic: string, data?: T, eventId?: number): void {
|
|
115
100
|
this.emitInternal(sender, topic, data || {}, eventId);
|
|
116
101
|
}
|
|
117
102
|
|
|
@@ -120,7 +105,7 @@ export class EventBusHandler {
|
|
|
120
105
|
topic: string,
|
|
121
106
|
data: EventPayload,
|
|
122
107
|
eventId?: number,
|
|
123
|
-
skipResponseTrigger = false
|
|
108
|
+
skipResponseTrigger = false,
|
|
124
109
|
): void {
|
|
125
110
|
if (!this.validateTopic(topic)) {
|
|
126
111
|
this.logAndThrowError(false, `Invalid topic: ` + topic);
|
|
@@ -143,11 +128,7 @@ export class EventBusHandler {
|
|
|
143
128
|
}
|
|
144
129
|
|
|
145
130
|
// If it's a response to a request
|
|
146
|
-
if (
|
|
147
|
-
eventId &&
|
|
148
|
-
this.responseResolvers.has(eventId) &&
|
|
149
|
-
!skipResponseTrigger
|
|
150
|
-
) {
|
|
131
|
+
if (eventId && this.responseResolvers.has(eventId) && !skipResponseTrigger) {
|
|
151
132
|
// console.log("[Rimori] Resolving response to request: " + eventId, event.data);
|
|
152
133
|
this.responseResolvers.get(eventId)!(event);
|
|
153
134
|
this.responseResolvers.delete(eventId);
|
|
@@ -164,7 +145,7 @@ export class EventBusHandler {
|
|
|
164
145
|
public on<T = EventPayload>(
|
|
165
146
|
topics: string | string[],
|
|
166
147
|
handler: EventHandler<T>,
|
|
167
|
-
ignoreSender: string[] = []
|
|
148
|
+
ignoreSender: string[] = [],
|
|
168
149
|
): EventListener {
|
|
169
150
|
const ids = this.toArray(topics).map((topic) => {
|
|
170
151
|
this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
|
|
@@ -180,13 +161,8 @@ export class EventBusHandler {
|
|
|
180
161
|
// To prevent infinite loops and processing the same eventId multiple times
|
|
181
162
|
const blackListedEventIds: { eventId: number; sender: string }[] = [];
|
|
182
163
|
const eventHandler = (data: EventBusMessage) => {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
(item) =>
|
|
186
|
-
item.eventId === data.eventId && item.sender === data.sender
|
|
187
|
-
)
|
|
188
|
-
) {
|
|
189
|
-
console.log("BLACKLISTED EVENT ID", data.eventId, data);
|
|
164
|
+
if (blackListedEventIds.some((item) => item.eventId === data.eventId && item.sender === data.sender)) {
|
|
165
|
+
console.log('BLACKLISTED EVENT ID', data.eventId, data);
|
|
190
166
|
return;
|
|
191
167
|
}
|
|
192
168
|
blackListedEventIds.push({
|
|
@@ -199,9 +175,7 @@ export class EventBusHandler {
|
|
|
199
175
|
return (handler as unknown as EventHandler<EventPayload>)(data);
|
|
200
176
|
};
|
|
201
177
|
|
|
202
|
-
this.listeners
|
|
203
|
-
.get(topic)!
|
|
204
|
-
.add({ id, handler: eventHandler, ignoreSender });
|
|
178
|
+
this.listeners.get(topic)!.add({ id, handler: eventHandler, ignoreSender });
|
|
205
179
|
|
|
206
180
|
this.logIfDebug(`Subscribed to ` + topic, {
|
|
207
181
|
listenerId: id,
|
|
@@ -226,15 +200,13 @@ export class EventBusHandler {
|
|
|
226
200
|
public respond(
|
|
227
201
|
sender: string,
|
|
228
202
|
topic: string | string[],
|
|
229
|
-
handler:
|
|
230
|
-
| EventPayload
|
|
231
|
-
| ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)
|
|
203
|
+
handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>),
|
|
232
204
|
): EventListener {
|
|
233
205
|
const topics = Array.isArray(topic) ? topic : [topic];
|
|
234
206
|
const listeners = topics.map((topic) => {
|
|
235
207
|
const blackListedEventIds: number[] = [];
|
|
236
208
|
//To allow event communication inside the same plugin the sender needs to be ignored but the events still need to be checked for the same event just reaching the subscriber to prevent infinite loops
|
|
237
|
-
const finalIgnoreSender = !topic.startsWith(
|
|
209
|
+
const finalIgnoreSender = !topic.startsWith('self.') ? [sender] : [];
|
|
238
210
|
|
|
239
211
|
const listener = this.on(
|
|
240
212
|
topic,
|
|
@@ -247,17 +219,13 @@ export class EventBusHandler {
|
|
|
247
219
|
if (blackListedEventIds.length > 100) {
|
|
248
220
|
blackListedEventIds.shift();
|
|
249
221
|
}
|
|
250
|
-
const response =
|
|
251
|
-
typeof handler === "function" ? await handler(data) : handler;
|
|
222
|
+
const response = typeof handler === 'function' ? await handler(data) : handler;
|
|
252
223
|
this.emit(sender, topic, response, data.eventId);
|
|
253
224
|
},
|
|
254
|
-
finalIgnoreSender
|
|
225
|
+
finalIgnoreSender,
|
|
255
226
|
);
|
|
256
227
|
|
|
257
|
-
this.logIfDebug(
|
|
258
|
-
`Added respond listener ` + sender + " to topic " + topic,
|
|
259
|
-
{ listener, sender }
|
|
260
|
-
);
|
|
228
|
+
this.logIfDebug(`Added respond listener ` + sender + ' to topic ' + topic, { listener, sender });
|
|
261
229
|
return {
|
|
262
230
|
off: () => listener.off(),
|
|
263
231
|
};
|
|
@@ -278,7 +246,7 @@ export class EventBusHandler {
|
|
|
278
246
|
return;
|
|
279
247
|
}
|
|
280
248
|
|
|
281
|
-
let listener: EventListener | undefined;
|
|
249
|
+
let listener: EventListener | undefined = undefined;
|
|
282
250
|
const wrapper = (event: EventBusMessage<T>) => {
|
|
283
251
|
handler(event);
|
|
284
252
|
listener?.off();
|
|
@@ -324,7 +292,7 @@ export class EventBusHandler {
|
|
|
324
292
|
public async request<T = EventPayload>(
|
|
325
293
|
sender: string,
|
|
326
294
|
topic: string,
|
|
327
|
-
data?: EventPayload
|
|
295
|
+
data?: EventPayload,
|
|
328
296
|
): Promise<EventBusMessage<T>> {
|
|
329
297
|
if (!this.validateTopic(topic)) {
|
|
330
298
|
this.logAndThrowError(true, `Invalid topic: ` + topic);
|
|
@@ -335,10 +303,8 @@ export class EventBusHandler {
|
|
|
335
303
|
this.logIfDebug(`Requesting data from ` + topic, { event });
|
|
336
304
|
|
|
337
305
|
return new Promise<EventBusMessage<T>>((resolve) => {
|
|
338
|
-
this.responseResolvers.set(
|
|
339
|
-
|
|
340
|
-
(value: EventBusMessage<unknown>) =>
|
|
341
|
-
resolve(value as EventBusMessage<T>)
|
|
306
|
+
this.responseResolvers.set(event.eventId, (value: EventBusMessage<unknown>) =>
|
|
307
|
+
resolve(value as EventBusMessage<T>),
|
|
342
308
|
);
|
|
343
309
|
this.emitInternal(sender, topic, data || {}, event.eventId, true);
|
|
344
310
|
});
|
|
@@ -354,9 +320,7 @@ export class EventBusHandler {
|
|
|
354
320
|
|
|
355
321
|
// Find wildcard matches
|
|
356
322
|
const wildcard = [...this.listeners.entries()]
|
|
357
|
-
.filter(
|
|
358
|
-
([key]) => key.endsWith("*") && topic.startsWith(key.slice(0, -1))
|
|
359
|
-
)
|
|
323
|
+
.filter(([key]) => key.endsWith('*') && topic.startsWith(key.slice(0, -1)))
|
|
360
324
|
.flatMap(([_, handlers]) => [...handlers]);
|
|
361
325
|
return new Set([...exact, ...wildcard]);
|
|
362
326
|
}
|
|
@@ -368,29 +332,26 @@ export class EventBusHandler {
|
|
|
368
332
|
*/
|
|
369
333
|
private validateTopic(topic: string): boolean {
|
|
370
334
|
// Split event type into parts
|
|
371
|
-
const parts = topic.split(
|
|
335
|
+
const parts = topic.split('.');
|
|
372
336
|
const [plugin, area, action] = parts;
|
|
373
337
|
|
|
374
338
|
if (parts.length !== 3) {
|
|
375
|
-
if (parts.length === 1 && plugin ===
|
|
339
|
+
if (parts.length === 1 && plugin === '*') {
|
|
376
340
|
return true;
|
|
377
341
|
}
|
|
378
|
-
if (parts.length === 2 && plugin !==
|
|
342
|
+
if (parts.length === 2 && plugin !== '*' && area === '*') {
|
|
379
343
|
return true;
|
|
380
344
|
}
|
|
381
|
-
this.logAndThrowError(
|
|
382
|
-
false,
|
|
383
|
-
`Event type must have 3 parts separated by dots. Received: ` + topic
|
|
384
|
-
);
|
|
345
|
+
this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
|
|
385
346
|
return false;
|
|
386
347
|
}
|
|
387
348
|
|
|
388
|
-
if (action ===
|
|
349
|
+
if (action === '*') {
|
|
389
350
|
return true;
|
|
390
351
|
}
|
|
391
352
|
|
|
392
353
|
// Validate action part
|
|
393
|
-
const validActions = [
|
|
354
|
+
const validActions = ['request', 'create', 'update', 'delete', 'trigger'];
|
|
394
355
|
|
|
395
356
|
if (validActions.some((a) => action.startsWith(a))) {
|
|
396
357
|
return true;
|
|
@@ -398,10 +359,7 @@ export class EventBusHandler {
|
|
|
398
359
|
|
|
399
360
|
this.logAndThrowError(
|
|
400
361
|
false,
|
|
401
|
-
`Invalid event topic name. The action: ` +
|
|
402
|
-
action +
|
|
403
|
-
". Must be or start with one of: " +
|
|
404
|
-
validActions.join(", ")
|
|
362
|
+
`Invalid event topic name. The action: ` + action + '. Must be or start with one of: ' + validActions.join(', '),
|
|
405
363
|
);
|
|
406
364
|
return false;
|
|
407
365
|
}
|
|
@@ -412,10 +370,7 @@ export class EventBusHandler {
|
|
|
412
370
|
}
|
|
413
371
|
}
|
|
414
372
|
|
|
415
|
-
private logAndThrowError(
|
|
416
|
-
throwError: boolean,
|
|
417
|
-
...args: (string | EventPayload)[]
|
|
418
|
-
) {
|
|
373
|
+
private logAndThrowError(throwError: boolean, ...args: (string | EventPayload)[]) {
|
|
419
374
|
const message = `[${this.evName}] ` + args[0];
|
|
420
375
|
console.error(message, ...args.slice(1));
|
|
421
376
|
if (throwError) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// whole configuration of a plugin (from the database)
|
|
2
|
-
export type Plugin<T extends
|
|
2
|
+
export type Plugin<T extends object = object> = Omit<RimoriPluginConfig<T>, 'context_menu_actions'> & {
|
|
3
3
|
version: string;
|
|
4
4
|
endpoint: string;
|
|
5
5
|
assetEndpoint: string;
|
|
6
6
|
context_menu_actions: MenuEntry[];
|
|
7
|
-
release_channel:
|
|
8
|
-
}
|
|
7
|
+
release_channel: 'alpha' | 'beta' | 'stable';
|
|
8
|
+
};
|
|
9
9
|
|
|
10
|
-
export type ActivePlugin = Plugin<{ active?: boolean }
|
|
10
|
+
export type ActivePlugin = Plugin<{ active?: boolean }>;
|
|
11
11
|
|
|
12
12
|
// browsable page of a plugin
|
|
13
13
|
export interface PluginPage {
|
|
@@ -17,13 +17,22 @@ export interface PluginPage {
|
|
|
17
17
|
// Whether the page should be shown in the navbar
|
|
18
18
|
show: boolean;
|
|
19
19
|
description: string;
|
|
20
|
-
root:
|
|
20
|
+
root:
|
|
21
|
+
| 'vocabulary'
|
|
22
|
+
| 'grammar'
|
|
23
|
+
| 'reading'
|
|
24
|
+
| 'listening'
|
|
25
|
+
| 'watching'
|
|
26
|
+
| 'writing'
|
|
27
|
+
| 'speaking'
|
|
28
|
+
| 'other'
|
|
29
|
+
| 'community';
|
|
21
30
|
// The actions that can be triggered in the plugin
|
|
22
31
|
// The key is the action key. The other entries are additional properties needed when triggering the action
|
|
23
32
|
action?: {
|
|
24
33
|
key: string;
|
|
25
34
|
parameters: ObjectTool;
|
|
26
|
-
}
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
// a sidebar page of a plugin
|
|
@@ -65,14 +74,14 @@ export interface ContextMenuAction {
|
|
|
65
74
|
// id of the plugin that the action belongs to
|
|
66
75
|
plugin_id: string;
|
|
67
76
|
// key of the action. Used to know which action to trigger when clicking on the context menu
|
|
68
|
-
action_key: string
|
|
77
|
+
action_key: string;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
/**
|
|
72
81
|
* Rimori plugin structure representing the complete configuration
|
|
73
82
|
* of a Rimori plugin with all metadata and configuration options.
|
|
74
83
|
*/
|
|
75
|
-
export interface RimoriPluginConfig<T extends
|
|
84
|
+
export interface RimoriPluginConfig<T extends object = object> {
|
|
76
85
|
id: string;
|
|
77
86
|
/**
|
|
78
87
|
* Basic information about the plugin including branding and core details.
|
|
@@ -86,7 +95,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
86
95
|
logo: string;
|
|
87
96
|
/** Optional website URL for the plugin's homepage or link to plugins owner for contributions */
|
|
88
97
|
website?: string;
|
|
89
|
-
}
|
|
98
|
+
};
|
|
90
99
|
/**
|
|
91
100
|
* Configuration for different types of pages.
|
|
92
101
|
*/
|
|
@@ -101,11 +110,11 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
101
110
|
settings?: string;
|
|
102
111
|
/** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
|
|
103
112
|
topics?: string[];
|
|
104
|
-
}
|
|
113
|
+
};
|
|
105
114
|
/**
|
|
106
115
|
* Context menu actions that the plugin registers to appear in right-click menus throughout the application.
|
|
107
116
|
*/
|
|
108
|
-
context_menu_actions: Omit<MenuEntry,
|
|
117
|
+
context_menu_actions: Omit<MenuEntry, 'plugin_id'>[];
|
|
109
118
|
/**
|
|
110
119
|
* Documentation paths for different types of plugin documentation.
|
|
111
120
|
*/
|
|
@@ -116,7 +125,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
116
125
|
user_path: string;
|
|
117
126
|
/** Path to developer documentation for plugin development */
|
|
118
127
|
developer_path: string;
|
|
119
|
-
}
|
|
128
|
+
};
|
|
120
129
|
/**
|
|
121
130
|
* Configuration for the plugin's web worker if it uses background processing or exposes actions to other plugins.
|
|
122
131
|
*/
|
|
@@ -136,7 +145,7 @@ export interface Tool {
|
|
|
136
145
|
parameters: {
|
|
137
146
|
name: string;
|
|
138
147
|
description: string;
|
|
139
|
-
type:
|
|
148
|
+
type: 'string' | 'number' | 'boolean';
|
|
140
149
|
}[];
|
|
141
150
|
execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
|
|
142
151
|
}
|
|
@@ -145,7 +154,7 @@ export interface Tool {
|
|
|
145
154
|
* The tool definition structure is used for LLM function calling and plugin action parameters.
|
|
146
155
|
* It defines the schema for tools that can be used by Language Learning Models (LLMs)
|
|
147
156
|
* and plugin actions.
|
|
148
|
-
*
|
|
157
|
+
*
|
|
149
158
|
* @example
|
|
150
159
|
* ```typescript
|
|
151
160
|
* const flashcardTool: Tool = {
|
|
@@ -155,13 +164,13 @@ export interface Tool {
|
|
|
155
164
|
* description: 'Number of flashcards to practice'
|
|
156
165
|
* },
|
|
157
166
|
* deck: {
|
|
158
|
-
* type: 'string',
|
|
167
|
+
* type: 'string',
|
|
159
168
|
* enum: ['latest', 'random', 'oldest', 'mix', 'best_known'],
|
|
160
169
|
* description: 'Type of deck to practice'
|
|
161
170
|
* }
|
|
162
171
|
* };
|
|
163
172
|
* ```
|
|
164
|
-
*
|
|
173
|
+
*
|
|
165
174
|
*/
|
|
166
175
|
export type ObjectTool = {
|
|
167
176
|
[key: string]: ToolParameter;
|
|
@@ -188,15 +197,15 @@ interface ToolParameter {
|
|
|
188
197
|
* Supports primitive types, nested objects for complex data structures,
|
|
189
198
|
* and arrays of objects for collections. The tuple notation [{}] indicates
|
|
190
199
|
* arrays of objects with a specific structure.
|
|
191
|
-
*
|
|
200
|
+
*
|
|
192
201
|
* @example Primitive: 'string' | 'number' | 'boolean'
|
|
193
202
|
* @example Nested object: { name: { type: 'string' }, age: { type: 'number' } }
|
|
194
203
|
* @example Array of objects: [{ id: { type: 'string' }, value: { type: 'number' } }]
|
|
195
204
|
*/
|
|
196
205
|
type ToolParameterType =
|
|
197
206
|
| PrimitiveType
|
|
198
|
-
| { [key: string]: ToolParameter }
|
|
199
|
-
| [{ [key: string]: ToolParameter }];
|
|
207
|
+
| { [key: string]: ToolParameter } // for nested objects
|
|
208
|
+
| [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
|
|
200
209
|
|
|
201
210
|
/**
|
|
202
211
|
* Primitive data types supported by the LLM tool system.
|