@rimori/client 1.1.10 → 1.3.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/README.md +189 -63
- package/dist/cli/scripts/init/dev-registration.d.ts +35 -0
- package/dist/cli/scripts/init/dev-registration.js +174 -0
- package/dist/cli/scripts/init/env-setup.d.ts +9 -0
- package/dist/cli/scripts/init/env-setup.js +43 -0
- package/dist/cli/scripts/init/file-operations.d.ts +4 -0
- package/dist/cli/scripts/init/file-operations.js +51 -0
- package/dist/cli/scripts/init/html-cleaner.d.ts +4 -0
- package/dist/cli/scripts/init/html-cleaner.js +38 -0
- package/dist/cli/scripts/init/main.d.ts +2 -0
- package/dist/cli/scripts/init/main.js +160 -0
- package/dist/cli/scripts/init/package-setup.d.ts +32 -0
- package/dist/cli/scripts/init/package-setup.js +75 -0
- package/dist/cli/scripts/init/router-transformer.d.ts +6 -0
- package/dist/cli/scripts/init/router-transformer.js +254 -0
- package/dist/cli/scripts/init/tailwind-config.d.ts +4 -0
- package/dist/cli/scripts/init/tailwind-config.js +56 -0
- package/dist/cli/scripts/init/vite-config.d.ts +20 -0
- package/dist/cli/scripts/init/vite-config.js +54 -0
- package/dist/cli/scripts/release/release-config-upload.d.ts +7 -0
- package/dist/cli/scripts/release/release-config-upload.js +116 -0
- package/dist/cli/scripts/release/release-db-update.d.ts +6 -0
- package/dist/cli/scripts/release/release-db-update.js +100 -0
- package/dist/cli/scripts/release/release-file-upload.d.ts +6 -0
- package/dist/cli/scripts/release/release-file-upload.js +136 -0
- package/dist/cli/scripts/release/release.d.ts +23 -0
- package/dist/cli/scripts/release/release.js +70 -0
- package/dist/cli/types/DatabaseTypes.d.ts +103 -0
- package/dist/cli/types/DatabaseTypes.js +2 -0
- package/dist/components/LoggerExample.d.ts +6 -0
- package/dist/components/LoggerExample.js +79 -0
- package/dist/components/ai/Assistant.js +5 -5
- package/dist/components/ai/Avatar.d.ts +3 -2
- package/dist/components/ai/Avatar.js +11 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +1 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +48 -33
- package/dist/components/ai/utils.js +0 -1
- package/dist/components/audio/Playbutton.js +4 -4
- package/dist/{core → components}/components/ContextMenu.js +50 -11
- package/dist/components.d.ts +5 -5
- package/dist/components.js +5 -5
- package/dist/core/controller/AIController.d.ts +15 -0
- package/dist/core/controller/AIController.js +253 -0
- package/dist/core/controller/AudioController.d.ts +0 -0
- package/dist/core/controller/AudioController.js +1 -0
- package/dist/{controller → core/controller}/ObjectController.d.ts +10 -2
- package/dist/{controller → core/controller}/ObjectController.js +8 -8
- package/dist/{controller → core/controller}/SettingsController.d.ts +28 -4
- package/dist/{controller → core/controller}/SettingsController.js +0 -25
- package/dist/{controller → core/controller}/SharedContentController.d.ts +31 -3
- package/dist/{controller → core/controller}/SharedContentController.js +77 -26
- package/dist/core/controller/VoiceController.d.ts +9 -0
- package/dist/{controller → core/controller}/VoiceController.js +11 -4
- package/dist/core/core.d.ts +14 -0
- package/dist/core/core.js +8 -0
- package/dist/{plugin/fromRimori → fromRimori}/EventBus.d.ts +3 -3
- package/dist/{plugin/fromRimori → fromRimori}/EventBus.js +26 -9
- package/dist/fromRimori/PluginTypes.d.ts +174 -0
- package/dist/hooks/UseChatHook.d.ts +2 -1
- package/dist/hooks/UseChatHook.js +6 -4
- package/dist/hooks/UseLogger.d.ts +30 -0
- package/dist/hooks/UseLogger.js +122 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +5 -3
- package/dist/plugin/AccomplishmentHandler.d.ts +1 -1
- package/dist/plugin/AccomplishmentHandler.js +1 -1
- package/dist/plugin/AudioController.d.ts +37 -0
- package/dist/plugin/AudioController.js +68 -0
- package/dist/plugin/Logger.d.ts +68 -0
- package/dist/plugin/Logger.js +256 -0
- package/dist/plugin/LoggerExample.d.ts +16 -0
- package/dist/plugin/LoggerExample.js +140 -0
- package/dist/plugin/PluginController.d.ts +30 -5
- package/dist/plugin/PluginController.js +182 -53
- package/dist/plugin/RimoriClient.d.ts +68 -21
- package/dist/plugin/RimoriClient.js +88 -41
- package/dist/plugin/StandaloneClient.d.ts +1 -0
- package/dist/plugin/StandaloneClient.js +24 -10
- package/dist/plugin/ThemeSetter.d.ts +2 -1
- package/dist/plugin/ThemeSetter.js +13 -7
- package/dist/providers/PluginProvider.d.ts +4 -1
- package/dist/providers/PluginProvider.js +39 -13
- package/dist/utils/Language.d.ts +2 -1
- package/dist/utils/Language.js +4 -2
- package/dist/utils/audioFormats.d.ts +26 -0
- package/dist/utils/audioFormats.js +67 -0
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.d.ts +2 -0
- package/dist/utils/endpoint.js +2 -0
- package/dist/worker/WorkerSetup.d.ts +3 -2
- package/dist/worker/WorkerSetup.js +22 -65
- package/example/docs/devdocs.md +231 -0
- package/example/docs/overview.md +29 -0
- package/example/docs/userdocs.md +123 -0
- package/example/rimori.config.ts +89 -0
- package/example/worker/vite.config.ts +23 -0
- package/example/worker/worker.ts +11 -0
- package/package.json +16 -9
- package/src/cli/scripts/init/dev-registration.ts +192 -0
- package/src/cli/scripts/init/env-setup.ts +44 -0
- package/src/cli/scripts/init/file-operations.ts +58 -0
- package/src/cli/scripts/init/html-cleaner.ts +48 -0
- package/src/cli/scripts/init/main.ts +172 -0
- package/src/cli/scripts/init/package-setup.ts +117 -0
- package/src/cli/scripts/init/router-transformer.ts +329 -0
- package/src/cli/scripts/init/tailwind-config.ts +75 -0
- package/src/cli/scripts/init/vite-config.ts +73 -0
- package/src/cli/scripts/release/release-config-upload.ts +114 -0
- package/src/cli/scripts/release/release-db-update.ts +97 -0
- package/src/cli/scripts/release/release-file-upload.ts +138 -0
- package/src/cli/scripts/release/release.ts +69 -0
- package/src/cli/types/DatabaseTypes.ts +117 -0
- package/src/components/ai/Assistant.tsx +5 -5
- package/src/components/ai/Avatar.tsx +25 -8
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +1 -1
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +50 -35
- package/src/components/ai/utils.ts +0 -2
- package/src/components/audio/Playbutton.tsx +4 -4
- package/src/{core → components}/components/ContextMenu.tsx +56 -12
- package/src/components.ts +6 -6
- package/src/core/controller/AIController.ts +283 -0
- package/src/core/controller/ObjectController.ts +115 -0
- package/src/{controller → core/controller}/SettingsController.ts +29 -29
- package/src/{controller → core/controller}/SharedContentController.ts +91 -29
- package/src/core/controller/VoiceController.ts +31 -0
- package/src/core/core.ts +16 -0
- package/src/{plugin/fromRimori → fromRimori}/EventBus.ts +29 -11
- package/src/fromRimori/PluginTypes.ts +205 -0
- package/src/hooks/UseChatHook.ts +8 -5
- package/src/index.ts +6 -3
- package/src/plugin/AccomplishmentHandler.ts +1 -1
- package/src/plugin/AudioController.ts +58 -0
- package/src/plugin/Logger.ts +324 -0
- package/src/plugin/PluginController.ts +203 -63
- package/src/plugin/RimoriClient.ts +127 -55
- package/src/plugin/StandaloneClient.ts +30 -11
- package/src/plugin/ThemeSetter.ts +16 -9
- package/src/providers/PluginProvider.tsx +46 -13
- package/src/utils/Language.ts +4 -2
- package/src/utils/difficultyConverter.ts +3 -3
- package/src/utils/endpoint.ts +2 -0
- package/src/worker/WorkerSetup.ts +13 -60
- package/dist/components/PluginController.d.ts +0 -21
- package/dist/components/PluginController.js +0 -116
- package/dist/controller/AIController.d.ts +0 -23
- package/dist/controller/AIController.js +0 -93
- package/dist/controller/SidePluginController.d.ts +0 -3
- package/dist/controller/SidePluginController.js +0 -31
- package/dist/controller/VoiceController.d.ts +0 -10
- package/dist/core.d.ts +0 -7
- package/dist/core.js +0 -7
- package/dist/plugin/ContextMenu.d.ts +0 -17
- package/dist/plugin/ContextMenu.js +0 -45
- package/dist/plugin/fromRimori/PluginTypes.d.ts +0 -48
- package/dist/plugin/fromRimori/SupabaseHandler.d.ts +0 -13
- package/dist/plugin/fromRimori/SupabaseHandler.js +0 -55
- package/dist/providers/PluginController.d.ts +0 -21
- package/dist/providers/PluginController.js +0 -116
- package/dist/types/Actions.d.ts +0 -4
- package/dist/types/Actions.js +0 -1
- package/src/controller/AIController.ts +0 -112
- package/src/controller/ObjectController.ts +0 -107
- package/src/controller/SidePluginController.ts +0 -25
- package/src/controller/VoiceController.ts +0 -26
- package/src/core.ts +0 -8
- package/src/plugin/fromRimori/PluginTypes.ts +0 -64
- package/src/types/Actions.ts +0 -6
- /package/dist/{core → components}/components/ContextMenu.d.ts +0 -0
- /package/dist/{plugin/fromRimori → fromRimori}/PluginTypes.js +0 -0
- /package/src/{plugin/fromRimori → fromRimori}/readme.md +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { RimoriClient } from './RimoriClient';
|
|
2
|
+
import html2canvas from 'html2canvas';
|
|
3
|
+
|
|
4
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
5
|
+
|
|
6
|
+
interface LogEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
message: string;
|
|
11
|
+
data?: any;
|
|
12
|
+
context?: {
|
|
13
|
+
url: string;
|
|
14
|
+
userAgent: string;
|
|
15
|
+
browserInfo: BrowserInfo;
|
|
16
|
+
screenshot?: string;
|
|
17
|
+
mousePosition?: MousePosition;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface BrowserInfo {
|
|
22
|
+
userAgent: string;
|
|
23
|
+
language: string;
|
|
24
|
+
cookieEnabled: boolean;
|
|
25
|
+
onLine: boolean;
|
|
26
|
+
screenResolution: string;
|
|
27
|
+
windowSize: string;
|
|
28
|
+
timestamp: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface MousePosition {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Singleton Logger class for Rimori client plugins.
|
|
39
|
+
* Handles all logging levels, production filtering, and log transmission to Rimori.
|
|
40
|
+
* Overrides console methods globally for seamless integration.
|
|
41
|
+
*/
|
|
42
|
+
export class Logger {
|
|
43
|
+
private static instance: Logger;
|
|
44
|
+
private isProduction: boolean;
|
|
45
|
+
private logs: LogEntry[] = [];
|
|
46
|
+
private logIdCounter = 0;
|
|
47
|
+
private originalConsole: {
|
|
48
|
+
log: typeof console.log;
|
|
49
|
+
info: typeof console.info;
|
|
50
|
+
warn: typeof console.warn;
|
|
51
|
+
error: typeof console.error;
|
|
52
|
+
debug: typeof console.debug;
|
|
53
|
+
};
|
|
54
|
+
private mousePosition: MousePosition | null = null;
|
|
55
|
+
|
|
56
|
+
private constructor(rimori: RimoriClient, isProduction?: boolean) {
|
|
57
|
+
this.isProduction = this.validateIsProduction(isProduction);
|
|
58
|
+
|
|
59
|
+
// Store original console methods
|
|
60
|
+
this.originalConsole = {
|
|
61
|
+
log: console.log,
|
|
62
|
+
info: console.info,
|
|
63
|
+
warn: console.warn,
|
|
64
|
+
error: console.error,
|
|
65
|
+
debug: console.debug
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Override console methods globally
|
|
69
|
+
this.overrideConsoleMethods();
|
|
70
|
+
|
|
71
|
+
// Track mouse position
|
|
72
|
+
this.trackMousePosition();
|
|
73
|
+
|
|
74
|
+
// Expose logs to global scope for DevTools access
|
|
75
|
+
this.exposeToDevTools();
|
|
76
|
+
|
|
77
|
+
// Set up navigation clearing
|
|
78
|
+
this.setupNavigationClearing();
|
|
79
|
+
|
|
80
|
+
rimori.event.respond('logging.requestPluginLogs', async () => {
|
|
81
|
+
this.addLogEntry(await this.createLogEntry('info', 'Screenshot capture', undefined, true));
|
|
82
|
+
const logs = {
|
|
83
|
+
logs: this.logs,
|
|
84
|
+
pluginId: rimori.plugin.pluginId,
|
|
85
|
+
timestamp: new Date().toISOString()
|
|
86
|
+
}
|
|
87
|
+
this.logs = [];
|
|
88
|
+
this.logIdCounter = 0;
|
|
89
|
+
return logs;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Initialize the Logger singleton and override console methods globally.
|
|
95
|
+
* @param rimori - Rimori client instance
|
|
96
|
+
* @param isProduction - Whether the environment is production
|
|
97
|
+
* @returns Logger instance
|
|
98
|
+
*/
|
|
99
|
+
public static getInstance(rimori: RimoriClient, isProduction?: boolean): Logger {
|
|
100
|
+
if (!Logger.instance) {
|
|
101
|
+
Logger.instance = new Logger(rimori, isProduction);
|
|
102
|
+
}
|
|
103
|
+
return Logger.instance;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private validateIsProduction(isProduction?: boolean): boolean {
|
|
107
|
+
if (isProduction !== undefined) {
|
|
108
|
+
return isProduction;
|
|
109
|
+
}
|
|
110
|
+
if (typeof window !== 'undefined' && window.location.href) {
|
|
111
|
+
return !window.location.href.includes('localhost');
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Expose log access to global scope for DevTools console access.
|
|
117
|
+
*/
|
|
118
|
+
private exposeToDevTools(): void {
|
|
119
|
+
if (typeof window !== 'undefined') {
|
|
120
|
+
// Expose a global function to access logs from DevTools console
|
|
121
|
+
(window as any).getRimoriLogs = () => this.logs;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Set up navigation event listeners to clear logs on page changes.
|
|
127
|
+
*/
|
|
128
|
+
private setupNavigationClearing(): void {
|
|
129
|
+
if (typeof window === 'undefined' || typeof history === 'undefined') return;
|
|
130
|
+
|
|
131
|
+
// Clear logs on browser back/forward
|
|
132
|
+
window.addEventListener('popstate', () => this.logs = []);
|
|
133
|
+
|
|
134
|
+
// Override history methods to clear logs on programmatic navigation
|
|
135
|
+
const originalPushState = history.pushState;
|
|
136
|
+
const originalReplaceState = history.replaceState;
|
|
137
|
+
|
|
138
|
+
history.pushState = (...args) => {
|
|
139
|
+
originalPushState.apply(history, args);
|
|
140
|
+
this.logs = [];
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
history.replaceState = (...args) => {
|
|
144
|
+
originalReplaceState.apply(history, args);
|
|
145
|
+
this.logs = [];
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Listen for URL changes (works with React Router and other SPAs)
|
|
149
|
+
let currentUrl = window.location.href;
|
|
150
|
+
const checkUrlChange = () => {
|
|
151
|
+
if (window.location.href !== currentUrl) {
|
|
152
|
+
currentUrl = window.location.href;
|
|
153
|
+
this.logs = [];
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Check for URL changes periodically
|
|
158
|
+
setInterval(checkUrlChange, 100);
|
|
159
|
+
|
|
160
|
+
// Also listen for hash changes (for hash-based routing)
|
|
161
|
+
window.addEventListener('hashchange', () => this.logs = []);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Override console methods globally to capture all console calls.
|
|
166
|
+
*/
|
|
167
|
+
private overrideConsoleMethods(): void {
|
|
168
|
+
// Override console.log
|
|
169
|
+
console.log = (...args: any[]) => {
|
|
170
|
+
this.originalConsole.log(...args);
|
|
171
|
+
this.handleConsoleCall('info', args);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Override console.info
|
|
175
|
+
console.info = (...args: any[]) => {
|
|
176
|
+
this.originalConsole.info(...args);
|
|
177
|
+
this.handleConsoleCall('info', args);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Override console.warn
|
|
181
|
+
console.warn = (...args: any[]) => {
|
|
182
|
+
this.originalConsole.warn(...args);
|
|
183
|
+
this.handleConsoleCall('warn', args);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Override console.error
|
|
187
|
+
console.error = (...args: any[]) => {
|
|
188
|
+
this.originalConsole.error(...args);
|
|
189
|
+
this.handleConsoleCall('error', args);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Override console.debug
|
|
193
|
+
console.debug = (...args: any[]) => {
|
|
194
|
+
this.originalConsole.debug(...args);
|
|
195
|
+
this.handleConsoleCall('debug', args);
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Track mouse position for screenshot context.
|
|
201
|
+
*/
|
|
202
|
+
private trackMousePosition(): void {
|
|
203
|
+
if (typeof window !== 'undefined') {
|
|
204
|
+
const updateMousePosition = (event: MouseEvent) => {
|
|
205
|
+
this.mousePosition = {
|
|
206
|
+
x: event.clientX,
|
|
207
|
+
y: event.clientY,
|
|
208
|
+
timestamp: new Date().toISOString()
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
window.addEventListener('mousemove', updateMousePosition);
|
|
213
|
+
window.addEventListener('click', updateMousePosition);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Handle console method calls and create log entries.
|
|
219
|
+
* @param level - Log level
|
|
220
|
+
* @param args - Console arguments
|
|
221
|
+
*/
|
|
222
|
+
private async handleConsoleCall(level: LogLevel, args: any[]): Promise<void> {
|
|
223
|
+
// Skip if this is a production log that shouldn't be stored
|
|
224
|
+
if (this.isProduction && (level === 'debug' || level === 'info')) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Convert console arguments to message and data
|
|
229
|
+
const message = args.map(arg =>
|
|
230
|
+
typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
|
|
231
|
+
).join(' ');
|
|
232
|
+
|
|
233
|
+
const data = args.length > 1 ? args.slice(1) : undefined;
|
|
234
|
+
|
|
235
|
+
const entry = await this.createLogEntry(level, message, data);
|
|
236
|
+
this.addLogEntry(entry);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get browser and system information for debugging.
|
|
241
|
+
* @returns Object with browser and system information
|
|
242
|
+
*/
|
|
243
|
+
private getBrowserInfo(): BrowserInfo {
|
|
244
|
+
return {
|
|
245
|
+
userAgent: navigator.userAgent,
|
|
246
|
+
language: navigator.language,
|
|
247
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
248
|
+
onLine: navigator.onLine,
|
|
249
|
+
screenResolution: `${screen.width}x${screen.height}`,
|
|
250
|
+
windowSize: `${window.innerWidth}x${window.innerHeight}`,
|
|
251
|
+
timestamp: new Date().toISOString()
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Capture a screenshot of the current page.
|
|
257
|
+
* @returns Promise resolving to base64 screenshot or null if failed
|
|
258
|
+
*/
|
|
259
|
+
private async captureScreenshot(): Promise<string | null> {
|
|
260
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
261
|
+
const canvas = await html2canvas(document.body);
|
|
262
|
+
const screenshot = canvas.toDataURL('image/png');
|
|
263
|
+
// this.originalConsole.log("screenshot captured", screenshot)
|
|
264
|
+
return screenshot;
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create a log entry with context information.
|
|
271
|
+
* @param level - Log level
|
|
272
|
+
* @param message - Log message
|
|
273
|
+
* @param data - Additional data
|
|
274
|
+
* @returns Log entry
|
|
275
|
+
*/
|
|
276
|
+
private async createLogEntry(level: LogLevel, message: string, data?: any, forceScreenshot?: boolean): Promise<LogEntry> {
|
|
277
|
+
const context: Partial<LogEntry['context']> = {};
|
|
278
|
+
|
|
279
|
+
// Add URL if available
|
|
280
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
281
|
+
return {
|
|
282
|
+
id: `log_${++this.logIdCounter}_${Date.now()}`,
|
|
283
|
+
timestamp: new Date().toISOString(),
|
|
284
|
+
level,
|
|
285
|
+
message,
|
|
286
|
+
data,
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
context.url = window.location.href;
|
|
291
|
+
|
|
292
|
+
// Add browser info (this method now handles worker context internally)
|
|
293
|
+
context.browserInfo = this.getBrowserInfo();
|
|
294
|
+
context.userAgent = context.browserInfo.userAgent;
|
|
295
|
+
|
|
296
|
+
// Add screenshot and mouse position if level is error or warn
|
|
297
|
+
if (level === 'error' || level === 'warn' || forceScreenshot) {
|
|
298
|
+
context.screenshot = await this.captureScreenshot() || undefined;
|
|
299
|
+
context.mousePosition = this.mousePosition || undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
id: `log_${++this.logIdCounter}_${Date.now()}`,
|
|
304
|
+
timestamp: new Date().toISOString(),
|
|
305
|
+
level,
|
|
306
|
+
message,
|
|
307
|
+
data,
|
|
308
|
+
context: context as LogEntry['context']
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Add a log entry to the internal log array.
|
|
314
|
+
* @param entry - Log entry to add
|
|
315
|
+
*/
|
|
316
|
+
private addLogEntry(entry: LogEntry): void {
|
|
317
|
+
this.logs.push(entry);
|
|
318
|
+
|
|
319
|
+
// Maintain log size limit (1000 entries)
|
|
320
|
+
if (this.logs.length > 1000) {
|
|
321
|
+
this.logs = this.logs.slice(-1000);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -1,59 +1,135 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { UserInfo } from '../core/controller/SettingsController';
|
|
3
|
+
import { EventBus, EventBusMessage } from '../fromRimori/EventBus';
|
|
4
|
+
import { ActivePlugin, Plugin } from '../fromRimori/PluginTypes';
|
|
3
5
|
import { RimoriClient } from "./RimoriClient";
|
|
4
|
-
import { setTheme } from './ThemeSetter';
|
|
5
6
|
import { StandaloneClient } from './StandaloneClient';
|
|
7
|
+
import { setTheme } from './ThemeSetter';
|
|
8
|
+
import { Logger } from './Logger';
|
|
6
9
|
|
|
7
10
|
// Add declaration for WorkerGlobalScope
|
|
8
11
|
declare const WorkerGlobalScope: any;
|
|
9
12
|
|
|
10
|
-
interface
|
|
13
|
+
export interface RimoriInfo {
|
|
11
14
|
url: string,
|
|
12
15
|
key: string,
|
|
16
|
+
backendUrl: string,
|
|
13
17
|
token: string,
|
|
14
18
|
expiration: Date,
|
|
15
19
|
tablePrefix: string,
|
|
16
20
|
pluginId: string
|
|
21
|
+
installedPlugins: Plugin[]
|
|
22
|
+
profile: UserInfo
|
|
23
|
+
mainPanelPlugin?: ActivePlugin
|
|
24
|
+
sidePanelPlugin?: ActivePlugin
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
export class PluginController {
|
|
20
28
|
private static client: RimoriClient;
|
|
21
29
|
private static instance: PluginController;
|
|
22
|
-
private
|
|
30
|
+
private port: MessagePort | null = null;
|
|
31
|
+
private queryParams: Record<string, string> = {};
|
|
23
32
|
private supabase: SupabaseClient | null = null;
|
|
24
|
-
private
|
|
33
|
+
private rimoriInfo: RimoriInfo | null = null;
|
|
25
34
|
private pluginId: string;
|
|
35
|
+
private isMessageChannelReady: boolean = false;
|
|
36
|
+
private pendingRequests: Array<() => void> = [];
|
|
26
37
|
|
|
27
38
|
private constructor(pluginId: string, standalone: boolean) {
|
|
28
39
|
this.pluginId = pluginId;
|
|
29
40
|
this.getClient = this.getClient.bind(this);
|
|
30
41
|
|
|
31
42
|
if (typeof WorkerGlobalScope === 'undefined') {
|
|
32
|
-
|
|
43
|
+
// In standalone mode, use URL fallback. In iframe mode, theme will be set after MessageChannel init
|
|
44
|
+
if (standalone) {
|
|
45
|
+
setTheme();
|
|
46
|
+
}
|
|
33
47
|
}
|
|
34
48
|
|
|
35
|
-
//no need to forward messages to parent in standalone mode
|
|
49
|
+
//no need to forward messages to parent in standalone mode or worker context
|
|
36
50
|
if (standalone) return;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
|
|
52
|
+
this.initMessageChannel(typeof WorkerGlobalScope !== 'undefined');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private initMessageChannel(worker: boolean = false) {
|
|
56
|
+
const listener = (event: MessageEvent) => {
|
|
57
|
+
console.log("[PluginController] window message", { origin: event.origin, data: event.data });
|
|
58
|
+
const { type, pluginId, queryParams, rimoriInfo } = event.data || {};
|
|
59
|
+
const [transferredPort] = event.ports || [];
|
|
60
|
+
|
|
61
|
+
if (type !== "rimori:init" || !transferredPort || pluginId !== this.pluginId) {
|
|
62
|
+
console.log("[PluginController] message ignored (not init or wrong plugin)", { type, pluginId, hasPort: !!transferredPort });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.queryParams = queryParams || {};
|
|
67
|
+
this.port = transferredPort;
|
|
68
|
+
|
|
69
|
+
// Initialize Supabase client immediately with provided info
|
|
70
|
+
if (rimoriInfo) {
|
|
71
|
+
this.rimoriInfo = rimoriInfo;
|
|
72
|
+
this.supabase = createClient(rimoriInfo.url, rimoriInfo.key, {
|
|
73
|
+
accessToken: () => Promise.resolve(rimoriInfo.token)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle messages from parent
|
|
78
|
+
this.port.onmessage = ({ data }) => {
|
|
79
|
+
const { event, type, eventId, response, error } = data || {};
|
|
80
|
+
|
|
81
|
+
// no idea why this is needed but it works for now
|
|
82
|
+
if (type === 'response' && eventId) {
|
|
83
|
+
EventBus.emit(this.pluginId, response.topic, response.data, eventId);
|
|
84
|
+
} else if (type === 'error' && eventId) {
|
|
85
|
+
EventBus.emit(this.pluginId, 'error', { error }, eventId);
|
|
86
|
+
} else if (event) {
|
|
87
|
+
const { topic, sender, data: eventData, eventId } = event as EventBusMessage;
|
|
88
|
+
if (sender !== this.pluginId) {
|
|
89
|
+
EventBus.emit(sender, topic, eventData, eventId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Set theme from MessageChannel query params
|
|
95
|
+
if (!worker) {
|
|
96
|
+
const theme = this.queryParams['rm_theme'];
|
|
97
|
+
setTheme(theme);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Forward plugin events to parent (only after MessageChannel is ready)
|
|
101
|
+
EventBus.on("*", (ev) => {
|
|
102
|
+
if (ev.sender === this.pluginId && !ev.topic.startsWith("self.")) {
|
|
103
|
+
this.port?.postMessage({ event: ev });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Mark MessageChannel as ready and process pending requests
|
|
108
|
+
this.isMessageChannelReady = true;
|
|
109
|
+
|
|
110
|
+
// Process any pending requests
|
|
111
|
+
this.pendingRequests.forEach(request => request());
|
|
112
|
+
this.pendingRequests = [];
|
|
113
|
+
};
|
|
114
|
+
if (worker) {
|
|
115
|
+
self.onmessage = listener;
|
|
116
|
+
} else {
|
|
117
|
+
window.addEventListener("message", listener);
|
|
118
|
+
}
|
|
119
|
+
this.sendHello(worker);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private sendHello(isWorker: boolean = false) {
|
|
123
|
+
try {
|
|
124
|
+
const payload = { type: "rimori:hello", pluginId: this.pluginId };
|
|
125
|
+
if (isWorker) {
|
|
126
|
+
self.postMessage(payload);
|
|
127
|
+
} else {
|
|
128
|
+
window.parent.postMessage(payload, "*");
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error("[PluginController] Error sending hello:", e);
|
|
132
|
+
}
|
|
57
133
|
}
|
|
58
134
|
|
|
59
135
|
public static async getInstance(pluginId: string, standalone = false): Promise<RimoriClient> {
|
|
@@ -63,62 +139,126 @@ export class PluginController {
|
|
|
63
139
|
}
|
|
64
140
|
PluginController.instance = new PluginController(pluginId, standalone);
|
|
65
141
|
PluginController.client = await RimoriClient.getInstance(PluginController.instance);
|
|
142
|
+
|
|
143
|
+
//only init logger in workers and on main plugin pages
|
|
144
|
+
if (PluginController.instance.getQueryParam("applicationMode") !== "sidebar") {
|
|
145
|
+
Logger.getInstance(PluginController.client);
|
|
146
|
+
}
|
|
66
147
|
}
|
|
67
148
|
return PluginController.client;
|
|
68
149
|
}
|
|
69
150
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const secret = new URLSearchParams(window.location.search).get("secret");
|
|
73
|
-
if (!secret) {
|
|
74
|
-
console.info("Communication secret not found in URL as query parameter");
|
|
75
|
-
}
|
|
76
|
-
this.communicationSecret = secret;
|
|
77
|
-
}
|
|
78
|
-
return this.communicationSecret;
|
|
151
|
+
public getQueryParam(key: string): string | null {
|
|
152
|
+
return this.queryParams[key] || null;
|
|
79
153
|
}
|
|
80
154
|
|
|
81
|
-
public async getClient(): Promise<{ supabase: SupabaseClient,
|
|
82
|
-
if
|
|
83
|
-
|
|
84
|
-
this.
|
|
85
|
-
this.supabaseInfo.expiration > new Date()
|
|
86
|
-
) {
|
|
87
|
-
return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
|
|
155
|
+
public async getClient(): Promise<{ supabase: SupabaseClient, info: RimoriInfo }> {
|
|
156
|
+
// Return cached client if valid
|
|
157
|
+
if (this.supabase && this.rimoriInfo && this.rimoriInfo.expiration > new Date()) {
|
|
158
|
+
return { supabase: this.supabase, info: this.rimoriInfo };
|
|
88
159
|
}
|
|
89
160
|
|
|
90
|
-
|
|
91
|
-
this.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
161
|
+
// If MessageChannel is not ready yet, queue the request
|
|
162
|
+
if (!this.isMessageChannelReady) {
|
|
163
|
+
return new Promise<{ supabase: SupabaseClient, info: RimoriInfo }>((resolve) => {
|
|
164
|
+
this.pendingRequests.push(async () => {
|
|
165
|
+
const result = await this.getClient();
|
|
166
|
+
resolve(result);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
95
170
|
|
|
96
|
-
|
|
97
|
-
|
|
171
|
+
// If we have rimoriInfo from MessageChannel init, use it directly
|
|
172
|
+
if (this.rimoriInfo && this.supabase) {
|
|
173
|
+
return { supabase: this.supabase, info: this.rimoriInfo };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Fallback: request from parent
|
|
177
|
+
if (!this.rimoriInfo) {
|
|
178
|
+
if (typeof WorkerGlobalScope !== 'undefined') {
|
|
179
|
+
// In worker context, send request via self.postMessage to WorkerHandler
|
|
180
|
+
const eventId = Math.floor(Math.random() * 1000000000);
|
|
181
|
+
const requestEvent = {
|
|
182
|
+
event: {
|
|
183
|
+
timestamp: new Date().toISOString(),
|
|
184
|
+
eventId,
|
|
185
|
+
sender: this.pluginId,
|
|
186
|
+
topic: 'global.supabase.requestAccess',
|
|
187
|
+
data: {},
|
|
188
|
+
debug: false
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return new Promise<{ supabase: SupabaseClient, info: RimoriInfo }>((resolve) => {
|
|
193
|
+
// Listen for the response
|
|
194
|
+
const originalOnMessage = self.onmessage;
|
|
195
|
+
self.onmessage = (event) => {
|
|
196
|
+
if (event.data?.topic === 'global.supabase.requestAccess' && event.data?.eventId === eventId) {
|
|
197
|
+
this.rimoriInfo = event.data.data;
|
|
198
|
+
this.supabase = createClient(this.rimoriInfo!.url, this.rimoriInfo!.key, {
|
|
199
|
+
accessToken: () => Promise.resolve(this.getToken())
|
|
200
|
+
});
|
|
201
|
+
self.onmessage = originalOnMessage; // Restore original handler
|
|
202
|
+
resolve({ supabase: this.supabase, info: this.rimoriInfo! });
|
|
203
|
+
} else if (originalOnMessage) {
|
|
204
|
+
originalOnMessage.call(self, event);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
98
207
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
208
|
+
// Send the request
|
|
209
|
+
self.postMessage(requestEvent);
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
// In main thread context, use EventBus
|
|
213
|
+
const { data } = await EventBus.request<RimoriInfo>(this.pluginId, "global.supabase.requestAccess");
|
|
214
|
+
this.rimoriInfo = data;
|
|
215
|
+
this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
|
|
216
|
+
accessToken: () => Promise.resolve(this.getToken())
|
|
217
|
+
});
|
|
218
|
+
}
|
|
102
219
|
}
|
|
103
220
|
|
|
104
|
-
|
|
221
|
+
return { supabase: this.supabase!, info: this.rimoriInfo };
|
|
222
|
+
}
|
|
105
223
|
|
|
106
|
-
|
|
107
|
-
|
|
224
|
+
public async getToken(): Promise<string> {
|
|
225
|
+
if (this.rimoriInfo && this.rimoriInfo.expiration && this.rimoriInfo.expiration > new Date()) {
|
|
226
|
+
return this.rimoriInfo.token;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// If we don't have rimoriInfo, request it
|
|
230
|
+
if (!this.rimoriInfo) {
|
|
231
|
+
const { data } = await EventBus.request<RimoriInfo>(this.pluginId, "global.supabase.requestAccess");
|
|
232
|
+
this.rimoriInfo = data;
|
|
233
|
+
return this.rimoriInfo.token;
|
|
108
234
|
}
|
|
109
235
|
|
|
110
|
-
|
|
111
|
-
|
|
236
|
+
// If token is expired, request fresh access
|
|
237
|
+
const { data } = await EventBus.request<{ token: string, expiration: Date }>(this.pluginId, "global.supabase.requestAccess");
|
|
238
|
+
this.rimoriInfo.token = data.token;
|
|
239
|
+
this.rimoriInfo.expiration = data.expiration;
|
|
112
240
|
|
|
113
|
-
return this.
|
|
241
|
+
return this.rimoriInfo.token;
|
|
114
242
|
}
|
|
115
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Gets the Supabase URL.
|
|
246
|
+
* @returns The Supabase URL.
|
|
247
|
+
* @deprecated All endpoints should use the backend URL instead.
|
|
248
|
+
*/
|
|
116
249
|
public getSupabaseUrl() {
|
|
117
|
-
if (!this.
|
|
250
|
+
if (!this.rimoriInfo) {
|
|
118
251
|
throw new Error("Supabase info not found");
|
|
119
252
|
}
|
|
120
253
|
|
|
121
|
-
return this.
|
|
254
|
+
return this.rimoriInfo.url;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public getBackendUrl() {
|
|
258
|
+
if (!this.rimoriInfo) {
|
|
259
|
+
throw new Error("Rimori info not found");
|
|
260
|
+
}
|
|
261
|
+
return this.rimoriInfo.backendUrl;
|
|
122
262
|
}
|
|
123
263
|
|
|
124
264
|
public getGlobalEventTopic(preliminaryTopic: string) {
|
|
@@ -138,7 +278,7 @@ export class PluginController {
|
|
|
138
278
|
throw new Error(`The event topic must consist of 3 parts. <pluginId>.<topic area>.<action>. Received: ${preliminaryTopic}`);
|
|
139
279
|
}
|
|
140
280
|
|
|
141
|
-
const topicRoot = this.
|
|
281
|
+
const topicRoot = this.rimoriInfo?.pluginId ?? "global";
|
|
142
282
|
return `${topicRoot}.${preliminaryTopic}`;
|
|
143
283
|
}
|
|
144
284
|
|