@lvce-editor/shared-process 0.32.0 → 0.32.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.32.0",
3
+ "version": "0.32.1",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@lvce-editor/assert": "^1.2.0",
21
- "@lvce-editor/extension-host-helper-process": "0.32.0",
21
+ "@lvce-editor/extension-host-helper-process": "0.32.1",
22
22
  "@lvce-editor/ipc": "^9.4.0",
23
23
  "@lvce-editor/json-rpc": "^1.4.0",
24
24
  "@lvce-editor/jsonc-parser": "^1.4.0",
@@ -1,5 +1,12 @@
1
1
  import * as GetContentSecurityPolicy from '../GetContentSecurityPolicy/GetContentSecurityPolicy.js';
2
2
  import * as IsElectron from '../IsElectron/IsElectron.js';
3
+ 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;
9
+ };
3
10
  export const value = GetContentSecurityPolicy.getContentSecurityPolicy([
4
11
  `default-src 'none'`,
5
12
  `font-src 'self'`,
@@ -7,6 +14,9 @@ export const value = GetContentSecurityPolicy.getContentSecurityPolicy([
7
14
  `media-src 'self'`,
8
15
  `script-src 'self'`,
9
16
  `style-src 'self'`,
10
- `frame-src 'self' http://localhost:3001`,
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`,
11
21
  ...(IsElectron.isElectron ? [] : [`manifest-src 'self'`]),
12
22
  ]);
@@ -0,0 +1,24 @@
1
+ import { VError } from '@lvce-editor/verror';
2
+ import { createServer } from 'node:http';
3
+ import * as Promises from '../Promises/Promises.js';
4
+ export const createWebViewServer = async (port) => {
5
+ try {
6
+ const server = createServer();
7
+ const { resolve, promise } = Promises.withResolvers();
8
+ server.listen(port, resolve);
9
+ await promise;
10
+ return {
11
+ handler: undefined,
12
+ setHandler(handleRequest) {
13
+ if (this.handler) {
14
+ return;
15
+ }
16
+ this.handler = this.handler;
17
+ server.on('request', handleRequest);
18
+ },
19
+ };
20
+ }
21
+ catch (error) {
22
+ throw new VError(error, `Failed to start webview server`);
23
+ }
24
+ };
@@ -0,0 +1,62 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { extname } from 'node:path';
3
+ import { pipeline } from 'node:stream/promises';
4
+ import { fileURLToPath } from 'node:url';
5
+ import * as GetContentSecurityPolicy from '../GetContentSecurityPolicy/GetContentSecurityPolicy.js';
6
+ import * as SetHeaders from '../SetHeaders/SetHeaders.js';
7
+ const getPathName = (request) => {
8
+ const { pathname } = new URL(request.url || '', `https://${request.headers.host}`);
9
+ return pathname;
10
+ };
11
+ const textMimeType = {
12
+ '.html': 'text/html',
13
+ '.js': 'text/javascript',
14
+ '.ts': 'text/javascript',
15
+ '.mjs': 'text/javascript',
16
+ '.json': 'application/json',
17
+ '.css': 'text/css',
18
+ '.svg': 'image/svg+xml',
19
+ '.avif': 'image/avif',
20
+ '.woff': 'application/font-woff',
21
+ '.ttf': 'font/ttf',
22
+ '.png': 'image/png',
23
+ '.jpe': 'image/jpg',
24
+ '.ico': 'image/x-icon',
25
+ '.jpeg': 'image/jpg',
26
+ '.jpg': 'image/jpg',
27
+ '.webp': 'image/webp',
28
+ };
29
+ const getContentType = (filePath) => {
30
+ return textMimeType[extname(filePath)] || 'text/plain';
31
+ };
32
+ export const createHandler = (frameAncestors, webViewRoot) => {
33
+ const csp = GetContentSecurityPolicy.getContentSecurityPolicy([`default-src 'none'`, `script-src 'self'`, `frame-ancestors ${frameAncestors}`]);
34
+ const handleRequest = async (request, response) => {
35
+ let pathName = getPathName(request);
36
+ if (pathName === '/') {
37
+ pathName += 'index.html';
38
+ }
39
+ const filePath = fileURLToPath(`file://${webViewRoot}${pathName}`);
40
+ const isHtml = filePath.endsWith('index.html');
41
+ const contentType = getContentType(filePath);
42
+ if (isHtml) {
43
+ SetHeaders.setHeaders(response, {
44
+ 'Cross-Origin-Resource-Policy': 'cross-origin',
45
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
46
+ 'Content-Security-Policy': csp,
47
+ 'Content-Type': contentType,
48
+ });
49
+ }
50
+ else {
51
+ // TODO figure out which of these headers are actually needed
52
+ SetHeaders.setHeaders(response, {
53
+ 'Cross-Origin-Resource-Policy': 'cross-origin',
54
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
55
+ 'Access-Control-Allow-Origin': '*',
56
+ 'Content-Type': contentType,
57
+ });
58
+ }
59
+ await pipeline(createReadStream(filePath), response);
60
+ };
61
+ return handleRequest;
62
+ };
@@ -9,6 +9,7 @@ export const Commands = {
9
9
  'ExtensionHost.getIconThemeJson': ExtensionHostIconTheme.getIconTheme,
10
10
  'ExtensionHost.getLanguageConfiguration': ExtensionHostLanguages.getLanguageConfiguration,
11
11
  'ExtensionHost.getLanguages': ExtensionHostLanguages.getLanguages,
12
+ 'ExtensionHost.getWebViews': ExtensionHostLanguages.getWebViews,
12
13
  'ExtensionHost.watchColorTheme': ExtensionHostColorTheme.watch,
13
14
  'ExtensionManagement.disable': ExtensionManagement.disable,
14
15
  'ExtensionManagement.enable': ExtensionManagement.enable,
@@ -38,6 +38,25 @@ export const getLanguages = async () => {
38
38
  const languages = getLanguagesFromExtensions(extensions);
39
39
  return languages;
40
40
  };
41
+ const getWebViewsFromExtensions = (extensions) => {
42
+ const webViews = [];
43
+ for (const extension of extensions) {
44
+ if (extension && extension.webViews) {
45
+ for (const webView of extension.webViews) {
46
+ webViews.push({
47
+ id: webView.id,
48
+ path: join(extension.path, webView.path),
49
+ });
50
+ }
51
+ }
52
+ }
53
+ return webViews;
54
+ };
55
+ export const getWebViews = async () => {
56
+ const extensions = await ExtensionManagement.getExtensions();
57
+ const webViews = getWebViewsFromExtensions(extensions);
58
+ return webViews;
59
+ };
41
60
  const getLanguageConfigurationPathFromExtensions = (extensions, languageId) => {
42
61
  for (const extension of extensions) {
43
62
  if (extension.languages && extension.languages) {
@@ -5,6 +5,7 @@ import * as CreateTestOverview from '../CreateTestOverview/CreateTestOverview.js
5
5
  import * as CrossOriginEmbedderPolicy from '../CrossOriginEmbedderPolicy/CrossOriginEmbedderPolicy.js';
6
6
  import * as CrossOriginOpenerPolicy from '../CrossOriginOpenerPolicy/CrossOriginOpenerPolicy.js';
7
7
  import * as CrossOriginResourcePolicy from '../CrossOriginResourcePolicy/CrossOriginResourcePolicy.js';
8
+ import * as ContentSecurityPolicyDocument from '../ContentSecurityPolicyDocument/ContentSecurityPolicyDocument.js';
8
9
  import * as GetPathName from '../GetPathName/GetPathName.js';
9
10
  import * as GetTestPath from '../GetTestPath/GetTestPath.js';
10
11
  import * as HttpHeader from '../HttpHeader/HttpHeader.js';
@@ -21,6 +22,7 @@ export const getTestRequestResponse = async (request, indexHtmlPath) => {
21
22
  headers: {
22
23
  [HttpHeader.CrossOriginEmbedderPolicy]: CrossOriginEmbedderPolicy.value,
23
24
  [HttpHeader.CrossOriginResourcePolicy]: CrossOriginResourcePolicy.value,
25
+ [HttpHeader.ContentSecurityPolicy]: ContentSecurityPolicyDocument.value,
24
26
  },
25
27
  },
26
28
  };
@@ -0,0 +1,2 @@
1
+ import * as Env from '../Env/Env.js';
2
+ export const isGitpod = Boolean(Env.env.GITPOD_WORKSPACE_ID);
@@ -129,6 +129,8 @@ export const load = (moduleId) => {
129
129
  return import('../HandleMessagePortForSearchProcess/HandleMessagePortForSearchProcess.ipc.js');
130
130
  case ModuleId.FileWatcher:
131
131
  return import('../FileWatcher/FileWatcher.ipc.js');
132
+ case ModuleId.WebViewServer:
133
+ return import('../WebViewServer/WebViewServer.ipc.js');
132
134
  default:
133
135
  throw new Error(`module ${moduleId} not found`);
134
136
  }
@@ -67,3 +67,4 @@ export const HandleMessagePortForEmbedsProcess = 72;
67
67
  export const HandleMessagePortForProcessExplorer = 73;
68
68
  export const HandleMessagePortForSearchProcess = 74;
69
69
  export const FileWatcher = 75;
70
+ export const WebViewServer = 76;
@@ -121,6 +121,7 @@ export const getModuleId = (commandId) => {
121
121
  case 'ExtensionHost.getIconThemeJson':
122
122
  case 'ExtensionHost.getLanguageConfiguration':
123
123
  case 'ExtensionHost.getLanguages':
124
+ case 'ExtensionHost.getWebViews':
124
125
  case 'ExtensionHost.watchColorTheme':
125
126
  case 'ExtensionManagement.disable':
126
127
  case 'ExtensionManagement.enable':
@@ -276,6 +277,9 @@ export const getModuleId = (commandId) => {
276
277
  return ModuleId.HandleMessagePortForSearchProcess;
277
278
  case 'FileWatcher.watch':
278
279
  return ModuleId.FileWatcher;
280
+ case 'WebViewServer.start':
281
+ case 'WebViewServer.setHandler':
282
+ return ModuleId.WebViewServer;
279
283
  default:
280
284
  throw new CommandNotFoundError(commandId);
281
285
  }
@@ -41,9 +41,9 @@ export const getAppImageName = () => {
41
41
  export const getSetupName = () => {
42
42
  return 'Lvce-Setup';
43
43
  };
44
- export const version = '0.32.0';
45
- export const commit = 'e432b14';
46
- export const date = '2024-08-15T19:40:42.000Z';
44
+ export const version = '0.32.1';
45
+ export const commit = 'f51fb63';
46
+ export const date = '2024-08-18T19:59:49.000Z';
47
47
  export const getVersion = () => {
48
48
  return version;
49
49
  };
@@ -55,6 +55,9 @@ export const getExtensionHostHelperProcessPath = async () => {
55
55
  const { extensionHostHelperProcessPath } = await import('@lvce-editor/extension-host-helper-process');
56
56
  return extensionHostHelperProcessPath;
57
57
  };
58
+ export const getWebViewRoot = (relativePath) => {
59
+ return Path.join(Root.root, relativePath);
60
+ };
58
61
  export const getRecentlyOpenedPath = () => {
59
62
  return Path.join(cacheDir, 'recently-opened.json');
60
63
  };
@@ -0,0 +1,6 @@
1
+ import * as WebViewServer from '../WebViewServer/WebViewServer.js';
2
+ export const name = 'WebViewServer';
3
+ export const Commands = {
4
+ start: WebViewServer.start,
5
+ setHandler: WebViewServer.setHandler,
6
+ };
@@ -0,0 +1,21 @@
1
+ import * as CreateWebViewServer from '../CreateWebViewServer/CreateWebViewServer.js';
2
+ import * as CreateWebViewServerHandler from '../CreateWebViewServerHandler/CreateWebViewServerHandler.js';
3
+ const state = {
4
+ /**
5
+ * @type {any }
6
+ */
7
+ promise: undefined,
8
+ };
9
+ // TODO move webview preview
10
+ // server into separate process
11
+ export const start = async (port) => {
12
+ if (!state.promise) {
13
+ state.promise = CreateWebViewServer.createWebViewServer(port);
14
+ }
15
+ return state.promise;
16
+ };
17
+ export const setHandler = async (frameAncestors, webViewRoot) => {
18
+ const server = await state.promise;
19
+ const handler = CreateWebViewServerHandler.createHandler(frameAncestors, webViewRoot);
20
+ server.setHandler(handler);
21
+ };