@lvce-editor/shared-process 0.33.4 → 0.33.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.33.4",
3
+ "version": "0.33.6",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -17,9 +17,9 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "@lvce-editor/assert": "^1.2.0",
21
- "@lvce-editor/extension-host-helper-process": "0.33.4",
22
- "@lvce-editor/ipc": "^10.0.4",
20
+ "@lvce-editor/assert": "^1.3.0",
21
+ "@lvce-editor/extension-host-helper-process": "0.33.6",
22
+ "@lvce-editor/ipc": "^10.2.1",
23
23
  "@lvce-editor/json-rpc": "^3.0.0",
24
24
  "@lvce-editor/jsonc-parser": "^1.4.0",
25
25
  "@lvce-editor/pretty-error": "^1.6.0",
@@ -35,7 +35,7 @@
35
35
  "@lvce-editor/typescript-compile-process": "^1.2.0",
36
36
  "@lvce-editor/pty-host": "^2.0.0",
37
37
  "@lvce-editor/embeds-process": "^1.5.0",
38
- "@lvce-editor/preview-process": "^1.2.1",
38
+ "@lvce-editor/preview-process": "^2.10.0",
39
39
  "open": "^10.1.0",
40
40
  "tail": "^2.2.6",
41
41
  "tmp-promise": "^3.0.3",
@@ -1,22 +1,35 @@
1
1
  import * as GetContentSecurityPolicy from '../GetContentSecurityPolicy/GetContentSecurityPolicy.js';
2
+ import * as GetGitpodPreviewUrl from '../GetGitpodPreviewUrl/GetGitpodPreviewUrl.js';
2
3
  import * as IsElectron from '../IsElectron/IsElectron.js';
3
4
  import * as IsGitpod from '../IsGitpod/IsGitpod.js';
4
- const getGitpodPreviewUrl = (port) => {
5
- const workspaceId = process.env.GITPOD_WORKSPACE_ID;
6
- const cluster = process.env.GITPOD_WORKSPACE_CLUSTER_HOST;
7
- const url = `https://${port}-${workspaceId}.${cluster}`;
8
- return url;
5
+ import * as Scheme from '../Scheme/Scheme.js';
6
+ const getFrameSrc = () => {
7
+ // TODO make ports configurable
8
+ if (IsGitpod.isGitpod) {
9
+ return [`frame-src 'self' ${GetGitpodPreviewUrl.getGitpodPreviewUrl(3001)} ${GetGitpodPreviewUrl.getGitpodPreviewUrl(3002)}`];
10
+ }
11
+ if (IsElectron.isElectron) {
12
+ return [`frame-src ${Scheme.WebView}:`];
13
+ }
14
+ return [`frame-src 'self' http://localhost:3001 http://localhost:3002`];
15
+ };
16
+ const getManifestSrc = () => {
17
+ if (IsElectron.isElectron) {
18
+ return [];
19
+ }
20
+ return [`manifest-src 'self'`];
21
+ };
22
+ const getFrameAncestors = () => {
23
+ return [`frame-ancestors 'none'`];
9
24
  };
10
25
  export const value = GetContentSecurityPolicy.getContentSecurityPolicy([
11
26
  `default-src 'none'`,
12
27
  `font-src 'self'`,
13
- `img-src 'self' https: data:`,
28
+ `img-src 'self' https: data:`, // TODO maybe disallow https and data images
14
29
  `media-src 'self'`,
15
30
  `script-src 'self'`,
16
31
  `style-src 'self'`,
17
- `frame-ancestors 'none' `,
18
- IsGitpod.isGitpod
19
- ? `frame-src 'self' ${getGitpodPreviewUrl(3001)} ${getGitpodPreviewUrl(3002)}`
20
- : `frame-src 'self' http://localhost:3001 http://localhost:3002`,
21
- ...(IsElectron.isElectron ? [] : [`manifest-src 'self'`]),
32
+ ...getFrameAncestors(),
33
+ ...getFrameSrc(),
34
+ ...getManifestSrc(),
22
35
  ]);
@@ -101,6 +101,16 @@ const applyOverrides = async ({ root, commitHash, pathPrefix }) => {
101
101
  }
102
102
  return languages;
103
103
  };
