@flighthq/host-electron 0.5.1-next.920.da8f530 → 0.6.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.
Files changed (137) hide show
  1. package/README.md +2 -2
  2. package/dist/contract.d.ts +19 -24
  3. package/dist/contract.d.ts.map +1 -1
  4. package/dist/contract.js +18 -24
  5. package/dist/contract.js.map +1 -1
  6. package/dist/electronApp.d.ts +62 -34
  7. package/dist/electronApp.d.ts.map +1 -1
  8. package/dist/electronApp.js +175 -234
  9. package/dist/electronApp.js.map +1 -1
  10. package/dist/electronClipboard.d.ts +7 -5
  11. package/dist/electronClipboard.d.ts.map +1 -1
  12. package/dist/electronClipboard.js +91 -66
  13. package/dist/electronClipboard.js.map +1 -1
  14. package/dist/electronDefaultHostGroups.d.ts +59 -0
  15. package/dist/electronDefaultHostGroups.d.ts.map +1 -0
  16. package/dist/electronDefaultHostGroups.js +109 -0
  17. package/dist/electronDefaultHostGroups.js.map +1 -0
  18. package/dist/electronDialog.d.ts +12 -9
  19. package/dist/electronDialog.d.ts.map +1 -1
  20. package/dist/electronDialog.js +28 -21
  21. package/dist/electronDialog.js.map +1 -1
  22. package/dist/electronIpc.d.ts +13 -11
  23. package/dist/electronIpc.d.ts.map +1 -1
  24. package/dist/electronIpc.js +32 -26
  25. package/dist/electronIpc.js.map +1 -1
  26. package/dist/electronMenu.d.ts +12 -6
  27. package/dist/electronMenu.d.ts.map +1 -1
  28. package/dist/electronMenu.js +41 -30
  29. package/dist/electronMenu.js.map +1 -1
  30. package/dist/electronMenuTemplate.d.ts +2 -1
  31. package/dist/electronMenuTemplate.d.ts.map +1 -1
  32. package/dist/electronMenuTemplate.js.map +1 -1
  33. package/dist/electronNotification.d.ts +25 -7
  34. package/dist/electronNotification.d.ts.map +1 -1
  35. package/dist/electronNotification.js +39 -16
  36. package/dist/electronNotification.js.map +1 -1
  37. package/dist/electronPlatform.d.ts +5 -3
  38. package/dist/electronPlatform.d.ts.map +1 -1
  39. package/dist/electronPlatform.js +9 -7
  40. package/dist/electronPlatform.js.map +1 -1
  41. package/dist/electronPower.d.ts +22 -11
  42. package/dist/electronPower.d.ts.map +1 -1
  43. package/dist/electronPower.js +48 -59
  44. package/dist/electronPower.js.map +1 -1
  45. package/dist/electronProtocol.d.ts +16 -8
  46. package/dist/electronProtocol.d.ts.map +1 -1
  47. package/dist/electronProtocol.js +45 -28
  48. package/dist/electronProtocol.js.map +1 -1
  49. package/dist/electronRegister.d.ts +5 -7
  50. package/dist/electronRegister.d.ts.map +1 -1
  51. package/dist/electronRegister.js +72 -99
  52. package/dist/electronRegister.js.map +1 -1
  53. package/dist/electronScreen.d.ts +7 -6
  54. package/dist/electronScreen.d.ts.map +1 -1
  55. package/dist/electronScreen.js +17 -14
  56. package/dist/electronScreen.js.map +1 -1
  57. package/dist/electronShell.d.ts +16 -8
  58. package/dist/electronShell.d.ts.map +1 -1
  59. package/dist/electronShell.js +50 -43
  60. package/dist/electronShell.js.map +1 -1
  61. package/dist/electronShortcut.d.ts +6 -4
  62. package/dist/electronShortcut.d.ts.map +1 -1
  63. package/dist/electronShortcut.js +16 -10
  64. package/dist/electronShortcut.js.map +1 -1
  65. package/dist/electronStorage.d.ts +5 -3
  66. package/dist/electronStorage.d.ts.map +1 -1
  67. package/dist/electronStorage.js +9 -7
  68. package/dist/electronStorage.js.map +1 -1
  69. package/dist/electronTray.d.ts +38 -21
  70. package/dist/electronTray.d.ts.map +1 -1
  71. package/dist/electronTray.js +110 -76
  72. package/dist/electronTray.js.map +1 -1
  73. package/dist/electronUpdater.d.ts +6 -3
  74. package/dist/electronUpdater.d.ts.map +1 -1
  75. package/dist/electronUpdater.js +10 -7
  76. package/dist/electronUpdater.js.map +1 -1
  77. package/dist/electronWindow.d.ts +23 -7
  78. package/dist/electronWindow.d.ts.map +1 -1
  79. package/dist/electronWindow.js +234 -152
  80. package/dist/electronWindow.js.map +1 -1
  81. package/dist/index.d.ts +19 -1
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +18 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/types/ElectronApi.d.ts +393 -0
  86. package/dist/types/ElectronApi.d.ts.map +1 -0
  87. package/dist/types/ElectronApi.js +20 -0
  88. package/dist/types/ElectronApi.js.map +1 -0
  89. package/dist/types/ElectronAppCapabilitiesFor.d.ts +8 -0
  90. package/dist/types/ElectronAppCapabilitiesFor.d.ts.map +1 -0
  91. package/dist/types/ElectronAppCapabilitiesFor.js +2 -0
  92. package/dist/types/ElectronAppCapabilitiesFor.js.map +1 -0
  93. package/dist/types/ElectronHost.d.ts +43 -0
  94. package/dist/types/ElectronHost.d.ts.map +1 -0
  95. package/dist/types/ElectronHost.js +2 -0
  96. package/dist/types/ElectronHost.js.map +1 -0
  97. package/dist/types/ElectronHostGroups.d.ts +85 -0
  98. package/dist/types/ElectronHostGroups.d.ts.map +1 -0
  99. package/dist/types/ElectronHostGroups.js +2 -0
  100. package/dist/types/ElectronHostGroups.js.map +1 -0
  101. package/dist/types/ElectronHostOptions.d.ts +7 -0
  102. package/dist/types/ElectronHostOptions.d.ts.map +1 -0
  103. package/dist/types/ElectronHostOptions.js +2 -0
  104. package/dist/types/ElectronHostOptions.js.map +1 -0
  105. package/dist/types/ElectronProtocolCapabilities.d.ts +3 -0
  106. package/dist/types/ElectronProtocolCapabilities.d.ts.map +1 -0
  107. package/dist/types/ElectronProtocolCapabilities.js +2 -0
  108. package/dist/types/ElectronProtocolCapabilities.js.map +1 -0
  109. package/dist/types/ElectronTrayCapabilitiesFor.d.ts +7 -0
  110. package/dist/types/ElectronTrayCapabilitiesFor.d.ts.map +1 -0
  111. package/dist/types/ElectronTrayCapabilitiesFor.js +2 -0
  112. package/dist/types/ElectronTrayCapabilitiesFor.js.map +1 -0
  113. package/dist/types/index.d.ts +7 -0
  114. package/dist/types/index.d.ts.map +1 -0
  115. package/dist/types/index.js +7 -0
  116. package/dist/types/index.js.map +1 -0
  117. package/package.json +42 -21
  118. package/src/electronApp.test.ts +158 -131
  119. package/src/electronClipboard.test.ts +40 -13
  120. package/src/electronDefaultHostGroups.test.ts +148 -0
  121. package/src/electronDialog.test.ts +55 -53
  122. package/src/electronHostTreeShaking.test.ts +51 -0
  123. package/src/electronIpc.test.ts +47 -66
  124. package/src/electronMenu.test.ts +61 -47
  125. package/src/electronMenuTemplate.test.ts +1 -1
  126. package/src/electronNotification.test.ts +71 -21
  127. package/src/electronPlatform.test.ts +17 -12
  128. package/src/electronPower.test.ts +112 -88
  129. package/src/electronProtocol.test.ts +68 -35
  130. package/src/electronRegister.test.ts +280 -26
  131. package/src/electronScreen.test.ts +34 -25
  132. package/src/electronShell.test.ts +99 -59
  133. package/src/electronShortcut.test.ts +22 -19
  134. package/src/electronStorage.test.ts +26 -24
  135. package/src/electronTray.test.ts +225 -109
  136. package/src/electronUpdater.test.ts +26 -19
  137. package/src/electronWindow.test.ts +229 -101
