@lvce-editor/api 8.46.0 → 8.47.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 CHANGED
@@ -21,3 +21,13 @@ export const main = () => {
21
21
  ```
22
22
 
23
23
  The package is published as unbundled ESM with TypeScript declaration files, so extensions can bundle it with their own build tooling.
24
+
25
+ Electron extensions can create an off-screen web contents view. It stays hidden for the lifetime of the handle, and disposing the handle destroys its web contents.
26
+
27
+ ```ts
28
+ import { createElectronWebContentsView } from '@lvce-editor/api'
29
+
30
+ const view = await createElectronWebContentsView({ url: 'https://example.com' })
31
+ const title = await view.executeJavaScript<string>('document.title')
32
+ await view.dispose()
33
+ ```
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { activate } from './parts/Activation/Activation.ts';
2
+ export { createElectronWebContentsView } from './parts/ElectronWebContentsView/ElectronWebContentsView.ts';
2
3
  export { executeCommand } from './parts/ExecuteCommand/ExecuteCommand.ts';
3
4
  export { showQuickPick } from './parts/QuickPick/QuickPick.ts';
4
5
  export { getPreference, setPreference } from './parts/Preferences/Preferences.ts';
@@ -24,6 +25,7 @@ export type { CommandRegistrySnapshot } from './parts/CommandRegistrySnapshot/Co
24
25
  export type { CompletionItem, CompletionProvider, CompletionProviderRegistrySnapshot, RegisteredCompletionProvider, } from './parts/Completion/Completion.ts';
25
26
  export type { Diagnostic, DiagnosticProvider, DiagnosticProviderRegistrySnapshot, RegisteredDiagnosticProvider, } from './parts/Diagnostic/Diagnostic.ts';
26
27
  export type { Disposable } from './parts/Disposable/Disposable.ts';
28
+ export type { CreateElectronWebContentsViewOptions, ElectronWebContentsView, ElectronWebContentsViewStats, } from './parts/ElectronWebContentsView/ElectronWebContentsView.ts';
27
29
  export type { FormattingEdit, FormattingProvider, FormattingProviderRegistrySnapshot, RegisteredFormattingProvider, } from './parts/Formatting/Formatting.ts';
28
30
  export type { FileSystemDirent } from './parts/FileSystem/FileSystem.ts';
29
31
  export type { HandleExtensionManagementMessagePortOptions } from './parts/HandleExtensionManagementMessagePort/HandleExtensionManagementMessagePort.ts';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { activate } from "./parts/Activation/Activation.js";
2
+ export { createElectronWebContentsView } from "./parts/ElectronWebContentsView/ElectronWebContentsView.js";
2
3
  export { executeCommand } from "./parts/ExecuteCommand/ExecuteCommand.js";
3
4
  export { showQuickPick } from "./parts/QuickPick/QuickPick.js";
4
5
  export { getPreference, setPreference } from "./parts/Preferences/Preferences.js";
@@ -0,0 +1,17 @@
1
+ export interface CreateElectronWebContentsViewOptions {
2
+ readonly url?: string;
3
+ }
4
+ export interface ElectronWebContentsViewStats {
5
+ readonly canGoBack: boolean;
6
+ readonly canGoForward: boolean;
7
+ readonly title: string;
8
+ readonly url: string;
9
+ }
10
+ export interface ElectronWebContentsView {
11
+ readonly dispose: () => Promise<void>;
12
+ readonly executeJavaScript: <T = unknown>(code: string) => Promise<T>;
13
+ readonly getStats: () => Promise<ElectronWebContentsViewStats>;
14
+ readonly loadUrl: (url: string) => Promise<void>;
15
+ readonly reload: () => Promise<void>;
16
+ }
17
+ export declare const createElectronWebContentsView: ({ url }?: CreateElectronWebContentsViewOptions) => Promise<ElectronWebContentsView>;
@@ -0,0 +1,59 @@
1
+ import { LazyTransferMessagePortRpcParent } from '@lvce-editor/rpc';
2
+ import { ExtensionManagementWorker } from '@lvce-editor/rpc-registry';
3
+ const sendMessagePortToEmbedsProcess = async (port) => {
4
+ await ExtensionManagementWorker.invokeAndTransfer('Extensions.sendMessagePortToElectron', port, 'HandleMessagePortForEmbedsProcess.handleMessagePortForEmbedsProcess');
5
+ };
6
+ const disposeAfterCreateError = async (rpc, id) => {
7
+ try {
8
+ await rpc.invoke('ElectronWebContentsView.disposeWebContentsView', id);
9
+ }
10
+ finally {
11
+ await rpc.dispose();
12
+ }
13
+ };
14
+ export const createElectronWebContentsView = async ({ url } = {}) => {
15
+ const rpc = await LazyTransferMessagePortRpcParent.create({
16
+ commandMap: {},
17
+ send: sendMessagePortToEmbedsProcess,
18
+ });
19
+ let id;
20
+ try {
21
+ id = (await rpc.invoke('ElectronWebContentsView.createWebContentsView', 0, []));
22
+ }
23
+ catch (error) {
24
+ await rpc.dispose();
25
+ throw error;
26
+ }
27
+ let disposed = false;
28
+ const loadUrl = async (nextUrl) => {
29
+ await rpc.invoke('ElectronWebContentsView.setIframeSrc', id, nextUrl);
30
+ };
31
+ try {
32
+ if (url) {
33
+ await loadUrl(url);
34
+ }
35
+ }
36
+ catch (error) {
37
+ await disposeAfterCreateError(rpc, id);
38
+ throw error;
39
+ }
40
+ return {
41
+ async dispose() {
42
+ if (disposed) {
43
+ return;
44
+ }
45
+ disposed = true;
46
+ await disposeAfterCreateError(rpc, id);
47
+ },
48
+ async executeJavaScript(code) {
49
+ return (await rpc.invoke('ElectronWebContentsView.insertJavaScript', id, code));
50
+ },
51
+ async getStats() {
52
+ return (await rpc.invoke('ElectronWebContentsView.getStats', id));
53
+ },
54
+ loadUrl,
55
+ async reload() {
56
+ await rpc.invoke('ElectronWebContentsView.reload', id);
57
+ },
58
+ };
59
+ };
@@ -1,6 +1,10 @@
1
1
  import { WebWorkerRpcClient } from '@lvce-editor/rpc';
2
2
  import * as ExtensionApiWorkerCommandMap from "../ExtensionApiWorkerCommandMap/ExtensionApiWorkerCommandMap.js";
3
+ import * as HandleUnhandledError from "../HandleUnhandledError/HandleUnhandledError.js";
4
+ import * as HandleUnhandledRejection from "../HandleUnhandledRejection/HandleUnhandledRejection.js";
3
5
  export const listen = async () => {
6
+ globalThis.addEventListener('error', HandleUnhandledError.handleUnhandledError);
7
+ globalThis.addEventListener('unhandledrejection', HandleUnhandledRejection.handleUnhandledRejection);
4
8
  const rpc = WebWorkerRpcClient.create({
5
9
  commandMap: ExtensionApiWorkerCommandMap.commandMap,
6
10
  });
@@ -0,0 +1 @@
1
+ export declare const handleUncaughtExtensionError: (error: unknown) => Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { ExtensionManagementWorker } from '@lvce-editor/rpc-registry';
2
+ import * as SerializeError from "../SerializeError/SerializeError.js";
3
+ export const handleUncaughtExtensionError = async (error) => {
4
+ const serializedError = SerializeError.serializeError(error);
5
+ await ExtensionManagementWorker.invoke('Extensions.handleUncaughtExtensionError', serializedError);
6
+ };
@@ -0,0 +1 @@
1
+ export declare const handleUnhandledError: (event: ErrorEvent) => Promise<void>;
@@ -0,0 +1,23 @@
1
+ import * as HandleUncaughtExtensionError from "../HandleUncaughtExtensionError/HandleUncaughtExtensionError.js";
2
+ const getError = (event) => {
3
+ if (event.error) {
4
+ return event.error;
5
+ }
6
+ return {
7
+ constructor: {
8
+ name: 'Error',
9
+ },
10
+ message: event.message,
11
+ stack: `${event.message}\n at ${event.filename}:${event.lineno}:${event.colno}`,
12
+ };
13
+ };
14
+ export const handleUnhandledError = async (event) => {
15
+ event.preventDefault();
16
+ const error = getError(event);
17
+ try {
18
+ await HandleUncaughtExtensionError.handleUncaughtExtensionError(error);
19
+ }
20
+ catch {
21
+ console.error(error);
22
+ }
23
+ };
@@ -0,0 +1 @@
1
+ export declare const handleUnhandledRejection: (event: PromiseRejectionEvent) => Promise<void>;
@@ -0,0 +1,10 @@
1
+ import * as HandleUncaughtExtensionError from "../HandleUncaughtExtensionError/HandleUncaughtExtensionError.js";
2
+ export const handleUnhandledRejection = async (event) => {
3
+ event.preventDefault();
4
+ try {
5
+ await HandleUncaughtExtensionError.handleUncaughtExtensionError(event.reason);
6
+ }
7
+ catch {
8
+ console.error(event.reason);
9
+ }
10
+ };
@@ -0,0 +1,11 @@
1
+ export interface SerializedError {
2
+ readonly code?: unknown;
3
+ readonly codeFrame?: unknown;
4
+ readonly constructor: {
5
+ readonly name: string;
6
+ };
7
+ readonly message?: unknown;
8
+ readonly name?: unknown;
9
+ readonly stack?: unknown;
10
+ }
11
+ export declare const serializeError: (error: unknown) => SerializedError | unknown;
@@ -0,0 +1,19 @@
1
+ const getConstructorName = (error) => {
2
+ return error.constructor?.name || 'Error';
3
+ };
4
+ export const serializeError = (error) => {
5
+ if (!error || (typeof error !== 'object' && typeof error !== 'function')) {
6
+ return error;
7
+ }
8
+ const value = error;
9
+ return {
10
+ code: value.code,
11
+ codeFrame: value.codeFrame,
12
+ constructor: {
13
+ name: getConstructorName(value),
14
+ },
15
+ message: value.message,
16
+ name: value.name,
17
+ stack: value.stack,
18
+ };
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/api",
3
- "version": "8.46.0",
3
+ "version": "8.47.0",
4
4
  "description": "Tree-shakeable extension API for Lvce Editor extensions.",
5
5
  "keywords": [
6
6
  "lvce-editor",