@lvce-editor/extension-management-worker 1.5.0 → 1.7.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.
@@ -54,6 +54,49 @@ class VError extends Error {
54
54
  }
55
55
  }
56
56
 
57
+ class AssertionError extends Error {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = 'AssertionError';
61
+ }
62
+ }
63
+ const Object$1 = 1;
64
+ const Number = 2;
65
+ const Array$1 = 3;
66
+ const String = 4;
67
+ const Boolean$1 = 5;
68
+ const Function = 6;
69
+ const Null = 7;
70
+ const Unknown = 8;
71
+ const getType = value => {
72
+ switch (typeof value) {
73
+ case 'number':
74
+ return Number;
75
+ case 'function':
76
+ return Function;
77
+ case 'string':
78
+ return String;
79
+ case 'object':
80
+ if (value === null) {
81
+ return Null;
82
+ }
83
+ if (Array.isArray(value)) {
84
+ return Array$1;
85
+ }
86
+ return Object$1;
87
+ case 'boolean':
88
+ return Boolean$1;
89
+ default:
90
+ return Unknown;
91
+ }
92
+ };
93
+ const string = value => {
94
+ const type = getType(value);
95
+ if (type !== String) {
96
+ throw new AssertionError('expected value to be of type string');
97
+ }
98
+ };
99
+
57
100
  const isMessagePort = value => {
58
101
  return value && value instanceof MessagePort;
59
102
  };
@@ -535,18 +578,18 @@ const create$4$1 = (method, params) => {
535
578
  };
536
579
  };
537
580
  const callbacks = Object.create(null);