104
+ const getWebViews = (extension) => {
105
+ const webViews = [];
106
+ for (const webView of extension.webViews || []) {
107
+ webViews.push({
108
+ ...webView,
109
+ path: `${pathPrefix}/${commitHash}/extensions/${webView.path}`,
110
+ });
111
+ }
112
+ return webViews;
113
+ };
104
114
  /**
105
115
  * @param {string} dirent
106
116
  */
@@ -111,6 +121,8 @@ const applyOverrides = async ({ root, commitHash, pathPrefix }) => {
111
121
  const manifests = await Promise.all(manifestPaths.map(readExtensionManifest));
112
122
  const languages = manifests.flatMap(getLanguages);
113
123
  await JsonFile.writeJson(Path.join(root, 'dist', commitHash, 'config', 'languages.json'), languages);
124
+ const webViews = manifests.flatMap(getWebViews);
125
+ await JsonFile.writeJson(Path.join(root, 'dist', commitHash, 'config', 'webViews.json'), webViews);
114
126
  for (const languageBasicsDirent of languageBasicsDirents) {
115
127
  await FileSystem.copy(Path.join(root, 'node_modules', '@lvce-editor', 'server', 'static', commitHash, 'extensions', languageBasicsDirent), Path.join(root, 'dist', commitHash, 'extensions', languageBasicsDirent));
116
128
  }
@@ -1,4 +1,4 @@
1
- import { stat } from 'fs/promises';
1
+ import { stat } from 'node:fs/promises';
2
2
  import * as GetElectronFileResponseAbsolutePath from '../GetElectronFileResponseAbsolutePath/GetElectronFileResponseAbsolutePath.js';
3
3
  import * as GetElectronFileResponseContent from '../GetElectronFileResponseContent/GetElectronFileResponseContent.js';
4
4
  import * as GetElectronFileResponseRelativePath from '../GetElectronFileResponseRelativePath/GetElectronFileResponseRelativePath.js';
@@ -7,27 +7,35 @@ import * as GetHeaders from '../GetHeaders/GetHeaders.js';
7
7
  import * as GetNotFoundResponse from '../GetNotFoundResponse/GetNotFoundResponse.js';
8
8
  import * as GetNotModifiedResponse from '../GetNotModifiedResponse/GetNotModifiedResponse.js';
9
9
  import * as GetServerErrorResponse from '../GetServerErrorResponse/GetServerErrorResponse.js';
10
+ import * as HttpHeader from '../HttpHeader/HttpHeader.js';
10
11
  import * as HttpStatusCode from '../HttpStatusCode/HttpStatusCode.js';
11
12
  import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js';
12
13
  import * as Logger from '../Logger/Logger.js';
14
+ // TODO maybe handle app responses and webview responses separately
15
+ // maybe send webview requests directly to preview process
13
16
  export const getElectronFileResponse = async (url, request) => {
14
17
  try {
15
18
  const pathName = GetElectronFileResponseRelativePath.getElectronFileResponseRelativePath(url);
16
- const absolutePath = GetElectronFileResponseAbsolutePath.getElectronFileResponseAbsolutePath(pathName);
19
+ let absolutePath = GetElectronFileResponseAbsolutePath.getElectronFileResponseAbsolutePath(pathName);
17
20
  let etag;
21
+ // TODO when is there no request?
18
22
  if (request) {
19
- const stats = await stat(absolutePath);
23
+ let stats = await stat(absolutePath);
24
+ if (stats.isDirectory()) {
25
+ absolutePath += '/index.html';
26
+ stats = await stat(absolutePath);
27
+ }
20
28
  etag = GetEtag.getEtag(stats);
21
- if (request.headers['if-none-match'] === etag) {
29
+ if (request.headers[HttpHeader.IfNotMatch] === etag) {
22
30
  const headers = GetHeaders.getHeaders(absolutePath, pathName);
23
31
  return GetNotModifiedResponse.getNotModifiedResponse(headers);
24
32
  }
25
33
  }
26
34
  const content = await GetElectronFileResponseContent.getElectronFileResponseContent(request, absolutePath, url);
27
35
  const headers = GetHeaders.getHeaders(absolutePath, pathName);
28
- headers['Cache-Control'] = 'public, max-age=0, must-revalidate';
36
+ headers[HttpHeader.CacheControl] = 'public, max-age=0, must-revalidate';
29
37
  if (etag) {
30
- headers['Etag'] = etag;
38
+ headers[HttpHeader.Etag] = etag;
31
39
  }
32
40
  return {
33
41
  body: content,
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
4
4
  import * as Path from '../Path/Path.js';
5
5
  import * as Platform from '../Platform/Platform.js';
6
6
  import * as Root from '../Root/Root.js';
7
+ // TODO clean up this code
7
8
  export const getElectronFileResponseAbsolutePath = (pathName) => {
8
9
  // TODO remove if/else in prod (use replacement)
9
10
  if (pathName === `/` || pathName.startsWith(`/?`)) {
@@ -30,5 +31,9 @@ export const getElectronFileResponseAbsolutePath = (pathName) => {
30
31
  const processExplorerThemeCss = join(tmpdir(), 'process-explorer-theme.css');
31
32
  return processExplorerThemeCss;
32
33
  }
34
+ // TODO move this to preview process
35
+ if (pathName.startsWith('/home')) {
36
+ return pathName;
37
+ }
33
38
  return Path.join(Root.root, 'static', pathName);
34
39
  };
@@ -1,12 +1,30 @@
1
1
  import * as Platform from '../Platform/Platform.js';
2
+ import * as Scheme from '../Scheme/Scheme.js';
2
3
  const { scheme } = Platform;
3
4
  const prefix = `${scheme}://-`;
4
5
  const prefixLength = prefix.length;
6
+ const webViewPrefix = `${Scheme.WebView}://-`;
7
+ const webViewPrefixLength = webViewPrefix.length;
5
8
  export const getElectronFileResponseRelativePath = (requestUrl) => {
9
+ if (requestUrl.startsWith(prefix)) {
10
+ // TODO only support reading from app root and extensions
11
+ // maybe add a separate protocol for extensions (e.g. lvce-remote)
12
+ const decoded = decodeURI(requestUrl);
13
+ const pathName = decoded.slice(prefixLength);
14
+ return pathName;
15
+ }
16
+ if (requestUrl.startsWith(webViewPrefix)) {
17
+ // TODO group this by extension so that
18
+ // extension-a can only access resources in extension-a folder and
19
+ // extension-b can only access resources in extension-b folder
20
+ const decoded = decodeURI(requestUrl);
21
+ const pathName = decoded.slice(webViewPrefixLength);
22
+ return pathName;
23
+ }
6
24
  if (requestUrl.startsWith('/')) {
25
+ // TODO this should not be supported anymore and could probably be removed
7
26
  return requestUrl;
8
27
  }
9
- const decoded = decodeURI(requestUrl);
10
- const pathName = decoded.slice(prefixLength);
11
- return pathName;
28
+ console.log({ requestUrl });
29
+ return '';
12
30
  };
@@ -0,0 +1,6 @@
1
+ export const getGitpodPreviewUrl = (port) => {
2
+ const workspaceId = process.env.GITPOD_WORKSPACE_ID;
3
+ const cluster = process.env.GITPOD_WORKSPACE_CLUSTER_HOST;
4
+ const url = `https://${port}-${workspaceId}.${cluster}`;
5
+ return url;
6
+ };
@@ -1,12 +1,23 @@
1
1
  import * as HandleIpc from '../HandleIpc/HandleIpc.js';
2
2
  import * as IpcParent from '../IpcParent/IpcParent.js';
3
3
  import * as IpcParentType from '../IpcParentType/IpcParentType.js';
4
+ import * as IsElectron from '../IsElectron/IsElectron.js';
5
+ import * as Preferences from '../Preferences/Preferences.js';
4
6
  import * as PreviewProcessPath from '../PreviewProcessPath/PreviewProcessPath.js';
7
+ const getMethod = (isElectron) => {
8
+ if (isElectron) {
9
+ return IpcParentType.ElectronUtilityProcess;
10
+ }
11
+ return IpcParentType.NodeForkedProcess;
12
+ };
5
13
  export const launchPreviewProcess = async () => {
6
- const method = IpcParentType.NodeForkedProcess;
14
+ const preferences = await Preferences.getUserPreferences();
15
+ const customPreviewProcessPath = preferences['develop.previewProcessPath'];
16
+ const previewProcessPath = customPreviewProcessPath || PreviewProcessPath.previewProcessPath;
17
+ const method = getMethod(IsElectron.isElectron);
7
18
  const previewProcess = await IpcParent.create({
8
19
  method,
9
- path: PreviewProcessPath.previewProcessPath,
20
+ path: previewProcessPath,
10
21
  argv: [],
11
22
  stdio: 'inherit',
12
23
  name: 'Preview Process',
@@ -279,8 +279,10 @@ export const getModuleId = (commandId) => {
279
279
  return ModuleId.FileWatcher;
280
280
  case 'Transferrable.transfer':
281
281
  return ModuleId.Transferrable;
282
+ case 'WebViewServer.create':
282
283
  case 'WebViewServer.start':
283
284
  case 'WebViewServer.setHandler':
285
+ case 'WebViewServer.registerProtocol':
284
286
  return ModuleId.WebViewServer;
285
287
  default:
286
288
  throw new CommandNotFoundError(commandId);
@@ -41,9 +41,9 @@ export const getAppImageName = () => {
41
41
  export const getSetupName = () => {
42
42
  return 'Lvce-Setup';
43
43
  };
44
- export const version = '0.33.4';
45
- export const commit = 'da8ae3e';
46
- export const date = '2024-08-28T17:12:51.000Z';
44
+ export const version = '0.33.6';
45
+ export const commit = '0fe4838';
46
+ export const date = '2024-09-01T17:57:05.000Z';
47
47
  export const getVersion = () => {
48
48
  return version;
49
49
  };
@@ -16,3 +16,7 @@ export const invoke = async (method, ...params) => {
16
16
  const ipc = await getOrCreate();
17
17
  return JsonRpc.invoke(ipc, method, ...params);
18
18
  };
19
+ export const invokeAndTransfer = async (method, ...params) => {
20
+ const ipc = await getOrCreate();
21
+ return JsonRpc.invokeAndTransfer(ipc, method, ...params);
22
+ };
@@ -0,0 +1 @@
1
+ export const WebView = 'lvce-oss-webview';
@@ -2,5 +2,7 @@ import * as WebViewServer from '../WebViewServer/WebViewServer.js';
2
2
  export const name = 'WebViewServer';
3
3
  export const Commands = {
4
4
  start: WebViewServer.start,
5
+ create: WebViewServer.create,
5
6
  setHandler: WebViewServer.setHandler,
7
+ registerProtocol: WebViewServer.registerProtocol,
6
8
  };
@@ -1,7 +1,26 @@
1
1
  import * as PreviewProcess from '../PreviewProcess/PreviewProcess.js';
2
- export const start = async (port) => {
3
- await PreviewProcess.invoke('WebViewServer.start', port);
2
+ import * as ParentIpc from '../ParentIpc/ParentIpc.js';
3
+ import * as GetPortTuple from '../GetPortTuple/GetPortTuple.js';
4
+ export const registerProtocol = async () => {
5
+ // TODO only do this once
6
+ // TODO send messageport to proview process
7
+ // TODO send other messageport to parent process
8
+ console.time('get-ports');
9
+ const { port1, port2 } = await GetPortTuple.getPortTuple();
10
+ console.timeEnd('get-ports');
11
+ console.time('register-protocol');
12
+ await ParentIpc.invokeAndTransfer('ElectronSession.registerWebviewProtocol', port1);
13
+ console.timeEnd('register-protocol');
14
+ console.time('handle-port');
15
+ await PreviewProcess.invokeAndTransfer('HandleElectronMessagePort.handleElectronMessagePort', port2);
16
+ console.timeEnd('handle-port');
4
17
  };
5
- export const setHandler = async (frameAncestors, webViewRoot) => {
6
- await PreviewProcess.invoke('WebViewServer.setHandler', frameAncestors, webViewRoot);
18
+ export const create = async (previewId) => {
19
+ await PreviewProcess.invoke('WebViewServer.create', previewId);
20
+ };
21
+ export const start = async (previewId, port) => {
22
+ await PreviewProcess.invoke('WebViewServer.start', previewId, port);
23
+ };
24
+ export const setHandler = async (previewId, frameAncestors, webViewRoot) => {
25
+ await PreviewProcess.invoke('WebViewServer.setHandler', previewId, frameAncestors, webViewRoot);
7
26
  };