@rimori/client 1.0.3 → 1.0.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.
Files changed (135) hide show
  1. package/README.md +51 -0
  2. package/dist/components/CRUDModal.js +0 -1
  3. package/dist/components/ai/Assistant.d.ts +9 -0
  4. package/dist/components/ai/Assistant.js +59 -0
  5. package/dist/components/ai/Avatar.d.ts +11 -0
  6. package/dist/components/ai/Avatar.js +39 -0
  7. package/dist/components/ai/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  8. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +38 -0
  9. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  10. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  11. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  12. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +86 -0
  13. package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  14. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +180 -0
  15. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  16. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +45 -0
  17. package/dist/components/ai/utils.d.ts +6 -0
  18. package/dist/components/ai/utils.js +14 -0
  19. package/dist/components/audio/Playbutton.js +4 -5
  20. package/dist/components/avatar/Assistant.d.ts +9 -0
  21. package/dist/components/avatar/Assistant.js +59 -0
  22. package/dist/components/avatar/Avatar.d.ts +12 -0
  23. package/dist/components/avatar/Avatar.js +42 -0
  24. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  25. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +38 -0
  26. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  27. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  28. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  29. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +84 -0
  30. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  31. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +180 -0
  32. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  33. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +45 -0
  34. package/dist/components/avatar/utils.d.ts +6 -0
  35. package/dist/components/avatar/utils.js +14 -0
  36. package/dist/components.d.ts +9 -0
  37. package/dist/components.js +10 -0
  38. package/dist/controller/AIController.d.ts +4 -3
  39. package/dist/controller/AIController.js +32 -8
  40. package/dist/controller/ObjectController.d.ts +2 -2
  41. package/dist/controller/ObjectController.js +4 -5
  42. package/dist/controller/SettingsController.d.ts +19 -10
  43. package/dist/controller/SettingsController.js +33 -26
  44. package/dist/controller/SharedContentController.js +6 -6
  45. package/dist/core.d.ts +9 -0
  46. package/dist/core.js +10 -0
  47. package/dist/hooks/UseChatHook.js +2 -2
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.js +4 -2
  50. package/dist/plugin/PluginController.d.ts +4 -12
  51. package/dist/plugin/PluginController.js +45 -70
  52. package/dist/plugin/RimoriClient.d.ts +85 -32
  53. package/dist/plugin/RimoriClient.js +98 -77
  54. package/dist/plugin/ThemeSetter.js +3 -3
  55. package/dist/plugin/fromRimori/EventBus.d.ts +98 -0
  56. package/dist/plugin/fromRimori/EventBus.js +240 -0
  57. package/dist/providers/PluginProvider.d.ts +1 -0
  58. package/dist/providers/PluginProvider.js +10 -12
  59. package/dist/worker/WorkerSetup.d.ts +6 -0
  60. package/dist/worker/WorkerSetup.js +80 -0
  61. package/package.json +17 -3
  62. package/src/components/CRUDModal.tsx +1 -3
  63. package/src/components/ai/Assistant.tsx +96 -0
  64. package/src/components/ai/Avatar.tsx +61 -0
  65. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +64 -0
  66. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +75 -0
  67. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +91 -0
  68. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +192 -0
  69. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +56 -0
  70. package/src/components/ai/utils.ts +23 -0
  71. package/src/components/audio/Playbutton.tsx +4 -5
  72. package/src/components.ts +10 -0
  73. package/src/controller/AIController.ts +84 -60
  74. package/src/controller/ObjectController.ts +4 -6
  75. package/src/controller/SettingsController.ts +49 -35
  76. package/src/controller/SharedContentController.ts +6 -6
  77. package/src/core.ts +10 -0
  78. package/src/hooks/UseChatHook.ts +2 -2
  79. package/src/index.ts +4 -2
  80. package/src/plugin/PluginController.ts +51 -76
  81. package/src/plugin/RimoriClient.ts +147 -85
  82. package/src/plugin/ThemeSetter.ts +3 -4
  83. package/src/plugin/fromRimori/EventBus.ts +301 -0
  84. package/src/plugin/fromRimori/readme.md +2 -0
  85. package/src/providers/PluginProvider.tsx +12 -14
  86. package/src/worker/WorkerSetup.ts +81 -0
  87. package/dist/CRUDModal.d.ts +0 -16
  88. package/dist/CRUDModal.js +0 -31
  89. package/dist/MarkdownEditor.d.ts +0 -8
  90. package/dist/MarkdownEditor.js +0 -46
  91. package/dist/audio/Playbutton.d.ts +0 -14
  92. package/dist/audio/Playbutton.js +0 -73
  93. package/dist/components/hooks/UseChatHook.d.ts +0 -15
  94. package/dist/components/hooks/UseChatHook.js +0 -21
  95. package/dist/controller/PluginController.d.ts +0 -14
  96. package/dist/controller/PluginController.js +0 -30
  97. package/dist/plugin/AIController copy.d.ts +0 -22
  98. package/dist/plugin/AIController copy.js +0 -68
  99. package/dist/plugin/AIController.d.ts +0 -22
  100. package/dist/plugin/AIController.js +0 -68
  101. package/dist/plugin/ObjectController.d.ts +0 -34
  102. package/dist/plugin/ObjectController.js +0 -77
  103. package/dist/plugin/SettingController.d.ts +0 -13
  104. package/dist/plugin/SettingController.js +0 -55
  105. package/dist/plugin/VoiceController.d.ts +0 -2
  106. package/dist/plugin/VoiceController.js +0 -27
  107. package/dist/providers/EventEmitter.d.ts +0 -11
  108. package/dist/providers/EventEmitter.js +0 -41
  109. package/dist/providers/EventEmitterContext.d.ts +0 -6
  110. package/dist/providers/EventEmitterContext.js +0 -19
  111. package/dist/utils/DifficultyConverter.d.ts +0 -3
  112. package/dist/utils/DifficultyConverter.js +0 -7
  113. package/dist/utils/constants.d.ts +0 -4
  114. package/dist/utils/constants.js +0 -12
  115. package/dist/utils/plugin/Client.d.ts +0 -72
  116. package/dist/utils/plugin/Client.js +0 -118
  117. package/dist/utils/plugin/PluginController.d.ts +0 -36
  118. package/dist/utils/plugin/PluginController.js +0 -119
  119. package/dist/utils/plugin/PluginUtils.d.ts +0 -2
  120. package/dist/utils/plugin/PluginUtils.js +0 -23
  121. package/dist/utils/plugin/RimoriClient.d.ts +0 -72
  122. package/dist/utils/plugin/RimoriClient.js +0 -118
  123. package/dist/utils/plugin/ThemeSetter.d.ts +0 -1
  124. package/dist/utils/plugin/ThemeSetter.js +0 -13
  125. package/dist/utils/plugin/WhereClauseBuilder.d.ts +0 -24
  126. package/dist/utils/plugin/WhereClauseBuilder.js +0 -79
  127. package/dist/utils/plugin/providers/EventEmitter.d.ts +0 -11
  128. package/dist/utils/plugin/providers/EventEmitter.js +0 -41
  129. package/dist/utils/plugin/providers/EventEmitterContext.d.ts +0 -6
  130. package/dist/utils/plugin/providers/EventEmitterContext.js +0 -19
  131. package/dist/utils/plugin/providers/PluginProvider.d.ts +0 -8
  132. package/dist/utils/plugin/providers/PluginProvider.js +0 -49
  133. package/src/providers/EventEmitter.ts +0 -48
  134. package/src/providers/EventEmitterContext.tsx +0 -27
  135. package/src/utils/constants.ts +0 -18
