@hyperfrontend/features 0.1.0 → 0.2.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.
- package/CHANGELOG.md +17 -0
- package/_dependencies/@hyperfrontend/builder/bundle/dependencies/index.cjs.js +1 -0
- package/_dependencies/@hyperfrontend/builder/bundle/dependencies/index.esm.js +1 -0
- package/_dependencies/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js +1 -0
- package/_dependencies/@hyperfrontend/builder/bundle/dependencies/worker/index.esm.js +1 -0
- package/_dependencies/@hyperfrontend/builder/bundle/index.cjs.js +12 -10
- package/_dependencies/@hyperfrontend/builder/bundle/index.esm.js +14 -12
- package/_dependencies/@hyperfrontend/builder/bundle/rollup/index.cjs.js +2 -0
- package/_dependencies/@hyperfrontend/builder/bundle/rollup/index.esm.js +2 -0
- package/_dependencies/@hyperfrontend/builder/bundle/rollup/worker/index.cjs.js +2 -0
- package/_dependencies/@hyperfrontend/builder/bundle/rollup/worker/index.esm.js +2 -0
- package/_dependencies/@hyperfrontend/builder/index.cjs.js +87 -53
- package/_dependencies/@hyperfrontend/builder/index.esm.js +89 -55
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js +4 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js +3 -1
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/reflect/index.cjs.js +10 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/reflect/index.esm.js +6 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.cjs.js +5 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js +5 -1
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/typed-arrays/index.cjs.js +2 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/typed-arrays/index.esm.js +2 -2
- package/_dependencies/@hyperfrontend/network-protocol/browser/channel/index.cjs.js +5 -19
- package/_dependencies/@hyperfrontend/network-protocol/browser/channel/index.esm.js +1 -15
- package/_dependencies/@hyperfrontend/network-protocol/browser/data/index.cjs.js +15 -23
- package/_dependencies/@hyperfrontend/network-protocol/browser/data/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/browser/packet/index.cjs.js +6 -14
- package/_dependencies/@hyperfrontend/network-protocol/browser/packet/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/browser/receiver/index.cjs.js +4 -18
- package/_dependencies/@hyperfrontend/network-protocol/browser/receiver/index.esm.js +1 -15
- package/_dependencies/@hyperfrontend/network-protocol/browser/sender/index.cjs.js +5 -19
- package/_dependencies/@hyperfrontend/network-protocol/browser/sender/index.esm.js +2 -16
- package/_dependencies/@hyperfrontend/network-protocol/browser/v1/index.cjs.js +16 -24
- package/_dependencies/@hyperfrontend/network-protocol/browser/v1/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/browser/v2/index.cjs.js +16 -24
- package/_dependencies/@hyperfrontend/network-protocol/browser/v2/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/channel/index.cjs.js +3 -17
- package/_dependencies/@hyperfrontend/network-protocol/node/channel/index.esm.js +1 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/data/index.cjs.js +6 -14
- package/_dependencies/@hyperfrontend/network-protocol/node/data/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/packet/index.cjs.js +6 -14
- package/_dependencies/@hyperfrontend/network-protocol/node/packet/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/receiver/index.cjs.js +3 -17
- package/_dependencies/@hyperfrontend/network-protocol/node/receiver/index.esm.js +1 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/sender/index.cjs.js +2 -16
- package/_dependencies/@hyperfrontend/network-protocol/node/sender/index.esm.js +2 -16
- package/_dependencies/@hyperfrontend/network-protocol/node/v1/index.cjs.js +6 -14
- package/_dependencies/@hyperfrontend/network-protocol/node/v1/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/network-protocol/node/v2/index.cjs.js +6 -14
- package/_dependencies/@hyperfrontend/network-protocol/node/v2/index.esm.js +7 -15
- package/_dependencies/@hyperfrontend/nexus/index.cjs.js +49 -19
- package/_dependencies/@hyperfrontend/nexus/index.esm.js +49 -19
- package/_dependencies/@hyperfrontend/project-scope/core/fs/index.cjs.js +62 -0
- package/_dependencies/@hyperfrontend/project-scope/core/fs/index.esm.js +60 -2
- package/_shared/generators/feature/generate-feature-module/index.esm.js +11 -6
- package/_shared/generators/metadata/generate-metadata/index.esm.js +1 -0
- package/_shared/shared/control/index.cjs.js +12 -2
- package/_shared/shared/control/index.esm.js +12 -2
- package/_shared/shared/request/index.cjs.js +91 -0
- package/_shared/shared/request/index.esm.js +88 -0
- package/_shared/shared/shutdown/index.esm.js +12 -0
- package/bin/hf.js +643 -70
- package/bundle/host/index.iife.js +290 -4041
- package/bundle/host/index.iife.min.js +1 -1
- package/bundle/host/index.umd.js +290 -4041
- package/bundle/host/index.umd.min.js +1 -1
- package/bundle/hostee/index.iife.js +215 -2893
- package/bundle/hostee/index.iife.min.js +1 -1
- package/bundle/hostee/index.umd.js +215 -2893
- package/bundle/hostee/index.umd.min.js +1 -1
- package/cli/args.d.ts +2 -0
- package/cli/args.d.ts.map +1 -1
- package/cli/commands/build.d.ts +8 -5
- package/cli/commands/build.d.ts.map +1 -1
- package/cli/commands/dev.d.ts +7 -2
- package/cli/commands/dev.d.ts.map +1 -1
- package/cli/config/resolve.d.ts +3 -1
- package/cli/config/resolve.d.ts.map +1 -1
- package/cli/index.cjs.js +643 -70
- package/cli/index.d.ts +21 -10
- package/cli/index.esm.js +591 -60
- package/cli/usage.d.ts +1 -1
- package/cli/usage.d.ts.map +1 -1
- package/generators/feature/generate-feature-module.d.ts.map +1 -1
- package/generators/index.cjs.js +435 -42
- package/generators/index.d.ts +9 -8
- package/generators/index.esm.js +404 -30
- package/generators/metadata/generate-metadata.d.ts +4 -4
- package/generators/metadata/generate-metadata.d.ts.map +1 -1
- package/generators/shell/connector-types.d.ts +19 -0
- package/generators/shell/connector-types.d.ts.map +1 -0
- package/generators/shell/generate-shell.d.ts +5 -4
- package/generators/shell/generate-shell.d.ts.map +1 -1
- package/generators/shell/schema-type.d.ts +20 -0
- package/generators/shell/schema-type.d.ts.map +1 -0
- package/generators/shell/source-literal.d.ts +28 -0
- package/generators/shell/source-literal.d.ts.map +1 -1
- package/host/create-shell.d.ts +4 -1
- package/host/create-shell.d.ts.map +1 -1
- package/host/display-modes/dialog.d.ts +1 -1
- package/host/display-modes/dialog.d.ts.map +1 -1
- package/host/display-modes/embedded.d.ts +1 -1
- package/host/display-modes/embedded.d.ts.map +1 -1
- package/host/index.cjs.js +150 -30
- package/host/index.d.ts +53 -38
- package/host/index.d.ts.map +1 -1
- package/host/index.esm.js +129 -9
- package/host/lifecycle.d.ts.map +1 -1
- package/host/plugins.d.ts +1 -34
- package/host/plugins.d.ts.map +1 -1
- package/host/types.d.ts +49 -0
- package/host/types.d.ts.map +1 -1
- package/hostee/index.cjs.js +54 -9
- package/hostee/index.d.ts +41 -1
- package/hostee/index.d.ts.map +1 -1
- package/hostee/index.esm.js +51 -6
- package/hostee/lifecycle.d.ts.map +1 -1
- package/hostee/types.d.ts +40 -0
- package/hostee/types.d.ts.map +1 -1
- package/index.cjs.js +32 -1
- package/index.d.ts +89 -3
- package/index.d.ts.map +1 -1
- package/index.esm.js +32 -1
- package/nx/executors/build/index.cjs.js +14975 -137
- package/nx/executors/build/index.esm.js +14935 -115
- package/nx/executors/serve/executor.d.ts.map +1 -1
- package/nx/executors/serve/index.cjs.js +6594 -80
- package/nx/executors/serve/index.esm.js +6529 -44
- package/nx/generators/feature/index.cjs.js +8751 -108
- package/nx/generators/feature/index.esm.js +8711 -81
- package/package.json +15 -5
- package/server/debug-ui/index.d.ts +2 -0
- package/server/debug-ui/index.d.ts.map +1 -0
- package/server/debug-ui/index.html +15 -0
- package/server/debug-ui/index.iife.js +427 -0
- package/server/debug-ui/index.iife.min.js +1 -0
- package/server/dev-server.d.ts.map +1 -1
- package/server/index.cjs.js +78 -10
- package/server/index.esm.js +78 -11
- package/server/module-dir.d.ts +17 -0
- package/server/module-dir.d.ts.map +1 -0
- package/server/module-dir.stub.d.ts +15 -0
- package/server/module-dir.stub.d.ts.map +1 -0
- package/shared/contract.d.ts +1 -1
- package/shared/contract.d.ts.map +1 -1
- package/shared/control.d.ts +4 -0
- package/shared/control.d.ts.map +1 -1
- package/shared/invert-contract.d.ts +20 -0
- package/shared/invert-contract.d.ts.map +1 -0
- package/shared/request.d.ts +68 -0
- package/shared/request.d.ts.map +1 -0
- package/{nx/shared → shared}/shutdown.d.ts +3 -2
- package/shared/shutdown.d.ts.map +1 -0
- package/shared/types.d.ts +72 -1
- package/shared/types.d.ts.map +1 -1
- package/_shared/nx/shared/context/index.cjs.js +0 -18
- package/_shared/nx/shared/context/index.esm.js +0 -16
- package/nx/shared/shutdown.d.ts.map +0 -1
- package/server/debug-ui/bootstrap.d.ts +0 -2
- package/server/debug-ui/bootstrap.d.ts.map +0 -1
package/host/index.esm.js
CHANGED
|
@@ -1,15 +1,50 @@
|
|
|
1
|
-
import { freeze } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
1
|
+
import { freeze, values } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
2
2
|
import { DEFAULT_CONTRACT, createBroker } from '../_dependencies/@hyperfrontend/nexus/index.esm.js';
|
|
3
|
+
import { parse, stringify } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.esm.js';
|
|
3
4
|
import { createElement, div, button } from '../_dependencies/@hyperfrontend/ui-utils/element/index.esm.js';
|
|
4
5
|
import { createError } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
|
|
5
6
|
import { round } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js';
|
|
6
7
|
import { dateNow } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.esm.js';
|
|
7
|
-
import { setInterval, clearInterval } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js';
|
|
8
|
+
import { setInterval, clearInterval, clearTimeout, setTimeout } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js';
|
|
9
|
+
import { createPromise, promiseResolve, promiseReject } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js';
|
|
8
10
|
import { createProtocol } from '../_dependencies/@hyperfrontend/network-protocol/browser/v1/index.esm.js';
|
|
9
11
|
import { createProtocol as createProtocol$1 } from '../_dependencies/@hyperfrontend/network-protocol/browser/v2/index.esm.js';
|
|
10
12
|
import { DisplayMode } from '../_shared/shared/types/index.esm.js';
|
|
11
13
|
import { ControlType, isControlType, withControlContract } from '../_shared/shared/control/index.esm.js';
|
|
12
14
|
import { createEventEmitter } from '../_shared/shared/event-emitter/index.esm.js';
|
|
15
|
+
import { createRequestPeer } from '../_shared/shared/request/index.esm.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Deep-copies an action list so the returned contract shares no object
|
|
19
|
+
* references with its source.
|
|
20
|
+
*
|
|
21
|
+
* @param actions - The action descriptions to copy.
|
|
22
|
+
* @returns Fresh action objects with identical contents.
|
|
23
|
+
*/
|
|
24
|
+
function copyActions(actions) {
|
|
25
|
+
// why: The messaging layer rejects configs containing repeated object references, so the inverted contract must be built from fresh literals rather than aliasing the caller's action objects.
|
|
26
|
+
return actions.map((action) => parse(stringify(action)));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Inverts a feature-authored contract into the host's perspective.
|
|
30
|
+
*
|
|
31
|
+
* A feature contract is written from the feature's point of view: `emitted` is
|
|
32
|
+
* what the feature sends, `accepted` is what the feature handles. The host
|
|
33
|
+
* channel needs the mirror image — it sends what the feature accepts and
|
|
34
|
+
* accepts what the feature emits — so the two lists are swapped.
|
|
35
|
+
*
|
|
36
|
+
* @param contract - The contract as authored by the feature.
|
|
37
|
+
* @returns A fresh contract oriented to the host side.
|
|
38
|
+
*
|
|
39
|
+
* @example Deriving the host-side contract
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const host = invertFeatureContract({ emitted: [{ type: 'tick' }], accepted: [{ type: 'setTimezone' }] })
|
|
42
|
+
* // => { emitted: [{ type: 'setTimezone' }], accepted: [{ type: 'tick' }] }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function invertFeatureContract(contract) {
|
|
46
|
+
return { emitted: copyActions(contract.accepted), accepted: copyActions(contract.emitted) };
|
|
47
|
+
}
|
|
13
48
|
|
|
14
49
|
// note: Borderless and transparent so the embedded feature blends into the host page.
|
|
15
50
|
/**
|
|
@@ -107,7 +142,7 @@ const dialogDefaults = freeze({
|
|
|
107
142
|
* @param context - Inputs the shell passes to this display mode.
|
|
108
143
|
* @param context.options - The merged shell options.
|
|
109
144
|
* @param context.requestClose - Requests the shell close itself.
|
|
110
|
-
* @returns The iframe content window and a teardown that unmounts the dialog.
|
|
145
|
+
* @returns The iframe content window, the dialog container as the mounted element, and a teardown that unmounts the dialog.
|
|
111
146
|
*
|
|
112
147
|
* @example Mounting a dialog
|
|
113
148
|
* ```typescript
|
|
@@ -151,6 +186,7 @@ const mountDialog = ({ options, requestClose }) => {
|
|
|
151
186
|
document.addEventListener('keydown', onKeydown);
|
|
152
187
|
return {
|
|
153
188
|
target: iframe.contentWindow,
|
|
189
|
+
element: container.ref,
|
|
154
190
|
cleanup: () => {
|
|
155
191
|
document.removeEventListener('keydown', onKeydown);
|
|
156
192
|
container.ref.remove();
|
|
@@ -163,7 +199,7 @@ const mountDialog = ({ options, requestClose }) => {
|
|
|
163
199
|
*
|
|
164
200
|
* @param context - Inputs the shell passes to this display mode.
|
|
165
201
|
* @param context.options - The merged shell options.
|
|
166
|
-
* @returns The iframe content window and a teardown that removes the iframe.
|
|
202
|
+
* @returns The iframe content window, the iframe as the mounted element, and a teardown that removes the iframe.
|
|
167
203
|
*
|
|
168
204
|
* @example Mounting embedded
|
|
169
205
|
* ```typescript
|
|
@@ -176,6 +212,7 @@ const mountEmbedded = ({ options }) => {
|
|
|
176
212
|
container.appendChild(iframe);
|
|
177
213
|
return {
|
|
178
214
|
target: iframe.contentWindow,
|
|
215
|
+
element: iframe,
|
|
179
216
|
frame: iframe,
|
|
180
217
|
cleanup: () => iframe.remove(),
|
|
181
218
|
};
|
|
@@ -348,6 +385,12 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
348
385
|
let opened = false;
|
|
349
386
|
let openCount = 0;
|
|
350
387
|
let monitor = null;
|
|
388
|
+
let plugins = null;
|
|
389
|
+
let pendingUnmount = null;
|
|
390
|
+
let queuedOpen = null;
|
|
391
|
+
// why: One peer outlives every open/close cycle so handlers registered before the first open (or across reopens) keep answering feature requests.
|
|
392
|
+
const requests = createRequestPeer('host', (type, data) => channel?.send(type, data));
|
|
393
|
+
const emitError = (error) => emitter.emit('error', error);
|
|
351
394
|
const runCleanup = () => {
|
|
352
395
|
if (cleanup) {
|
|
353
396
|
cleanup();
|
|
@@ -360,8 +403,45 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
360
403
|
monitor = null;
|
|
361
404
|
}
|
|
362
405
|
};
|
|
363
|
-
const
|
|
364
|
-
|
|
406
|
+
const mountPlugins = (registered, element, displayMode) => {
|
|
407
|
+
const context = freeze({ element, displayMode });
|
|
408
|
+
const teardowns = [];
|
|
409
|
+
for (const plugin of registered) {
|
|
410
|
+
try {
|
|
411
|
+
const teardown = plugin.onMount?.(context);
|
|
412
|
+
if (teardown) {
|
|
413
|
+
teardowns.push(teardown);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
emitError(error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
plugins = { registered, context, teardowns };
|
|
421
|
+
};
|
|
422
|
+
const startUnmount = (state, finish) => {
|
|
423
|
+
plugins = null;
|
|
424
|
+
let chain = promiseResolve();
|
|
425
|
+
for (const plugin of [...state.registered].reverse()) {
|
|
426
|
+
chain = chain.then(() => plugin.onUnmount?.(state.context)).catch(emitError);
|
|
427
|
+
}
|
|
428
|
+
pendingUnmount = chain.then(() => {
|
|
429
|
+
for (const teardown of [...state.teardowns].reverse()) {
|
|
430
|
+
try {
|
|
431
|
+
teardown();
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
emitError(error);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
finish();
|
|
438
|
+
pendingUnmount = null;
|
|
439
|
+
const reopen = queuedOpen;
|
|
440
|
+
queuedOpen = null;
|
|
441
|
+
reopen?.();
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
const releaseChannelAndCleanup = () => {
|
|
365
445
|
if (channel) {
|
|
366
446
|
channel.destroy();
|
|
367
447
|
channel = null;
|
|
@@ -369,6 +449,20 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
369
449
|
opened = false;
|
|
370
450
|
runCleanup();
|
|
371
451
|
};
|
|
452
|
+
const destroy = () => {
|
|
453
|
+
requests.rejectAll('The shell was destroyed before the feature responded.');
|
|
454
|
+
queuedOpen = null;
|
|
455
|
+
if (pendingUnmount) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
stopMonitor();
|
|
459
|
+
const state = plugins;
|
|
460
|
+
if (state) {
|
|
461
|
+
startUnmount(state, releaseChannelAndCleanup);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
releaseChannelAndCleanup();
|
|
465
|
+
};
|
|
372
466
|
const close = () => {
|
|
373
467
|
if (channel) {
|
|
374
468
|
channel.disconnect();
|
|
@@ -389,13 +483,21 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
389
483
|
};
|
|
390
484
|
const open = (overrides) => {
|
|
391
485
|
destroy();
|
|
486
|
+
if (pendingUnmount) {
|
|
487
|
+
queuedOpen = () => open(overrides);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
392
490
|
const options = { ...baseOptions, ...overrides };
|
|
393
|
-
const
|
|
491
|
+
const displayMode = options.displayMode ?? DisplayMode.Embedded;
|
|
492
|
+
const result = wiring.selectMount(displayMode)({ options, requestClose: close });
|
|
394
493
|
cleanup = result.cleanup;
|
|
395
494
|
if (result.target === null) {
|
|
396
495
|
emitter.emit('error', createError('Feature window could not be opened.'));
|
|
397
496
|
return;
|
|
398
497
|
}
|
|
498
|
+
if (options.plugins && options.plugins.length > 0) {
|
|
499
|
+
mountPlugins(options.plugins, result.element ?? null, displayMode);
|
|
500
|
+
}
|
|
399
501
|
channel = broker.addChannel(`feature-${(openCount += 1)}`, result.target, wiring.registerSecurity(broker, options.protocol, options.sharedKey));
|
|
400
502
|
const sizeFrame = options.embedSizing === 'content' ? result.frame : undefined;
|
|
401
503
|
const activeMonitor = wiring.createHeartbeatMonitor((missedBeats, lastBeatAt) => applyUnresponsive(options, missedBeats, lastBeatAt));
|
|
@@ -409,6 +511,15 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
409
511
|
opened = false;
|
|
410
512
|
emitter.emit('close');
|
|
411
513
|
stopMonitor();
|
|
514
|
+
requests.rejectAll('The feature channel closed before the feature responded.');
|
|
515
|
+
if (pendingUnmount) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const state = plugins;
|
|
519
|
+
if (state) {
|
|
520
|
+
startUnmount(state, runCleanup);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
412
523
|
runCleanup();
|
|
413
524
|
});
|
|
414
525
|
channel.on('deny', (data) => emitter.emit('error', data));
|
|
@@ -421,6 +532,9 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
421
532
|
else if (message.type === ControlType.Size && sizeFrame) {
|
|
422
533
|
applyContentSize(sizeFrame, message.data);
|
|
423
534
|
}
|
|
535
|
+
else {
|
|
536
|
+
requests.dispatch(message.type, message.data);
|
|
537
|
+
}
|
|
424
538
|
return;
|
|
425
539
|
}
|
|
426
540
|
emitter.emit(message.type, message.data);
|
|
@@ -432,6 +546,8 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
|
|
|
432
546
|
close,
|
|
433
547
|
destroy,
|
|
434
548
|
send: (type, data) => channel?.send(type, data),
|
|
549
|
+
request: (type, data, options) => channel ? requests.request(type, data, options) : promiseReject(createError(`Cannot send request '${type}': the shell is not open.`)),
|
|
550
|
+
handle: requests.handle,
|
|
435
551
|
on: emitter.on,
|
|
436
552
|
get isOpen() {
|
|
437
553
|
return opened;
|
|
@@ -556,7 +672,10 @@ function deriveShellName(options, sequence) {
|
|
|
556
672
|
* Creates a host-side shell for embedding a feature.
|
|
557
673
|
*
|
|
558
674
|
* Provisions a nexus broker and returns a handle whose `open` mounts the feature
|
|
559
|
-
* in the requested display mode.
|
|
675
|
+
* in the requested display mode. The `contract` option takes the feature's
|
|
676
|
+
* contract exactly as the feature authored it; the shell derives the host-side
|
|
677
|
+
* orientation itself, so the handle sends what the feature accepts and receives
|
|
678
|
+
* what the feature emits.
|
|
560
679
|
*
|
|
561
680
|
* @param options - Create-time shell options, overridable per `open` call.
|
|
562
681
|
* @returns A handle exposing `open`, `close`, `destroy`, `send`, `on`, and `isOpen`.
|
|
@@ -570,7 +689,8 @@ function deriveShellName(options, sequence) {
|
|
|
570
689
|
*/
|
|
571
690
|
function createShell(options) {
|
|
572
691
|
const emitter = createEventEmitter();
|
|
573
|
-
|
|
692
|
+
// how: A feature-authored contract is inverted into the host's perspective; the generic default contract is already channel-oriented and is used as-is.
|
|
693
|
+
const contract = withControlContract(options.contract ? invertFeatureContract(options.contract) : DEFAULT_CONTRACT);
|
|
574
694
|
const broker = createBroker({ name: deriveShellName(options, (shellCount += 1)), contract });
|
|
575
695
|
return createShellHandle(broker, options, emitter, { selectMount, registerSecurity, createHeartbeatMonitor });
|
|
576
696
|
}
|
package/host/lifecycle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,sBAAsB,CAAA;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,sBAAsB,CAAA;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,OAAO,KAAK,EAA6C,gBAAgB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAChH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAM5D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAO7C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,gBAAgB,CAAA;IAChD;;;;;;;OAOG;IACH,gBAAgB,CACd,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACtC;;;;;OAKG;IACH,sBAAsB,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,gBAAgB,CAAA;CACnH;AAcD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,YAAY,EACzB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,WAAW,CA0Lb"}
|
package/host/plugins.d.ts
CHANGED
|
@@ -1,35 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Context handed to an {@link ExperiencePlugin} around a feature's mount lifecycle.
|
|
4
|
-
*/
|
|
5
|
-
export interface ExperiencePluginContext {
|
|
6
|
-
/** The root element the display mode mounted (dialog container or embedded frame). */
|
|
7
|
-
element: HTMLElement;
|
|
8
|
-
/** The display mode the feature was surfaced in. */
|
|
9
|
-
displayMode: DisplayMode;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Opt-in extension that decorates a feature's mount lifecycle (e.g. transitions, animations).
|
|
13
|
-
*
|
|
14
|
-
* Implement this interface to layer experiences onto the built-in display modes,
|
|
15
|
-
* then pass the plugin to the host shell. The SDK ships no built-in plugins.
|
|
16
|
-
*/
|
|
17
|
-
export interface ExperiencePlugin {
|
|
18
|
-
/** Unique plugin name, surfaced in debug logs. */
|
|
19
|
-
name: string;
|
|
20
|
-
/**
|
|
21
|
-
* Runs after the feature mounts; may animate it in and return a teardown.
|
|
22
|
-
*
|
|
23
|
-
* @param context - The mounted element and its display mode.
|
|
24
|
-
* @returns An optional teardown invoked on unmount.
|
|
25
|
-
*/
|
|
26
|
-
onMount?(context: ExperiencePluginContext): void | (() => void);
|
|
27
|
-
/**
|
|
28
|
-
* Runs before the feature unmounts; may return a promise to defer teardown until an exit animation finishes.
|
|
29
|
-
*
|
|
30
|
-
* @param context - The mounted element and its display mode.
|
|
31
|
-
* @returns Optionally a promise the shell awaits before tearing down.
|
|
32
|
-
*/
|
|
33
|
-
onUnmount?(context: ExperiencePluginContext): void | Promise<void>;
|
|
34
|
-
}
|
|
1
|
+
export type { ExperiencePlugin, ExperiencePluginContext } from '../shared/types';
|
|
35
2
|
//# sourceMappingURL=plugins.d.ts.map
|
package/host/plugins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/plugins.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/plugins.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA"}
|
package/host/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EventHandler } from '../shared/event-emitter';
|
|
2
|
+
import type { RequestHandler, RequestOptions } from '../shared/request';
|
|
2
3
|
import type { ShellOptions } from '../shared/types';
|
|
3
4
|
/**
|
|
4
5
|
* Public handle returned by {@link createShell}.
|
|
@@ -7,6 +8,9 @@ export interface ShellHandle {
|
|
|
7
8
|
/**
|
|
8
9
|
* Mounts the feature using the merged create-time and call-time options.
|
|
9
10
|
*
|
|
11
|
+
* When a plugin teardown from the previous mount is still in flight, the
|
|
12
|
+
* remount is queued until it settles; the latest `open` call wins.
|
|
13
|
+
*
|
|
10
14
|
* @param options - Per-open overrides layered over the create-time options.
|
|
11
15
|
*/
|
|
12
16
|
open(options?: Partial<ShellOptions>): void;
|
|
@@ -16,6 +20,10 @@ export interface ShellHandle {
|
|
|
16
20
|
close(): void;
|
|
17
21
|
/**
|
|
18
22
|
* Closes the feature and releases all resources (channel and DOM).
|
|
23
|
+
*
|
|
24
|
+
* When experience plugins are registered, their `onUnmount` hooks are
|
|
25
|
+
* awaited before the channel and DOM are released; calling `destroy` again
|
|
26
|
+
* while that teardown is in flight is a no-op.
|
|
19
27
|
*/
|
|
20
28
|
destroy(): void;
|
|
21
29
|
/**
|
|
@@ -25,6 +33,45 @@ export interface ShellHandle {
|
|
|
25
33
|
* @param data - Optional payload for the action.
|
|
26
34
|
*/
|
|
27
35
|
send(type: string, data?: unknown): void;
|
|
36
|
+
/**
|
|
37
|
+
* Sends a request to the feature and resolves with its response.
|
|
38
|
+
*
|
|
39
|
+
* The feature answers through a handler it registered with its own
|
|
40
|
+
* `handle(type, handler)`. The promise rejects when the feature's handler
|
|
41
|
+
* throws, when the feature has no handler for the type, when no response
|
|
42
|
+
* arrives within the timeout (30 seconds by default), when the shell is not
|
|
43
|
+
* open, or when the channel closes or the shell is destroyed while the
|
|
44
|
+
* request is pending.
|
|
45
|
+
*
|
|
46
|
+
* @param type - Request action type, drawn from the feature contract.
|
|
47
|
+
* @param data - Optional payload for the request.
|
|
48
|
+
* @param options - Per-request settings such as `timeoutMs`.
|
|
49
|
+
* @returns A promise settling with the feature's response payload.
|
|
50
|
+
*
|
|
51
|
+
* @example Querying the feature for its current state
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const time = await shell.request('getTime', { timezone: 'UTC' }, { timeoutMs: 5000 })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
request(type: string, data?: unknown, options?: RequestOptions): Promise<unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Registers the handler that answers feature requests of a given type.
|
|
59
|
+
*
|
|
60
|
+
* One handler per type: registering a second handler for a type that already
|
|
61
|
+
* has one throws. The handler may return the response value directly or a
|
|
62
|
+
* promise of it; a thrown error or rejected promise reaches the feature as a
|
|
63
|
+
* failed response carrying the error's message.
|
|
64
|
+
*
|
|
65
|
+
* @param type - Request type to answer.
|
|
66
|
+
* @param handler - Receives the request payload and returns the response.
|
|
67
|
+
* @returns A function that unregisters this handler.
|
|
68
|
+
*
|
|
69
|
+
* @example Answering a feature's request for host settings
|
|
70
|
+
* ```typescript
|
|
71
|
+
* shell.handle('getSettings', () => ({ locale: 'en-US' }))
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
handle(type: string, handler: RequestHandler): () => void;
|
|
28
75
|
/**
|
|
29
76
|
* Subscribes to feature messages or lifecycle events (`open`, `close`, `error`).
|
|
30
77
|
*
|
|
@@ -44,6 +91,8 @@ export interface ShellHandle {
|
|
|
44
91
|
export interface MountResult {
|
|
45
92
|
/** Window the host messages, or `null` when a popup/standalone was blocked. */
|
|
46
93
|
target: Window | null;
|
|
94
|
+
/** In-document root the mode mounted (embedded iframe or dialog container); unset when the feature opens in a separate window. */
|
|
95
|
+
element?: HTMLElement;
|
|
47
96
|
/** Resizable element for content-driven sizing, when the mode embeds an iframe inline. */
|
|
48
97
|
frame?: HTMLElement;
|
|
49
98
|
/** Removes any DOM or closes any window created by the mount. */
|
package/host/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;IAC3C;;OAEG;IACH,KAAK,IAAI,IAAI,CAAA;IACb;;;;;;OAMG;IACH,OAAO,IAAI,IAAI,CAAA;IACf;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACxC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACjF;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CAAA;IACzD;;;;;;OAMG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI,CAAA;IACpD;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,kIAAkI;IAClI,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,iEAAiE;IACjE,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,OAAO,EAAE,YAAY,CAAA;IACrB,6EAA6E;IAC7E,YAAY,IAAI,IAAI,CAAA;CACrB;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,WAAW,CAAA"}
|
package/hostee/index.cjs.js
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
const index_cjs_js$7 = require('../_dependencies/@hyperfrontend/nexus/index.cjs.js');
|
|
4
4
|
const index_cjs_js$1 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js');
|
|
5
5
|
const index_cjs_js = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
|
|
6
|
-
const { ControlType, withControlContract } = require('../_shared/shared/control/index.cjs.js');
|
|
6
|
+
const { ControlType, isControlType, withControlContract } = require('../_shared/shared/control/index.cjs.js');
|
|
7
7
|
const { createEventEmitter } = require('../_shared/shared/event-emitter/index.cjs.js');
|
|
8
|
+
const { createRequestPeer } = require('../_shared/shared/request/index.cjs.js');
|
|
8
9
|
require('../_dependencies/@hyperfrontend/json-utils/index.cjs.js');
|
|
9
10
|
const index_cjs_js$2 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js');
|
|
10
|
-
const index_cjs_js$
|
|
11
|
+
const index_cjs_js$4 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js');
|
|
11
12
|
const index_cjs_js$3 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.cjs.js');
|
|
12
|
-
const index_cjs_js$
|
|
13
|
-
const index_cjs_js$
|
|
13
|
+
const index_cjs_js$6 = require('../_dependencies/@hyperfrontend/ui-utils/element/index.cjs.js');
|
|
14
|
+
const index_cjs_js$5 = require('../_dependencies/@hyperfrontend/ui-utils/style/index.cjs.js');
|
|
14
15
|
|
|
15
16
|
// note: Runtime validation shared by the host/hostee factories and the config loader.
|
|
16
17
|
/**
|
|
@@ -45,6 +46,31 @@ function collectActionListIssues(actions, field, issues) {
|
|
|
45
46
|
if (typeof action['type'] !== 'string' || action['type'].length === 0) {
|
|
46
47
|
issues.push(`"${field}[${index}]" must have a non-empty string "type".`);
|
|
47
48
|
}
|
|
49
|
+
if (action['respondsWith'] !== undefined && (typeof action['respondsWith'] !== 'string' || action['respondsWith'].length === 0)) {
|
|
50
|
+
issues.push(`"${field}[${index}]" has a "respondsWith" that must be a non-empty string.`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Collects every `respondsWith` reference that does not name an action in the other direction.
|
|
56
|
+
*
|
|
57
|
+
* A request emitted by one side is answered by an action the same side accepts (and
|
|
58
|
+
* vice versa), so each `respondsWith` must resolve across the contract's directions.
|
|
59
|
+
*
|
|
60
|
+
* @param actions - The already well-formed action list to check.
|
|
61
|
+
* @param field - The field name of `actions`, used to locate problems in messages.
|
|
62
|
+
* @param other - The action list of the opposite direction.
|
|
63
|
+
* @param otherField - The field name of `other`, used in messages.
|
|
64
|
+
* @param issues - The running list of human-readable problems, appended to in place.
|
|
65
|
+
*/
|
|
66
|
+
function collectRespondsWithIssues(actions, field, other, otherField, issues) {
|
|
67
|
+
actions.forEach((action, index) => {
|
|
68
|
+
if (action.respondsWith === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!other.some((candidate) => candidate.type === action.respondsWith)) {
|
|
72
|
+
issues.push(`"${field}[${index}]" responds with "${action.respondsWith}", but "${otherField}" has no action of that type.`);
|
|
73
|
+
}
|
|
48
74
|
});
|
|
49
75
|
}
|
|
50
76
|
/**
|
|
@@ -70,7 +96,7 @@ function describeType(value) {
|
|
|
70
96
|
*
|
|
71
97
|
* @param contract - The candidate contract, typically parsed from disk.
|
|
72
98
|
* @returns The validated contract, typed.
|
|
73
|
-
* @throws {Error} When the value is not an object,
|
|
99
|
+
* @throws {Error} When the value is not an object, any action is malformed, or a `respondsWith` names no action in the other direction.
|
|
74
100
|
*
|
|
75
101
|
* @example Validating a parsed contract file
|
|
76
102
|
* ```typescript
|
|
@@ -85,6 +111,12 @@ function validateContract(contract) {
|
|
|
85
111
|
const issues = [];
|
|
86
112
|
collectActionListIssues(contract['emitted'], 'emitted', issues);
|
|
87
113
|
collectActionListIssues(contract['accepted'], 'accepted', issues);
|
|
114
|
+
if (issues.length === 0) {
|
|
115
|
+
const emitted = contract['emitted'];
|
|
116
|
+
const accepted = contract['accepted'];
|
|
117
|
+
collectRespondsWithIssues(emitted, 'emitted', accepted, 'accepted', issues);
|
|
118
|
+
collectRespondsWithIssues(accepted, 'accepted', emitted, 'emitted', issues);
|
|
119
|
+
}
|
|
88
120
|
if (issues.length > 0) {
|
|
89
121
|
throw index_cjs_js.createError(`Invalid contract:\n${issues.map((issue) => ` - ${issue}`).join('\n')}`);
|
|
90
122
|
}
|
|
@@ -135,7 +167,7 @@ const BODY_RESET_CSS = 'html,body{margin:0;padding:0;background:transparent}';
|
|
|
135
167
|
* ```
|
|
136
168
|
*/
|
|
137
169
|
function applyBodyReset() {
|
|
138
|
-
index_cjs_js$
|
|
170
|
+
index_cjs_js$5.addStylesheet(BODY_RESET_CSS);
|
|
139
171
|
}
|
|
140
172
|
/**
|
|
141
173
|
* Creates the hostee-side content-size announcer.
|
|
@@ -158,7 +190,7 @@ function createSizeAnnouncer(send) {
|
|
|
158
190
|
return;
|
|
159
191
|
}
|
|
160
192
|
announce();
|
|
161
|
-
cleanup = index_cjs_js$
|
|
193
|
+
cleanup = index_cjs_js$6.onElementResize(document.body, announce);
|
|
162
194
|
},
|
|
163
195
|
stop() {
|
|
164
196
|
if (cleanup) {
|
|
@@ -207,6 +239,7 @@ function resolveHostWindow(win) {
|
|
|
207
239
|
function createFeatureHandle(broker, hostWindow, emitter) {
|
|
208
240
|
let channel = null;
|
|
209
241
|
let opened = false;
|
|
242
|
+
const requests = createRequestPeer('feature', (type, data) => channel?.send(type, data));
|
|
210
243
|
if (hostWindow) {
|
|
211
244
|
const activeChannel = broker.addChannel('host', hostWindow);
|
|
212
245
|
channel = activeChannel;
|
|
@@ -223,16 +256,28 @@ function createFeatureHandle(broker, hostWindow, emitter) {
|
|
|
223
256
|
emitter.emit('close');
|
|
224
257
|
heartbeat.stop();
|
|
225
258
|
announcer.stop();
|
|
259
|
+
requests.rejectAll('The host channel closed before the host responded.');
|
|
226
260
|
});
|
|
227
261
|
activeChannel.on('deny', (data) => emitter.emit('error', data));
|
|
228
262
|
activeChannel.on('invalid', (data) => emitter.emit('error', data));
|
|
229
|
-
activeChannel.onMessage((message) =>
|
|
263
|
+
activeChannel.onMessage((message) => {
|
|
264
|
+
// why: Control traffic (heartbeat/size echoes, request/response envelopes) is SDK-internal; forwarding it would leak reserved __hf: types into consumer handlers.
|
|
265
|
+
if (isControlType(message.type)) {
|
|
266
|
+
requests.dispatch(message.type, message.data);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
emitter.emit(message.type, message.data);
|
|
270
|
+
});
|
|
230
271
|
activeChannel.connect();
|
|
231
272
|
}
|
|
232
273
|
return index_cjs_js$2.freeze({
|
|
233
274
|
send: (type, data) => channel?.send(type, data),
|
|
275
|
+
request: (type, data, options) => channel
|
|
276
|
+
? requests.request(type, data, options)
|
|
277
|
+
: index_cjs_js$4.promiseReject(index_cjs_js.createError(`Cannot send request '${type}': the feature is not connected to a host.`)),
|
|
278
|
+
handle: requests.handle,
|
|
234
279
|
on: emitter.on,
|
|
235
|
-
ready: () => index_cjs_js$
|
|
280
|
+
ready: () => index_cjs_js$4.createPromise((resolve) => {
|
|
236
281
|
if (opened) {
|
|
237
282
|
resolve();
|
|
238
283
|
return;
|
package/hostee/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { EventHandler, FeatureOptions } from '..';
|
|
1
|
+
import { RequestOptions, RequestHandler, EventHandler, FeatureOptions } from '..';
|
|
2
|
+
export { ActionDescription, FeatureContract, FeatureOptions, RequestHandler, RequestOptions } from '..';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Public handle returned by the hostee-side feature factory.
|
|
@@ -11,6 +12,45 @@ interface FeatureHandle {
|
|
|
11
12
|
* @param data - Optional payload for the action.
|
|
12
13
|
*/
|
|
13
14
|
send(type: string, data?: unknown): void;
|
|
15
|
+
/**
|
|
16
|
+
* Sends a request to the host and resolves with its response.
|
|
17
|
+
*
|
|
18
|
+
* The host answers through a handler it registered with its own
|
|
19
|
+
* `handle(type, handler)`. The promise rejects when the host's handler
|
|
20
|
+
* throws, when the host has no handler for the type, when no response
|
|
21
|
+
* arrives within the timeout (30 seconds by default), when the feature is
|
|
22
|
+
* not connected to a host, or when the host channel closes while the
|
|
23
|
+
* request is pending.
|
|
24
|
+
*
|
|
25
|
+
* @param type - Request action type, drawn from the feature contract.
|
|
26
|
+
* @param data - Optional payload for the request.
|
|
27
|
+
* @param options - Per-request settings such as `timeoutMs`.
|
|
28
|
+
* @returns A promise settling with the host's response payload.
|
|
29
|
+
*
|
|
30
|
+
* @example Asking the host for its settings
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const settings = await feature.request('getSettings', undefined, { timeoutMs: 5000 })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
request(type: string, data?: unknown, options?: RequestOptions): Promise<unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* Registers the handler that answers host requests of a given type.
|
|
38
|
+
*
|
|
39
|
+
* One handler per type: registering a second handler for a type that already
|
|
40
|
+
* has one throws. The handler may return the response value directly or a
|
|
41
|
+
* promise of it; a thrown error or rejected promise reaches the host as a
|
|
42
|
+
* failed response carrying the error's message.
|
|
43
|
+
*
|
|
44
|
+
* @param type - Request type to answer.
|
|
45
|
+
* @param handler - Receives the request payload and returns the response.
|
|
46
|
+
* @returns A function that unregisters this handler.
|
|
47
|
+
*
|
|
48
|
+
* @example Answering the host's request for the feature's state
|
|
49
|
+
* ```typescript
|
|
50
|
+
* feature.handle('getTime', (data) => formatTime(data))
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
handle(type: string, handler: RequestHandler): () => void;
|
|
14
54
|
/**
|
|
15
55
|
* Subscribes to host messages or lifecycle events (`open`, `close`, `error`).
|
|
16
56
|
*
|
package/hostee/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/hostee/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/hostee/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACzF,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|