538
- const set$4 = (id, fn) => {
581
+ const set$7 = (id, fn) => {
539
582
  callbacks[id] = fn;
540
583
  };
541
- const get$2 = id => {
584
+ const get$3 = id => {
542
585
  return callbacks[id];
543
586
  };
544
587
  const remove = id => {
545
588
  delete callbacks[id];
546
589
  };
547
- let id = 0;
590
+ let id$1 = 0;
548
591
  const create$3$1 = () => {
549
- return ++id;
592
+ return ++id$1;
550
593
  };
551
594
  const registerPromise = () => {
552
595
  const id = create$3$1();
@@ -554,7 +597,7 @@ const registerPromise = () => {
554
597
  resolve,
555
598
  promise
556
599
  } = Promise.withResolvers();
557
- set$4(id, resolve);
600
+ set$7(id, resolve);
558
601
  return {
559
602
  id,
560
603
  promise
@@ -719,7 +762,7 @@ const warn = (...args) => {
719
762
  console.warn(...args);
720
763
  };
721
764
  const resolve = (id, response) => {
722
- const fn = get$2(id);
765
+ const fn = get$3(id);
723
766
  if (!fn) {
724
767
  console.log(response);
725
768
  warn(`callback ${id} may already be disposed`);
@@ -769,7 +812,7 @@ const getErrorProperty = (error, prettyError) => {
769
812
  }
770
813
  };
771
814
  };
772
- const create$1$1 = (id, error) => {
815
+ const create$1$2 = (id, error) => {
773
816
  return {
774
817
  jsonrpc: Two,
775
818
  id,
@@ -780,7 +823,7 @@ const getErrorResponse = (id, error, preparePrettyError, logError) => {
780
823
  const prettyError = preparePrettyError(error);
781
824
  logError(error, prettyError);
782
825
  const errorProperty = getErrorProperty(error, prettyError);
783
- return create$1$1(id, errorProperty);
826
+ return create$1$2(id, errorProperty);
784
827
  };
785
828
  const create$6 = (message, result) => {
786
829
  return {
@@ -899,7 +942,7 @@ const send = (transport, method, ...params) => {
899
942
  const message = create$4$1(method, params);
900
943
  transport.send(message);
901
944
  };
902
- const invoke$2 = (ipc, method, ...params) => {
945
+ const invoke$4 = (ipc, method, ...params) => {
903
946
  return invokeHelper(ipc, method, params, false);
904
947
  };
905
948
  const invokeAndTransfer$1 = (ipc, method, ...params) => {
@@ -938,7 +981,7 @@ const createRpc = ipc => {
938
981
  send(ipc, method, ...params);
939
982
  },
940
983
  invoke(method, ...params) {
941
- return invoke$2(ipc, method, ...params);
984
+ return invoke$4(ipc, method, ...params);
942
985
  },
943
986
  invokeAndTransfer(method, ...params) {
944
987
  return invokeAndTransfer$1(ipc, method, ...params);
@@ -1047,7 +1090,7 @@ const getHost = () => {
1047
1090
  const getProtocol = () => {
1048
1091
  return location.protocol;
1049
1092
  };
1050
- const create$1 = async ({
1093
+ const create$1$1 = async ({
1051
1094
  commandMap,
1052
1095
  type
1053
1096
  }) => {
@@ -1063,7 +1106,7 @@ const create$1 = async ({
1063
1106
  };
1064
1107
  const WebSocketRpcParent2 = {
1065
1108
  __proto__: null,
1066
- create: create$1
1109
+ create: create$1$1
1067
1110
  };
1068
1111
  const create$4 = async ({
1069
1112
  commandMap
@@ -1080,63 +1123,278 @@ const WebWorkerRpcClient = {
1080
1123
  create: create$4
1081
1124
  };
1082
1125
 
1126
+ const Web = 1;
1083
1127
  const Electron = 2;
1084
1128
  const Remote = 3;
1085
1129
 
1130
+ const ExtensionHostWorker = 44;
1131
+ const FileSystemWorker = 209;
1086
1132
  const RendererWorker = 1;
1087
1133
  const SharedProcess = 1;
1088
1134
 
1089
1135
  const rpcs = Object.create(null);
1090
- const set$3 = (id, rpc) => {
1136
+ const set$6 = (id, rpc) => {
1091
1137
  rpcs[id] = rpc;
1092
1138
  };
1093
- const get$1 = id => {
1139
+ const get$2 = id => {
1094
1140
  return rpcs[id];
1095
1141
  };
1096
1142
 
1097
- const create = rpcId => {
1143
+ const create$1 = rpcId => {
1098
1144
  return {
1099
1145
  async dispose() {
1100
- const rpc = get$1(rpcId);
1146
+ const rpc = get$2(rpcId);
1101
1147
  await rpc.dispose();
1102
1148
  },
1103
1149
  // @ts-ignore
1104
1150
  invoke(method, ...params) {
1105
- const rpc = get$1(rpcId);
1151
+ const rpc = get$2(rpcId);
1106
1152
  // @ts-ignore
1107
1153
  return rpc.invoke(method, ...params);
1108
1154
  },
1109
1155
  // @ts-ignore
1110
1156
  invokeAndTransfer(method, ...params) {
1111
- const rpc = get$1(rpcId);
1157
+ const rpc = get$2(rpcId);
1112
1158
  // @ts-ignore
1113
1159
  return rpc.invokeAndTransfer(method, ...params);
1114
1160
  },
1115
1161
  set(rpc) {
1116
- set$3(rpcId, rpc);
1162
+ set$6(rpcId, rpc);
1117
1163
  }
1118
1164
  };
1119
1165
  };
1120
1166
 
1167
+ const {
1168
+ invoke: invoke$3,
1169
+ set: set$5
1170
+ } = create$1(ExtensionHostWorker);
1171
+
1172
+ const {
1173
+ invoke: invoke$2,
1174
+ set: set$4
1175
+ } = create$1(FileSystemWorker);
1176
+ const readFile = async uri => {
1177
+ return invoke$2('FileSystem.readFile', uri);
1178
+ };
1179
+ const writeFile = async (uri, content) => {
1180
+ return invoke$2('FileSystem.writeFile', uri, content);
1181
+ };
1182
+ const exists = async uri => {
1183
+ // @ts-ignore
1184
+ return invoke$2('FileSystem.exists', uri);
1185
+ };
1186
+
1121
1187
  const {
1122
1188
  invoke: invoke$1,
1123
1189
  invokeAndTransfer,
1124
- set: set$2
1125
- } = create(RendererWorker);
1190
+ set: set$3
1191
+ } = create$1(RendererWorker);
1192
+ const sendMessagePortToFileSystemWorker = async (port, rpcId) => {
1193
+ const command = 'FileSystem.handleMessagePort';
1194
+ // @ts-ignore
1195
+ await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToFileSystemWorker', port, command, rpcId);
1196
+ };
1197
+ const sendMessagePortToExtensionHostWorker = async (port, rpcId = 0) => {
1198
+ const command = 'HandleMessagePort.handleMessagePort2';
1199
+ await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
1200
+ };
1126
1201
  const sendMessagePortToSharedProcess = async port => {
1127
1202
  const command = 'HandleElectronMessagePort.handleElectronMessagePort';
1128
1203
  // @ts-ignore
1129
1204
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToSharedProcess', port, command, 0);
1130
1205
  };
1131
- const getExtension$1 = async id => {
1132
- // @ts-ignore
1133
- return invoke$1('ExtensionManagement.getExtension', id);
1134
- };
1135
1206
 
1136
1207
  const {
1137
1208
  invoke,
1138
- set: set$1
1139
- } = create(SharedProcess);
1209
+ set: set$2
1210
+ } = create$1(SharedProcess);
1211
+
1212
+ const create = () => {
1213
+ return {
1214
+ finished: false
1215
+ };
1216
+ };
1217
+ const cancel = token => {
1218
+ token.finished = true;
1219
+ };
1220
+ const isCanceled = token => {
1221
+ return token.finished;
1222
+ };
1223
+
1224
+ const baseName = path => {
1225
+ const slashIndex = path.lastIndexOf('/');
1226
+ return path.slice(slashIndex + 1);
1227
+ };
1228
+ const getExtensionId = extension => {
1229
+ if (extension && extension.id) {
1230
+ return extension.id;
1231
+ }
1232
+ if (extension && extension.path) {
1233
+ return baseName(extension.path);
1234
+ }
1235
+ return '<unknown>';
1236
+ };
1237
+
1238
+ const isImportErrorChrome = error => {
1239
+ return Boolean(error && error instanceof Error && error.message.startsWith('Failed to fetch dynamically imported module'));
1240
+ };
1241
+
1242
+ const isImportErrorFirefox = error => {
1243
+ return Boolean(error && error instanceof TypeError && error.message === 'error loading dynamically imported module');
1244
+ };
1245
+
1246
+ const isSyntaxError = error => {
1247
+ return error instanceof SyntaxError;
1248
+ };
1249
+
1250
+ const isImportError = error => {
1251
+ return isImportErrorChrome(error) || isImportErrorFirefox(error) || isSyntaxError(error);
1252
+ };
1253
+
1254
+ const states = Object.create(null);
1255
+ const set$1 = status => {
1256
+ states[status.id] = status;
1257
+ };
1258
+ const get$1 = extensionId => {
1259
+ return states[extensionId];
1260
+ };
1261
+ const update$1 = (id, update) => {
1262
+ states[id] = {
1263
+ ...states[id],
1264
+ ...update
1265
+ };
1266
+ };
1267
+
1268
+ const None = 0;
1269
+ const Importing = 1;
1270
+ const Activating = 2;
1271
+ const Activated = 3;
1272
+ const Error$1 = 4;
1273
+
1274
+ const sleep = duration => {
1275
+ const {
1276
+ promise,
1277
+ resolve
1278
+ } = Promise.withResolvers();
1279
+ setTimeout(resolve, duration);
1280
+ return promise;
1281
+ };
1282
+
1283
+ const NotFound = 404;
1284
+
1285
+ const tryToGetActualImportErrorMessage = async (url, error) => {
1286
+ let response;
1287
+ try {
1288
+ response = await fetch(url);
1289
+ } catch (error) {
1290
+ return `Failed to import ${url}: ${error}`;
1291
+ }
1292
+ if (response.ok) {
1293
+ throw new Error(`Failed to import ${url}: Unknown Error`);
1294
+ }
1295
+ switch (response.status) {
1296
+ case NotFound:
1297
+ throw new Error(`Failed to import ${url}: Not found (404)`);
1298
+ default:
1299
+ return `Failed to import ${url}: ${error}`;
1300
+ }
1301
+ };
1302
+
1303
+ // TODO make activation timeout configurable or remove it.
1304
+ // some extension might do workspace indexing which could take some time
1305
+ const activationTimeout = 10_000;
1306
+ const rejectAfterTimeout = async (timeout, token) => {
1307
+ await sleep(timeout);
1308
+ if (isCanceled(token)) {
1309
+ return;
1310
+ }
1311
+ throw new Error(`Activation timeout of ${timeout}ms exceeded`);
1312
+ };
1313
+ const activate = async (extensionId, extension) => {
1314
+ await invoke$3('ExtensionHost.activateExtension3', extensionId, extension);
1315
+ };
1316
+ const activateExtension2 = async (extensionId, extension, absolutePath) => {
1317
+ const token = create();
1318
+ try {
1319
+ const startTime = performance.now();
1320
+ update$1(extensionId, {
1321
+ activationStartTime: startTime,
1322
+ status: Activating
1323
+ });
1324
+ await Promise.race([activate(extensionId, extension), rejectAfterTimeout(activationTimeout, token)]);
1325
+ const endTime = performance.now();
1326
+ const time = endTime - startTime;
1327
+ update$1(extensionId, {
1328
+ activationEndTime: endTime,
1329
+ activationTime: time,
1330
+ status: Activated
1331
+ });
1332
+ } catch (error) {
1333
+ const id = getExtensionId(extension);
1334
+ if (isImportError(error)) {
1335
+ const actualErrorMessage = await tryToGetActualImportErrorMessage(absolutePath, error);
1336
+ throw new Error(`Failed to activate extension ${id}: ${actualErrorMessage}`);
1337
+ }
1338
+ update$1(extensionId, {
1339
+ status: Error$1 // TODO maybe store error also in runtime status state
1340
+ });
1341
+ throw new VError(error, `Failed to activate extension ${id}`);
1342
+ } finally {
1343
+ cancel(token);
1344
+ }
1345
+ };
1346
+
1347
+ const cache = Object.create(null);
1348
+ const id = 1;
1349
+ const clear = () => {
1350
+ delete cache[id];
1351
+ };
1352
+
1353
+ const getJson = async url => {
1354
+ try {
1355
+ const response = await fetch(url);
1356
+ if (!response.ok) {
1357
+ throw new Error(response.statusText);
1358
+ }
1359
+ const json = await response.json();
1360
+ return json;
1361
+ } catch (error) {
1362
+ throw new VError(error, `Failed to get json`);
1363
+ }
1364
+ };
1365
+
1366
+ const Slash = '/';
1367
+
1368
+ const interExtensionId = path => {
1369
+ const slashIndex = path.lastIndexOf(Slash);
1370
+ return path.slice(slashIndex + 1);
1371
+ };
1372
+
1373
+ const getWebExtensionManifest = async (path, manifestPath) => {
1374
+ try {
1375
+ const manifest = await getJson(manifestPath);
1376
+ return {
1377
+ ...manifest,
1378
+ path,
1379
+ uri: path
1380
+ };
1381
+ } catch (error) {
1382
+ const id = interExtensionId(path);
1383
+ throw new VError(error, `Failed to load extension manifest for ${id}`);
1384
+ }
1385
+ };
1386
+
1387
+ const getWebManifestPath = path => {
1388
+ const manifestPath = `${path}/extension.json`;
1389
+ return manifestPath;
1390
+ };
1391
+
1392
+ const addWebExtension = async path => {
1393
+ const manifestPath = getWebManifestPath(path);
1394
+ const manifest = await getWebExtensionManifest(path, manifestPath);
1395
+ clear();
1396
+ return manifest;
1397
+ };
1140
1398
 
1141
1399
  const invalidateExtensionsCache = async () => {
1142
1400
  await invoke$1('ExtensionManagement.invalidateExtensionsCache');
@@ -1161,9 +1419,9 @@ const get = () => {
1161
1419
  };
1162
1420
 
1163
1421
  const disableExtension = async (id, isTest) => {
1422
+ const oldState = get();
1164
1423
  try {
1165
1424
  if (isTest) {
1166
- const oldState = get();
1167
1425
  const newState = {
1168
1426
  ...oldState,
1169
1427
  disabledIds: [...oldState.disabledIds, id]
@@ -1180,28 +1438,67 @@ const disableExtension = async (id, isTest) => {
1180
1438
  };
1181
1439
 
1182
1440
  const enableExtension = async (id, isTest) => {
1183
- try {
1184
- if (isTest) {
1185
- const oldState = get();
1186
- const newState = {
1187
- ...oldState,
1188
- disabledIds: oldState.disabledIds.filter(existing => existing !== id)
1189
- };
1190
- set(newState);
1191
- } else {
1192
- await invoke('ExtensionManagement.enable', id);
1441
+ const oldState = get();
1442
+ const {
1443
+ platform
1444
+ } = oldState;
1445
+ if (platform === Remote || platform === Electron) {
1446
+ const disabledExtensionsJsonPath = await invoke$1('PlatformPaths.getBuiltinExtensionsJsonPath');
1447
+ const exists$1 = await exists(disabledExtensionsJsonPath);
1448
+ if (!exists$1) {
1449
+ return undefined;
1193
1450
  }
1194
- await invalidateExtensionsCache();
1195
- return undefined;
1196
- } catch (error) {
1197
- return error;
1451
+ const content = await readFile(disabledExtensionsJsonPath);
1452
+ const parsed = JSON.parse(content);
1453
+ const oldDisabled = parsed.disabledExtensions || [];
1454
+ const newDisabled = oldDisabled.filter(item => item !== id);
1455
+ const newData = {
1456
+ disabledExtensions: newDisabled
1457
+ };
1458
+ const newContent = JSON.stringify(newData, null, 2) + '\n';
1459
+ await writeFile(disabledExtensionsJsonPath, newContent);
1460
+ }
1461
+ if (isTest) {
1462
+ const newState = {
1463
+ ...oldState,
1464
+ disabledIds: oldState.disabledIds.filter(existing => existing !== id)
1465
+ };
1466
+ set(newState);
1467
+ }
1468
+ return undefined;
1469
+ };
1470
+
1471
+ const getAllExtensions = async () => {
1472
+ const state = get();
1473
+ if (state.platform === Web) {
1474
+ return [];
1198
1475
  }
1476
+ return invoke('ExtensionManagement.getAllExtensions');
1199
1477
  };
1200
1478
 
1201
1479
  const getExtension = async id => {
1202
- // TODO
1203
- const extension = await getExtension$1(id);
1204
- return extension;
1480
+ const allExtensions = await getAllExtensions();
1481
+ for (const extension of allExtensions) {
1482
+ if (extension.id === id) {
1483
+ return extension;
1484
+ }
1485
+ }
1486
+ return undefined;
1487
+ };
1488
+
1489
+ const emptyStatus = {
1490
+ activationEndTime: 0,
1491
+ activationEvent: '',
1492
+ activationStartTime: 0,
1493
+ activationTime: 0,
1494
+ id: '',
1495
+ importEndTime: 0,
1496
+ importStartTime: 0,
1497
+ importTime: 0,
1498
+ status: None
1499
+ };
1500
+ const getRuntimeStatus = extensionId => {
1501
+ return get$1(extensionId) || emptyStatus;
1205
1502
  };
1206
1503
 
1207
1504
  const commandMapRef = {};
@@ -1213,6 +1510,64 @@ const handleMessagePort = async port => {
1213
1510
  });
1214
1511
  };
1215
1512
 
1513
+ const importExtension = async (extensionId, absolutePath, activationEvent) => {
1514
+ try {
1515
+ string(absolutePath);
1516
+ const startTime = performance.now();
1517
+ set$1({
1518
+ activationEndTime: 0,
1519
+ activationEvent: activationEvent,
1520
+ activationStartTime: performance.now(),
1521
+ activationTime: 0,
1522
+ id: extensionId,
1523
+ importEndTime: 0,
1524
+ importStartTime: startTime,
1525
+ importTime: 0,
1526
+ status: Importing
1527
+ });
1528
+ try {
1529
+ await invoke$3('ExtneionHost.importExtension2', extensionId, absolutePath);
1530
+ const endTime = performance.now();
1531
+ const time = endTime - startTime;
1532
+ update$1(extensionId, {
1533
+ importEndTime: endTime,
1534
+ importTime: time
1535
+ });
1536
+ } catch (error) {
1537
+ update$1(extensionId, {
1538
+ status: Error$1 // TODO maybe store error also in runtime status state
1539
+ });
1540
+ if (isImportError(error)) {
1541
+ const actualErrorMessage = await tryToGetActualImportErrorMessage(absolutePath, error);
1542
+ throw new Error(actualErrorMessage);
1543
+ }
1544
+ throw error;
1545
+ }
1546
+ } catch (error) {
1547
+ throw new VError(error, `Failed to import extension ${extensionId}`);
1548
+ }
1549
+ };
1550
+
1551
+ const initializeExtensionHostWorker = async () => {
1552
+ const rpc = await TransferMessagePortRpcParent.create({
1553
+ commandMap: commandMapRef,
1554
+ async send(port) {
1555
+ await sendMessagePortToExtensionHostWorker(port, 0);
1556
+ }
1557
+ });
1558
+ set$5(rpc);
1559
+ };
1560
+
1561
+ const initializeFileSystemWorker = async () => {
1562
+ const rpc = await TransferMessagePortRpcParent.create({
1563
+ commandMap: commandMapRef,
1564
+ async send(port) {
1565
+ await sendMessagePortToFileSystemWorker(port, 0);
1566
+ }
1567
+ });
1568
+ set$4(rpc);
1569
+ };
1570
+
1216
1571
  const getRpc = async platform => {
1217
1572
  // TODO create connection to shared process
1218
1573
  if (platform === Remote) {
@@ -1236,7 +1591,7 @@ const getRpc = async platform => {
1236
1591
  const initializeSharedProcess = async platform => {
1237
1592
  const rpc = await getRpc(platform);
1238
1593
  if (rpc) {
1239
- set$1(rpc);
1594
+ set$2(rpc);
1240
1595
  }
1241
1596
  };
1242
1597
 
@@ -1244,7 +1599,7 @@ const initialize = async platform => {
1244
1599
  update({
1245
1600
  platform
1246
1601
  });
1247
- await initializeSharedProcess(platform);
1602
+ await Promise.all([initializeFileSystemWorker(), initializeSharedProcess(platform), initializeExtensionHostWorker()]);
1248
1603
  };
1249
1604
 
1250
1605
  const installExtension = async () => {
@@ -1256,10 +1611,15 @@ const uninstallExtension = async () => {
1256
1611
  };
1257
1612
 
1258
1613
  const commandMap = {
1614
+ 'Extensions.activate2': activateExtension2,
1615
+ 'Extensions.addWebExtension': addWebExtension,
1259
1616
  'Extensions.disable': disableExtension,
1260
1617
  'Extensions.enable': enableExtension,
1618
+ 'Extensions.getAllExtensions': getAllExtensions,
1261
1619
  'Extensions.getExtension': getExtension,
1620
+ 'Extensions.getRuntimeStatus': getRuntimeStatus,
1262
1621
  'Extensions.handleMessagePort': handleMessagePort,
1622
+ 'Extensions.importExtension': importExtension,
1263
1623
  'Extensions.initialize': initialize,
1264
1624
  'Extensions.install': installExtension,
1265
1625
  'Extensions.uninstall': uninstallExtension
@@ -1270,7 +1630,7 @@ const listen = async () => {
1270
1630
  const rpc = await WebWorkerRpcClient.create({
1271
1631
  commandMap: commandMap
1272
1632
  });
1273
- set$2(rpc);
1633
+ set$3(rpc);
1274
1634
  };
1275
1635
 
1276
1636
  const main = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/extension-management-worker",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Webworker for the Extension Management functionality in Lvce Editor.",
5
5
  "keywords": [
6
6
  "web-worker"