@@ -1,13 +1,184 @@
1
- import { getAppName } from '@flighthq/app/contract';
1
+ import { getAppName } from '@flighthq/app';
2
2
  import { readClipboardText } from '@flighthq/clipboard/contract';
3
- import { EntityRuntimeKey } from '@flighthq/types/contract';
4
- import type { ElectronApi } from '@flighthq/types/contract';
3
+ import { EntityRuntimeKey } from '@flighthq/entity/contract';
4
+ import type { EntityRaw, EntityRuntime } from '@flighthq/entity/contract';
5
5
 
6
- import { initializeElectronHost, registerElectronBackends } from './electronRegister';
6
+ import * as contractApi from './contract.ts';
7
+ import { electronHost } from './electronRegister.ts';
8
+ import * as publicApi from './index.ts';
9
+ import type { ElectronApi } from './types/index.ts';
7
10
 
8
- // A fake Electron module broad enough that every createElectron*Backend constructs without touching
9
- // missing members. Backends close over `electron` and only call into it when their methods run, so a
10
- // thin fake suffices to prove registration routes the capability seams to the Electron backends.
11
+ const GROUPS = [
12
+ ['accessibility', 'electronHostAccessibilityGroup'],
13
+ ['app', 'electronHostApp'],
14
+ ['audio', 'electronHostAudioGroup'],
15
+ ['audioDecode', 'electronHostAudioDecodeGroup'],
16
+ ['bitmap', 'electronHostBitmapGroup'],
17
+ ['canvas', 'electronHostCanvasGroup'],
18
+ ['clipboard', 'electronHostClipboard'],
19
+ ['compress', 'electronHostCompressGroup'],
20
+ ['connectivity', 'electronHostConnectivityGroup'],
21
+ ['decompress', 'electronHostDecompressGroup'],
22
+ ['device', 'electronHostDeviceGroup'],
23
+ ['dialog', 'electronHostDialog'],
24
+ ['fileSystem', 'electronHostFileSystemGroup'],
25
+ ['font', 'electronHostFontGroup'],
26
+ ['fullscreen', 'electronHostFullscreenGroup'],
27
+ ['geolocation', 'electronHostGeolocationGroup'],
28
+ ['gl', 'electronHostGlGroup'],
29
+ ['glyph', 'electronHostGlyphGroup'],
30
+ ['haptics', 'electronHostHapticsGroup'],
31
+ ['image', 'electronHostImageGroup'],
32
+ ['imageDecode', 'electronHostImageDecodeGroup'],
33
+ ['imageEncode', 'electronHostImageEncodeGroup'],
34
+ ['input', 'electronHostInputGroup'],
35
+ ['ipc', 'electronHostIpc'],
36
+ ['lifecycle', 'electronHostLifecycleGroup'],
37
+ ['mediaSession', 'electronHostMediaSessionGroup'],
38
+ ['menu', 'electronHostMenu'],
39
+ ['midi', 'electronHostMidiGroup'],
40
+ ['net', 'electronHostNetGroup'],
41
+ ['notification', 'electronHostNotification'],
42
+ ['permissions', 'electronHostPermissionsGroup'],
43
+ ['platform', 'electronHostPlatformGroup'],
44
+ ['power', 'electronHostPower'],
45
+ ['preferences', 'electronHostStorageGroup'],
46
+ ['protocol', 'electronHostProtocol'],
47
+ ['screen', 'electronHostScreen'],
48
+ ['sensors', 'electronHostSensorsGroup'],
49
+ ['share', 'electronHostShareGroup'],
50
+ ['shell', 'electronHostShell'],
51
+ ['shortcut', 'electronHostShortcut'],
52
+ ['socket', 'electronHostSocketGroup'],
53
+ ['softKeyboard', 'electronHostSoftKeyboardGroup'],
54
+ ['statusBar', 'electronHostStatusBarGroup'],
55
+ ['surface', 'electronHostSurfaceGroup'],
56
+ ['textSegment', 'electronHostTextSegmentGroup'],
57
+ ['textShaper', 'electronHostTextShaperGroup'],
58
+ ['tray', 'electronHostTray'],
59
+ ['updater', 'electronHostUpdater'],
60
+ ['video', 'electronHostVideoGroup'],
61
+ ['wgpu', 'electronHostWgpuGroup'],
62
+ ['window', 'electronHostWindow'],
63
+ ] as const;
64
+
65
+ const LEAVES = [
66
+ 'electronHostAppActivate',
67
+ 'electronHostAppActivationPolicy',
68
+ 'electronHostAppAllWindowsClosed',
69
+ 'electronHostAppBadge',
70
+ 'electronHostAppDock',
71
+ 'electronHostAppFocus',
72
+ 'electronHostAppHide',
73
+ 'electronHostAppLocale',
74
+ 'electronHostAppLoginItem',
75
+ 'electronHostAppName',
76
+ 'electronHostAppNameWrite',
77
+ 'electronHostAppOpenFile',
78
+ 'electronHostAppPath',
79
+ 'electronHostAppQuit',
80
+ 'electronHostAppQuitRequest',
81
+ 'electronHostAppReady',
82
+ 'electronHostAppRecentDocuments',
83
+ 'electronHostAppRelaunch',
84
+ 'electronHostAppSecondInstance',
85
+ 'electronHostAppShow',
86
+ 'electronHostAppSingleInstance',
87
+ 'electronHostAppUserModelId',
88
+ 'electronHostAppVersion',
89
+ 'electronHostClipboardBookmark',
90
+ 'electronHostClipboardFormats',
91
+ 'electronHostClipboardImage',
92
+ 'electronHostClipboardText',
93
+ 'electronHostDirectoryOpenDialog',
94
+ 'electronHostFileOpenDialog',
95
+ 'electronHostFileSaveDialog',
96
+ 'electronHostIpcHandle',
97
+ 'electronHostIpcInvoke',
98
+ 'electronHostIpcMessage',
99
+ 'electronHostIpcSend',
100
+ 'electronHostIpcTargetedSend',
101
+ 'electronHostAppMenu',
102
+ 'electronHostMenuPopup',
103
+ 'electronHostMenuSelect',
104
+ 'electronHostMessageDialog',
105
+ 'electronHostNotificationAction',
106
+ 'electronHostNotificationClick',
107
+ 'electronHostNotificationClose',
108
+ 'electronHostNotificationDelivery',
109
+ 'electronHostNotificationDismiss',
110
+ 'electronHostNotificationLifecycle',
111
+ 'electronHostNotificationReceived',
112
+ 'electronHostNotificationReply',
113
+ 'electronHostPlatform',
114
+ 'electronHostPowerBatteryHealth',
115
+ 'electronHostPowerChange',
116
+ 'electronHostPowerIdle',
117
+ 'electronHostPowerKeepAwake',
118
+ 'electronHostPowerSessionLock',
119
+ 'electronHostPowerStatus',
120
+ 'electronHostPowerSuspension',
121
+ 'electronHostPowerThermal',
122
+ 'electronHostProtocolDefault',
123
+ 'electronHostProtocolOpen',
124
+ 'electronHostProtocolRegistration',
125
+ 'electronHostProtocolRegistrationQuery',
126
+ 'electronHostProtocolUnregistration',
127
+ 'electronHostScreenChange',
128
+ 'electronHostScreenQuery',
129
+ 'electronHostShellBeep',
130
+ 'electronHostShellExternal',
131
+ 'electronHostShellPathOpen',
132
+ 'electronHostShellPathReveal',
133
+ 'electronHostShellShortcutLink',
134
+ 'electronHostShellTrash',
135
+ 'electronHostShortcutQuery',
136
+ 'electronHostShortcutTrigger',
137
+ 'electronHostStorage',
138
+ 'electronHostTrayBalloon',
139
+ 'electronHostTrayBalloonEvents',
140
+ 'electronHostTrayBounds',
141
+ 'electronHostTrayDoubleClickPolicy',
142
+ 'electronHostTrayDropEvents',
143
+ 'electronHostTrayImage',
144
+ 'electronHostTrayInteractionEvents',
145
+ 'electronHostTrayLifecycle',
146
+ 'electronHostTrayMenu',
147
+ 'electronHostTrayMenuSelectionEvents',
148
+ 'electronHostTrayPopupMenu',
149
+ 'electronHostTrayPressedImage',
150
+ 'electronHostTrayTemplateImage',
151
+ 'electronHostTrayTitle',
152
+ 'electronHostTrayTooltip',
153
+ 'electronHostUpdaterCommand',
154
+ 'electronHostWindowAppearance',
155
+ 'electronHostWindowAttach',
156
+ 'electronHostWindowAttention',
157
+ 'electronHostWindowContentProtection',
158
+ 'electronHostWindowFocus',
159
+ 'electronHostWindowFullscreen',
160
+ 'electronHostWindowGeometry',
161
+ 'electronHostWindowHierarchy',
162
+ 'electronHostWindowLifecycle',
163
+ 'electronHostWindowProgress',
164
+ 'electronHostWindowShadow',
165
+ 'electronHostWindowShell',
166
+ 'electronHostWindowSizeConstraints',
167
+ 'electronHostWindowState',
168
+ 'electronHostWindowVisibility',
169
+ 'electronHostWindowZOrder',
170
+ ] as const;
171
+
172
+ const AUXILIARY_EXPORTS = ['getAppWindowForElectronId', 'getElectronBrowserWindow', 'getElectronWindowId'] as const;
173
+
174
+ type EntityCompatibility = EntityRaw & { [EntityRuntimeKey]: EntityRuntime | undefined };
175
+ type ElectronLeafResult = ReturnType<(typeof publicApi)[(typeof LEAVES)[number]]>;
176
+ type ElectronLeafProvidersAreAllEntities =
177
+ Exclude<ElectronLeafResult, undefined> extends EntityCompatibility ? true : false;
178
+ type ElectronFullHostIsEntity = ReturnType<typeof publicApi.electronHost> extends EntityCompatibility ? true : false;
179
+
180
+ // A fake Electron API broad enough that every host constructor can close over its injected dependency
181
+ // without calling missing native operations during construction.
11
182
  function fakeElectron(): ElectronApi {
12
183
  const noop = () => {};
13
184
  const off = () => () => {};
@@ -40,25 +211,56 @@ function fakeElectron(): ElectronApi {
40
211
  dialog: {},
41
212
  Menu: {
42
213
  buildFromTemplate: () => ({ popup: noop }),
43
- setApplicationMenu: noop,
214
+ setAppMenu: noop,
44
215
  },
45
216
  // The remaining members are unused at registration time.
46
217
  _off: off,
47
218
  } as unknown as ElectronApi;
48
219
  }