@@ -1,5 +1,3 @@
1
- import { env } from "../utils/constants";
2
-
3
1
  type PrimitiveType = 'string' | 'number' | 'boolean';
4
2
 
5
3
  // This is the type that can appear in the `type` property
@@ -35,8 +33,8 @@ export interface ObjectRequest {
35
33
  instructions: string;
36
34
  }
37
35
 
38
- export async function generateObject(request: ObjectRequest, token: string) {
39
- return await fetch(`${env.SUPABASE_URL}/functions/v1/llm-object`, {
36
+ export async function generateObject(supabaseUrl: string, request: ObjectRequest, token: string) {
37
+ return await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
40
38
  method: 'POST',
41
39
  body: JSON.stringify({
42
40
  stream: false,
@@ -51,9 +49,9 @@ export async function generateObject(request: ObjectRequest, token: string) {
51
49
  // TODO adjust stream to work with object
52
50
  export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: any[]) => void;
53
51
 
54
- export async function streamObject(request: ObjectRequest, onResponse: OnLLMResponse, token: string) {
52
+ export async function streamObject(supabaseUrl: string, request: ObjectRequest, onResponse: OnLLMResponse, token: string) {
55
53
  const messageId = Math.random().toString(36).substring(3);
56
- const response = await fetch(`${env.SUPABASE_URL}/functions/v1/llm-object`, {
54
+ const response = await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
57
55
  method: 'POST',
58
56
  body: JSON.stringify({
59
57
  stream: true,
@@ -1,16 +1,23 @@
1
1
  import { SupabaseClient } from "@supabase/supabase-js";
2
2
  import { LanguageLevel } from "../utils/difficultyConverter";
3
3
 
4
- type SettingsType = "user" | "system" | "plugin";
5
-
6
- export interface UserSettings {
4
+ export interface UserInfo {
7
5
  motherTongue: string;
8
- languageLevel: LanguageLevel;
9
- contextMenuOnSelect: boolean;
10
- }
11
-
12
- export interface SystemSettings {
13
- // TODO: add system settings
6
+ xp: number;
7
+ listening_level: LanguageLevel;
8
+ reading_level: LanguageLevel;
9
+ speaking_level: LanguageLevel;
10
+ writing_level: LanguageLevel;
11
+ understanding_level: LanguageLevel;
12
+ grammar_level: LanguageLevel;
13
+ longterm_goal: string;
14
+ motivation_type: string;
15
+ study_buddy: string;
16
+ preferred_genre: string;
17
+ milestone: string;
18
+ settings: {
19
+ contextMenuOnSelect: boolean;
20
+ }
14
21
  }
15
22
 
16
23
  export class SettingsController {
@@ -22,13 +29,8 @@ export class SettingsController {
22
29
  this.pluginId = pluginId;
23
30
  }
24
31
 
25
- private getSettingsType(genericSettings?: "user" | "system"): SettingsType {
26
- return genericSettings || "plugin";
27
- }
28
-
29
- private async fetchSettings(type: SettingsType): Promise<any | null> {
30
- const pluginId = type === "plugin" ? this.pluginId : type;
31
- const { data } = await this.supabase.from("plugin_settings").select("*").eq("plugin_id", pluginId)
32
+ private async fetchSettings(): Promise<any | null> {
33
+ const { data } = await this.supabase.from("plugin_settings").select("*").eq("plugin_id", this.pluginId)
32
34
 
33
35
  if (!data || data.length === 0) {
34
36
  return null;
@@ -37,28 +39,47 @@ export class SettingsController {
37
39
  return data[0].settings;
38
40
  }
39
41
 
40
- private async saveSettings(settings: any, type: SettingsType): Promise<void> {
41
- if (type !== "plugin") {
42
- throw new Error(`Cannot modify ${type} settings`);
42
+ public async setSettings(settings: any): Promise<void> {
43
+ await this.supabase.from("plugin_settings").upsert({ plugin_id: this.pluginId, settings });
44
+ }
45
+
46
+ public async getUserInfo(): Promise<UserInfo> {
47
+ const { data } = await this.supabase.from("profiles").select("*");
48
+
49
+ if (!data || data.length === 0) {
50
+ return {
51
+ motherTongue: "en",
52
+ xp: 0,
53
+ listening_level: "Pre-A1",
54
+ reading_level: "Pre-A1",
55
+ speaking_level: "Pre-A1",
56
+ writing_level: "Pre-A1",
57
+ understanding_level: "Pre-A1",
58
+ grammar_level: "Pre-A1",
59
+ longterm_goal: "",
60
+ motivation_type: "self-motivated",
61
+ study_buddy: "clarence",
62
+ preferred_genre: "adventure",
63
+ milestone: "",
64
+ settings: {
65
+ contextMenuOnSelect: false,
66
+ }
67
+ }
43
68
  }
44
69
 
45
- await this.supabase.from("plugin_settings").upsert({ plugin_id: this.pluginId, settings });
70
+ return data[0];
46
71
  }
47
72
 
48
73
  /**
49
74
  * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
50
75
  * @param defaultSettings The default settings to use if no settings are found.
51
- * @param genericSettings The type of settings to get.
52
76
  * @returns The settings for the plugin.
53
77
  */
54
- public async getSettings<T extends object>(defaultSettings: T, genericSettings?: "user" | "system"): Promise<T> {
55
- const type = this.getSettingsType(genericSettings);
56
- const storedSettings = await this.fetchSettings(type) as T | null;
78
+ public async getSettings<T extends object>(defaultSettings: T): Promise<T> {
79
+ const storedSettings = await this.fetchSettings() as T | null;
57
80
 
58
81
  if (!storedSettings) {
59
- if (type === "plugin") {
60
- await this.saveSettings(defaultSettings, type);
61
- }
82
+ await this.setSettings(defaultSettings);
62
83
  return defaultSettings;
63
84
  }
64
85
 
@@ -72,17 +93,10 @@ export class SettingsController {
72
93
  );
73
94
  const mergedSettings = { ...defaultSettings, ...validStoredSettings } as T;
74
95
 
75
- if (type === "plugin") {
76
- await this.saveSettings(mergedSettings, type);
77
- }
96
+ await this.setSettings(mergedSettings);
78
97
  return mergedSettings;
79
98
  }
80
99
 
81
100
  return storedSettings;
82
101
  }
83
-
84
- public async setSettings(settings: any, genericSettings?: "user" | "system"): Promise<void> {
85
- const type = this.getSettingsType(genericSettings);
86
- await this.saveSettings(settings, type);
87
- }
88
102
  }
@@ -24,14 +24,14 @@ export class SharedContentController {
24
24
  filter?: { column: string, value: string | number | boolean },
25
25
  ): Promise<R[]> {
26
26
  const queryParameter = { filter_column: filter?.column || null, filter_value: filter?.value || null, unread: true }
27
- const { data: newAssignments } = await this.rimoriClient.rpc(type + "_entries", queryParameter)
27
+ const { data: newAssignments } = await this.rimoriClient.db.rpc(type + "_entries", queryParameter)
28
28
  console.log('newAssignments:', newAssignments);
29
29
 
30
30
  if ((newAssignments as any[]).length > 0) {
31
31
  return newAssignments as R[];
32
32
  }
33
33
  // generate new assignments
34
- const { data: oldAssignments } = await this.rimoriClient.rpc(type + "_entries", { ...queryParameter, unread: false })
34
+ const { data: oldAssignments } = await this.rimoriClient.db.rpc(type + "_entries", { ...queryParameter, unread: false })
35
35
  console.log('oldAssignments:', oldAssignments);
36
36
  const reservedTopics = this.getReservedTopics(oldAssignments as BasicAssignment[]);
37
37
 
@@ -39,7 +39,7 @@ export class SharedContentController {
39
39
  if (!request.tool.keywords || !request.tool.topic) {
40
40
  throw new Error("topic or keywords not found in the request schema");
41
41
  }
42
- const instructions = await this.rimoriClient.generateObject(request);
42
+ const instructions = await this.rimoriClient.llm.getObject(request);
43
43
  console.log('instructions:', instructions);
44
44
 
45
45
  const preparedData = {
@@ -47,7 +47,7 @@ export class SharedContentController {
47
47
  ...instructions,
48
48
  keywords: this.purifyStringArray(instructions.keywords),
49
49
  };
50
- return await this.rimoriClient.from(type).insert(preparedData).then(() => [preparedData] as R[]);
50
+ return await this.rimoriClient.db.from(type).insert(preparedData).then(() => [preparedData] as R[]);
51
51
  }
52
52
 
53
53
  private getReservedTopics(oldAssignments: BasicAssignment[]) {
@@ -62,10 +62,10 @@ export class SharedContentController {
62
62
  }
63
63
 
64
64
  public async getSharedContent<T extends BasicAssignment>(type: string, id: string): Promise<T> {
65
- return await this.rimoriClient.from(type).select().eq('id', id).single() as unknown as T;
65
+ return await this.rimoriClient.db.from(type).select().eq('id', id).single() as unknown as T;
66
66
  }
67
67
 
68
68
  public async completeSharedContent(type: string, assignmentId: string) {
69
- await this.rimoriClient.from(type + "_result").insert({ assignment_id: assignmentId });
69
+ await this.rimoriClient.db.from(type + "_result").insert({ assignment_id: assignmentId });
70
70
  }
71
71
  }
package/src/core.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Core functionality exports
2
+ export * from "./controller/AIController";
3
+ export * from "./controller/SharedContentController";
4
+ export * from "./controller/SettingsController";
5
+ export * from "./plugin/RimoriClient";
6
+ export * from "./plugin/PluginController";
7
+ export * from "./utils/difficultyConverter";
8
+ export * from "./utils/PluginUtils";
9
+ export * from "./worker/WorkerSetup";
10
+ export * from "./plugin/fromRimori/EventBus";
@@ -5,10 +5,10 @@ import { ToolInvocation, Tool, Message } from "../controller/AIController";
5
5
  export function useChat(tools?: Tool[]) {
6
6
  const [messages, setMessages] = React.useState<Message[]>([]);
7
7
  const [isLoading, setIsLoading] = React.useState(false);
8
- const { getAIResponseStream } = usePlugin();
8
+ const { llm } = usePlugin();
9
9
 
10
10
  const append = (appendMessages: Message[]) => {
11
- getAIResponseStream([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
11
+ llm.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
12
12
  const lastMessage = messages[messages.length - 1];
13
13
  setIsLoading(!finished);
14
14
 
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Re-export everything
2
+ export * from './core';
3
+ export * from './components';
1
4
  export * from "./components/MarkdownEditor";
2
5
  export * from "./components/CRUDModal";
3
6
  export * from "./components/Spinner";
@@ -8,8 +11,7 @@ export * from "./controller/SettingsController";
8
11
  export * from "./hooks/UseChatHook";
9
12
  export * from "./plugin/RimoriClient";
10
13
  export * from "./plugin/ThemeSetter";
11
- export * from "./providers/EventEmitter";
12
- export * from "./providers/EventEmitterContext";
13
14
  export * from "./providers/PluginProvider";
14
15
  export * from "./utils/difficultyConverter";
15
16
  export * from "./utils/PluginUtils";
17
+ export * from "./plugin/PluginController";
@@ -1,6 +1,10 @@
1
- import { Child } from "ibridge-flex";
2
1
  import { createClient, SupabaseClient } from '@supabase/supabase-js'
3
2
  import { RimoriClient } from "./RimoriClient";
3
+ import { EventBus, EventBusMessage } from './fromRimori/EventBus';
4
+ import { setTheme } from './ThemeSetter';
5
+
6
+ // Add declaration for WorkerGlobalScope
7
+ declare const WorkerGlobalScope: any;
4
8
 
5
9
  interface SupabaseInfo {
6
10
  url: string,
@@ -12,55 +16,47 @@ interface SupabaseInfo {
12
16
  }
13
17
 
14
18
  export class PluginController {
15
- private static instance: PluginController;
16
19
  private static client: RimoriClient;
17
- private plugin: Child<null, null>;
18
- private onceListeners: Map<string, any[]> = new Map();
19
- private listeners: Map<string, any[]> = new Map();
20
+ private static instance: PluginController;
20
21
  private communicationSecret: string | null = null;
21
- private initialized = false;
22
22
  private supabase: SupabaseClient | null = null;
23
23
  private supabaseInfo: SupabaseInfo | null = null;
24
+ private pluginId: string;
24
25
 
25
- private constructor() {
26
- // localStorage.debug = "*";
27
- this.plugin = new Child({
28
- triggerChild: ({ topic, data, _id }: any) => {
29
- // console.log("trigger child with topic:" + topic + " and data: ", data);
30
- this.onceListeners.get(topic)?.forEach((callback: any) => callback(_id, data));
31
- this.onceListeners.set(topic, []);
32
- this.listeners.get(topic)?.forEach((callback: any) => callback(_id, data));
33
- }
26
+ private constructor(pluginId: string) {
27
+ this.pluginId = pluginId;
28
+ this.getClient = this.getClient.bind(this);
29
+
30
+ if (typeof WorkerGlobalScope === 'undefined') {
31
+ setTheme();
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);
34
42
  });
35
43
 
36
- this.emit = this.emit.bind(this);
37
- this.onOnce = this.onOnce.bind(this);
38
- this.getClient = this.getClient.bind(this);
39
- this.subscribe = this.subscribe.bind(this);
40
- this.internalEmit = this.internalEmit.bind(this);
41
- this.request = this.request.bind(this);
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
+ });
42
50
  }
43
51
 
44
- public static async getInstance(): Promise<RimoriClient> {
52
+ public static async getInstance(sender: string): Promise<RimoriClient> {
45
53
  if (!PluginController.instance) {
46
- PluginController.instance = new PluginController();
47
- await PluginController.instance.init();
48
- PluginController.client = await RimoriClient.getInstance(
49
- PluginController.instance
50
- );
54
+ PluginController.instance = new PluginController(sender);
55
+ PluginController.client = await RimoriClient.getInstance(PluginController.instance);
51
56
  }
52
57
  return PluginController.client;
53
58
  }
54
59
 
55
- async init() {
56
- if (this.initialized) return;
57
-
58
- // Wait for the plugin to be ready
59
- await this.plugin.handshake().then(() => this.initialized = true).catch((error: any) => {
60
- console.error("Failed to initialize the plugin communication:", error);
61
- });
62
- }
63
-
64
60
  private getSecret() {
65
61
  if (!this.communicationSecret) {
66
62
  const secret = new URLSearchParams(window.location.search).get("secret");
@@ -81,8 +77,8 @@ export class PluginController {
81
77
  return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
82
78
  }
83
79
 
84
- this.supabaseInfo = await this.request<SupabaseInfo>("getSupabaseAccess");
85
-
80
+ const { data } = await EventBus.request<SupabaseInfo>(this.pluginId, "global.supabase.requestAccess");
81
+ this.supabaseInfo = data;
86
82
  this.supabase = createClient(this.supabaseInfo.url, this.supabaseInfo.key, {
87
83
  accessToken: () => Promise.resolve(this.getToken())
88
84
  });
@@ -95,14 +91,14 @@ export class PluginController {
95
91
  return this.supabaseInfo.token;
96
92
  }
97
93
 
98
- const response = await this.request<{ token: string, expiration: Date }>("getSupabaseAccess");
94
+ const { data } = await EventBus.request<{ token: string, expiration: Date }>(this.pluginId, "global.supabase.requestAccess");
99
95
 
100
96
  if (!this.supabaseInfo) {
101
97
  throw new Error("Supabase info not found");
102
98
  }
103
99
 
104
- this.supabaseInfo.token = response.token;
105
- this.supabaseInfo.expiration = response.expiration;
100
+ this.supabaseInfo.token = data.token;
101
+ this.supabaseInfo.expiration = data.expiration;
106
102
 
107
103
  return this.supabaseInfo.token;
108
104
  }
@@ -115,44 +111,23 @@ export class PluginController {
115
111
  return this.supabaseInfo.url;
116
112
  }
117
113
 
118
- public emit(eventName: string, data?: any) {
119
- this.internalEmit(eventName, 0, data);
120
- }
121
-
122
- // the communication needs to have an id to be able to distinguish between different responses
123
- private internalEmit(eventName: string, id: number, data?: any) {
124
- this.init().then(() => this.plugin.emitToParent(eventName, { data, _id: id, secret: this.getSecret() }));
125
- }
126
-
127
- public subscribe(eventName: string, callback: (_id: number, data: any) => void) {
128
- if (!this.listeners.has(eventName)) {
129
- this.listeners.set(eventName, []);
114
+ public getGlobalEventTopic(preliminaryTopic: string) {
115
+ if (preliminaryTopic.startsWith("global.")) {
116
+ return preliminaryTopic;
130
117
  }
131
-
132
- this.listeners.get(eventName)?.push(callback);
133
- }
134
-
135
- public onOnce(eventName: string, callback: (data: any) => void) {
136
- if (!this.onceListeners.has(eventName)) {
137
- this.onceListeners.set(eventName, []);
118
+ if (preliminaryTopic.startsWith("self.")) {
119
+ return preliminaryTopic;
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;
138
127
  }
139
128
 
140
- this.onceListeners.get(eventName)?.push(callback);
129
+ const topicRoot = this.supabaseInfo?.pluginId ?? "global";
130
+ return `${topicRoot}.${preliminaryTopic}`;
141
131
  }
142
132
 
143
- async request<T>(topic: string, data: any = {}): Promise<T> {
144
- return await new Promise((resolve) => {
145
- let triggered = false;
146
- const id = Math.random();
147
-
148
- this.internalEmit(topic, id, data);
149
-
150
- this.subscribe(topic, (_id: number, data: any) => {
151
- if (triggered || (_id !== id && _id !== 0)) return;
152
- triggered = true;
153
-
154
- resolve(data)
155
- })
156
- });
157
- }
158
133
  }