@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/hostee/index.esm.js
CHANGED
|
@@ -2,13 +2,14 @@ import { createBroker } from '../_dependencies/@hyperfrontend/nexus/index.esm.js
|
|
|
2
2
|
import { isArray } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js';
|
|
3
3
|
import { createError } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js';
|
|
4
4
|
import '../_dependencies/@hyperfrontend/json-utils/index.esm.js';
|
|
5
|
-
import { freeze } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
6
|
-
import { createPromise } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js';
|
|
7
|
-
import { clearInterval, setInterval } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js';
|
|
5
|
+
import { freeze, values } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js';
|
|
6
|
+
import { createPromise, promiseResolve, promiseReject } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js';
|
|
7
|
+
import { clearTimeout, setTimeout, clearInterval, setInterval } from '../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js';
|
|
8
8
|
import { onElementResize } from '../_dependencies/@hyperfrontend/ui-utils/element/index.esm.js';
|
|
9
9
|
import { addStylesheet } from '../_dependencies/@hyperfrontend/ui-utils/style/index.esm.js';
|
|
10
|
-
import { ControlType, withControlContract } from '../_shared/shared/control/index.esm.js';
|
|
10
|
+
import { ControlType, isControlType, withControlContract } from '../_shared/shared/control/index.esm.js';
|
|
11
11
|
import { createEventEmitter } from '../_shared/shared/event-emitter/index.esm.js';
|
|
12
|
+
import { createRequestPeer } from '../_shared/shared/request/index.esm.js';
|
|
12
13
|
|
|
13
14
|
// note: Runtime validation shared by the host/hostee factories and the config loader.
|
|
14
15
|
/**
|
|
@@ -43,6 +44,31 @@ function collectActionListIssues(actions, field, issues) {
|
|
|
43
44
|
if (typeof action['type'] !== 'string' || action['type'].length === 0) {
|
|
44
45
|
issues.push(`"${field}[${index}]" must have a non-empty string "type".`);
|
|
45
46
|
}
|
|
47
|
+
if (action['respondsWith'] !== undefined && (typeof action['respondsWith'] !== 'string' || action['respondsWith'].length === 0)) {
|
|
48
|
+
issues.push(`"${field}[${index}]" has a "respondsWith" that must be a non-empty string.`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Collects every `respondsWith` reference that does not name an action in the other direction.
|
|
54
|
+
*
|
|
55
|
+
* A request emitted by one side is answered by an action the same side accepts (and
|
|
56
|
+
* vice versa), so each `respondsWith` must resolve across the contract's directions.
|
|
57
|
+
*
|
|
58
|
+
* @param actions - The already well-formed action list to check.
|
|
59
|
+
* @param field - The field name of `actions`, used to locate problems in messages.
|
|
60
|
+
* @param other - The action list of the opposite direction.
|
|
61
|
+
* @param otherField - The field name of `other`, used in messages.
|
|
62
|
+
* @param issues - The running list of human-readable problems, appended to in place.
|
|
63
|
+
*/
|
|
64
|
+
function collectRespondsWithIssues(actions, field, other, otherField, issues) {
|
|
65
|
+
actions.forEach((action, index) => {
|
|
66
|
+
if (action.respondsWith === undefined) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!other.some((candidate) => candidate.type === action.respondsWith)) {
|
|
70
|
+
issues.push(`"${field}[${index}]" responds with "${action.respondsWith}", but "${otherField}" has no action of that type.`);
|
|
71
|
+
}
|
|
46
72
|
});
|
|
47
73
|
}
|
|
48
74
|
/**
|
|
@@ -68,7 +94,7 @@ function describeType(value) {
|
|
|
68
94
|
*
|
|
69
95
|
* @param contract - The candidate contract, typically parsed from disk.
|
|
70
96
|
* @returns The validated contract, typed.
|
|
71
|
-
* @throws {Error} When the value is not an object,
|
|
97
|
+
* @throws {Error} When the value is not an object, any action is malformed, or a `respondsWith` names no action in the other direction.
|
|
72
98
|
*
|
|
73
99
|
* @example Validating a parsed contract file
|
|
74
100
|
* ```typescript
|
|
@@ -83,6 +109,12 @@ function validateContract(contract) {
|
|
|
83
109
|
const issues = [];
|
|
84
110
|
collectActionListIssues(contract['emitted'], 'emitted', issues);
|
|
85
111
|
collectActionListIssues(contract['accepted'], 'accepted', issues);
|
|
112
|
+
if (issues.length === 0) {
|
|
113
|
+
const emitted = contract['emitted'];
|
|
114
|
+
const accepted = contract['accepted'];
|
|
115
|
+
collectRespondsWithIssues(emitted, 'emitted', accepted, 'accepted', issues);
|
|
116
|
+
collectRespondsWithIssues(accepted, 'accepted', emitted, 'emitted', issues);
|
|
117
|
+
}
|
|
86
118
|
if (issues.length > 0) {
|
|
87
119
|
throw createError(`Invalid contract:\n${issues.map((issue) => ` - ${issue}`).join('\n')}`);
|
|
88
120
|
}
|
|
@@ -205,6 +237,7 @@ function resolveHostWindow(win) {
|
|
|
205
237
|
function createFeatureHandle(broker, hostWindow, emitter) {
|
|
206
238
|
let channel = null;
|
|
207
239
|
let opened = false;
|
|
240
|
+
const requests = createRequestPeer('feature', (type, data) => channel?.send(type, data));
|
|
208
241
|
if (hostWindow) {
|
|
209
242
|
const activeChannel = broker.addChannel('host', hostWindow);
|
|
210
243
|
channel = activeChannel;
|
|
@@ -221,14 +254,26 @@ function createFeatureHandle(broker, hostWindow, emitter) {
|
|
|
221
254
|
emitter.emit('close');
|
|
222
255
|
heartbeat.stop();
|
|
223
256
|
announcer.stop();
|
|
257
|
+
requests.rejectAll('The host channel closed before the host responded.');
|
|
224
258
|
});
|
|
225
259
|
activeChannel.on('deny', (data) => emitter.emit('error', data));
|
|
226
260
|
activeChannel.on('invalid', (data) => emitter.emit('error', data));
|
|
227
|
-
activeChannel.onMessage((message) =>
|
|
261
|
+
activeChannel.onMessage((message) => {
|
|
262
|
+
// why: Control traffic (heartbeat/size echoes, request/response envelopes) is SDK-internal; forwarding it would leak reserved __hf: types into consumer handlers.
|
|
263
|
+
if (isControlType(message.type)) {
|
|
264
|
+
requests.dispatch(message.type, message.data);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
emitter.emit(message.type, message.data);
|
|
268
|
+
});
|
|
228
269
|
activeChannel.connect();
|
|
229
270
|
}
|
|
230
271
|
return freeze({
|
|
231
272
|
send: (type, data) => channel?.send(type, data),
|
|
273
|
+
request: (type, data, options) => channel
|
|
274
|
+
? requests.request(type, data, options)
|
|
275
|
+
: promiseReject(createError(`Cannot send request '${type}': the feature is not connected to a host.`)),
|
|
276
|
+
handle: requests.handle,
|
|
232
277
|
on: emitter.on,
|
|
233
278
|
ready: () => createPromise((resolve) => {
|
|
234
279
|
if (opened) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/hostee/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/hostee/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,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,YAAY,GAAG,aAAa,CAsDzH"}
|
package/hostee/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
|
/**
|
|
3
4
|
* Public handle returned by the hostee-side feature factory.
|
|
4
5
|
*/
|
|
@@ -10,6 +11,45 @@ export interface FeatureHandle {
|
|
|
10
11
|
* @param data - Optional payload for the action.
|
|
11
12
|
*/
|
|
12
13
|
send(type: string, data?: unknown): void;
|
|
14
|
+
/**
|
|
15
|
+
* Sends a request to the host and resolves with its response.
|
|
16
|
+
*
|
|
17
|
+
* The host answers through a handler it registered with its own
|
|
18
|
+
* `handle(type, handler)`. The promise rejects when the host's handler
|
|
19
|
+
* throws, when the host has no handler for the type, when no response
|
|
20
|
+
* arrives within the timeout (30 seconds by default), when the feature is
|
|
21
|
+
* not connected to a host, or when the host channel closes while the
|
|
22
|
+
* request is pending.
|
|
23
|
+
*
|
|
24
|
+
* @param type - Request action type, drawn from the feature contract.
|
|
25
|
+
* @param data - Optional payload for the request.
|
|
26
|
+
* @param options - Per-request settings such as `timeoutMs`.
|
|
27
|
+
* @returns A promise settling with the host's response payload.
|
|
28
|
+
*
|
|
29
|
+
* @example Asking the host for its settings
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const settings = await feature.request('getSettings', undefined, { timeoutMs: 5000 })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
request(type: string, data?: unknown, options?: RequestOptions): Promise<unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Registers the handler that answers host requests of a given type.
|
|
37
|
+
*
|
|
38
|
+
* One handler per type: registering a second handler for a type that already
|
|
39
|
+
* has one throws. The handler may return the response value directly or a
|
|
40
|
+
* promise of it; a thrown error or rejected promise reaches the host as a
|
|
41
|
+
* failed response carrying the error's message.
|
|
42
|
+
*
|
|
43
|
+
* @param type - Request type to answer.
|
|
44
|
+
* @param handler - Receives the request payload and returns the response.
|
|
45
|
+
* @returns A function that unregisters this handler.
|
|
46
|
+
*
|
|
47
|
+
* @example Answering the host's request for the feature's state
|
|
48
|
+
* ```typescript
|
|
49
|
+
* feature.handle('getTime', (data) => formatTime(data))
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
handle(type: string, handler: RequestHandler): () => void;
|
|
13
53
|
/**
|
|
14
54
|
* Subscribes to host messages or lifecycle events (`open`, `close`, `error`).
|
|
15
55
|
*
|
package/hostee/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/hostee/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/hostee/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;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;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB;;OAEG;IACH,KAAK,IAAI,IAAI,CAAA;CACd"}
|
package/index.cjs.js
CHANGED
|
@@ -39,6 +39,31 @@ function collectActionListIssues(actions, field, issues) {
|
|
|
39
39
|
if (typeof action['type'] !== 'string' || action['type'].length === 0) {
|
|
40
40
|
issues.push(`"${field}[${index}]" must have a non-empty string "type".`);
|
|
41
41
|
}
|
|
42
|
+
if (action['respondsWith'] !== undefined && (typeof action['respondsWith'] !== 'string' || action['respondsWith'].length === 0)) {
|
|
43
|
+
issues.push(`"${field}[${index}]" has a "respondsWith" that must be a non-empty string.`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Collects every `respondsWith` reference that does not name an action in the other direction.
|
|
49
|
+
*
|
|
50
|
+
* A request emitted by one side is answered by an action the same side accepts (and
|
|
51
|
+
* vice versa), so each `respondsWith` must resolve across the contract's directions.
|
|
52
|
+
*
|
|
53
|
+
* @param actions - The already well-formed action list to check.
|
|
54
|
+
* @param field - The field name of `actions`, used to locate problems in messages.
|
|
55
|
+
* @param other - The action list of the opposite direction.
|
|
56
|
+
* @param otherField - The field name of `other`, used in messages.
|
|
57
|
+
* @param issues - The running list of human-readable problems, appended to in place.
|
|
58
|
+
*/
|
|
59
|
+
function collectRespondsWithIssues(actions, field, other, otherField, issues) {
|
|
60
|
+
actions.forEach((action, index) => {
|
|
61
|
+
if (action.respondsWith === undefined) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!other.some((candidate) => candidate.type === action.respondsWith)) {
|
|
65
|
+
issues.push(`"${field}[${index}]" responds with "${action.respondsWith}", but "${otherField}" has no action of that type.`);
|
|
66
|
+
}
|
|
42
67
|
});
|
|
43
68
|
}
|
|
44
69
|
/**
|
|
@@ -64,7 +89,7 @@ function describeType(value) {
|
|
|
64
89
|
*
|
|
65
90
|
* @param contract - The candidate contract, typically parsed from disk.
|
|
66
91
|
* @returns The validated contract, typed.
|
|
67
|
-
* @throws {Error} When the value is not an object,
|
|
92
|
+
* @throws {Error} When the value is not an object, any action is malformed, or a `respondsWith` names no action in the other direction.
|
|
68
93
|
*
|
|
69
94
|
* @example Validating a parsed contract file
|
|
70
95
|
* ```typescript
|
|
@@ -79,6 +104,12 @@ function validateContract(contract) {
|
|
|
79
104
|
const issues = [];
|
|
80
105
|
collectActionListIssues(contract['emitted'], 'emitted', issues);
|
|
81
106
|
collectActionListIssues(contract['accepted'], 'accepted', issues);
|
|
107
|
+
if (issues.length === 0) {
|
|
108
|
+
const emitted = contract['emitted'];
|
|
109
|
+
const accepted = contract['accepted'];
|
|
110
|
+
collectRespondsWithIssues(emitted, 'emitted', accepted, 'accepted', issues);
|
|
111
|
+
collectRespondsWithIssues(accepted, 'accepted', emitted, 'emitted', issues);
|
|
112
|
+
}
|
|
82
113
|
if (issues.length > 0) {
|
|
83
114
|
throw index_cjs_js.createError(`Invalid contract:\n${issues.map((issue) => ` - ${issue}`).join('\n')}`);
|
|
84
115
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { ValidationResult } from './_dependencies/@hyperfrontend/json-utils/index.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Per-request settings accepted by `request`.
|
|
5
|
+
*/
|
|
6
|
+
interface RequestOptions {
|
|
7
|
+
/** Milliseconds to wait for the response before rejecting; defaults to 30000. */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Answers one request type; may return the response value directly or a promise of it.
|
|
12
|
+
*
|
|
13
|
+
* @param data - The request payload sent by the other side.
|
|
14
|
+
* @returns The response value, or a promise that resolves to it.
|
|
15
|
+
*/
|
|
16
|
+
type RequestHandler = (data: unknown) => unknown;
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* Supported ways a host can surface an embedded feature.
|
|
5
20
|
*
|
|
@@ -65,6 +80,8 @@ interface ActionDescription {
|
|
|
65
80
|
description?: string;
|
|
66
81
|
/** Optional JSON-schema-like shape describing the action payload. */
|
|
67
82
|
schema?: object;
|
|
83
|
+
/** When this action is used as a request, the type of the action in the other direction that answers it. */
|
|
84
|
+
respondsWith?: string;
|
|
68
85
|
}
|
|
69
86
|
/**
|
|
70
87
|
* The set of actions a feature emits to, and accepts from, its counterpart.
|
|
@@ -78,6 +95,66 @@ interface FeatureContract {
|
|
|
78
95
|
/** Actions this side handles from the other side. */
|
|
79
96
|
accepted: ActionDescription[];
|
|
80
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Context handed to an {@link ExperiencePlugin} around a feature's mount lifecycle.
|
|
100
|
+
*/
|
|
101
|
+
interface ExperiencePluginContext {
|
|
102
|
+
/**
|
|
103
|
+
* The in-document root the display mode mounted: the iframe for `embedded`,
|
|
104
|
+
* the dialog container for `dialog`, and `null` for `popup` and
|
|
105
|
+
* `standalone`, which open a separate window with no in-document element.
|
|
106
|
+
*/
|
|
107
|
+
element: HTMLElement | null;
|
|
108
|
+
/** The display mode the feature was surfaced in. */
|
|
109
|
+
displayMode: DisplayMode;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Opt-in extension that decorates a feature's mount lifecycle (e.g. transitions, animations).
|
|
113
|
+
*
|
|
114
|
+
* Register plugins through {@link ShellOptions.plugins}. After each successful
|
|
115
|
+
* mount the shell calls `onMount` on every plugin in registration order; before
|
|
116
|
+
* each unmount it calls `onUnmount` one plugin at a time in reverse
|
|
117
|
+
* registration order, awaiting any returned promise, then runs the teardowns
|
|
118
|
+
* returned by `onMount` (also in reverse registration order) and finally
|
|
119
|
+
* removes the feature. The SDK ships no built-in plugins.
|
|
120
|
+
*
|
|
121
|
+
* @example Registering a fade-out plugin on a shell
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const shell = createShell({
|
|
124
|
+
* container: '#shell',
|
|
125
|
+
* plugins: [
|
|
126
|
+
* {
|
|
127
|
+
* name: 'fade',
|
|
128
|
+
* onUnmount: ({ element }) => (element ? animateFadeOut(element) : undefined),
|
|
129
|
+
* },
|
|
130
|
+
* ],
|
|
131
|
+
* })
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
interface ExperiencePlugin {
|
|
135
|
+
/** Unique plugin name, surfaced in debug logs. */
|
|
136
|
+
name: string;
|
|
137
|
+
/**
|
|
138
|
+
* Runs after the feature mounts; may animate it in and return a teardown.
|
|
139
|
+
*
|
|
140
|
+
* A thrown error surfaces as an `error` event on the shell and does not
|
|
141
|
+
* stop the remaining plugins from mounting.
|
|
142
|
+
*
|
|
143
|
+
* @param context - The mounted element and its display mode.
|
|
144
|
+
* @returns An optional teardown invoked on unmount, after every `onUnmount` has settled.
|
|
145
|
+
*/
|
|
146
|
+
onMount?(context: ExperiencePluginContext): void | (() => void);
|
|
147
|
+
/**
|
|
148
|
+
* Runs before the feature unmounts; may return a promise to defer teardown until an exit animation finishes.
|
|
149
|
+
*
|
|
150
|
+
* A rejected promise (or thrown error) surfaces as an `error` event on the
|
|
151
|
+
* shell and teardown continues with the remaining plugins.
|
|
152
|
+
*
|
|
153
|
+
* @param context - The mounted element and its display mode.
|
|
154
|
+
* @returns Optionally a promise the shell awaits before tearing down.
|
|
155
|
+
*/
|
|
156
|
+
onUnmount?(context: ExperiencePluginContext): void | Promise<void>;
|
|
157
|
+
}
|
|
81
158
|
/**
|
|
82
159
|
* Options accepted by the host-side {@link FeatureContract} consumer when
|
|
83
160
|
* creating or opening a shell.
|
|
@@ -87,7 +164,12 @@ interface ShellOptions {
|
|
|
87
164
|
container: string | HTMLElement;
|
|
88
165
|
/** Stable identifier for the feature; seeds the broker name surfaced in debug logs. */
|
|
89
166
|
name?: string;
|
|
90
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* The feature's contract exactly as the feature authored it (`emitted` = what
|
|
169
|
+
* the feature sends, `accepted` = what the feature handles). The shell
|
|
170
|
+
* derives the host-side orientation itself — hand it the feature's contract,
|
|
171
|
+
* never a pre-swapped copy. Replaces the generic default when provided.
|
|
172
|
+
*/
|
|
91
173
|
contract?: FeatureContract;
|
|
92
174
|
/** How the feature should be surfaced; defaults to {@link DisplayMode.Embedded}. */
|
|
93
175
|
displayMode?: DisplayMode;
|
|
@@ -109,6 +191,8 @@ interface ShellOptions {
|
|
|
109
191
|
protocol?: SecurityProtocol;
|
|
110
192
|
/** Pre-shared key used by the `v2` protocol. */
|
|
111
193
|
sharedKey?: string;
|
|
194
|
+
/** Experience plugins wrapped around each mount/unmount; `onMount` runs in registration order, `onUnmount` in reverse. */
|
|
195
|
+
plugins?: readonly ExperiencePlugin[];
|
|
112
196
|
}
|
|
113
197
|
/**
|
|
114
198
|
* Options accepted by the hostee-side feature factory.
|
|
@@ -163,6 +247,8 @@ interface ResolvedFeatureConfig extends FeatureConfig {
|
|
|
163
247
|
url: string;
|
|
164
248
|
/** Default display options baked into the connector as the feature's defaults. */
|
|
165
249
|
display?: DisplayDefaults;
|
|
250
|
+
/** Security envelope the build resolved; baked into the generated connector as its default. */
|
|
251
|
+
protocol?: SecurityProtocol;
|
|
166
252
|
}
|
|
167
253
|
/**
|
|
168
254
|
* A single feature app entry served by the dev server.
|
|
@@ -230,7 +316,7 @@ declare const defineDevConfig: (config: DevConfig) => DevConfig;
|
|
|
230
316
|
*
|
|
231
317
|
* @param contract - The candidate contract, typically parsed from disk.
|
|
232
318
|
* @returns The validated contract, typed.
|
|
233
|
-
* @throws {Error} When the value is not an object,
|
|
319
|
+
* @throws {Error} When the value is not an object, any action is malformed, or a `respondsWith` names no action in the other direction.
|
|
234
320
|
*
|
|
235
321
|
* @example Validating a parsed contract file
|
|
236
322
|
* ```typescript
|
|
@@ -279,4 +365,4 @@ declare const sdkInfo: {
|
|
|
279
365
|
};
|
|
280
366
|
|
|
281
367
|
export { DisplayMode, defineConfig, defineDevConfig, sdkInfo, validateContract, validateFeatureConfig, validatePayload };
|
|
282
|
-
export type { ActionDescription, DevConfig, DisplayDefaults, EmbedSizing, FeatureConfig, FeatureContract, FeatureOptions, ResolvedFeatureConfig, SecurityProtocol, ShellOptions, UnresponsiveInfo, UnresponsivePolicy };
|
|
368
|
+
export type { ActionDescription, DevConfig, DisplayDefaults, EmbedSizing, ExperiencePlugin, ExperiencePluginContext, FeatureConfig, FeatureContract, FeatureOptions, RequestHandler, RequestOptions, ResolvedFeatureConfig, SecurityProtocol, ShellOptions, UnresponsiveInfo, UnresponsivePolicy };
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../libs/features/src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../libs/features/src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtE,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA"}
|
package/index.esm.js
CHANGED
|
@@ -37,6 +37,31 @@ function collectActionListIssues(actions, field, issues) {
|
|
|
37
37
|
if (typeof action['type'] !== 'string' || action['type'].length === 0) {
|
|
38
38
|
issues.push(`"${field}[${index}]" must have a non-empty string "type".`);
|
|
39
39
|
}
|
|
40
|
+
if (action['respondsWith'] !== undefined && (typeof action['respondsWith'] !== 'string' || action['respondsWith'].length === 0)) {
|
|
41
|
+
issues.push(`"${field}[${index}]" has a "respondsWith" that must be a non-empty string.`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Collects every `respondsWith` reference that does not name an action in the other direction.
|
|
47
|
+
*
|
|
48
|
+
* A request emitted by one side is answered by an action the same side accepts (and
|
|
49
|
+
* vice versa), so each `respondsWith` must resolve across the contract's directions.
|
|
50
|
+
*
|
|
51
|
+
* @param actions - The already well-formed action list to check.
|
|
52
|
+
* @param field - The field name of `actions`, used to locate problems in messages.
|
|
53
|
+
* @param other - The action list of the opposite direction.
|
|
54
|
+
* @param otherField - The field name of `other`, used in messages.
|
|
55
|
+
* @param issues - The running list of human-readable problems, appended to in place.
|
|
56
|
+
*/
|
|
57
|
+
function collectRespondsWithIssues(actions, field, other, otherField, issues) {
|
|
58
|
+
actions.forEach((action, index) => {
|
|
59
|
+
if (action.respondsWith === undefined) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!other.some((candidate) => candidate.type === action.respondsWith)) {
|
|
63
|
+
issues.push(`"${field}[${index}]" responds with "${action.respondsWith}", but "${otherField}" has no action of that type.`);
|
|
64
|
+
}
|
|
40
65
|
});
|
|
41
66
|
}
|
|
42
67
|
/**
|
|
@@ -62,7 +87,7 @@ function describeType(value) {
|
|
|
62
87
|
*
|
|
63
88
|
* @param contract - The candidate contract, typically parsed from disk.
|
|
64
89
|
* @returns The validated contract, typed.
|
|
65
|
-
* @throws {Error} When the value is not an object,
|
|
90
|
+
* @throws {Error} When the value is not an object, any action is malformed, or a `respondsWith` names no action in the other direction.
|
|
66
91
|
*
|
|
67
92
|
* @example Validating a parsed contract file
|
|
68
93
|
* ```typescript
|
|
@@ -77,6 +102,12 @@ function validateContract(contract) {
|
|
|
77
102
|
const issues = [];
|
|
78
103
|
collectActionListIssues(contract['emitted'], 'emitted', issues);
|
|
79
104
|
collectActionListIssues(contract['accepted'], 'accepted', issues);
|
|
105
|
+
if (issues.length === 0) {
|
|
106
|
+
const emitted = contract['emitted'];
|
|
107
|
+
const accepted = contract['accepted'];
|
|
108
|
+
collectRespondsWithIssues(emitted, 'emitted', accepted, 'accepted', issues);
|
|
109
|
+
collectRespondsWithIssues(accepted, 'accepted', emitted, 'emitted', issues);
|
|
110
|
+
}
|
|
80
111
|
if (issues.length > 0) {
|
|
81
112
|
throw createError(`Invalid contract:\n${issues.map((issue) => ` - ${issue}`).join('\n')}`);
|
|
82
113
|
}
|