@lvce-editor/extension-search-view 1.4.0 → 2.0.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.
@@ -1,73 +1,58 @@
1
- const commands = Object.create(null);
2
- const register = commandMap => {
3
- Object.assign(commands, commandMap);
4
- };
5
- const getCommand = key => {
6
- return commands[key];
7
- };
8
- const execute = (command, ...args) => {
9
- const fn = getCommand(command);
10
- if (!fn) {
11
- throw new Error(`command not found ${command}`);
1
+ const normalizeLine = line => {
2
+ if (line.startsWith('Error: ')) {
3
+ return line.slice('Error: '.length);
12
4
  }
13
- return fn(...args);
14
- };
15
-
16
- const handleError = async (error, notify = true, prefix = '') => {
17
- console.error(error);
18
- };
19
-
20
- const emptyObject = {};
21
- const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
22
- const i18nString = (key, placeholders = emptyObject) => {
23
- if (placeholders === emptyObject) {
24
- return key;
5
+ if (line.startsWith('VError: ')) {
6
+ return line.slice('VError: '.length);
25
7
  }
26
- const replacer = (match, rest) => {
27
- // @ts-ignore
28
- return placeholders[rest];
29
- };
30
- return key.replaceAll(RE_PLACEHOLDER, replacer);
31
- };
32
-
33
- /**
34
- * @enum {string}
35
- */
36
- const UiStrings = {
37
- NoExtensionsFound: 'No extensions found.',
38
- Filter: 'Filter',
39
- Refresh: 'Refresh',
40
- ClearExtensionSearchResults: 'Clear extension search results',
41
- Enable: 'Enable',
42
- Disable: 'Disable',
43
- Uninstall: 'Uninstall',
44
- InstallAnotherVersion: 'Install Another Version',
45
- SearchExtensionsInMarketplace: 'Search Extensions in Marketplace',
46
- ViewsAndMoreActions: 'Views and more Actions...',
47
- Extensions: 'Extensions',
48
- Installed: 'Installed'
49
- };
50
- const noExtensionsFound = () => {
51
- return i18nString(UiStrings.NoExtensionsFound);
52
- };
53
- const filter = () => {
54
- return i18nString(UiStrings.Filter);
55
- };
56
- const extensions = () => {
57
- return i18nString(UiStrings.Extensions);
8
+ return line;
58
9
  };
59
- const clearExtensionSearchResults = () => {
60
- return i18nString(UiStrings.ClearExtensionSearchResults);
10
+ const getCombinedMessage = (error, message) => {
11
+ const stringifiedError = normalizeLine(`${error}`);
12
+ if (message) {
13
+ return `${message}: ${stringifiedError}`;
14
+ }
15
+ return stringifiedError;
61
16
  };
62
- const searchExtensionsInMarketPlace = () => {
63
- return i18nString(UiStrings.SearchExtensionsInMarketplace);
17
+ const NewLine$2 = '\n';
18
+ const getNewLineIndex$1 = (string, startIndex = undefined) => {
19
+ return string.indexOf(NewLine$2, startIndex);
64
20
  };
65
-
66
- const getFinalDeltaY = (height, itemHeight, itemsLength) => {
67
- const contentHeight = itemsLength * itemHeight;
68
- const finalDeltaY = Math.max(contentHeight - height, 0);
69
- return finalDeltaY;
21
+ const mergeStacks = (parent, child) => {
22
+ if (!child) {
23
+ return parent;
24
+ }
25
+ const parentNewLineIndex = getNewLineIndex$1(parent);
26
+ const childNewLineIndex = getNewLineIndex$1(child);
27
+ if (childNewLineIndex === -1) {
28
+ return parent;
29
+ }
30
+ const parentFirstLine = parent.slice(0, parentNewLineIndex);
31
+ const childRest = child.slice(childNewLineIndex);
32
+ const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
33
+ if (parentFirstLine.includes(childFirstLine)) {
34
+ return parentFirstLine + childRest;
35
+ }
36
+ return child;
70
37
  };
38
+ class VError extends Error {
39
+ constructor(error, message) {
40
+ const combinedMessage = getCombinedMessage(error, message);
41
+ super(combinedMessage);
42
+ this.name = 'VError';
43
+ if (error instanceof Error) {
44
+ this.stack = mergeStacks(this.stack, error.stack);
45
+ }
46
+ if (error.codeFrame) {
47
+ // @ts-ignore
48
+ this.codeFrame = error.codeFrame;
49
+ }
50
+ if (error.code) {
51
+ // @ts-ignore
52
+ this.code = error.code;
53
+ }
54
+ }
55
+ }
71
56
 
72
57
  class AssertionError extends Error {
73
58
  constructor(message) {
@@ -104,759 +89,512 @@ const number = value => {
104
89
  }
105
90
  };
106
91
 
107
- const getListHeight$1 = (itemsLength, itemHeight, maxHeight) => {
108
- number(itemsLength);
109
- number(itemHeight);
110
- number(maxHeight);
111
- if (itemsLength === 0) {
112
- return itemHeight;
113
- }
114
- const totalHeight = itemsLength * itemHeight;
115
- return Math.min(totalHeight, maxHeight);
92
+ const isMessagePort = value => {
93
+ return value && value instanceof MessagePort;
116
94
  };
117
-
118
- // TODO optimize this function to return the minimum number
119
- // of visible items needed, e.g. when not scrolled 5 items with
120
- // 20px fill 100px but when scrolled 6 items are needed
121
- const getNumberOfVisibleItems = (listHeight, itemHeight) => {
122
- return Math.ceil(listHeight / itemHeight) + 1;
95
+ const isMessagePortMain = value => {
96
+ return value && value.constructor && value.constructor.name === 'MessagePortMain';
123
97
  };
124
-
125
- /**
126
- *
127
- * @param {number} size
128
- * @param {number} contentSize
129
- * @param {number} minimumSliderSize
130
- * @returns
131
- */
132
- const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
133
- if (size >= contentSize) {
134
- return 0;
135
- }
136
- return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
98
+ const isOffscreenCanvas = value => {
99
+ return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
137
100
  };
138
- const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
139
- const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
140
- return scrollBarOffset;
101
+ const isInstanceOf = (value, constructorName) => {
102
+ return value?.constructor?.name === constructorName;
141
103
  };
142
- const getScrollBarY = getScrollBarOffset;
143
-
144
- const Installed = '@installed';
145
- const Enabled = '@enabled';
146
- const Disabled = '@disabled';
147
- const Builtin = '@builtin';
148
- const Sort = '@sort';
149
- const Id = '@id';
150
- const Outdated = '@outdated';
151
-
152
- const RE_PARAM = /@\w+/g;
153
-
154
- // TODO test sorting and filtering
155
- const parseValue = value => {
156
- const parameters = Object.create(null);
157
- // TODO this is not very functional code (assignment)
158
- const replaced = value.replaceAll(RE_PARAM, (match, by, order) => {
159
- if (match.startsWith(Installed)) {
160
- parameters.installed = true;
161
- }
162
- if (match.startsWith(Enabled)) {
163
- parameters.enabled = true;
164
- }
165
- if (match.startsWith(Disabled)) {
166
- parameters.disabled = true;
167
- }
168
- if (match.startsWith(Builtin)) {
169
- parameters.builtin = true;
170
- }
171
- if (match.startsWith(Sort)) {
172
- // TODO
173
- parameters.sort = 'installs';
174
- }
175
- if (match.startsWith(Id)) {
176
- // TODO
177
- parameters.id = 'abc';
178
- }
179
- if (match.startsWith(Outdated)) {
180
- parameters.outdated = true;
181
- }
182
- return '';
183
- });
184
- const isLocal = parameters.enabled || parameters.builtin || parameters.disabled || parameters.outdated || parameters.installed;
185
- return {
186
- query: replaced,
187
- ...parameters,
188
- isLocal
189
- };
104
+ const isSocket = value => {
105
+ return isInstanceOf(value, 'Socket');
190
106
  };
191
-
192
- const assetDir = '';
193
-
194
- const Web = 1;
195
- const Electron = 2;
196
- const Remote = 3;
197
- const Test = 4;
198
-
199
- // TODO treeshake this function out
200
-
201
- /**
202
- * @returns {number}
203
- */
204
- const getPlatform = () => {
205
- // @ts-ignore
206
- if (typeof PLATFORM !== 'undefined') {
207
- // @ts-ignore
208
- return PLATFORM;
107
+ const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
108
+ const isTransferrable = value => {
109
+ for (const fn of transferrables) {
110
+ if (fn(value)) {
111
+ return true;
112
+ }
209
113
  }
210
- if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
211
- return Test;
114
+ return false;
115
+ };
116
+ const walkValue = (value, transferrables, isTransferrable) => {
117
+ if (!value) {
118
+ return;
212
119
  }
213
- // TODO find a better way to pass runtime environment
214
- if (typeof name !== 'undefined' && name.endsWith('(Electron)')) {
215
- return Electron;
120
+ if (isTransferrable(value)) {
121
+ transferrables.push(value);
122
+ return;
216
123
  }
217
- if (typeof name !== 'undefined' && name.endsWith('(Web)')) {
218
- return Web;
124
+ if (Array.isArray(value)) {
125
+ for (const item of value) {
126
+ walkValue(item, transferrables, isTransferrable);
127
+ }
128
+ return;
219
129
  }
220
- return Remote;
221
- };
222
- const platform = getPlatform();
223
-
224
- const getRemoteUrl = extension => {
225
- if (platform === Remote || platform === Electron) {
226
- if (extension.builtin) {
227
- return `${assetDir}/extensions/${extension.id}/${extension.icon}`;
130
+ if (typeof value === 'object') {
131
+ for (const property of Object.values(value)) {
132
+ walkValue(property, transferrables, isTransferrable);
228
133
  }
229
- return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
134
+ return;
230
135
  }
231
- return '';
232
136
  };
233
-
234
- const ExtensionDefaultIcon = `${assetDir}/icons/extensionDefaultIcon.png`;
235
- const ExtensionLanguageBasics = `${assetDir}/icons/language-icon.svg`;
236
- const ExtensionTheme = `${assetDir}/icons/theme-icon.png`;
237
-
238
- const isLanguageBasicsExtension = extension => {
239
- return extension.name && extension.name.startsWith('Language Basics');
137
+ const getTransferrables = value => {
138
+ const transferrables = [];
139
+ walkValue(value, transferrables, isTransferrable);
140
+ return transferrables;
240
141
  };
241
- const isThemeExtension = extension => {
242
- return extension.name && extension.name.endsWith(' Theme');
142
+ const attachEvents = that => {
143
+ const handleMessage = (...args) => {
144
+ const data = that.getData(...args);
145
+ that.dispatchEvent(new MessageEvent('message', {
146
+ data
147
+ }));
148
+ };
149
+ that.onMessage(handleMessage);
150
+ const handleClose = event => {
151
+ that.dispatchEvent(new Event('close'));
152
+ };
153
+ that.onClose(handleClose);
243
154
  };
244
- const getIcon = extension => {
245
- if (!extension) {
246
- return ExtensionDefaultIcon;
247
- }
248
- if (!extension.path || !extension.icon) {
249
- if (isLanguageBasicsExtension(extension)) {
250
- return ExtensionLanguageBasics;
251
- }
252
- if (isThemeExtension(extension)) {
253
- return ExtensionTheme;
254
- }
255
- return ExtensionDefaultIcon;
155
+ class Ipc extends EventTarget {
156
+ constructor(rawIpc) {
157
+ super();
158
+ this._rawIpc = rawIpc;
159
+ attachEvents(this);
256
160
  }
257
- return getRemoteUrl(extension);
161
+ }
162
+ const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
163
+ const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
164
+ const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
165
+ const NewLine$1 = '\n';
166
+ const joinLines$1 = lines => {
167
+ return lines.join(NewLine$1);
258
168
  };
259
- const RE_PUBLISHER = /^[a-z\d\-]+/;
260
-
261
- // TODO handle case when extension is of type number|array|null|string
262
- const getPublisher = extension => {
263
- if (!extension || !extension.id) {
264
- return 'n/a';
265
- }
266
- // TODO handle case when id is not of type string -> should not crash application
267
- const match = extension.id.match(RE_PUBLISHER);
268
- if (!match) {
269
- return 'n/a';
270
- }
271
- return match[0];
169
+ const RE_AT = /^\s+at/;
170
+ const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
171
+ const isNormalStackLine = line => {
172
+ return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
272
173
  };
273
- const getName = extension => {
274
- if (extension && extension.name) {
275
- return extension.name;
174
+ const getDetails = lines => {
175
+ const index = lines.findIndex(isNormalStackLine);
176
+ if (index === -1) {
177
+ return {
178
+ actualMessage: joinLines$1(lines),
179
+ rest: []
180
+ };
276
181
  }
277
- if (extension && extension.id) {
278
- return extension.id;
182
+ let lastIndex = index - 1;
183
+ while (++lastIndex < lines.length) {
184
+ if (!isNormalStackLine(lines[lastIndex])) {
185
+ break;
186
+ }
279
187
  }
280
- return 'n/a';
188
+ return {
189
+ actualMessage: lines[index - 1],
190
+ rest: lines.slice(index, lastIndex)
191
+ };
281
192
  };
282
- const getDescription = extension => {
283
- if (!extension || !extension.description) {
284
- return 'n/a';
285
- }
286
- return extension.description;
193
+ const splitLines$1 = lines => {
194
+ return lines.split(NewLine$1);
287
195
  };
288
- const getId = extension => {
289
- if (!extension || !extension.id) {
290
- return 'n/a';
291
- }
292
- return extension.id;
196
+ const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
197
+ const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
198
+ const isMessageCodeBlockStartIndex = line => {
199
+ return RE_MESSAGE_CODE_BLOCK_START.test(line);
293
200
  };
294
-
295
- const matchesParsedValue = (extension, parsedValue) => {
296
- if (extension && typeof extension.name === 'string') {
297
- const extensionNameLower = extension.name.toLowerCase();
298
- return extensionNameLower.includes(parsedValue.query);
299
- }
300
- if (extension && typeof extension.id === 'string') {
301
- const extensionIdLower = extension.id.toLowerCase();
302
- return extensionIdLower.includes(parsedValue.query);
303
- }
304
- return false;
201
+ const isMessageCodeBlockEndIndex = line => {
202
+ return RE_MESSAGE_CODE_BLOCK_END.test(line);
305
203
  };
306
-
307
- const toSorted = (array, compare) => {
308
- return [...array].sort(compare);
204
+ const getMessageCodeBlock = stderr => {
205
+ const lines = splitLines$1(stderr);
206
+ const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
207
+ const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
208
+ const relevantLines = lines.slice(startIndex, endIndex);
209
+ const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
210
+ return relevantMessage;
309
211
  };
310
-
311
- const compareExtension = (extensionA, extensionB) => {
312
- return extensionA.name.localeCompare(extensionB.name) || extensionA.id.localeCompare(extensionB.id);
212
+ const isModuleNotFoundMessage = line => {
213
+ return line.includes('[ERR_MODULE_NOT_FOUND]');
313
214
  };
314
-
315
- const sortExtensions = extensions => {
316
- return toSorted(extensions, compareExtension);
215
+ const getModuleNotFoundError = stderr => {
216
+ const lines = splitLines$1(stderr);
217
+ const messageIndex = lines.findIndex(isModuleNotFoundMessage);
218
+ const message = lines[messageIndex];
219
+ return {
220
+ message,
221
+ code: ERR_MODULE_NOT_FOUND
222
+ };
317
223
  };
318
-
319
- const getExtensions = async (extensions, parsedValue) => {
320
- const filteredExtensions = [];
321
- for (const extension of extensions) {
322
- if (matchesParsedValue(extension, parsedValue)) {
323
- filteredExtensions.push({
324
- name: getName(extension),
325
- id: getId(extension),
326
- publisher: getPublisher(extension),
327
- icon: getIcon(extension),
328
- description: getDescription(extension)
329
- });
330
- }
224
+ const isModuleNotFoundError = stderr => {
225
+ if (!stderr) {
226
+ return false;
331
227
  }
332
- const sortedExtensions = sortExtensions(filteredExtensions);
333
- return sortedExtensions;
228
+ return stderr.includes('ERR_MODULE_NOT_FOUND');
334
229
  };
335
-
336
- const normalizeLine$1 = line => {
337
- if (line.startsWith('Error: ')) {
338
- return line.slice('Error: '.length);
339
- }
340
- if (line.startsWith('VError: ')) {
341
- return line.slice('VError: '.length);
230
+ const isModulesSyntaxError = stderr => {
231
+ if (!stderr) {
232
+ return false;
342
233
  }
343
- return line;
234
+ return stderr.includes('SyntaxError: Cannot use import statement outside a module');
344
235
  };
345
- const getCombinedMessage$1 = (error, message) => {
346
- const stringifiedError = normalizeLine$1(`${error}`);
347
- if (message) {
348
- return `${message}: ${stringifiedError}`;
349
- }
350
- return stringifiedError;
236
+ const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
237
+ const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
238
+ const isUnhelpfulNativeModuleError = stderr => {
239
+ return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
351
240
  };
352
- const NewLine$2 = '\n';
353
- const getNewLineIndex$1 = (string, startIndex = undefined) => {
354
- return string.indexOf(NewLine$2, startIndex);
241
+ const getNativeModuleErrorMessage = stderr => {
242
+ const message = getMessageCodeBlock(stderr);
243
+ return {
244
+ message: `Incompatible native node module: ${message}`,
245
+ code: E_INCOMPATIBLE_NATIVE_MODULE
246
+ };
355
247
  };
356
- const mergeStacks$1 = (parent, child) => {
357
- if (!child) {
358
- return parent;
248
+ const getModuleSyntaxError = () => {
249
+ return {
250
+ message: `ES Modules are not supported in electron`,
251
+ code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
252
+ };
253
+ };
254
+ const getHelpfulChildProcessError = (stdout, stderr) => {
255
+ if (isUnhelpfulNativeModuleError(stderr)) {
256
+ return getNativeModuleErrorMessage(stderr);
359
257
  }
360
- const parentNewLineIndex = getNewLineIndex$1(parent);
361
- const childNewLineIndex = getNewLineIndex$1(child);
362
- if (childNewLineIndex === -1) {
363
- return parent;
258
+ if (isModulesSyntaxError(stderr)) {
259
+ return getModuleSyntaxError();
364
260
  }
365
- const parentFirstLine = parent.slice(0, parentNewLineIndex);
366
- const childRest = child.slice(childNewLineIndex);
367
- const childFirstLine = normalizeLine$1(child.slice(0, childNewLineIndex));
368
- if (parentFirstLine.includes(childFirstLine)) {
369
- return parentFirstLine + childRest;
261
+ if (isModuleNotFoundError(stderr)) {
262
+ return getModuleNotFoundError(stderr);
370
263
  }
371
- return child;
264
+ const lines = splitLines$1(stderr);
265
+ const {
266
+ actualMessage,
267
+ rest
268
+ } = getDetails(lines);
269
+ return {
270
+ message: actualMessage,
271
+ code: '',
272
+ stack: rest
273
+ };
372
274
  };
373
- let VError$1 = class VError extends Error {
374
- constructor(error, message) {
375
- const combinedMessage = getCombinedMessage$1(error, message);
376
- super(combinedMessage);
377
- this.name = 'VError';
378
- if (error instanceof Error) {
379
- this.stack = mergeStacks$1(this.stack, error.stack);
380
- }
381
- if (error.codeFrame) {
275
+ class IpcError extends VError {
276
+ // @ts-ignore
277
+ constructor(betterMessage, stdout = '', stderr = '') {
278
+ if (stdout || stderr) {
382
279
  // @ts-ignore
383
- this.codeFrame = error.codeFrame;
384
- }
385
- if (error.code) {
280
+ const {
281
+ message,
282
+ code,
283
+ stack
284
+ } = getHelpfulChildProcessError(stdout, stderr);
285
+ const cause = new Error(message);
386
286
  // @ts-ignore
387
- this.code = error.code;
287
+ cause.code = code;
288
+ cause.stack = stack;
289
+ super(cause, betterMessage);
290
+ } else {
291
+ super(betterMessage);
388
292
  }
293
+ // @ts-ignore
294
+ this.name = 'IpcError';
295
+ // @ts-ignore
296
+ this.stdout = stdout;
297
+ // @ts-ignore
298
+ this.stderr = stderr;
389
299
  }
300
+ }
301
+ const readyMessage = 'ready';
302
+ const getData$2 = event => {
303
+ return event.data;
390
304
  };
391
-
392
- const searchExtensions = async (extensions, value) => {
393
- try {
394
- const parsedValue = parseValue(value);
395
- const filteredExtensions = await getExtensions(extensions, parsedValue);
396
- return filteredExtensions;
397
- } catch (error) {
398
- throw new VError$1(error, 'Failed to search for extensions');
305
+ const listen$7 = () => {
306
+ // @ts-ignore
307
+ if (typeof WorkerGlobalScope === 'undefined') {
308
+ throw new TypeError('module is not in web worker scope');
399
309
  }
310
+ return globalThis;
400
311
  };
401
-
402
- // TODO debounce
403
- const handleInput = async (state, value) => {
404
- try {
405
- const {
406
- allExtensions,
407
- itemHeight,
408
- minimumSliderSize,
409
- height
410
- } = state;
411
- // TODO cancel ongoing requests
412
- // TODO handle errors
413
- const items = await searchExtensions(allExtensions, value);
414
- if (items.length === 0) {
415
- return {
416
- ...state,
417
- items,
418
- minLineY: 0,
419
- deltaY: 0,
420
- allExtensions,
421
- maxLineY: 0,
422
- scrollBarHeight: 0,
423
- finalDeltaY: 0,
424
- message: noExtensionsFound(),
425
- searchValue: value,
426
- placeholder: searchExtensionsInMarketPlace()
427
- };
428
- }
312
+ const signal$8 = global => {
313
+ global.postMessage(readyMessage);
314
+ };
315
+ class IpcChildWithModuleWorker extends Ipc {
316
+ getData(event) {
317
+ return getData$2(event);
318
+ }
319
+ send(message) {
429
320
  // @ts-ignore
430
- const listHeight = getListHeight$1(state);
431
- const total = items.length;
432
- const contentHeight = total * itemHeight;
433
- const scrollBarHeight = getScrollBarSize(height, contentHeight, minimumSliderSize);
434
- const numberOfVisible = getNumberOfVisibleItems(listHeight, itemHeight);
435
- const maxLineY = Math.min(numberOfVisible, total);
436
- const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, total);
437
- return {
438
- ...state,
439
- items,
440
- minLineY: 0,
441
- deltaY: 0,
442
- allExtensions,
443
- maxLineY,
444
- scrollBarHeight,
445
- finalDeltaY,
446
- message: '',
447
- searchValue: value,
448
- placeholder: searchExtensionsInMarketPlace()
449
- };
450
-
451
- // TODO handle out of order responses (a bit complicated)
452
- // for now just assume everything comes back in order
453
- } catch (error) {
454
- await handleError(error);
455
- return {
456
- ...state,
457
- searchValue: value,
458
- message: `${error}`
459
- };
321
+ this._rawIpc.postMessage(message);
460
322
  }
323
+ sendAndTransfer(message) {
324
+ const transfer = getTransferrables(message);
325
+ // @ts-ignore
326
+ this._rawIpc.postMessage(message, transfer);
327
+ }
328
+ dispose() {
329
+ // ignore
330
+ }
331
+ onClose(callback) {
332
+ // ignore
333
+ }
334
+ onMessage(callback) {
335
+ this._rawIpc.addEventListener('message', callback);
336
+ }
337
+ }
338
+ const wrap$f = global => {
339
+ return new IpcChildWithModuleWorker(global);
461
340
  };
462
-
463
- const clearSearchResults = state => {
464
- return handleInput(state, '');
465
- };
466
-
467
- const Enter = 3;
468
- const Space = 9;
469
- const PageUp = 10;
470
- const PageDown = 11;
471
- const End = 255;
472
- const Home = 12;
473
- const UpArrow = 14;
474
- const DownArrow = 16;
475
-
476
- const CtrlCmd = 1 << 11 >>> 0;
477
-
478
- const FocusExtensions = 15;
479
-
480
- const getKeyBindings = () => {
481
- return [{
482
- key: Home,
483
- command: 'Extensions.focusFirst',
484
- when: FocusExtensions
485
- }, {
486
- key: End,
487
- command: 'Extensions.focusLast',
488
- when: FocusExtensions
489
- }, {
490
- key: PageUp,
491
- command: 'Extensions.focusPreviousPage',
492
- when: FocusExtensions
493
- }, {
494
- key: PageDown,
495
- command: 'Extensions.focusNextPage',
496
- when: FocusExtensions
497
- }, {
498
- key: UpArrow,
499
- command: 'Extensions.focusPrevious',
500
- when: FocusExtensions
501
- }, {
502
- key: DownArrow,
503
- command: 'Extensions.focusNext',
504
- when: FocusExtensions
505
- }, {
506
- key: Space,
507
- command: 'Extensions.handleClickCurrentButKeepFocus',
508
- when: FocusExtensions
509
- }, {
510
- key: Enter,
511
- command: 'Extensions.handleClickCurrent',
512
- when: FocusExtensions
513
- }, {
514
- key: CtrlCmd | Space,
515
- command: 'Extensions.toggleSuggest',
516
- when: FocusExtensions
517
- }, {
518
- key: CtrlCmd | DownArrow,
519
- command: 'Extensions.scrollDown',
520
- when: FocusExtensions
521
- }];
341
+ const withResolvers = () => {
342
+ let _resolve;
343
+ const promise = new Promise(resolve => {
344
+ _resolve = resolve;
345
+ });
346
+ return {
347
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
348
+ resolve: _resolve,
349
+ promise
350
+ };
522
351
  };
523
-
524
- const Button = 1;
525
-
526
- const ExtensionActions = 'ExtensionActions';
527
- const ExtensionActive = 'ExtensionActive';
528
- const ExtensionHeader = 'ExtensionHeader';
529
- const ExtensionListItem = 'ExtensionListItem';
530
- const ExtensionListItemAuthorName = 'ExtensionListItemAuthorName';
531
- const ExtensionListItemDescription = 'ExtensionListItemDescription';
532
- const ExtensionListItemDetail = 'ExtensionListItemDetail';
533
- const ExtensionListItemFooter = 'ExtensionListItemFooter';
534
- const ExtensionListItemIcon = 'ExtensionListItemIcon';
535
- const ExtensionListItemName = 'ExtensionListItemName';
536
- const ListItems = 'ListItems';
537
- const MultilineInputBox = 'MultilineInputBox';
538
- const ScrollBarThumb = 'ScrollBarThumb';
539
- const ScrollBarThumbActive = 'ScrollBarThumbActive';
540
- const SearchField = 'SearchField';
541
- const SearchFieldButtons = 'SearchFieldButtons';
542
- const SearchFieldContainer = 'SearchFieldContainer';
543
-
544
- const CheckBox = 'checkbox';
545
- const List = 'list';
546
- const ListItem = 'listitem';
547
- const None = 'none';
548
-
549
- const Div = 4;
550
- const Text = 12;
551
- const Img = 17;
552
- const TextArea = 62;
553
-
554
- const getSearchFieldButtonVirtualDom = button => {
352
+ const waitForFirstMessage = async port => {
555
353
  const {
556
- icon,
557
- checked,
558
- title
559
- } = button;
560
- return [{
561
- type: Div,
562
- className: `SearchFieldButton ${checked ? 'SearchFieldButtonChecked' : ''}`,
563
- title,
564
- role: CheckBox,
565
- ariaChecked: checked,
566
- tabIndex: 0,
567
- childCount: 1
568
- }, {
569
- type: Div,
570
- className: `MaskIcon ${icon}`,
571
- childCount: 0
572
- }];
354
+ resolve,
355
+ promise
356
+ } = withResolvers();
357
+ port.addEventListener('message', resolve, {
358
+ once: true
359
+ });
360
+ const event = await promise;
361
+ // @ts-ignore
362
+ return event.data;
573
363
  };
574
-
575
- const getSearchFieldVirtualDom = (name, placeholder, onInput, insideButtons, outsideButtons, onFocus = '') => {
576
- const dom = [{
577
- type: Div,
578
- className: SearchField,
579
- role: None,
580
- childCount: 2
581
- }, {
582
- type: TextArea,
583
- className: MultilineInputBox,
584
- spellcheck: false,
585
- autocapitalize: 'off',
586
- autocorrect: 'off',
587
- placeholder,
588
- name,
589
- onInput,
590
- onFocus,
591
- childCount: 0
592
- }, {
593
- type: Div,
594
- className: SearchFieldButtons,
595
- childCount: insideButtons.length
596
- }, ...insideButtons.flatMap(getSearchFieldButtonVirtualDom)];
597
- if (outsideButtons.length > 0) {
598
- dom.unshift({
599
- type: Div,
600
- className: SearchFieldContainer,
601
- role: None,
602
- childCount: 1 + outsideButtons.length
364
+ const listen$6 = async () => {
365
+ const parentIpcRaw = listen$7();
366
+ signal$8(parentIpcRaw);
367
+ const parentIpc = wrap$f(parentIpcRaw);
368
+ const firstMessage = await waitForFirstMessage(parentIpc);
369
+ if (firstMessage.method !== 'initialize') {
370
+ throw new IpcError('unexpected first message');
371
+ }
372
+ const type = firstMessage.params[0];
373
+ if (type === 'message-port') {
374
+ parentIpc.send({
375
+ jsonrpc: '2.0',
376
+ id: firstMessage.id,
377
+ result: null
603
378
  });
604
- dom.push(...outsideButtons.flatMap(getSearchFieldButtonVirtualDom));
379
+ parentIpc.dispose();
380
+ const port = firstMessage.params[1];
381
+ return port;
605
382
  }
606
- return dom;
383
+ return globalThis;
607
384
  };
608
-
609
- const getExtensionHeaderVirtualDom = (placeholder, actions) => {
610
- return [{
611
- type: Div,
612
- className: ExtensionHeader,
613
- childCount: 1
614
- }, ...getSearchFieldVirtualDom('extensions', placeholder, 'handleExtensionsInput', actions, [])];
385
+ class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
386
+ getData(event) {
387
+ return getData$2(event);
388
+ }
389
+ send(message) {
390
+ this._rawIpc.postMessage(message);
391
+ }
392
+ sendAndTransfer(message) {
393
+ const transfer = getTransferrables(message);
394
+ this._rawIpc.postMessage(message, transfer);
395
+ }
396
+ dispose() {
397
+ if (this._rawIpc.close) {
398
+ this._rawIpc.close();
399
+ }
400
+ }
401
+ onClose(callback) {
402
+ // ignore
403
+ }
404
+ onMessage(callback) {
405
+ this._rawIpc.addEventListener('message', callback);
406
+ this._rawIpc.start();
407
+ }
408
+ }
409
+ const wrap$e = port => {
410
+ return new IpcChildWithModuleWorkerAndMessagePort(port);
411
+ };
412
+ const IpcChildWithModuleWorkerAndMessagePort$1 = {
413
+ __proto__: null,
414
+ listen: listen$6,
415
+ wrap: wrap$e
615
416
  };
616
417
 
617
- const HandleContextMenu = 'handleContextMenu';
618
- const HandlePointerDown = 'handlePointerDown';
619
- const HandleTouchStart = 'handleTouchStart';
620
- const HandleWheel = 'handleWheel';
621
-
622
- const Extension = 'Extension';
623
-
624
- const text = data => {
418
+ const Two = '2.0';
419
+ const create$4 = (method, params) => {
625
420
  return {
626
- type: Text,
627
- text: data,
628
- childCount: 0
421
+ jsonrpc: Two,
422
+ method,
423
+ params
629
424
  };
630
425
  };
631
-
632
- const listItemDetail = {
633
- type: Div,
634
- className: ExtensionListItemDetail,
635
- childCount: 3
636
- };
637
- const listItemName = {
638
- type: Div,
639
- className: ExtensionListItemName,
640
- childCount: 1
426
+ const callbacks = Object.create(null);
427
+ const set$1 = (id, fn) => {
428
+ callbacks[id] = fn;
641
429
  };
642
- const listItemDescription = {
643
- type: Div,
644
- className: ExtensionListItemDescription,
645
- childCount: 1
430
+ const get = id => {
431
+ return callbacks[id];
646
432
  };
647
- const listItemFooter = {
648
- type: Div,
649
- className: ExtensionListItemFooter,
650
- childCount: 2
433
+ const remove = id => {
434
+ delete callbacks[id];
651
435
  };
652
- const listItemAuthorName = {
653
- type: Div,
654
- className: ExtensionListItemAuthorName,
655
- childCount: 1
436
+ let id = 0;
437
+ const create$3 = () => {
438
+ return ++id;
656
439
  };
657
- const getExtensionListItemVirtualDom = extension => {
440
+ const registerPromise = () => {
441
+ const id = create$3();
658
442
  const {
659
- posInSet,
660
- setSize,
661
- top,
662
- icon,
663
- name,
664
- description,
665
- publisher,
666
- focused
667
- } = extension;
668
- const dom = [{
669
- type: Div,
670
- role: ListItem,
671
- ariaRoleDescription: Extension,
672
- className: ExtensionListItem,
673
- ariaPosInSet: posInSet,
674
- ariaSetSize: setSize,
675
- top,
676
- childCount: 2
677
- }, {
678
- type: Img,
679
- src: icon,
680
- className: ExtensionListItemIcon,
681
- role: None,
682
- childCount: 0
683
- }, listItemDetail, listItemName, text(name), listItemDescription, text(description), listItemFooter, listItemAuthorName, text(publisher), {
684
- type: Div,
685
- className: ExtensionActions,
686
- childCount: 0
687
- }];
688
- if (focused) {
689
- dom[0].id = 'ExtensionActive';
690
- dom[0].className += ' ' + ExtensionActive;
691
- }
692
- return dom;
693
- };
694
-
695
- const getExtensionsListVirtualDom = visibleExtensions => {
696
- const dom = [{
697
- type: Div,
698
- className: ListItems,
699
- tabIndex: 0,
700
- ariaLabel: extensions(),
701
- role: List,
702
- oncontextmenu: HandleContextMenu,
703
- onpointerdown: HandlePointerDown,
704
- ontouchstart: HandleTouchStart,
705
- onwheelpassive: HandleWheel,
706
- childCount: visibleExtensions.length
707
- }, ...visibleExtensions.flatMap(getExtensionListItemVirtualDom)];
708
- return dom;
709
- };
710
-
711
- const getExtensionsVirtualDom = visibleExtensions => {
712
- const dom = getExtensionsListVirtualDom(visibleExtensions);
713
- // TODO
714
- return dom;
715
- };
716
-
717
- const getVisibleItem = (item, setSize, itemHeight, minLineY, relative, i, focusedIndex) => {
443
+ resolve,
444
+ promise
445
+ } = Promise.withResolvers();
446
+ set$1(id, resolve);
718
447
  return {
719
- ...item,
720
- setSize,
721
- posInSet: i + 1,
722
- top: (i - minLineY) * itemHeight - relative,
723
- focused: i === focusedIndex
448
+ id,
449
+ promise
724
450
  };
725
451
  };
726
- const getVisible = state => {
727
- const {
728
- minLineY,
729
- maxLineY,
730
- items,
731
- itemHeight,
732
- deltaY,
733
- focusedIndex
734
- } = state;
735
- const setSize = items.length;
736
- const visible = [];
737
- const relative = deltaY % itemHeight;
738
- for (let i = minLineY; i < maxLineY; i++) {
739
- const item = items[i];
740
- visible.push(getVisibleItem(item, setSize, itemHeight, minLineY, relative, i, focusedIndex));
741
- }
742
- return visible;
743
- };
744
-
745
- const ClearAll = 'ClearAll';
746
- const Filter = 'Filter';
747
-
748
- const px = value => {
749
- return `${value}px`;
750
- };
751
- const position = (x, y) => {
752
- return `${x}px ${y}px`;
753
- };
754
-
755
- const SetMessage = 'setMessage';
756
- const SetScrollBar = 'setScrollBar';
757
- const SetSearchValue = 'setSearchValue';
758
-
759
- const getListHeight = state => {
452
+ const create$2 = (method, params) => {
760
453
  const {
761
- height,
762
- headerHeight
763
- } = state;
764
- return height - headerHeight;
454
+ id,
455
+ promise
456
+ } = registerPromise();
457
+ const message = {
458
+ jsonrpc: Two,
459
+ method,
460
+ params,
461
+ id
462
+ };
463
+ return {
464
+ message,
465
+ promise
466
+ };
765
467
  };
766
- const renderExtensions = {
767
- isEqual(oldState, newState) {
768
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.deltaY === newState.deltaY && oldState.focusedIndex === newState.focusedIndex;
769
- },
770
- apply(oldState, newState) {
771
- // TODO render extensions incrementally when scrolling
772
- const visibleExtensions = getVisible(newState);
773
- const dom = getExtensionsVirtualDom(visibleExtensions);
774
- return ['setExtensionsDom', dom];
468
+ class JsonRpcError extends Error {
469
+ constructor(message) {
470
+ super(message);
471
+ this.name = 'JsonRpcError';
775
472
  }
776
- };
777
- const renderScrollBar = {
778
- isEqual(oldState, newState) {
779
- return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length && oldState.scrollBarActive === newState.scrollBarActive;
780
- },
781
- apply(oldState, newState) {
782
- // @ts-ignore
783
- const listHeight = getListHeight(newState);
784
- const total = newState.items.length;
785
- const contentHeight = total * newState.itemHeight;
786
- const scrollBarHeight = getScrollBarSize(listHeight, contentHeight, newState.minimumSliderSize);
787
- const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
788
- const roundedScrollBarY = Math.round(scrollBarY);
789
- const heightString = px(scrollBarHeight);
790
- const translateString = position(0, roundedScrollBarY);
791
- let className = ScrollBarThumb;
792
- if (newState.scrollBarActive) {
793
- className += ' ' + ScrollBarThumbActive;
473
+ }
474
+ const NewLine = '\n';
475
+ const DomException = 'DOMException';
476
+ const ReferenceError$1 = 'ReferenceError';
477
+ const SyntaxError$1 = 'SyntaxError';
478
+ const TypeError$1 = 'TypeError';
479
+ const getErrorConstructor = (message, type) => {
480
+ if (type) {
481
+ switch (type) {
482
+ case DomException:
483
+ return DOMException;
484
+ case TypeError$1:
485
+ return TypeError;
486
+ case SyntaxError$1:
487
+ return SyntaxError;
488
+ case ReferenceError$1:
489
+ return ReferenceError;
490
+ default:
491
+ return Error;
794
492
  }
795
- return [/* method */SetScrollBar, translateString, heightString, className];
796
493
  }
797
- };
798
- const renderMessage = {
799
- isEqual(oldState, newState) {
800
- return oldState.message === newState.message;
801
- },
802
- apply(oldState, newState) {
803
- return [/* method */SetMessage, /* message */newState.message];
494
+ if (message.startsWith('TypeError: ')) {
495
+ return TypeError;
804
496
  }
805
- };
806
- const renderSearchValue = {
807
- isEqual(oldState, newState) {
808
- return oldState.searchValue === newState.searchValue;
809
- },
810
- apply(oldState, newState) {
811
- return [/* method */SetSearchValue, oldState.searchValue, newState.searchValue];
497
+ if (message.startsWith('SyntaxError: ')) {
498
+ return SyntaxError;
812
499
  }
813
- };
814
- const renderHeader = {
815
- isEqual(oldState, newState) {
816
- return oldState.placeholder === newState.placeholder;
817
- },
818
- apply(oldState, newState) {
819
- const actions = [{
820
- type: Button,
821
- title: clearExtensionSearchResults(),
822
- icon: `MaskIcon${ClearAll}`,
823
- command: 'Extensions.clearSearchResults'
824
- }, {
825
- type: Button,
826
- title: filter(),
827
- icon: `MaskIcon${Filter}`
828
- }];
829
- const dom = getExtensionHeaderVirtualDom(newState.placeholder, actions);
830
- return ['setHeaderDom', dom];
500
+ if (message.startsWith('ReferenceError: ')) {
501
+ return ReferenceError;
831
502
  }
503
+ return Error;
832
504
  };
833
- const render = [renderScrollBar, renderMessage, renderExtensions, renderSearchValue, renderHeader];
834
- const doRender = (oldState, newState) => {
835
- const commands = [];
836
- for (const item of render) {
837
- if (!item.isEqual(oldState, newState)) {
838
- commands.push(item.apply(oldState, newState));
505
+ const constructError = (message, type, name) => {
506
+ const ErrorConstructor = getErrorConstructor(message, type);
507
+ if (ErrorConstructor === DOMException && name) {
508
+ return new ErrorConstructor(message, name);
509
+ }
510
+ if (ErrorConstructor === Error) {
511
+ const error = new Error(message);
512
+ if (name && name !== 'VError') {
513
+ error.name = name;
839
514
  }
515
+ return error;
840
516
  }
841
- return commands;
517
+ return new ErrorConstructor(message);
842
518
  };
843
-
844
- const commandMap = {
845
- 'SearchExtensions.clearSearchResults': clearSearchResults,
846
- 'SearchExtensions.getKeyBindings': getKeyBindings,
847
- 'SearchExtensions.render': doRender,
848
- 'SearchExtensions.searchExtensions': searchExtensions
519
+ const getNewLineIndex = (string, startIndex = undefined) => {
520
+ return string.indexOf(NewLine, startIndex);
849
521
  };
850
-
851
- const Two = '2.0';
852
- const state = {
853
- callbacks: Object.create(null)
522
+ const getParentStack = error => {
523
+ let parentStack = error.stack || error.data || error.message || '';
524
+ if (parentStack.startsWith(' at')) {
525
+ parentStack = error.message + NewLine + parentStack;
526
+ }
527
+ return parentStack;
854
528
  };
855
- const get = id => {
856
- return state.callbacks[id];
529
+ const joinLines = lines => {
530
+ return lines.join(NewLine);
857
531
  };
858
- const remove = id => {
859
- delete state.callbacks[id];
532
+ const MethodNotFound = -32601;
533
+ const Custom = -32001;
534
+ const splitLines = lines => {
535
+ return lines.split(NewLine);
536
+ };
537
+ const restoreJsonRpcError = error => {
538
+ if (error && error instanceof Error) {
539
+ return error;
540
+ }
541
+ const currentStack = joinLines(splitLines(new Error().stack || '').slice(1));
542
+ if (error && error.code && error.code === MethodNotFound) {
543
+ const restoredError = new JsonRpcError(error.message);
544
+ const parentStack = getParentStack(error);
545
+ restoredError.stack = parentStack + NewLine + currentStack;
546
+ return restoredError;
547
+ }
548
+ if (error && error.message) {
549
+ const restoredError = constructError(error.message, error.type, error.name);
550
+ if (error.data) {
551
+ if (error.data.stack && error.data.type && error.message) {
552
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
553
+ } else if (error.data.stack) {
554
+ restoredError.stack = error.data.stack;
555
+ }
556
+ if (error.data.codeFrame) {
557
+ // @ts-ignore
558
+ restoredError.codeFrame = error.data.codeFrame;
559
+ }
560
+ if (error.data.code) {
561
+ // @ts-ignore
562
+ restoredError.code = error.data.code;
563
+ }
564
+ if (error.data.type) {
565
+ // @ts-ignore
566
+ restoredError.name = error.data.type;
567
+ }
568
+ } else {
569
+ if (error.stack) {
570
+ const lowerStack = restoredError.stack || '';
571
+ // @ts-ignore
572
+ const indexNewLine = getNewLineIndex(lowerStack);
573
+ const parentStack = getParentStack(error);
574
+ // @ts-ignore
575
+ restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
576
+ }
577
+ if (error.codeFrame) {
578
+ // @ts-ignore
579
+ restoredError.codeFrame = error.codeFrame;
580
+ }
581
+ }
582
+ return restoredError;
583
+ }
584
+ if (typeof error === 'string') {
585
+ return new Error(`JsonRpc Error: ${error}`);
586
+ }
587
+ return new Error(`JsonRpc Error: ${error}`);
588
+ };
589
+ const unwrapJsonRpcResult = responseMessage => {
590
+ if ('error' in responseMessage) {
591
+ const restoredError = restoreJsonRpcError(responseMessage.error);
592
+ throw restoredError;
593
+ }
594
+ if ('result' in responseMessage) {
595
+ return responseMessage.result;
596
+ }
597
+ throw new JsonRpcError('unexpected response message');
860
598
  };
861
599
  const warn = (...args) => {
862
600
  console.warn(...args);
@@ -866,19 +604,11 @@ const resolve = (id, response) => {
866
604
  if (!fn) {
867
605
  console.log(response);
868
606
  warn(`callback ${id} may already be disposed`);
869
- return;
870
- }
871
- fn(response);
872
- remove(id);
873
- };
874
- class JsonRpcError extends Error {
875
- constructor(message) {
876
- super(message);
877
- this.name = 'JsonRpcError';
607
+ return;
878
608
  }
879
- }
880
- const MethodNotFound = -32601;
881
- const Custom = -32001;
609
+ fn(response);
610
+ remove(id);
611
+ };
882
612
  const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
883
613
  const getErrorType = prettyError => {
884
614
  if (prettyError && prettyError.type) {
@@ -922,7 +652,7 @@ const getErrorResponse = (message, error, preparePrettyError, logError) => {
922
652
  const errorProperty = getErrorProperty(error, prettyError);
923
653
  return create$1(message, errorProperty);
924
654
  };
925
- const create = (message, result) => {
655
+ const create$5 = (message, result) => {
926
656
  return {
927
657
  jsonrpc: Two,
928
658
  id: message.id,
@@ -931,7 +661,7 @@ const create = (message, result) => {
931
661
  };
932
662
  const getSuccessResponse = (message, result) => {
933
663
  const resultProperty = result ?? null;
934
- return create(message, resultProperty);
664
+ return create$5(message, resultProperty);
935
665
  };
936
666
  const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
937
667
  try {
@@ -1007,458 +737,851 @@ const handleJsonRpcMessage = async (...args) => {
1007
737
  }
1008
738
  throw new JsonRpcError('unexpected message');
1009
739
  };
740
+ const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
741
+ const {
742
+ message,
743
+ promise
744
+ } = create$2(method, params);
745
+ if (useSendAndTransfer && ipc.sendAndTransfer) {
746
+ ipc.sendAndTransfer(message);
747
+ } else {
748
+ ipc.send(message);
749
+ }
750
+ const responseMessage = await promise;
751
+ return unwrapJsonRpcResult(responseMessage);
752
+ };
753
+ const send = (transport, method, ...params) => {
754
+ const message = create$4(method, params);
755
+ transport.send(message);
756
+ };
757
+ const invoke = (ipc, method, ...params) => {
758
+ return invokeHelper(ipc, method, params, false);
759
+ };
760
+ const invokeAndTransfer = (ipc, method, ...params) => {
761
+ return invokeHelper(ipc, method, params, true);
762
+ };
763
+
764
+ const commands = Object.create(null);
765
+ const register = commandMap => {
766
+ Object.assign(commands, commandMap);
767
+ };
768
+ const getCommand = key => {
769
+ return commands[key];
770
+ };
771
+ const execute = (command, ...args) => {
772
+ const fn = getCommand(command);
773
+ if (!fn) {
774
+ throw new Error(`command not found ${command}`);
775
+ }
776
+ return fn(...args);
777
+ };
1010
778
 
779
+ const createRpc = ipc => {
780
+ const rpc = {
781
+ // @ts-ignore
782
+ ipc,
783
+ /**
784
+ * @deprecated
785
+ */
786
+ send(method, ...params) {
787
+ send(ipc, method, ...params);
788
+ },
789
+ invoke(method, ...params) {
790
+ return invoke(ipc, method, ...params);
791
+ },
792
+ invokeAndTransfer(method, ...params) {
793
+ return invokeAndTransfer(ipc, method, ...params);
794
+ }
795
+ };
796
+ return rpc;
797
+ };
1011
798
  const requiresSocket = () => {
1012
799
  return false;
1013
800
  };
1014
801
  const preparePrettyError = error => {
1015
802
  return error;
1016
803
  };
1017
- const logError = error => {
804
+ const logError = () => {
1018
805
  // handled by renderer worker
1019
806
  };
1020
807
  const handleMessage = event => {
1021
- return handleJsonRpcMessage(event.target, event.data, execute, resolve, preparePrettyError, logError, requiresSocket);
808
+ const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
809
+ const actualExecute = event?.target?.execute || execute;
810
+ return handleJsonRpcMessage(event.target, event.data, actualExecute, resolve, preparePrettyError, logError, actualRequiresSocket);
1022
811
  };
1023
-
1024
812
  const handleIpc = ipc => {
1025
- ipc.addEventListener('message', handleMessage);
813
+ if ('addEventListener' in ipc) {
814
+ ipc.addEventListener('message', handleMessage);
815
+ } else if ('on' in ipc) {
816
+ // deprecated
817
+ ipc.on('message', handleMessage);
818
+ }
819
+ };
820
+ const listen$1 = async (module, options) => {
821
+ const rawIpc = await module.listen(options);
822
+ if (module.signal) {
823
+ module.signal(rawIpc);
824
+ }
825
+ const ipc = module.wrap(rawIpc);
826
+ return ipc;
827
+ };
828
+ const create = async ({
829
+ commandMap
830
+ }) => {
831
+ // TODO create a commandMap per rpc instance
832
+ register(commandMap);
833
+ const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
834
+ handleIpc(ipc);
835
+ const rpc = createRpc(ipc);
836
+ return rpc;
837
+ };
838
+ const WebWorkerRpcClient = {
839
+ __proto__: null,
840
+ create
1026
841
  };
1027
842
 
1028
- const MessagePort$1 = 1;
1029
- const ModuleWorker = 2;
1030
- const ReferencePort = 3;
1031
- const ModuleWorkerAndMessagePort = 8;
1032
- const Auto = () => {
1033
- // @ts-ignore
1034
- if (globalThis.acceptPort) {
1035
- return MessagePort$1;
843
+ const handleError = (error, notify = true, prefix = '') => {
844
+ console.error(error);
845
+ };
846
+
847
+ const emptyObject = {};
848
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
849
+ const i18nString = (key, placeholders = emptyObject) => {
850
+ if (placeholders === emptyObject) {
851
+ return key;
852
+ }
853
+ const replacer = (match, rest) => {
854
+ return placeholders[rest];
855
+ };
856
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
857
+ };
858
+
859
+ /**
860
+ * @enum {string}
861
+ */
862
+ const UiStrings = {
863
+ NoExtensionsFound: 'No extensions found.',
864
+ Filter: 'Filter',
865
+ ClearExtensionSearchResults: 'Clear extension search results',
866
+ SearchExtensionsInMarketplace: 'Search Extensions in Marketplace',
867
+ Extensions: 'Extensions'};
868
+ const noExtensionsFound = () => {
869
+ return i18nString(UiStrings.NoExtensionsFound);
870
+ };
871
+ const filter = () => {
872
+ return i18nString(UiStrings.Filter);
873
+ };
874
+ const extensions = () => {
875
+ return i18nString(UiStrings.Extensions);
876
+ };
877
+ const clearExtensionSearchResults = () => {
878
+ return i18nString(UiStrings.ClearExtensionSearchResults);
879
+ };
880
+ const searchExtensionsInMarketPlace = () => {
881
+ return i18nString(UiStrings.SearchExtensionsInMarketplace);
882
+ };
883
+
884
+ const getFinalDeltaY = (height, itemHeight, itemsLength) => {
885
+ const contentHeight = itemsLength * itemHeight;
886
+ const finalDeltaY = Math.max(contentHeight - height, 0);
887
+ return finalDeltaY;
888
+ };
889
+
890
+ const getListHeight$1 = (itemsLength, itemHeight, maxHeight) => {
891
+ number(itemsLength);
892
+ number(itemHeight);
893
+ number(maxHeight);
894
+ if (itemsLength === 0) {
895
+ return itemHeight;
1036
896
  }
897
+ const totalHeight = itemsLength * itemHeight;
898
+ return Math.min(totalHeight, maxHeight);
899
+ };
900
+
901
+ // TODO optimize this function to return the minimum number
902
+ // of visible items needed, e.g. when not scrolled 5 items with
903
+ // 20px fill 100px but when scrolled 6 items are needed
904
+ const getNumberOfVisibleItems = (listHeight, itemHeight) => {
905
+ return Math.ceil(listHeight / itemHeight) + 1;
906
+ };
907
+
908
+ const Web = 1;
909
+ const Electron = 2;
910
+ const Remote = 3;
911
+ const Test = 4;
912
+
913
+ // TODO treeshake this function out
914
+
915
+ /**
916
+ * @returns {number}
917
+ */
918
+ const getPlatform = () => {
1037
919
  // @ts-ignore
1038
- if (globalThis.acceptReferencePort) {
1039
- return ReferencePort;
920
+ if (typeof PLATFORM !== 'undefined') {
921
+ // @ts-ignore
922
+ return PLATFORM;
923
+ }
924
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
925
+ return Test;
926
+ }
927
+ // TODO find a better way to pass runtime environment
928
+ if (typeof name !== 'undefined' && name.endsWith('(Electron)')) {
929
+ return Electron;
930
+ }
931
+ if (typeof name !== 'undefined' && name.endsWith('(Web)')) {
932
+ return Web;
1040
933
  }
1041
- return ModuleWorkerAndMessagePort;
934
+ return Remote;
1042
935
  };
936
+ const platform = getPlatform();
1043
937
 
1044
- const getData$1 = event => {
1045
- return event.data;
938
+ const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
939
+ if (size >= contentSize) {
940
+ return 0;
941
+ }
942
+ return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
1046
943
  };
1047
- const attachEvents = that => {
1048
- const handleMessage = (...args) => {
1049
- const data = that.getData(...args);
1050
- that.dispatchEvent(new MessageEvent('message', {
1051
- data
1052
- }));
1053
- };
1054
- that.onMessage(handleMessage);
1055
- const handleClose = event => {
1056
- that.dispatchEvent(new Event('close'));
944
+
945
+ const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
946
+ const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
947
+ return scrollBarOffset;
948
+ };
949
+ const getScrollBarY = getScrollBarOffset;
950
+
951
+ const Installed = '@installed';
952
+ const Enabled = '@enabled';
953
+ const Disabled = '@disabled';
954
+ const Builtin = '@builtin';
955
+ const Sort = '@sort';
956
+ const Id = '@id';
957
+ const Outdated = '@outdated';
958
+
959
+ const RE_PARAM = /@\w+/g;
960
+
961
+ // TODO test sorting and filtering
962
+ const parseValue = value => {
963
+ const parameters = Object.create(null);
964
+ // TODO this is not very functional code (assignment)
965
+ const replaced = value.replaceAll(RE_PARAM, (match, by, order) => {
966
+ if (match.startsWith(Installed)) {
967
+ parameters.installed = true;
968
+ }
969
+ if (match.startsWith(Enabled)) {
970
+ parameters.enabled = true;
971
+ }
972
+ if (match.startsWith(Disabled)) {
973
+ parameters.disabled = true;
974
+ }
975
+ if (match.startsWith(Builtin)) {
976
+ parameters.builtin = true;
977
+ }
978
+ if (match.startsWith(Sort)) {
979
+ // TODO
980
+ parameters.sort = 'installs';
981
+ }
982
+ if (match.startsWith(Id)) {
983
+ // TODO
984
+ parameters.id = 'abc';
985
+ }
986
+ if (match.startsWith(Outdated)) {
987
+ parameters.outdated = true;
988
+ }
989
+ return '';
990
+ });
991
+ const isLocal = parameters.enabled || parameters.builtin || parameters.disabled || parameters.outdated || parameters.installed;
992
+ return {
993
+ query: replaced,
994
+ ...parameters,
995
+ isLocal
1057
996
  };
1058
- that.onClose(handleClose);
1059
997
  };
1060
- class Ipc extends EventTarget {
1061
- constructor(rawIpc) {
1062
- super();
1063
- this._rawIpc = rawIpc;
1064
- attachEvents(this);
998
+
999
+ const assetDir = '';
1000
+
1001
+ const getRemoteUrl = (extension, platform$1) => {
1002
+ if (platform === Remote || platform === Electron) {
1003
+ if (extension.builtin) {
1004
+ return `${assetDir}/extensions/${extension.id}/${extension.icon}`;
1005
+ }
1006
+ return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
1007
+ }
1008
+ return '';
1009
+ };
1010
+
1011
+ const ExtensionDefaultIcon = `${assetDir}/icons/extensionDefaultIcon.png`;
1012
+ const ExtensionLanguageBasics = `${assetDir}/icons/language-icon.svg`;
1013
+ const ExtensionTheme = `${assetDir}/icons/theme-icon.png`;
1014
+
1015
+ const isLanguageBasicsExtension = extension => {
1016
+ return extension.name && extension.name.startsWith('Language Basics');
1017
+ };
1018
+ const isThemeExtension = extension => {
1019
+ return extension.name && extension.name.endsWith(' Theme');
1020
+ };
1021
+ const getIcon = (extension, platform) => {
1022
+ if (!extension) {
1023
+ return ExtensionDefaultIcon;
1024
+ }
1025
+ if (!extension.path || !extension.icon) {
1026
+ if (isLanguageBasicsExtension(extension)) {
1027
+ return ExtensionLanguageBasics;
1028
+ }
1029
+ if (isThemeExtension(extension)) {
1030
+ return ExtensionTheme;
1031
+ }
1032
+ return ExtensionDefaultIcon;
1033
+ }
1034
+ return getRemoteUrl(extension);
1035
+ };
1036
+ const RE_PUBLISHER = /^[a-z\d-]+/;
1037
+
1038
+ // TODO handle case when extension is of type number|array|null|string
1039
+ const getPublisher = extension => {
1040
+ if (!extension || !extension.id) {
1041
+ return 'n/a';
1042
+ }
1043
+ // TODO handle case when id is not of type string -> should not crash application
1044
+ const match = extension.id.match(RE_PUBLISHER);
1045
+ if (!match) {
1046
+ return 'n/a';
1047
+ }
1048
+ return match[0];
1049
+ };
1050
+ const getName = extension => {
1051
+ if (extension && extension.name) {
1052
+ return extension.name;
1065
1053
  }
1066
- }
1067
- const readyMessage = 'ready';
1068
- const walkValue = (value, transferrables, isTransferrable) => {
1069
- if (!value) {
1070
- return;
1054
+ if (extension && extension.id) {
1055
+ return extension.id;
1071
1056
  }
1072
- if (isTransferrable(value)) {
1073
- transferrables.push(value);
1074
- return;
1057
+ return 'n/a';
1058
+ };
1059
+ const getDescription = extension => {
1060
+ if (!extension || !extension.description) {
1061
+ return 'n/a';
1075
1062
  }
1076
- if (Array.isArray(value)) {
1077
- for (const item of value) {
1078
- walkValue(item, transferrables, isTransferrable);
1079
- }
1080
- return;
1063
+ return extension.description;
1064
+ };
1065
+ const getId = extension => {
1066
+ if (!extension || !extension.id) {
1067
+ return 'n/a';
1081
1068
  }
1082
- if (typeof value === 'object') {
1083
- for (const property of Object.values(value)) {
1084
- walkValue(property, transferrables, isTransferrable);
1085
- }
1086
- return;
1069
+ return extension.id;
1070
+ };
1071
+
1072
+ const matchesParsedValue = (extension, parsedValue) => {
1073
+ if (extension && typeof extension.name === 'string') {
1074
+ const extensionNameLower = extension.name.toLowerCase();
1075
+ return extensionNameLower.includes(parsedValue.query);
1076
+ }
1077
+ if (extension && typeof extension.id === 'string') {
1078
+ const extensionIdLower = extension.id.toLowerCase();
1079
+ return extensionIdLower.includes(parsedValue.query);
1087
1080
  }
1081
+ return false;
1088
1082
  };
1089
- const isMessagePort = value => {
1090
- return value && value instanceof MessagePort;
1083
+
1084
+ const toSorted = (array, compare) => {
1085
+ return [...array].sort(compare);
1091
1086
  };
1092
- const isMessagePortMain = value => {
1093
- return value && value.constructor && value.constructor.name === 'MessagePortMain';
1087
+
1088
+ const compareExtension = (extensionA, extensionB) => {
1089
+ return extensionA.name.localeCompare(extensionB.name) || extensionA.id.localeCompare(extensionB.id);
1094
1090
  };
1095
- const isOffscreenCanvas = value => {
1096
- return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
1091
+
1092
+ const sortExtensions = extensions => {
1093
+ return toSorted(extensions, compareExtension);
1097
1094
  };
1098
- const isInstanceOf = (value, constructorName) => {
1099
- return value?.constructor?.name === constructorName;
1095
+
1096
+ const getExtensions = async (extensions, parsedValue, platform) => {
1097
+ const filteredExtensions = [];
1098
+ for (const extension of extensions) {
1099
+ if (matchesParsedValue(extension, parsedValue)) {
1100
+ filteredExtensions.push({
1101
+ name: getName(extension),
1102
+ id: getId(extension),
1103
+ publisher: getPublisher(extension),
1104
+ icon: getIcon(extension),
1105
+ description: getDescription(extension)
1106
+ });
1107
+ }
1108
+ }
1109
+ const sortedExtensions = sortExtensions(filteredExtensions);
1110
+ return sortedExtensions;
1100
1111
  };
1101
- const isSocket = value => {
1102
- return isInstanceOf(value, 'Socket');
1112
+
1113
+ const searchExtensions = async (extensions, value, platform$1) => {
1114
+ try {
1115
+ const parsedValue = parseValue(value);
1116
+ const filteredExtensions = await getExtensions(extensions, parsedValue, platform$1 || platform);
1117
+ return filteredExtensions;
1118
+ } catch (error) {
1119
+ throw new VError(error, 'Failed to search for extensions');
1120
+ }
1103
1121
  };
1104
- const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
1105
- const isTransferrable = value => {
1106
- for (const fn of transferrables) {
1107
- if (fn(value)) {
1108
- return true;
1122
+
1123
+ // TODO debounce
1124
+ const handleInput = async (state, value) => {
1125
+ try {
1126
+ const {
1127
+ allExtensions,
1128
+ itemHeight,
1129
+ minimumSliderSize,
1130
+ height,
1131
+ platform: platform$1
1132
+ } = state;
1133
+ // TODO cancel ongoing requests
1134
+ // TODO handle errors
1135
+ const items = await searchExtensions(allExtensions, value, platform$1 || platform);
1136
+ if (items.length === 0) {
1137
+ return {
1138
+ ...state,
1139
+ items,
1140
+ minLineY: 0,
1141
+ deltaY: 0,
1142
+ allExtensions,
1143
+ maxLineY: 0,
1144
+ scrollBarHeight: 0,
1145
+ finalDeltaY: 0,
1146
+ message: noExtensionsFound(),
1147
+ searchValue: value,
1148
+ placeholder: searchExtensionsInMarketPlace()
1149
+ };
1109
1150
  }
1151
+ // @ts-ignore
1152
+ const listHeight = getListHeight$1(state);
1153
+ const total = items.length;
1154
+ const contentHeight = total * itemHeight;
1155
+ const scrollBarHeight = getScrollBarSize(height, contentHeight, minimumSliderSize);
1156
+ const numberOfVisible = getNumberOfVisibleItems(listHeight, itemHeight);
1157
+ const maxLineY = Math.min(numberOfVisible, total);
1158
+ const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, total);
1159
+ return {
1160
+ ...state,
1161
+ items,
1162
+ minLineY: 0,
1163
+ deltaY: 0,
1164
+ allExtensions,
1165
+ maxLineY,
1166
+ scrollBarHeight,
1167
+ finalDeltaY,
1168
+ message: '',
1169
+ searchValue: value,
1170
+ placeholder: searchExtensionsInMarketPlace()
1171
+ };
1172
+
1173
+ // TODO handle out of order responses (a bit complicated)
1174
+ // for now just assume everything comes back in order
1175
+ } catch (error) {
1176
+ handleError(error);
1177
+ return {
1178
+ ...state,
1179
+ searchValue: value,
1180
+ message: `${error}`
1181
+ };
1110
1182
  }
1111
- return false;
1112
1183
  };
1113
- const getTransferrables = value => {
1114
- const transferrables = [];
1115
- walkValue(value, transferrables, isTransferrable);
1116
- return transferrables;
1184
+
1185
+ const clearSearchResults = state => {
1186
+ return handleInput(state, '');
1117
1187
  };
1118
- const listen$2 = () => {
1119
- // @ts-ignore
1120
- if (typeof WorkerGlobalScope === 'undefined') {
1121
- throw new TypeError('module is not in web worker scope');
1122
- }
1123
- return globalThis;
1188
+
1189
+ const Enter = 3;
1190
+ const Space = 9;
1191
+ const PageUp = 10;
1192
+ const PageDown = 11;
1193
+ const End = 255;
1194
+ const Home = 12;
1195
+ const UpArrow = 14;
1196
+ const DownArrow = 16;
1197
+
1198
+ const CtrlCmd = 1 << 11 >>> 0;
1199
+
1200
+ const FocusExtensions = 15;
1201
+
1202
+ const getKeyBindings = () => {
1203
+ return [{
1204
+ key: Home,
1205
+ command: 'Extensions.focusFirst',
1206
+ when: FocusExtensions
1207
+ }, {
1208
+ key: End,
1209
+ command: 'Extensions.focusLast',
1210
+ when: FocusExtensions
1211
+ }, {
1212
+ key: PageUp,
1213
+ command: 'Extensions.focusPreviousPage',
1214
+ when: FocusExtensions
1215
+ }, {
1216
+ key: PageDown,
1217
+ command: 'Extensions.focusNextPage',
1218
+ when: FocusExtensions
1219
+ }, {
1220
+ key: UpArrow,
1221
+ command: 'Extensions.focusPrevious',
1222
+ when: FocusExtensions
1223
+ }, {
1224
+ key: DownArrow,
1225
+ command: 'Extensions.focusNext',
1226
+ when: FocusExtensions
1227
+ }, {
1228
+ key: Space,
1229
+ command: 'Extensions.handleClickCurrentButKeepFocus',
1230
+ when: FocusExtensions
1231
+ }, {
1232
+ key: Enter,
1233
+ command: 'Extensions.handleClickCurrent',
1234
+ when: FocusExtensions
1235
+ }, {
1236
+ key: CtrlCmd | Space,
1237
+ command: 'Extensions.toggleSuggest',
1238
+ when: FocusExtensions
1239
+ }, {
1240
+ key: CtrlCmd | DownArrow,
1241
+ command: 'Extensions.scrollDown',
1242
+ when: FocusExtensions
1243
+ }];
1124
1244
  };
1125
- const signal$2 = global => {
1126
- global.postMessage(readyMessage);
1245
+
1246
+ const Button = 1;
1247
+
1248
+ const ExtensionActions = 'ExtensionActions';
1249
+ const ExtensionActive = 'ExtensionActive';
1250
+ const ExtensionHeader = 'ExtensionHeader';
1251
+ const ExtensionListItem = 'ExtensionListItem';
1252
+ const ExtensionListItemAuthorName = 'ExtensionListItemAuthorName';
1253
+ const ExtensionListItemDescription = 'ExtensionListItemDescription';
1254
+ const ExtensionListItemDetail = 'ExtensionListItemDetail';
1255
+ const ExtensionListItemFooter = 'ExtensionListItemFooter';
1256
+ const ExtensionListItemIcon = 'ExtensionListItemIcon';
1257
+ const ExtensionListItemName = 'ExtensionListItemName';
1258
+ const ListItems = 'ListItems';
1259
+ const MultilineInputBox = 'MultilineInputBox';
1260
+ const ScrollBarThumb = 'ScrollBarThumb';
1261
+ const ScrollBarThumbActive = 'ScrollBarThumbActive';
1262
+ const SearchField = 'SearchField';
1263
+ const SearchFieldButtons = 'SearchFieldButtons';
1264
+ const SearchFieldContainer = 'SearchFieldContainer';
1265
+
1266
+ const CheckBox = 'checkbox';
1267
+ const List = 'list';
1268
+ const ListItem = 'listitem';
1269
+ const None = 'none';
1270
+
1271
+ const Div = 4;
1272
+ const Text = 12;
1273
+ const Img = 17;
1274
+ const TextArea = 62;
1275
+
1276
+ const getSearchFieldButtonVirtualDom = button => {
1277
+ const {
1278
+ icon,
1279
+ checked,
1280
+ title
1281
+ } = button;
1282
+ return [{
1283
+ type: Div,
1284
+ className: `SearchFieldButton ${checked ? 'SearchFieldButtonChecked' : ''}`,
1285
+ title,
1286
+ role: CheckBox,
1287
+ ariaChecked: checked,
1288
+ tabIndex: 0,
1289
+ childCount: 1
1290
+ }, {
1291
+ type: Div,
1292
+ className: `MaskIcon ${icon}`,
1293
+ childCount: 0
1294
+ }];
1127
1295
  };
1128
- class IpcChildWithModuleWorker extends Ipc {
1129
- getData(event) {
1130
- return getData$1(event);
1131
- }
1132
- send(message) {
1133
- // @ts-ignore
1134
- this._rawIpc.postMessage(message);
1135
- }
1136
- sendAndTransfer(message) {
1137
- const transfer = getTransferrables(message);
1138
- // @ts-ignore
1139
- this._rawIpc.postMessage(message, transfer);
1140
- }
1141
- dispose() {
1142
- // ignore
1143
- }
1144
- onClose(callback) {
1145
- // ignore
1146
- }
1147
- onMessage(callback) {
1148
- this._rawIpc.addEventListener('message', callback);
1296
+
1297
+ const getSearchFieldVirtualDom = (name, placeholder, onInput, insideButtons, outsideButtons, onFocus = '') => {
1298
+ const dom = [{
1299
+ type: Div,
1300
+ className: SearchField,
1301
+ role: None,
1302
+ childCount: 2
1303
+ }, {
1304
+ type: TextArea,
1305
+ className: MultilineInputBox,
1306
+ spellcheck: false,
1307
+ autocapitalize: 'off',
1308
+ autocorrect: 'off',
1309
+ placeholder,
1310
+ name,
1311
+ onInput,
1312
+ onFocus,
1313
+ childCount: 0
1314
+ }, {
1315
+ type: Div,
1316
+ className: SearchFieldButtons,
1317
+ childCount: insideButtons.length
1318
+ }, ...insideButtons.flatMap(getSearchFieldButtonVirtualDom)];
1319
+ if (outsideButtons.length > 0) {
1320
+ dom.unshift({
1321
+ type: Div,
1322
+ className: SearchFieldContainer,
1323
+ role: None,
1324
+ childCount: 1 + outsideButtons.length
1325
+ });
1326
+ dom.push(...outsideButtons.flatMap(getSearchFieldButtonVirtualDom));
1149
1327
  }
1150
- }
1151
- const wrap$5 = global => {
1152
- return new IpcChildWithModuleWorker(global);
1153
- };
1154
- const IpcChildWithModuleWorker$1 = {
1155
- __proto__: null,
1156
- listen: listen$2,
1157
- signal: signal$2,
1158
- wrap: wrap$5
1159
- };
1160
- const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
1161
- const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
1162
- const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
1163
- const NewLine$1 = '\n';
1164
- const joinLines = lines => {
1165
- return lines.join(NewLine$1);
1166
- };
1167
- const splitLines = lines => {
1168
- return lines.split(NewLine$1);
1328
+ return dom;
1169
1329
  };
1170
- const isModuleNotFoundMessage = line => {
1171
- return line.includes('[ERR_MODULE_NOT_FOUND]');
1330
+
1331
+ const getExtensionHeaderVirtualDom = (placeholder, actions) => {
1332
+ return [{
1333
+ type: Div,
1334
+ className: ExtensionHeader,
1335
+ childCount: 1
1336
+ }, ...getSearchFieldVirtualDom('extensions', placeholder, 'handleExtensionsInput', actions, [])];
1172
1337
  };
1173
- const getModuleNotFoundError = stderr => {
1174
- const lines = splitLines(stderr);
1175
- const messageIndex = lines.findIndex(isModuleNotFoundMessage);
1176
- const message = lines[messageIndex];
1338
+
1339
+ const HandleContextMenu = 'handleContextMenu';
1340
+ const HandlePointerDown = 'handlePointerDown';
1341
+ const HandleTouchStart = 'handleTouchStart';
1342
+ const HandleWheel = 'handleWheel';
1343
+
1344
+ const Extension = 'Extension';
1345
+
1346
+ const text = data => {
1177
1347
  return {
1178
- message,
1179
- code: ERR_MODULE_NOT_FOUND
1348
+ type: Text,
1349
+ text: data,
1350
+ childCount: 0
1180
1351
  };
1181
1352
  };
1182
- const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
1183
- const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
1184
- const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
1185
- const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
1186
- const RE_AT = /^\s+at/;
1187
- const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
1188
- const isUnhelpfulNativeModuleError = stderr => {
1189
- return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
1190
- };
1191
- const isMessageCodeBlockStartIndex = line => {
1192
- return RE_MESSAGE_CODE_BLOCK_START.test(line);
1193
- };
1194
- const isMessageCodeBlockEndIndex = line => {
1195
- return RE_MESSAGE_CODE_BLOCK_END.test(line);
1353
+
1354
+ const listItemDetail = {
1355
+ type: Div,
1356
+ className: ExtensionListItemDetail,
1357
+ childCount: 3
1196
1358
  };
1197
- const getMessageCodeBlock = stderr => {
1198
- const lines = splitLines(stderr);
1199
- const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
1200
- const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
1201
- const relevantLines = lines.slice(startIndex, endIndex);
1202
- const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
1203
- return relevantMessage;
1359
+ const listItemName = {
1360
+ type: Div,
1361
+ className: ExtensionListItemName,
1362
+ childCount: 1
1204
1363
  };
1205
- const getNativeModuleErrorMessage = stderr => {
1206
- const message = getMessageCodeBlock(stderr);
1207
- return {
1208
- message: `Incompatible native node module: ${message}`,
1209
- code: E_INCOMPATIBLE_NATIVE_MODULE
1210
- };
1364
+ const listItemDescription = {
1365
+ type: Div,
1366
+ className: ExtensionListItemDescription,
1367
+ childCount: 1
1211
1368
  };
1212
- const isModulesSyntaxError = stderr => {
1213
- if (!stderr) {
1214
- return false;
1215
- }
1216
- return stderr.includes('SyntaxError: Cannot use import statement outside a module');
1369
+ const listItemFooter = {
1370
+ type: Div,
1371
+ className: ExtensionListItemFooter,
1372
+ childCount: 2
1217
1373
  };
1218
- const getModuleSyntaxError = () => {
1219
- return {
1220
- message: `ES Modules are not supported in electron`,
1221
- code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
1222
- };
1374
+ const listItemAuthorName = {
1375
+ type: Div,
1376
+ className: ExtensionListItemAuthorName,
1377
+ childCount: 1
1223
1378
  };
1224
- const isModuleNotFoundError = stderr => {
1225
- if (!stderr) {
1226
- return false;
1379
+ const getExtensionListItemVirtualDom = extension => {
1380
+ const {
1381
+ posInSet,
1382
+ setSize,
1383
+ top,
1384
+ icon,
1385
+ name,
1386
+ description,
1387
+ publisher,
1388
+ focused
1389
+ } = extension;
1390
+ const dom = [{
1391
+ type: Div,
1392
+ role: ListItem,
1393
+ ariaRoleDescription: Extension,
1394
+ className: ExtensionListItem,
1395
+ ariaPosInSet: posInSet,
1396
+ ariaSetSize: setSize,
1397
+ top,
1398
+ childCount: 2
1399
+ }, {
1400
+ type: Img,
1401
+ src: icon,
1402
+ className: ExtensionListItemIcon,
1403
+ role: None,
1404
+ childCount: 0
1405
+ }, listItemDetail, listItemName, text(name), listItemDescription, text(description), listItemFooter, listItemAuthorName, text(publisher), {
1406
+ type: Div,
1407
+ className: ExtensionActions,
1408
+ childCount: 0
1409
+ }];
1410
+ if (focused) {
1411
+ dom[0].id = 'ExtensionActive';
1412
+ dom[0].className += ' ' + ExtensionActive;
1227
1413
  }
1228
- return stderr.includes('ERR_MODULE_NOT_FOUND');
1414
+ return dom;
1229
1415
  };
1230
- const isNormalStackLine = line => {
1231
- return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
1416
+
1417
+ const getExtensionsListVirtualDom = visibleExtensions => {
1418
+ const dom = [{
1419
+ type: Div,
1420
+ className: ListItems,
1421
+ tabIndex: 0,
1422
+ ariaLabel: extensions(),
1423
+ role: List,
1424
+ oncontextmenu: HandleContextMenu,
1425
+ onpointerdown: HandlePointerDown,
1426
+ ontouchstart: HandleTouchStart,
1427
+ onwheelpassive: HandleWheel,
1428
+ childCount: visibleExtensions.length
1429
+ }, ...visibleExtensions.flatMap(getExtensionListItemVirtualDom)];
1430
+ return dom;
1232
1431
  };
1233
- const getDetails = lines => {
1234
- const index = lines.findIndex(isNormalStackLine);
1235
- if (index === -1) {
1236
- return {
1237
- actualMessage: joinLines(lines),
1238
- rest: []
1239
- };
1240
- }
1241
- let lastIndex = index - 1;
1242
- while (++lastIndex < lines.length) {
1243
- if (!isNormalStackLine(lines[lastIndex])) {
1244
- break;
1245
- }
1246
- }
1247
- return {
1248
- actualMessage: lines[index - 1],
1249
- rest: lines.slice(index, lastIndex)
1250
- };
1432
+
1433
+ const getExtensionsVirtualDom = visibleExtensions => {
1434
+ const dom = getExtensionsListVirtualDom(visibleExtensions);
1435
+ // TODO
1436
+ return dom;
1251
1437
  };
1252
- const getHelpfulChildProcessError = (stdout, stderr) => {
1253
- if (isUnhelpfulNativeModuleError(stderr)) {
1254
- return getNativeModuleErrorMessage(stderr);
1255
- }
1256
- if (isModulesSyntaxError(stderr)) {
1257
- return getModuleSyntaxError();
1258
- }
1259
- if (isModuleNotFoundError(stderr)) {
1260
- return getModuleNotFoundError(stderr);
1261
- }
1262
- const lines = splitLines(stderr);
1263
- const {
1264
- actualMessage,
1265
- rest
1266
- } = getDetails(lines);
1438
+
1439
+ const getVisibleItem = (item, setSize, itemHeight, minLineY, relative, i, focusedIndex) => {
1267
1440
  return {
1268
- message: `${actualMessage}`,
1269
- code: '',
1270
- stack: rest
1441
+ ...item,
1442
+ setSize,
1443
+ posInSet: i + 1,
1444
+ top: (i - minLineY) * itemHeight - relative,
1445
+ focused: i === focusedIndex
1271
1446
  };
1272
1447
  };
1273
- const normalizeLine = line => {
1274
- if (line.startsWith('Error: ')) {
1275
- return line.slice('Error: '.length);
1276
- }
1277
- if (line.startsWith('VError: ')) {
1278
- return line.slice('VError: '.length);
1448
+ const getVisible = state => {
1449
+ const {
1450
+ minLineY,
1451
+ maxLineY,
1452
+ items,
1453
+ itemHeight,
1454
+ deltaY,
1455
+ focusedIndex
1456
+ } = state;
1457
+ const setSize = items.length;
1458
+ const visible = [];
1459
+ const relative = deltaY % itemHeight;
1460
+ for (let i = minLineY; i < maxLineY; i++) {
1461
+ const item = items[i];
1462
+ visible.push(getVisibleItem(item, setSize, itemHeight, minLineY, relative, i, focusedIndex));
1279
1463
  }
1280
- return line;
1464
+ return visible;
1281
1465
  };
1282
- const getCombinedMessage = (error, message) => {
1283
- const stringifiedError = normalizeLine(`${error}`);
1284
- if (message) {
1285
- return `${message}: ${stringifiedError}`;
1286
- }
1287
- return stringifiedError;
1466
+
1467
+ const ClearAll = 'ClearAll';
1468
+ const Filter = 'Filter';
1469
+
1470
+ const px = value => {
1471
+ return `${value}px`;
1288
1472
  };
1289
- const NewLine = '\n';
1290
- const getNewLineIndex = (string, startIndex = undefined) => {
1291
- return string.indexOf(NewLine, startIndex);
1473
+ const position = (x, y) => {
1474
+ return `${x}px ${y}px`;
1292
1475
  };
1293
- const mergeStacks = (parent, child) => {
1294
- if (!child) {
1295
- return parent;
1296
- }
1297
- const parentNewLineIndex = getNewLineIndex(parent);
1298
- const childNewLineIndex = getNewLineIndex(child);
1299
- if (childNewLineIndex === -1) {
1300
- return parent;
1301
- }
1302
- const parentFirstLine = parent.slice(0, parentNewLineIndex);
1303
- const childRest = child.slice(childNewLineIndex);
1304
- const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
1305
- if (parentFirstLine.includes(childFirstLine)) {
1306
- return parentFirstLine + childRest;
1307
- }
1308
- return child;
1476
+
1477
+ const SetMessage = 'setMessage';
1478
+ const SetScrollBar = 'setScrollBar';
1479
+ const SetSearchValue = 'setSearchValue';
1480
+
1481
+ const getListHeight = state => {
1482
+ const {
1483
+ height,
1484
+ headerHeight
1485
+ } = state;
1486
+ return height - headerHeight;
1309
1487
  };
1310
- class VError extends Error {
1311
- constructor(error, message) {
1312
- const combinedMessage = getCombinedMessage(error, message);
1313
- super(combinedMessage);
1314
- this.name = 'VError';
1315
- if (error instanceof Error) {
1316
- this.stack = mergeStacks(this.stack, error.stack);
1317
- }
1318
- if (error.codeFrame) {
1319
- // @ts-ignore
1320
- this.codeFrame = error.codeFrame;
1321
- }
1322
- if (error.code) {
1323
- // @ts-ignore
1324
- this.code = error.code;
1325
- }
1488
+ const renderExtensions = {
1489
+ isEqual(oldState, newState) {
1490
+ return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.deltaY === newState.deltaY && oldState.focusedIndex === newState.focusedIndex;
1491
+ },
1492
+ apply(oldState, newState) {
1493
+ // TODO render extensions incrementally when scrolling
1494
+ const visibleExtensions = getVisible(newState);
1495
+ const dom = getExtensionsVirtualDom(visibleExtensions);
1496
+ return ['setExtensionsDom', dom];
1326
1497
  }
1327
- }
1328
- class IpcError extends VError {
1329
- // @ts-ignore
1330
- constructor(betterMessage, stdout = '', stderr = '') {
1331
- if (stdout || stderr) {
1332
- // @ts-ignore
1333
- const {
1334
- message,
1335
- code,
1336
- stack
1337
- } = getHelpfulChildProcessError(stdout, stderr);
1338
- const cause = new Error(message);
1339
- // @ts-ignore
1340
- cause.code = code;
1341
- cause.stack = stack;
1342
- super(cause, betterMessage);
1343
- } else {
1344
- super(betterMessage);
1345
- }
1346
- // @ts-ignore
1347
- this.name = 'IpcError';
1348
- // @ts-ignore
1349
- this.stdout = stdout;
1498
+ };
1499
+ const renderScrollBar = {
1500
+ isEqual(oldState, newState) {
1501
+ return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length && oldState.scrollBarActive === newState.scrollBarActive;
1502
+ },
1503
+ apply(oldState, newState) {
1350
1504
  // @ts-ignore
1351
- this.stderr = stderr;
1505
+ const listHeight = getListHeight(newState);
1506
+ const total = newState.items.length;
1507
+ const contentHeight = total * newState.itemHeight;
1508
+ const scrollBarHeight = getScrollBarSize(listHeight, contentHeight, newState.minimumSliderSize);
1509
+ const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
1510
+ const roundedScrollBarY = Math.round(scrollBarY);
1511
+ const heightString = px(scrollBarHeight);
1512
+ const translateString = position(0, roundedScrollBarY);
1513
+ let className = ScrollBarThumb;
1514
+ if (newState.scrollBarActive) {
1515
+ className += ' ' + ScrollBarThumbActive;
1516
+ }
1517
+ return [/* method */SetScrollBar, translateString, heightString, className];
1352
1518
  }
1353
- }
1354
- const withResolvers = () => {
1355
- let _resolve;
1356
- const promise = new Promise(resolve => {
1357
- _resolve = resolve;
1358
- });
1359
- return {
1360
- resolve: _resolve,
1361
- promise
1362
- };
1363
- };
1364
- const waitForFirstMessage = async port => {
1365
- const {
1366
- resolve,
1367
- promise
1368
- } = withResolvers();
1369
- port.addEventListener('message', resolve, {
1370
- once: true
1371
- });
1372
- const event = await promise;
1373
- // @ts-ignore
1374
- return event.data;
1375
1519
  };
1376
- const listen$1$1 = async () => {
1377
- const parentIpcRaw = listen$2();
1378
- signal$2(parentIpcRaw);
1379
- const parentIpc = wrap$5(parentIpcRaw);
1380
- const firstMessage = await waitForFirstMessage(parentIpc);
1381
- if (firstMessage.method !== 'initialize') {
1382
- throw new IpcError('unexpected first message');
1383
- }
1384
- const type = firstMessage.params[0];
1385
- if (type === 'message-port') {
1386
- parentIpc.send({
1387
- jsonrpc: '2.0',
1388
- id: firstMessage.id,
1389
- result: null
1390
- });
1391
- parentIpc.dispose();
1392
- const port = firstMessage.params[1];
1393
- return port;
1520
+ const renderMessage = {
1521
+ isEqual(oldState, newState) {
1522
+ return oldState.message === newState.message;
1523
+ },
1524
+ apply(oldState, newState) {
1525
+ return [/* method */SetMessage, /* message */newState.message];
1394
1526
  }
1395
- return globalThis;
1396
1527
  };
1397
- class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
1398
- constructor(port) {
1399
- super(port);
1400
- }
1401
- getData(event) {
1402
- return getData$1(event);
1403
- }
1404
- send(message) {
1405
- this._rawIpc.postMessage(message);
1528
+ const renderSearchValue = {
1529
+ isEqual(oldState, newState) {
1530
+ return oldState.searchValue === newState.searchValue;
1531
+ },
1532
+ apply(oldState, newState) {
1533
+ return [/* method */SetSearchValue, oldState.searchValue, newState.searchValue];
1406
1534
  }
1407
- sendAndTransfer(message) {
1408
- const transfer = getTransferrables(message);
1409
- this._rawIpc.postMessage(message, transfer);
1535
+ };
1536
+ const renderHeader = {
1537
+ isEqual(oldState, newState) {
1538
+ return oldState.placeholder === newState.placeholder;
1539
+ },
1540
+ apply(oldState, newState) {
1541
+ const actions = [{
1542
+ type: Button,
1543
+ title: clearExtensionSearchResults(),
1544
+ icon: `MaskIcon${ClearAll}`,
1545
+ command: 'Extensions.clearSearchResults'
1546
+ }, {
1547
+ type: Button,
1548
+ title: filter(),
1549
+ icon: `MaskIcon${Filter}`
1550
+ }];
1551
+ const dom = getExtensionHeaderVirtualDom(newState.placeholder, actions);
1552
+ return ['setHeaderDom', dom];
1410
1553
  }
1411
- dispose() {
1412
- if (this._rawIpc.close) {
1413
- this._rawIpc.close();
1554
+ };
1555
+ const render = [renderScrollBar, renderMessage, renderExtensions, renderSearchValue, renderHeader];
1556
+ const doRender = (oldState, newState) => {
1557
+ const commands = [];
1558
+ for (const item of render) {
1559
+ if (!item.isEqual(oldState, newState)) {
1560
+ commands.push(item.apply(oldState, newState));
1414
1561
  }
1415
1562
  }
1416
- onClose(callback) {
1417
- // ignore
1418
- }
1419
- onMessage(callback) {
1420
- this._rawIpc.addEventListener('message', callback);
1421
- this._rawIpc.start();
1422
- }
1423
- }
1424
- const wrap$4 = port => {
1425
- return new IpcChildWithModuleWorkerAndMessagePort(port);
1426
- };
1427
- const IpcChildWithModuleWorkerAndMessagePort$1 = {
1428
- __proto__: null,
1429
- listen: listen$1$1,
1430
- wrap: wrap$4
1563
+ return commands;
1431
1564
  };
1432
1565
 
1433
- const getModule = method => {
1434
- switch (method) {
1435
- case ModuleWorker:
1436
- return IpcChildWithModuleWorker$1;
1437
- case ModuleWorkerAndMessagePort:
1438
- return IpcChildWithModuleWorkerAndMessagePort$1;
1439
- default:
1440
- throw new Error('unexpected ipc type');
1441
- }
1566
+ const commandMap = {
1567
+ 'SearchExtensions.clearSearchResults': clearSearchResults,
1568
+ 'SearchExtensions.getKeyBindings': getKeyBindings,
1569
+ 'SearchExtensions.render': doRender,
1570
+ 'SearchExtensions.searchExtensions': searchExtensions
1442
1571
  };
1443
1572
 
1444
- const listen$1 = async ({
1445
- method
1446
- }) => {
1447
- const module = await getModule(method);
1448
- const rawIpc = await module.listen();
1449
- if (module.signal) {
1450
- module.signal(rawIpc);
1451
- }
1452
- const ipc = module.wrap(rawIpc);
1453
- return ipc;
1573
+ const RendererWorker = 1;
1574
+
1575
+ const rpcs = Object.create(null);
1576
+ const set = (id, rpc) => {
1577
+ rpcs[id] = rpc;
1454
1578
  };
1455
1579
 
1456
1580
  const listen = async () => {
1457
- register(commandMap);
1458
- const ipc = await listen$1({
1459
- method: Auto()
1581
+ const rpc = await WebWorkerRpcClient.create({
1582
+ commandMap: commandMap
1460
1583
  });
1461
- handleIpc(ipc);
1584
+ set(RendererWorker, rpc);
1462
1585
  };
1463
1586
 
1464
1587
  const main = async () => {