@rimori/client 1.0.5 → 1.1.1
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 +955 -28
- package/dist/components/MarkdownEditor.js +6 -4
- package/dist/components/PluginController.d.ts +21 -0
- package/dist/components/PluginController.js +116 -0
- package/dist/components/ai/Assistant.js +1 -1
- package/dist/components/ai/Avatar.d.ts +6 -4
- package/dist/components/ai/Avatar.js +14 -6
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +2 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +36 -15
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +3 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +2 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +5 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +3 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +41 -5
- package/dist/components/ai/utils.d.ts +1 -1
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/controller/AIController.js +2 -1
- package/dist/controller/SettingsController.d.ts +15 -15
- package/dist/controller/SettingsController.js +15 -16
- package/dist/controller/SharedContentController.d.ts +58 -11
- package/dist/controller/SharedContentController.js +161 -26
- package/dist/controller/SidePluginController.d.ts +1 -12
- package/dist/controller/SidePluginController.js +2 -1
- package/dist/core/components/ContextMenu.d.ts +10 -0
- package/dist/core/components/ContextMenu.js +93 -0
- package/dist/core.d.ts +1 -4
- package/dist/core.js +1 -4
- package/dist/hooks/UseChatHook.d.ts +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/plugin/AccomplishmentHandler.d.ts +38 -0
- package/dist/plugin/AccomplishmentHandler.js +108 -0
- package/dist/plugin/ContextMenu.d.ts +17 -0
- package/dist/plugin/ContextMenu.js +45 -0
- package/dist/plugin/PluginController.js +6 -3
- package/dist/plugin/RimoriClient.d.ts +92 -65
- package/dist/plugin/RimoriClient.js +105 -75
- package/dist/plugin/ThemeSetter.js +2 -2
- package/dist/plugin/fromRimori/EventBus.d.ts +6 -3
- package/dist/plugin/fromRimori/EventBus.js +15 -9
- package/dist/plugin/fromRimori/PluginTypes.d.ts +48 -0
- package/dist/plugin/fromRimori/PluginTypes.js +1 -0
- package/dist/providers/PluginController.d.ts +21 -0
- package/dist/providers/PluginController.js +116 -0
- package/dist/providers/PluginProvider.js +26 -73
- package/dist/types/Actions.d.ts +4 -0
- package/dist/types/Actions.js +1 -0
- package/dist/utils/Language.d.ts +66 -0
- package/dist/utils/Language.js +67 -0
- package/dist/utils/difficultyConverter.d.ts +1 -0
- package/dist/utils/difficultyConverter.js +3 -0
- package/dist/worker/WorkerSetup.js +4 -4
- package/package.json +2 -3
- package/src/components/MarkdownEditor.tsx +78 -76
- package/src/components/ai/Assistant.tsx +1 -1
- package/src/components/ai/Avatar.tsx +66 -49
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +1 -1
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +82 -59
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +4 -0
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +6 -0
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +51 -8
- package/src/components/ai/utils.ts +1 -1
- package/src/components.ts +2 -1
- package/src/controller/AIController.ts +2 -1
- package/src/controller/SettingsController.ts +83 -84
- package/src/controller/SharedContentController.ts +214 -53
- package/src/controller/SidePluginController.ts +3 -14
- package/src/core/components/ContextMenu.tsx +123 -0
- package/src/core.ts +1 -4
- package/src/hooks/UseChatHook.ts +17 -17
- package/src/index.ts +0 -8
- package/src/plugin/AccomplishmentHandler.ts +165 -0
- package/src/plugin/PluginController.ts +105 -103
- package/src/plugin/RimoriClient.ts +267 -250
- package/src/plugin/ThemeSetter.ts +2 -2
- package/src/plugin/fromRimori/EventBus.ts +23 -12
- package/src/plugin/fromRimori/PluginTypes.ts +64 -0
- package/src/providers/PluginProvider.tsx +63 -110
- package/src/types/Actions.ts +6 -0
- package/src/utils/Language.ts +70 -0
- package/src/utils/difficultyConverter.ts +4 -0
- package/src/worker/WorkerSetup.ts +4 -4
- package/dist/components/avatar/Assistant.d.ts +0 -9
- package/dist/components/avatar/Assistant.js +0 -59
- package/dist/components/avatar/Avatar.d.ts +0 -12
- package/dist/components/avatar/Avatar.js +0 -42
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +0 -38
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +0 -59
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +0 -84
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +0 -25
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +0 -180
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +0 -45
- package/dist/components/avatar/utils.d.ts +0 -6
- package/dist/components/avatar/utils.js +0 -14
package/src/hooks/UseChatHook.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { Message, Tool, ToolInvocation } from "../controller/AIController";
|
|
2
3
|
import { usePlugin } from "../providers/PluginProvider";
|
|
3
|
-
import { ToolInvocation, Tool, Message } from "../controller/AIController";
|
|
4
4
|
|
|
5
5
|
export function useChat(tools?: Tool[]) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const [messages, setMessages] = React.useState<Message[]>([]);
|
|
7
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
8
|
+
const { llm } = usePlugin();
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const append = (appendMessages: Message[]) => {
|
|
11
|
+
llm.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
12
|
+
const lastMessage = messages[messages.length - 1];
|
|
13
|
+
setIsLoading(!finished);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
if (lastMessage?.id === id) {
|
|
16
|
+
lastMessage.content = message;
|
|
17
|
+
setMessages([...messages, lastMessage]);
|
|
18
|
+
} else {
|
|
19
|
+
setMessages([...messages, ...appendMessages, { id, role: 'assistant', content: message, toolInvocations }]);
|
|
20
|
+
}
|
|
21
|
+
}, tools);
|
|
22
|
+
};
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
return { messages, append, isLoading, setMessages, lastMessage: messages[messages.length - 1] as Message | undefined };
|
|
25
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
// Re-export everything
|
|
2
2
|
export * from './core';
|
|
3
3
|
export * from './components';
|
|
4
|
-
export * from "./components/MarkdownEditor";
|
|
5
|
-
export * from "./components/CRUDModal";
|
|
6
|
-
export * from "./components/Spinner";
|
|
7
|
-
export * from "./components/audio/Playbutton";
|
|
8
|
-
export * from "./controller/AIController";
|
|
9
|
-
export * from "./controller/SharedContentController";
|
|
10
|
-
export * from "./controller/SettingsController";
|
|
11
4
|
export * from "./hooks/UseChatHook";
|
|
12
5
|
export * from "./plugin/RimoriClient";
|
|
13
|
-
export * from "./plugin/ThemeSetter";
|
|
14
6
|
export * from "./providers/PluginProvider";
|
|
15
7
|
export * from "./utils/difficultyConverter";
|
|
16
8
|
export * from "./utils/PluginUtils";
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { EventBus, EventBusMessage } from "./fromRimori/EventBus";
|
|
2
|
+
|
|
3
|
+
export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
|
|
4
|
+
|
|
5
|
+
export const skillCategories = ["reading", "listening", "speaking", "writing", "learning", "community"] as const;
|
|
6
|
+
|
|
7
|
+
interface BaseAccomplishmentPayload {
|
|
8
|
+
type: "micro" | "macro";
|
|
9
|
+
skillCategory: (typeof skillCategories)[number];
|
|
10
|
+
/*
|
|
11
|
+
what is the accomplishment? e.g. chapter, flashcard, story, etc.
|
|
12
|
+
only one keyword per skill category, written in lowercase without spaces, numbers, or special characters
|
|
13
|
+
*/
|
|
14
|
+
accomplishmentKeyword: string;
|
|
15
|
+
// the human readable description of the accomplishment. Important for other plugin developers to understand the accomplishment.
|
|
16
|
+
description: string;
|
|
17
|
+
meta?: {
|
|
18
|
+
//the key of the meta data in snake_case
|
|
19
|
+
key: string;
|
|
20
|
+
//the value of the meta data
|
|
21
|
+
value: string | number | boolean;
|
|
22
|
+
//the human readable description of the meta data. Important for other plugin developers to understand the meta data.
|
|
23
|
+
description: string;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MicroAccomplishmentPayload extends BaseAccomplishmentPayload {
|
|
28
|
+
type: "micro";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
|
|
32
|
+
type: "macro";
|
|
33
|
+
errorRatio: number;
|
|
34
|
+
durationMinutes: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type AccomplishmentPayload = MicroAccomplishmentPayload | MacroAccomplishmentPayload;
|
|
38
|
+
|
|
39
|
+
export class AccomplishmentHandler {
|
|
40
|
+
private pluginId: string;
|
|
41
|
+
|
|
42
|
+
public constructor(pluginId: string) {
|
|
43
|
+
this.pluginId = pluginId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
emitAccomplishment(payload: Omit<AccomplishmentPayload, "type">) {
|
|
47
|
+
const accomplishmentPayload = { ...payload, type: "durationMinutes" in payload ? "macro" : "micro" } as AccomplishmentPayload;
|
|
48
|
+
|
|
49
|
+
this.validateAccomplishment(accomplishmentPayload);
|
|
50
|
+
|
|
51
|
+
const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
|
|
52
|
+
|
|
53
|
+
const topic = "global.accomplishment.trigger" + (accomplishmentPayload.type === "macro" ? "Macro" : "Micro");
|
|
54
|
+
|
|
55
|
+
EventBus.emit(this.pluginId, topic, sanitizedPayload);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private validateAccomplishment(payload: AccomplishmentPayload) {
|
|
59
|
+
if (!skillCategories.includes(payload.skillCategory)) {
|
|
60
|
+
throw new Error(`Invalid skill category: ${payload.skillCategory}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//regex validate accomplishmentKeyword
|
|
64
|
+
if (!/^[a-z_-]+$/.test(payload.accomplishmentKeyword)) {
|
|
65
|
+
throw new Error(`The accomplishment keyword: ${payload.accomplishmentKeyword} is invalid. Only lowercase letters, minuses and underscores are allowed`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//description is required
|
|
69
|
+
if (payload.description.length < 10) {
|
|
70
|
+
throw new Error("Description is too short");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//check that the type is valid
|
|
74
|
+
if (!["micro", "macro"].includes(payload.type)) {
|
|
75
|
+
throw new Error("Invalid accomplishment type " + payload.type);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//durationMinutes is required
|
|
79
|
+
if (payload.type === "macro" && payload.durationMinutes < 4) {
|
|
80
|
+
throw new Error("The duration must be at least 4 minutes");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//errorRatio is required
|
|
84
|
+
if (payload.type === "macro" && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
|
|
85
|
+
throw new Error("The error ratio must be between 0 and 1");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//regex check meta data key
|
|
89
|
+
if (payload.meta) {
|
|
90
|
+
payload.meta.forEach(meta => {
|
|
91
|
+
if (!/^[a-z_]+$/.test(meta.key)) {
|
|
92
|
+
throw new Error("Invalid meta data key " + meta.key + ", only lowercase letters and underscores are allowed");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private sanitizeAccomplishment(payload: AccomplishmentPayload) {
|
|
99
|
+
payload.description = payload.description.replace(/[^\x20-\x7E]/g, "");
|
|
100
|
+
|
|
101
|
+
payload.meta?.forEach((meta) => {
|
|
102
|
+
meta.description = meta.description.replace(/[^\x20-\x7E]/g, "");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return payload;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getDecoupledTopic(topic: string) {
|
|
109
|
+
const [plugin, skillCategory, accomplishmentKeyword] = topic.split(".");
|
|
110
|
+
|
|
111
|
+
return { plugin: plugin || "*", skillCategory: skillCategory || "*", accomplishmentKeyword: accomplishmentKeyword || "*" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Subscribe to accomplishment events
|
|
116
|
+
* @param accomplishmentTopic - The topic of the accomplishment event. The pattern can be any pattern of plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword
|
|
117
|
+
* @param callback - The callback function to be called when the accomplishment event is triggered
|
|
118
|
+
*/
|
|
119
|
+
subscribe(accomplishmentTopics = "*" as string | string[], callback: (payload: EventBusMessage<AccomplishmentPayload>) => void) {
|
|
120
|
+
if (typeof accomplishmentTopics === "string") {
|
|
121
|
+
accomplishmentTopics = [accomplishmentTopics];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
accomplishmentTopics.forEach((accomplishmentTopic) => {
|
|
125
|
+
const topicLength = accomplishmentTopic.split(".").length
|
|
126
|
+
if (topicLength === 1) {
|
|
127
|
+
accomplishmentTopic += ".*.*"
|
|
128
|
+
} else if (topicLength === 2) {
|
|
129
|
+
accomplishmentTopic += ".*"
|
|
130
|
+
} else if (topicLength !== 3) {
|
|
131
|
+
throw new Error("Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
EventBus.on<AccomplishmentPayload>(["global.accomplishment.triggerMicro", "global.accomplishment.triggerMacro"], (event) => {
|
|
135
|
+
const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
|
|
136
|
+
|
|
137
|
+
if (plugin !== "*" && event.sender !== plugin) return;
|
|
138
|
+
if (skillCategory !== "*" && event.data.skillCategory !== skillCategory) return;
|
|
139
|
+
if (accomplishmentKeyword !== "*" && event.data.accomplishmentKeyword !== accomplishmentKeyword) return;
|
|
140
|
+
|
|
141
|
+
callback(event);
|
|
142
|
+
}, [this.pluginId]);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// const accomplishmentHandler = AccomplishmentHandler.getInstance("my-plugin");
|
|
148
|
+
|
|
149
|
+
// accomplishmentHandler.subscribe("*", (payload) => {
|
|
150
|
+
// console.log(payload);
|
|
151
|
+
// });
|
|
152
|
+
|
|
153
|
+
// accomplishmentHandler.emitAccomplishment({
|
|
154
|
+
// skillCategory: "reading",
|
|
155
|
+
// accomplishmentKeyword: "chapter",
|
|
156
|
+
// description: "Read chapter 1 of the book",
|
|
157
|
+
// durationMinutes: 10,
|
|
158
|
+
// meta: [
|
|
159
|
+
// {
|
|
160
|
+
// key: "book",
|
|
161
|
+
// value: "The Great Gatsby",
|
|
162
|
+
// description: "The book I read",
|
|
163
|
+
// },
|
|
164
|
+
// ],
|
|
165
|
+
// });
|
|
@@ -7,127 +7,129 @@ import { setTheme } from './ThemeSetter';
|
|
|
7
7
|
declare const WorkerGlobalScope: any;
|
|
8
8
|
|
|
9
9
|
interface SupabaseInfo {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
url: string,
|
|
11
|
+
key: string,
|
|
12
|
+
token: string,
|
|
13
|
+
expiration: Date,
|
|
14
|
+
tablePrefix: string,
|
|
15
|
+
pluginId: string
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export class PluginController {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
window.addEventListener("message", (event) => {
|
|
35
|
-
// console.log("client: message received", event);
|
|
36
|
-
const { topic, sender, data, eventId } = event.data.event as EventBusMessage;
|
|
37
|
-
|
|
38
|
-
// skip forwarding messages from own plugin
|
|
39
|
-
if (sender === pluginId) return;
|
|
40
|
-
|
|
41
|
-
EventBus.emit(sender, topic, data, eventId);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
EventBus.on("*", (event) => {
|
|
45
|
-
// skip messages which are not from the own plugin
|
|
46
|
-
if (event.sender !== this.pluginId) return;
|
|
47
|
-
if (event.topic.startsWith("self.")) return;
|
|
48
|
-
window.parent.postMessage({ event, secret: this.getSecret() }, "*")
|
|
49
|
-
});
|
|
19
|
+
private static client: RimoriClient;
|
|
20
|
+
private static instance: PluginController;
|
|
21
|
+
private communicationSecret: string | null = null;
|
|
22
|
+
private supabase: SupabaseClient | null = null;
|
|
23
|
+
private supabaseInfo: SupabaseInfo | null = null;
|
|
24
|
+
private pluginId: string;
|
|
25
|
+
|
|
26
|
+
private constructor(pluginId: string) {
|
|
27
|
+
this.pluginId = pluginId;
|
|
28
|
+
this.getClient = this.getClient.bind(this);
|
|
29
|
+
|
|
30
|
+
if (typeof WorkerGlobalScope === 'undefined') {
|
|
31
|
+
setTheme();
|
|
50
32
|
}
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
PluginController.client = await RimoriClient.getInstance(PluginController.instance);
|
|
56
|
-
}
|
|
57
|
-
return PluginController.client;
|
|
58
|
-
}
|
|
34
|
+
window.addEventListener("message", (event) => {
|
|
35
|
+
// console.log("client: message received", event);
|
|
36
|
+
const { topic, sender, data, eventId } = event.data.event as EventBusMessage;
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
this.communicationSecret = secret;
|
|
67
|
-
}
|
|
68
|
-
return this.communicationSecret;
|
|
69
|
-
}
|
|
38
|
+
// skip forwarding messages from own plugin
|
|
39
|
+
if (sender === pluginId) return;
|
|
40
|
+
|
|
41
|
+
EventBus.emit(sender, topic, data, eventId);
|
|
42
|
+
});
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
44
|
+
EventBus.on("*", (event) => {
|
|
45
|
+
// skip messages which are not from the own plugin
|
|
46
|
+
if (event.sender !== this.pluginId) return;
|
|
47
|
+
if (event.topic.startsWith("self.")) return;
|
|
48
|
+
window.parent.postMessage({ event, secret: this.getSecret() }, "*")
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static async getInstance(sender: string): Promise<RimoriClient> {
|
|
53
|
+
if (!PluginController.instance) {
|
|
54
|
+
PluginController.instance = new PluginController(sender);
|
|
55
|
+
PluginController.client = await RimoriClient.getInstance(PluginController.instance);
|
|
56
|
+
}
|
|
57
|
+
return PluginController.client;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private getSecret() {
|
|
61
|
+
if (!this.communicationSecret) {
|
|
62
|
+
const secret = new URLSearchParams(window.location.search).get("secret");
|
|
63
|
+
if (!secret) {
|
|
64
|
+
throw new Error("Communication secret not found in URL as query parameter");
|
|
65
|
+
}
|
|
66
|
+
this.communicationSecret = secret;
|
|
67
|
+
}
|
|
68
|
+
return this.communicationSecret;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public async getClient(): Promise<{ supabase: SupabaseClient, tablePrefix: string, pluginId: string }> {
|
|
72
|
+
if (
|
|
73
|
+
this.supabase &&
|
|
74
|
+
this.supabaseInfo &&
|
|
75
|
+
this.supabaseInfo.expiration > new Date()
|
|
76
|
+
) {
|
|
77
|
+
return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
|
|
87
78
|
}
|
|
88
79
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
const { data } = await EventBus.request<SupabaseInfo>(this.pluginId, "global.supabase.requestAccess");
|
|
81
|
+
this.supabaseInfo = data;
|
|
82
|
+
this.supabase = createClient(this.supabaseInfo.url, this.supabaseInfo.key, {
|
|
83
|
+
accessToken: () => Promise.resolve(this.getToken())
|
|
84
|
+
});
|
|
93
85
|
|
|
94
|
-
|
|
86
|
+
return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
|
|
87
|
+
}
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
public async getToken() {
|
|
90
|
+
if (this.supabaseInfo && this.supabaseInfo.expiration && this.supabaseInfo.expiration > new Date()) {
|
|
91
|
+
return this.supabaseInfo.token;
|
|
92
|
+
}
|
|
99
93
|
|
|
100
|
-
|
|
101
|
-
this.supabaseInfo.expiration = data.expiration;
|
|
94
|
+
const { data } = await EventBus.request<{ token: string, expiration: Date }>(this.pluginId, "global.supabase.requestAccess");
|
|
102
95
|
|
|
103
|
-
|
|
96
|
+
if (!this.supabaseInfo) {
|
|
97
|
+
throw new Error("Supabase info not found");
|
|
104
98
|
}
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
this.supabaseInfo.token = data.token;
|
|
101
|
+
this.supabaseInfo.expiration = data.expiration;
|
|
102
|
+
|
|
103
|
+
return this.supabaseInfo.token;
|
|
104
|
+
}
|
|
110
105
|
|
|
111
|
-
|
|
106
|
+
public getSupabaseUrl() {
|
|
107
|
+
if (!this.supabaseInfo) {
|
|
108
|
+
throw new Error("Supabase info not found");
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
const topicParts = preliminaryTopic.split(".");
|
|
122
|
-
if (topicParts.length === 3) {
|
|
123
|
-
if (![this.supabaseInfo?.pluginId, "global"].includes(topicParts[0])) {
|
|
124
|
-
throw new Error("The event topic must start with the plugin id or 'global'.");
|
|
125
|
-
}
|
|
126
|
-
return preliminaryTopic;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const topicRoot = this.supabaseInfo?.pluginId ?? "global";
|
|
130
|
-
return `${topicRoot}.${preliminaryTopic}`;
|
|
111
|
+
return this.supabaseInfo.url;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public getGlobalEventTopic(preliminaryTopic: string) {
|
|
115
|
+
if (preliminaryTopic.startsWith("global.")) {
|
|
116
|
+
return preliminaryTopic;
|
|
131
117
|
}
|
|
118
|
+
if (preliminaryTopic.startsWith("self.")) {
|
|
119
|
+
return preliminaryTopic;
|
|
120
|
+
}
|
|
121
|
+
const topicParts = preliminaryTopic.split(".");
|
|
122
|
+
if (topicParts.length === 3) {
|
|
123
|
+
if (!topicParts[0].startsWith("pl") && topicParts[0] !== "global") {
|
|
124
|
+
throw new Error("The event topic must start with the plugin id or 'global'.");
|
|
125
|
+
}
|
|
126
|
+
return preliminaryTopic;
|
|
127
|
+
} else if (topicParts.length > 3) {
|
|
128
|
+
throw new Error(`The event topic must consist of 3 parts. <pluginId>.<topic area>.<action>. Received: ${preliminaryTopic}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const topicRoot = this.supabaseInfo?.pluginId ?? "global";
|
|
132
|
+
return `${topicRoot}.${preliminaryTopic}`;
|
|
133
|
+
}
|
|
132
134
|
|
|
133
135
|
}
|