49
220
 
50
- describe('initializeElectronHost', () => {
51
- it('is the construction initializer of createElectronHost', () => {
52
- expect(typeof initializeElectronHost).toBe('function');
221
+ describe('electronHost', () => {
222
+ it('exports exactly the canonical full, group, and supported leaf constructors from both lanes', () => {
223
+ const expected = [...new Set(['electronHost', ...GROUPS.map((entry) => entry[1]), ...LEAVES])].sort();
224
+ const expectedSurface = [...expected, ...AUXILIARY_EXPORTS].sort();
225
+ for (const api of [publicApi, contractApi]) {
226
+ expect(Object.keys(api).sort()).toEqual(expectedSurface);
227
+ const names = Object.keys(api)
228
+ .filter((name) => /^electronHost(?:$|[A-Z])/u.test(name))
229
+ .sort();
230
+ expect(names).toEqual(expected);
231
+ expect(
232
+ Object.keys(api).filter((name) =>
233
+ /^(?:createElectron|initializeElectron|makeElectron|registerElectron)/u.test(name),
234
+ ),
235
+ ).toEqual([]);
236
+ for (const name of names) expect(Reflect.get(api, name), name).toBeTypeOf('function');
237
+ }
238
+ for (const name of expected) expect(Reflect.get(publicApi, name)).toBe(Reflect.get(contractApi, name));
53
239
  });
54
- });
55
- describe('registerElectronBackends', () => {
240
+
241
+ it('types the full host as an Entity and leaf capabilities as structural', () => {
242
+ expectTypeOf<ElectronFullHostIsEntity>().toEqualTypeOf<true>();
243
+ expectTypeOf<ElectronLeafProvidersAreAllEntities>().toEqualTypeOf<false>();
244
+ });
245
+
246
+ it('constructs every Host group through explicit canonical boundaries', () => {
247
+ const host = electronHost(fakeElectron(), { platform: 'linux' }) as unknown as Record<string, unknown>;
248
+ const hostGroups = [...HOST_GROUP_NAMES];
249
+ expect(GROUPS.map((entry) => entry[0]).sort()).toEqual(hostGroups);
250
+ expect(
251
+ Object.keys(host)
252
+ .filter((key) => key !== String(EntityRuntimeKey))
253
+ .sort(),
254
+ ).toEqual(hostGroups);
255
+ for (const [path] of GROUPS) expect(host[path], path).not.toBeUndefined();
256
+ });
257
+
56
258
  it('routes capability seams to the Electron backends without throwing', async () => {
57
- const host = registerElectronBackends(fakeElectron(), {
259
+ const host = electronHost(fakeElectron(), {
58
260
  platform: 'linux',
59
261
  });
60
- expect(host.media).toEqual({});
61
- expect(EntityRuntimeKey in host).toBe(true);
262
+ expect(host.audio).toEqual({});
263
+ expect(host.video).toEqual({});
62
264
  expect(host.dialog.directoryOpen.open).toBeTypeOf('function');
63
265
  expect(host.dialog.fileOpen.open).toBeTypeOf('function');
64
266
  expect(host.dialog.fileSave.save).toBeTypeOf('function');
@@ -68,26 +270,23 @@ describe('registerElectronBackends', () => {
68
270
  expect(host.ipc.handle.handle).toBeTypeOf('function');
69
271
  expect(host.ipc.message.subscribe).toBeTypeOf('function');
70
272
  expect(host.ipc.targetedSend.send).toBeTypeOf('function');
71
- expect(EntityRuntimeKey in host.shortcut.query).toBe(true);
72
- expect(EntityRuntimeKey in host.shortcut.trigger).toBe(true);
73
- expect(EntityRuntimeKey in host.updater.command).toBe(true);
74
273
  expect(host.updater.command.check).toBeTypeOf('function');
75
274
  expect(Object.keys(host.clipboard).sort()).toEqual(['bookmark', 'formats', 'image', 'text']);
76
275
  expect(host.connectivity).toEqual({});
77
- expect(host.storage.local.getItem('missing')).toEqual({
276
+ expect(host.preferences.local.getItem('missing')).toEqual({
78
277
  reason: 'ok',
79
278
  value: null,
80
279
  });
81
- expect(host.window.open).toBeTypeOf('function');
82
- expect(getAppName(host)).toBe('ElectronApp');
83
- expect(await readClipboardText(host)).toBe('ELECTRON-TEXT');
280
+ expect(host.window.lifecycle.open).toBeTypeOf('function');
281
+ expect(getAppName(host.app.name)).toBe('ElectronApp');
282
+ expect(await readClipboardText(host.clipboard.text)).toBe('ELECTRON-TEXT');
84
283
  });
85
284
 
86
285
  it('constructs the exact six Shell slots from an injected platform fact', () => {
87
- const windowsHost = registerElectronBackends(fakeElectron(), {
286
+ const windowsHost = electronHost(fakeElectron(), {
88
287
  platform: 'windows',
89
288
  });
90
- const linuxHost = registerElectronBackends(fakeElectron(), {
289
+ const linuxHost = electronHost(fakeElectron(), {
91
290
  platform: 'linux',
92
291
  });
93
292
  expect(Object.keys(windowsHost.shell).sort()).toEqual([
@@ -99,6 +298,61 @@ describe('registerElectronBackends', () => {
99
298
  'trash',
100
299
  ]);
101
300
  expect(Object.keys(linuxHost.shell).sort()).toEqual(['beep', 'external', 'pathOpen', 'pathReveal', 'trash']);
102
- for (const provider of Object.values(windowsHost.shell)) expect(EntityRuntimeKey in provider).toBe(true);
103
301
  });
104
302
  });
303
+
304
+ // The capability groups every provider host carries (D26: each provider declares its own; there is no
305
+ // aggregate Host to read them from).
306
+ const HOST_GROUP_NAMES = [
307
+ 'accessibility',
308
+ 'app',
309
+ 'audio',
310
+ 'audioDecode',
311
+ 'bitmap',
312
+ 'canvas',
313
+ 'clipboard',
314
+ 'compress',
315
+ 'connectivity',
316
+ 'decompress',
317
+ 'device',
318
+ 'dialog',
319
+ 'fileSystem',
320
+ 'font',
321
+ 'fullscreen',
322
+ 'geolocation',
323
+ 'gl',
324
+ 'glyph',
325
+ 'haptics',
326
+ 'image',
327
+ 'imageDecode',
328
+ 'imageEncode',
329
+ 'input',
330
+ 'ipc',
331
+ 'lifecycle',
332
+ 'mediaSession',
333
+ 'menu',
334
+ 'midi',
335
+ 'net',
336
+ 'notification',
337
+ 'permissions',
338
+ 'platform',
339
+ 'power',
340
+ 'preferences',
341
+ 'protocol',
342
+ 'screen',
343
+ 'sensors',
344
+ 'share',
345
+ 'shell',
346
+ 'shortcut',
347
+ 'socket',
348
+ 'softKeyboard',
349
+ 'statusBar',
350
+ 'surface',
351
+ 'textSegment',
352
+ 'textShaper',
353
+ 'tray',
354
+ 'updater',
355
+ 'video',
356
+ 'wgpu',
357
+ 'window',
358
+ ];
@@ -1,12 +1,14 @@
1
- import type { ScreenInfo, ElectronApi, ElectronDisplay } from '@flighthq/types/contract';
2
- import { EntityRuntimeKey } from '@flighthq/types/contract';
1
+ import { createScreenInfo } from '@flighthq/screen/contract';
2
+ import type { ScreenInfo } from '@flighthq/screen/contract';
3
3
 
4
4
  import {
5
- createElectronScreenCapabilities,
6
- initializeEmptyScreenInfo,
7
- initializeScreenChangeBackend,
8
- initializeScreenQueryBackend,
9
- } from './electronScreen';
5
+ electronHostScreen,
6
+ electronHostScreenChange,
7
+ electronHostScreenQuery,
8
+ populateElectronHostScreenChange,
9
+ populateElectronHostScreenQuery,
10
+ } from './electronScreen.ts';
11
+ import type { ElectronApi, ElectronDisplay } from './types/index.ts';
10
12
 
11
13
  function display(id: number, x: number): ElectronDisplay {
12
14
  return {
@@ -46,16 +48,16 @@ function fakeElectron(): {
46
48
  return { electron, listeners };
47
49
  }
48
50
 
49
- describe('createElectronScreenCapabilities', () => {
50
- it('returns Entity-backed query and change facets', () => {
51
- const capabilities = createElectronScreenCapabilities(fakeElectron().electron);
52
- expect(EntityRuntimeKey in capabilities.query).toBe(true);
53
- expect(EntityRuntimeKey in capabilities.change).toBe(true);
51
+ describe('electronHostScreen', () => {
52
+ it('returns query and change facets', () => {
53
+ const capabilities = electronHostScreen(fakeElectron().electron);
54
+ expect(capabilities.query).toBeDefined();
55
+ expect(capabilities.change).toBeDefined();
54
56
  });
55
57
 
56
58
  it('fills the primary screen into out', () => {
57
59
  const { electron } = fakeElectron();
58
- const backend = createElectronScreenCapabilities(electron).query;
60
+ const backend = electronHostScreen(electron).query;
59
61
  const out = {} as ScreenInfo;
60
62
  const result = backend.getPrimaryScreen(out);
61
63
  expect(result).toBe(out);
@@ -70,12 +72,12 @@ describe('createElectronScreenCapabilities', () => {
70
72
  scaleFactor: 2,
71
73
  isPrimary: true,
72
74
  });
73
- expect(Object.keys(out).filter((key) => key !== String(EntityRuntimeKey))).toHaveLength(25);
75
+ expect(Object.keys(out).sort()).toEqual(Object.keys(createScreenInfo()).sort());
74
76
  });
75
77
 
76
78
  it('enumerates all screens marking the primary', () => {
77
79
  const { electron } = fakeElectron();
78
- const backend = createElectronScreenCapabilities(electron).query;
80
+ const backend = electronHostScreen(electron).query;
79
81
  const out: ScreenInfo[] = [];
80
82
  backend.getScreens(out);
81
83
  expect(out).toHaveLength(2);
@@ -88,7 +90,7 @@ describe('createElectronScreenCapabilities', () => {
88
90
 
89
91
  it('subscribes to all change events and unsubscribes from all', () => {
90
92
  const fake = fakeElectron();
91
- const backend = createElectronScreenCapabilities(fake.electron).change;
93
+ const backend = electronHostScreen(fake.electron).change;
92
94
  let count = 0;
93
95
  const off = backend.subscribe(() => {
94
96
  count++;
@@ -103,20 +105,27 @@ describe('createElectronScreenCapabilities', () => {
103
105
  expect(fake.listeners.get('display-metrics-changed')).toHaveLength(0);
104
106
  });
105
107
  });
106
- describe('initializeEmptyScreenInfo', () => {
107
- it('is the construction initializer of createEmptyScreenInfo', () => {
108
- expect(typeof initializeEmptyScreenInfo).toBe('function');
108
+
109
+ describe('electronHostScreenChange', () => {
110
+ it('constructs a screen change provider', () => {
111
+ expect(electronHostScreenChange(fakeElectron().electron)).toBeDefined();
112
+ });
113
+ });
114
+
115
+ describe('electronHostScreenQuery', () => {
116
+ it('constructs a screen query provider', () => {
117
+ expect(electronHostScreenQuery(fakeElectron().electron)).toBeDefined();
109
118
  });
110
119
  });
111
120
 
112
- describe('initializeScreenChangeBackend', () => {
113
- it('is the construction initializer of createScreenChangeBackend', () => {
114
- expect(typeof initializeScreenChangeBackend).toBe('function');
121
+ describe('populateElectronHostScreenChange', () => {
122
+ it('is the construction initializer of electronHostScreenChange', () => {
123
+ expect(typeof populateElectronHostScreenChange).toBe('function');
115
124
  });
116
125
  });
117
126
 
118
- describe('initializeScreenQueryBackend', () => {
119
- it('is the construction initializer of createScreenQueryBackend', () => {
120
- expect(typeof initializeScreenQueryBackend).toBe('function');
127
+ describe('populateElectronHostScreenQuery', () => {
128
+ it('is the construction initializer of electronHostScreenQuery', () => {
129
+ expect(typeof populateElectronHostScreenQuery).toBe('function');
121
130
  });
122
131
  });
@@ -1,58 +1,27 @@
1
- import type { ElectronApi } from '@flighthq/types/contract';
2
- import { EntityRuntimeKey } from '@flighthq/types/contract';
3
-
4
1
  import {
5
- initializeShellBeepBackend,
6
- initializeShellExternalBackend,
7
- initializeShellPathOpenBackend,
8
- initializeShellPathRevealBackend,
9
- initializeShellShortcutLinkBackend,
10
- initializeShellTrashBackend,
11
- makeElectronShellCapabilities,
12
- } from './electronShell';
2
+ electronHostShell,
3
+ electronHostShellBeep,
4
+ electronHostShellExternal,
5
+ electronHostShellPathOpen,
6
+ electronHostShellPathReveal,
7
+ electronHostShellShortcutLink,
8
+ electronHostShellTrash,
9
+ populateElectronHostShellBeep,
10
+ populateElectronHostShellExternal,
11
+ populateElectronHostShellPathOpen,
12
+ populateElectronHostShellPathReveal,
13
+ populateElectronHostShellShortcutLink,
14
+ populateElectronHostShellTrash,
15
+ } from './electronShell.ts';
16
+ import type { ElectronApi } from './types/index.ts';
13
17
 
14
18
  function fakeElectron(shell: Partial<ElectronApi['shell']>): ElectronApi {
15
19
  return { shell } as unknown as ElectronApi;
16
20
  }
17
21
 
18
- describe('initializeShellBeepBackend', () => {
19
- it('is the construction initializer of createShellBeepBackend', () => {
20
- expect(typeof initializeShellBeepBackend).toBe('function');
21
- });
22
- });
23
- describe('initializeShellExternalBackend', () => {
24
- it('is the construction initializer of createShellExternalBackend', () => {
25
- expect(typeof initializeShellExternalBackend).toBe('function');
26
- });
27
- });
28
-
29
- describe('initializeShellPathOpenBackend', () => {
30
- it('is the construction initializer of createShellPathOpenBackend', () => {
31
- expect(typeof initializeShellPathOpenBackend).toBe('function');
32
- });
33
- });
34
-
35
- describe('initializeShellPathRevealBackend', () => {
36
- it('is the construction initializer of createShellPathRevealBackend', () => {
37
- expect(typeof initializeShellPathRevealBackend).toBe('function');
38
- });
39
- });
40
-
41
- describe('initializeShellShortcutLinkBackend', () => {
42
- it('is the construction initializer of createShellShortcutLinkBackend', () => {
43
- expect(typeof initializeShellShortcutLinkBackend).toBe('function');
44
- });
45
- });
46
-
47
- describe('initializeShellTrashBackend', () => {
48
- it('is the construction initializer of createShellTrashBackend', () => {
49
- expect(typeof initializeShellTrashBackend).toBe('function');
50
- });
51
- });
52
-
53
- describe('makeElectronShellCapabilities', () => {
54
- it('constructs every Windows provider as an Entity', () => {
55
- const capabilities = makeElectronShellCapabilities(fakeElectron({}), 'windows');
22
+ describe('electronHostShell', () => {
23
+ it('constructs every Windows provider', () => {
24
+ const capabilities = electronHostShell(fakeElectron({}), 'windows');
56
25
  expect(Object.keys(capabilities).sort()).toEqual([
57
26
  'beep',
58
27
  'external',
@@ -61,11 +30,10 @@ describe('makeElectronShellCapabilities', () => {
61
30
  'shortcutLink',
62
31
  'trash',
63
32
  ]);
64
- for (const provider of Object.values(capabilities)) expect(EntityRuntimeKey in provider).toBe(true);
65
33
  });
66
34
 
67
35
  it('omits shortcutLink on an injected non-Windows platform', () => {
68
- expect(Object.keys(makeElectronShellCapabilities(fakeElectron({}), 'linux')).sort()).toEqual([
36
+ expect(Object.keys(electronHostShell(fakeElectron({}), 'linux')).sort()).toEqual([
69
37
  'beep',
70
38
  'external',
71
39
  'pathOpen',
@@ -75,9 +43,9 @@ describe('makeElectronShellCapabilities', () => {
75
43
  });
76
44
 
77
45
  it('maps awaited external completion and rejection', async () => {
78
- const ok = makeElectronShellCapabilities(fakeElectron({ openExternal: async () => {} }), 'linux');
46
+ const ok = electronHostShell(fakeElectron({ openExternal: async () => {} }), 'linux');
79
47
  await expect(ok.external?.open('https://example.test')).resolves.toEqual({ reason: 'ok' });
80
- const failed = makeElectronShellCapabilities(
48
+ const failed = electronHostShell(
81
49
  fakeElectron({
82
50
  openExternal: async () => {
83
51
  throw new Error('denied');
@@ -89,9 +57,9 @@ describe('makeElectronShellCapabilities', () => {
89
57
  });
90
58
 
91
59
  it('preserves an Electron path error string', async () => {
92
- const success = makeElectronShellCapabilities(fakeElectron({ openPath: async () => '' }), 'linux');
60
+ const success = electronHostShell(fakeElectron({ openPath: async () => '' }), 'linux');
93
61
  await expect(success.pathOpen?.open('/a')).resolves.toEqual({ reason: 'ok' });
94
- const failed = makeElectronShellCapabilities(fakeElectron({ openPath: async () => 'no such file' }), 'linux');
62
+ const failed = electronHostShell(fakeElectron({ openPath: async () => 'no such file' }), 'linux');
95
63
  await expect(failed.pathOpen?.open('/a')).resolves.toEqual({
96
64
  message: 'no such file',
97
65
  reason: 'operation-failed',
@@ -99,7 +67,7 @@ describe('makeElectronShellCapabilities', () => {
99
67
  });
100
68
 
101
69
  it('does not turn an empty rejected path open into success', async () => {
102
- const capabilities = makeElectronShellCapabilities(
70
+ const capabilities = electronHostShell(
103
71
  fakeElectron({
104
72
  openPath: async () => {
105
73
  throw new Error('');
@@ -112,7 +80,7 @@ describe('makeElectronShellCapabilities', () => {
112
80
 
113
81
  it('maps path reveal and trash outcomes', async () => {
114
82
  let revealed = '';
115
- const capabilities = makeElectronShellCapabilities(
83
+ const capabilities = electronHostShell(
116
84
  fakeElectron({
117
85
  showItemInFolder(path) {
118
86
  revealed = path;
@@ -127,7 +95,7 @@ describe('makeElectronShellCapabilities', () => {
127
95
  });
128
96
 
129
97
  it('keeps shortcut read and write outcomes method-tight', async () => {
130
- const capabilities = makeElectronShellCapabilities(
98
+ const capabilities = electronHostShell(
131
99
  fakeElectron({
132
100
  readShortcutLink: () => ({ target: '/target' }),
133
101
  writeShortcutLink: () => false,
@@ -145,7 +113,79 @@ describe('makeElectronShellCapabilities', () => {
145
113
 
146
114
  it('dispatches beep synchronously', () => {
147
115
  const beep = vi.fn();
148
- makeElectronShellCapabilities(fakeElectron({ beep }), 'linux').beep?.beep();
116
+ electronHostShell(fakeElectron({ beep }), 'linux').beep?.beep();
149
117
  expect(beep).toHaveBeenCalledOnce();
150
118
  });
151
119
  });
120
+
121
+ describe('electronHostShellBeep', () => {
122
+ it('constructs a shell beep provider', () => {
123
+ expect(electronHostShellBeep(fakeElectron({}))).toBeDefined();
124
+ });
125
+ });
126
+
127
+ describe('electronHostShellExternal', () => {
128
+ it('constructs a shell external provider', () => {
129
+ expect(electronHostShellExternal(fakeElectron({}))).toBeDefined();
130
+ });
131
+ });
132
+
133
+ describe('electronHostShellPathOpen', () => {
134
+ it('constructs a shell path open provider', () => {
135
+ expect(electronHostShellPathOpen(fakeElectron({}))).toBeDefined();
136
+ });
137
+ });
138
+
139
+ describe('electronHostShellPathReveal', () => {
140
+ it('constructs a shell path reveal provider', () => {
141
+ expect(electronHostShellPathReveal(fakeElectron({}))).toBeDefined();
142
+ });
143
+ });
144
+
145
+ describe('electronHostShellShortcutLink', () => {
146
+ it('constructs a shell shortcut link provider', () => {
147
+ expect(electronHostShellShortcutLink(fakeElectron({}))).toBeDefined();
148
+ });
149
+ });
150
+
151
+ describe('electronHostShellTrash', () => {
152
+ it('constructs a shell trash provider', () => {
153
+ expect(electronHostShellTrash(fakeElectron({}))).toBeDefined();
154
+ });
155
+ });
156
+
157
+ describe('populateElectronHostShellBeep', () => {
158
+ it('is the construction initializer of electronHostShellBeep', () => {
159
+ expect(typeof populateElectronHostShellBeep).toBe('function');
160
+ });
161
+ });
162
+
163
+ describe('populateElectronHostShellExternal', () => {
164
+ it('is the construction initializer of electronHostShellExternal', () => {
165
+ expect(typeof populateElectronHostShellExternal).toBe('function');
166
+ });
167
+ });
168
+
169
+ describe('populateElectronHostShellPathOpen', () => {
170
+ it('is the construction initializer of electronHostShellPathOpen', () => {
171
+ expect(typeof populateElectronHostShellPathOpen).toBe('function');
172
+ });
173
+ });
174
+
175
+ describe('populateElectronHostShellPathReveal', () => {
176
+ it('is the construction initializer of electronHostShellPathReveal', () => {
177
+ expect(typeof populateElectronHostShellPathReveal).toBe('function');
178
+ });
179
+ });
180
+
181
+ describe('populateElectronHostShellShortcutLink', () => {
182
+ it('is the construction initializer of electronHostShellShortcutLink', () => {
183
+ expect(typeof populateElectronHostShellShortcutLink).toBe('function');
184
+ });
185
+ });
186
+
187
+ describe('populateElectronHostShellTrash', () => {
188
+ it('is the construction initializer of electronHostShellTrash', () => {
189
+ expect(typeof populateElectronHostShellTrash).toBe('function');
190
+ });
191
+ });