@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/_dependencies/@hyperfrontend/builder/bundle/dependencies/index.cjs.js +1 -0
  3. package/_dependencies/@hyperfrontend/builder/bundle/dependencies/index.esm.js +1 -0
  4. package/_dependencies/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js +1 -0
  5. package/_dependencies/@hyperfrontend/builder/bundle/dependencies/worker/index.esm.js +1 -0
  6. package/_dependencies/@hyperfrontend/builder/bundle/index.cjs.js +12 -10
  7. package/_dependencies/@hyperfrontend/builder/bundle/index.esm.js +14 -12
  8. package/_dependencies/@hyperfrontend/builder/bundle/rollup/index.cjs.js +2 -0
  9. package/_dependencies/@hyperfrontend/builder/bundle/rollup/index.esm.js +2 -0
  10. package/_dependencies/@hyperfrontend/builder/bundle/rollup/worker/index.cjs.js +2 -0
  11. package/_dependencies/@hyperfrontend/builder/bundle/rollup/worker/index.esm.js +2 -0
  12. package/_dependencies/@hyperfrontend/builder/index.cjs.js +87 -53
  13. package/_dependencies/@hyperfrontend/builder/index.esm.js +89 -55
  14. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js +4 -0
  15. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js +3 -1
  16. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/reflect/index.cjs.js +10 -0
  17. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/reflect/index.esm.js +6 -0
  18. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.cjs.js +5 -0
  19. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.esm.js +5 -1
  20. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/typed-arrays/index.cjs.js +2 -2
  21. package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/typed-arrays/index.esm.js +2 -2
  22. package/_dependencies/@hyperfrontend/network-protocol/browser/channel/index.cjs.js +5 -19
  23. package/_dependencies/@hyperfrontend/network-protocol/browser/channel/index.esm.js +1 -15
  24. package/_dependencies/@hyperfrontend/network-protocol/browser/data/index.cjs.js +15 -23
  25. package/_dependencies/@hyperfrontend/network-protocol/browser/data/index.esm.js +7 -15
  26. package/_dependencies/@hyperfrontend/network-protocol/browser/packet/index.cjs.js +6 -14
  27. package/_dependencies/@hyperfrontend/network-protocol/browser/packet/index.esm.js +7 -15
  28. package/_dependencies/@hyperfrontend/network-protocol/browser/receiver/index.cjs.js +4 -18
  29. package/_dependencies/@hyperfrontend/network-protocol/browser/receiver/index.esm.js +1 -15
  30. package/_dependencies/@hyperfrontend/network-protocol/browser/sender/index.cjs.js +5 -19
  31. package/_dependencies/@hyperfrontend/network-protocol/browser/sender/index.esm.js +2 -16
  32. package/_dependencies/@hyperfrontend/network-protocol/browser/v1/index.cjs.js +16 -24
  33. package/_dependencies/@hyperfrontend/network-protocol/browser/v1/index.esm.js +7 -15
  34. package/_dependencies/@hyperfrontend/network-protocol/browser/v2/index.cjs.js +16 -24
  35. package/_dependencies/@hyperfrontend/network-protocol/browser/v2/index.esm.js +7 -15
  36. package/_dependencies/@hyperfrontend/network-protocol/node/channel/index.cjs.js +3 -17
  37. package/_dependencies/@hyperfrontend/network-protocol/node/channel/index.esm.js +1 -15
  38. package/_dependencies/@hyperfrontend/network-protocol/node/data/index.cjs.js +6 -14
  39. package/_dependencies/@hyperfrontend/network-protocol/node/data/index.esm.js +7 -15
  40. package/_dependencies/@hyperfrontend/network-protocol/node/packet/index.cjs.js +6 -14
  41. package/_dependencies/@hyperfrontend/network-protocol/node/packet/index.esm.js +7 -15
  42. package/_dependencies/@hyperfrontend/network-protocol/node/receiver/index.cjs.js +3 -17
  43. package/_dependencies/@hyperfrontend/network-protocol/node/receiver/index.esm.js +1 -15
  44. package/_dependencies/@hyperfrontend/network-protocol/node/sender/index.cjs.js +2 -16
  45. package/_dependencies/@hyperfrontend/network-protocol/node/sender/index.esm.js +2 -16
  46. package/_dependencies/@hyperfrontend/network-protocol/node/v1/index.cjs.js +6 -14
  47. package/_dependencies/@hyperfrontend/network-protocol/node/v1/index.esm.js +7 -15
  48. package/_dependencies/@hyperfrontend/network-protocol/node/v2/index.cjs.js +6 -14
  49. package/_dependencies/@hyperfrontend/network-protocol/node/v2/index.esm.js +7 -15
  50. package/_dependencies/@hyperfrontend/nexus/index.cjs.js +49 -19
  51. package/_dependencies/@hyperfrontend/nexus/index.esm.js +49 -19
  52. package/_dependencies/@hyperfrontend/project-scope/core/fs/index.cjs.js +62 -0
  53. package/_dependencies/@hyperfrontend/project-scope/core/fs/index.esm.js +60 -2
  54. package/_shared/generators/feature/generate-feature-module/index.esm.js +11 -6
  55. package/_shared/generators/metadata/generate-metadata/index.esm.js +1 -0
  56. package/_shared/shared/control/index.cjs.js +12 -2
  57. package/_shared/shared/control/index.esm.js +12 -2
  58. package/_shared/shared/request/index.cjs.js +91 -0
  59. package/_shared/shared/request/index.esm.js +88 -0
  60. package/_shared/shared/shutdown/index.esm.js +12 -0
  61. package/bin/hf.js +643 -70
  62. package/bundle/host/index.iife.js +290 -4041
  63. package/bundle/host/index.iife.min.js +1 -1
  64. package/bundle/host/index.umd.js +290 -4041
  65. package/bundle/host/index.umd.min.js +1 -1
  66. package/bundle/hostee/index.iife.js +215 -2893
  67. package/bundle/hostee/index.iife.min.js +1 -1
  68. package/bundle/hostee/index.umd.js +215 -2893
  69. package/bundle/hostee/index.umd.min.js +1 -1
  70. package/cli/args.d.ts +2 -0
  71. package/cli/args.d.ts.map +1 -1
  72. package/cli/commands/build.d.ts +8 -5
  73. package/cli/commands/build.d.ts.map +1 -1
  74. package/cli/commands/dev.d.ts +7 -2
  75. package/cli/commands/dev.d.ts.map +1 -1
  76. package/cli/config/resolve.d.ts +3 -1
  77. package/cli/config/resolve.d.ts.map +1 -1
  78. package/cli/index.cjs.js +643 -70
  79. package/cli/index.d.ts +21 -10
  80. package/cli/index.esm.js +591 -60
  81. package/cli/usage.d.ts +1 -1
  82. package/cli/usage.d.ts.map +1 -1
  83. package/generators/feature/generate-feature-module.d.ts.map +1 -1
  84. package/generators/index.cjs.js +435 -42
  85. package/generators/index.d.ts +9 -8
  86. package/generators/index.esm.js +404 -30
  87. package/generators/metadata/generate-metadata.d.ts +4 -4
  88. package/generators/metadata/generate-metadata.d.ts.map +1 -1
  89. package/generators/shell/connector-types.d.ts +19 -0
  90. package/generators/shell/connector-types.d.ts.map +1 -0
  91. package/generators/shell/generate-shell.d.ts +5 -4
  92. package/generators/shell/generate-shell.d.ts.map +1 -1
  93. package/generators/shell/schema-type.d.ts +20 -0
  94. package/generators/shell/schema-type.d.ts.map +1 -0
  95. package/generators/shell/source-literal.d.ts +28 -0
  96. package/generators/shell/source-literal.d.ts.map +1 -1
  97. package/host/create-shell.d.ts +4 -1
  98. package/host/create-shell.d.ts.map +1 -1
  99. package/host/display-modes/dialog.d.ts +1 -1
  100. package/host/display-modes/dialog.d.ts.map +1 -1
  101. package/host/display-modes/embedded.d.ts +1 -1
  102. package/host/display-modes/embedded.d.ts.map +1 -1
  103. package/host/index.cjs.js +150 -30
  104. package/host/index.d.ts +53 -38
  105. package/host/index.d.ts.map +1 -1
  106. package/host/index.esm.js +129 -9
  107. package/host/lifecycle.d.ts.map +1 -1
  108. package/host/plugins.d.ts +1 -34
  109. package/host/plugins.d.ts.map +1 -1
  110. package/host/types.d.ts +49 -0
  111. package/host/types.d.ts.map +1 -1
  112. package/hostee/index.cjs.js +54 -9
  113. package/hostee/index.d.ts +41 -1
  114. package/hostee/index.d.ts.map +1 -1
  115. package/hostee/index.esm.js +51 -6
  116. package/hostee/lifecycle.d.ts.map +1 -1
  117. package/hostee/types.d.ts +40 -0
  118. package/hostee/types.d.ts.map +1 -1
  119. package/index.cjs.js +32 -1
  120. package/index.d.ts +89 -3
  121. package/index.d.ts.map +1 -1
  122. package/index.esm.js +32 -1
  123. package/nx/executors/build/index.cjs.js +14975 -137
  124. package/nx/executors/build/index.esm.js +14935 -115
  125. package/nx/executors/serve/executor.d.ts.map +1 -1
  126. package/nx/executors/serve/index.cjs.js +6594 -80
  127. package/nx/executors/serve/index.esm.js +6529 -44
  128. package/nx/generators/feature/index.cjs.js +8751 -108
  129. package/nx/generators/feature/index.esm.js +8711 -81
  130. package/package.json +15 -5
  131. package/server/debug-ui/index.d.ts +2 -0
  132. package/server/debug-ui/index.d.ts.map +1 -0
  133. package/server/debug-ui/index.html +15 -0
  134. package/server/debug-ui/index.iife.js +427 -0
  135. package/server/debug-ui/index.iife.min.js +1 -0
  136. package/server/dev-server.d.ts.map +1 -1
  137. package/server/index.cjs.js +78 -10
  138. package/server/index.esm.js +78 -11
  139. package/server/module-dir.d.ts +17 -0
  140. package/server/module-dir.d.ts.map +1 -0
  141. package/server/module-dir.stub.d.ts +15 -0
  142. package/server/module-dir.stub.d.ts.map +1 -0
  143. package/shared/contract.d.ts +1 -1
  144. package/shared/contract.d.ts.map +1 -1
  145. package/shared/control.d.ts +4 -0
  146. package/shared/control.d.ts.map +1 -1
  147. package/shared/invert-contract.d.ts +20 -0
  148. package/shared/invert-contract.d.ts.map +1 -0
  149. package/shared/request.d.ts +68 -0
  150. package/shared/request.d.ts.map +1 -0
  151. package/{nx/shared → shared}/shutdown.d.ts +3 -2
  152. package/shared/shutdown.d.ts.map +1 -0
  153. package/shared/types.d.ts +72 -1
  154. package/shared/types.d.ts.map +1 -1
  155. package/_shared/nx/shared/context/index.cjs.js +0 -18
  156. package/_shared/nx/shared/context/index.esm.js +0 -16
  157. package/nx/shared/shutdown.d.ts.map +0 -1
  158. package/server/debug-ui/bootstrap.d.ts +0 -2
  159. package/server/debug-ui/bootstrap.d.ts.map +0 -1
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Projects a JSON-schema-like payload description into TypeScript type source.
3
+ *
4
+ * Bounded mapping: scalar `type`s, `object` with `properties`/`required`,
5
+ * `array` with a single `items` schema, `enum`, and `const` are projected;
6
+ * anything else (including a missing schema) falls back to `unknown`, so a
7
+ * generated type is never wrong — at worst it is loose.
8
+ *
9
+ * @param schema - The schema value to project.
10
+ * @param indent - Indentation prefix applied to nested object members.
11
+ * @returns The TypeScript type as source text.
12
+ *
13
+ * @example Projecting an action payload schema
14
+ * ```typescript
15
+ * schemaToType({ type: 'object', properties: { tz: { type: 'string' } }, required: ['tz'] })
16
+ * // => '{\n tz: string\n}'
17
+ * ```
18
+ */
19
+ export declare function schemaToType(schema: unknown, indent?: string): string;
20
+ //# sourceMappingURL=schema-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-type.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/generators/shell/schema-type.ts"],"names":[],"mappings":"AA2FA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,CA0BjE"}
@@ -1,3 +1,31 @@
1
+ /**
2
+ * Renders a string as a single-quoted TypeScript string literal.
3
+ *
4
+ * Embedded double quotes are left bare (legal inside single quotes); single
5
+ * quotes and backslashes are escaped; control characters keep their JSON escape.
6
+ *
7
+ * @param value - The string to render.
8
+ * @returns A single-quoted, safely escaped string literal.
9
+ *
10
+ * @example Quoting a string containing a single quote
11
+ * ```typescript
12
+ * quoteString("it's") // => "'it\\'s'"
13
+ * ```
14
+ */
15
+ export declare function quoteString(value: string): string;
16
+ /**
17
+ * Renders an object key, leaving valid identifiers bare and quoting the rest.
18
+ *
19
+ * @param key - The property name.
20
+ * @returns The key as written in an object literal.
21
+ *
22
+ * @example Formatting identifier and non-identifier keys
23
+ * ```typescript
24
+ * formatKey('url') // => 'url'
25
+ * formatKey('time-zone') // => "'time-zone'"
26
+ * ```
27
+ */
28
+ export declare function formatKey(key: string): string;
1
29
  /**
2
30
  * Renders a JSON-compatible value as formatted TypeScript source.
3
31
  *
@@ -1 +1 @@
1
- {"version":3,"file":"source-literal.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/generators/shell/source-literal.ts"],"names":[],"mappings":"AAgFA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,CAwBnE"}
1
+ {"version":3,"file":"source-literal.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/generators/shell/source-literal.ts"],"names":[],"mappings":"AA0CA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBjD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,CAwBnE"}
@@ -21,7 +21,10 @@ export declare function deriveShellName(options: ShellOptions, sequence: number)
21
21
  * Creates a host-side shell for embedding a feature.
22
22
  *
23
23
  * Provisions a nexus broker and returns a handle whose `open` mounts the feature
24
- * in the requested display mode.
24
+ * in the requested display mode. The `contract` option takes the feature's
25
+ * contract exactly as the feature authored it; the shell derives the host-side
26
+ * orientation itself, so the handle sends what the feature accepts and receives
27
+ * what the feature emits.
25
28
  *
26
29
  * @param options - Create-time shell options, overridable per `open` call.
27
30
  * @returns A handle exposing `open`, `close`, `destroy`, `send`, `on`, and `isOpen`.
@@ -1 +1 @@
1
- {"version":3,"file":"create-shell.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/create-shell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA6E1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CAK9D"}
1
+ {"version":3,"file":"create-shell.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/create-shell.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA8E1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CAM9D"}
@@ -5,7 +5,7 @@ import type { DisplayModeMount } from '../types';
5
5
  * @param context - Inputs the shell passes to this display mode.
6
6
  * @param context.options - The merged shell options.
7
7
  * @param context.requestClose - Requests the shell close itself.
8
- * @returns The iframe content window and a teardown that unmounts the dialog.
8
+ * @returns The iframe content window, the dialog container as the mounted element, and a teardown that unmounts the dialog.
9
9
  *
10
10
  * @example Mounting a dialog
11
11
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/host/display-modes/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAOhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,EAAE,gBAgDzB,CAAA"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/host/display-modes/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAOhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,EAAE,gBAiDzB,CAAA"}
@@ -4,7 +4,7 @@ import type { DisplayModeMount } from '../types';
4
4
  *
5
5
  * @param context - Inputs the shell passes to this display mode.
6
6
  * @param context.options - The merged shell options.
7
- * @returns The iframe content window and a teardown that removes the iframe.
7
+ * @returns The iframe content window, the iframe as the mounted element, and a teardown that removes the iframe.
8
8
  *
9
9
  * @example Mounting embedded
10
10
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"embedded.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/host/display-modes/embedded.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAGhD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,gBAS3B,CAAA"}
1
+ {"version":3,"file":"embedded.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/features/src/host/display-modes/embedded.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAGhD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,gBAU3B,CAAA"}
package/host/index.cjs.js CHANGED
@@ -1,17 +1,52 @@
1
1
  'use strict';
2
2
 
3
3
  const index_cjs_js = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js');
4
- const index_cjs_js$8 = require('../_dependencies/@hyperfrontend/nexus/index.cjs.js');
5
- const index_cjs_js$1 = require('../_dependencies/@hyperfrontend/ui-utils/element/index.cjs.js');
6
- const index_cjs_js$2 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
7
- const index_cjs_js$3 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js');
8
- const index_cjs_js$5 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.cjs.js');
9
- const index_cjs_js$4 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.cjs.js');
10
- const index_cjs_js$6 = require('../_dependencies/@hyperfrontend/network-protocol/browser/v1/index.cjs.js');
11
- const index_cjs_js$7 = require('../_dependencies/@hyperfrontend/network-protocol/browser/v2/index.cjs.js');
4
+ const index_cjs_js$a = require('../_dependencies/@hyperfrontend/nexus/index.cjs.js');
5
+ const index_cjs_js$1 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js');
6
+ const index_cjs_js$2 = require('../_dependencies/@hyperfrontend/ui-utils/element/index.cjs.js');
7
+ const index_cjs_js$3 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
8
+ const index_cjs_js$4 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js');
9
+ const index_cjs_js$6 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.cjs.js');
10
+ const index_cjs_js$5 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/timers/index.cjs.js');
11
+ const index_cjs_js$7 = require('../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js');
12
+ const index_cjs_js$8 = require('../_dependencies/@hyperfrontend/network-protocol/browser/v1/index.cjs.js');
13
+ const index_cjs_js$9 = require('../_dependencies/@hyperfrontend/network-protocol/browser/v2/index.cjs.js');
12
14
  const { DisplayMode } = require('../_shared/shared/types/index.cjs.js');
13
15
  const { ControlType, isControlType, withControlContract } = require('../_shared/shared/control/index.cjs.js');
14
16
  const { createEventEmitter } = require('../_shared/shared/event-emitter/index.cjs.js');
17
+ const { createRequestPeer } = require('../_shared/shared/request/index.cjs.js');
18
+
19
+ /**
20
+ * Deep-copies an action list so the returned contract shares no object
21
+ * references with its source.
22
+ *
23
+ * @param actions - The action descriptions to copy.
24
+ * @returns Fresh action objects with identical contents.
25
+ */
26
+ function copyActions(actions) {
27
+ // 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.
28
+ return actions.map((action) => index_cjs_js$1.parse(index_cjs_js$1.stringify(action)));
29
+ }
30
+ /**
31
+ * Inverts a feature-authored contract into the host's perspective.
32
+ *
33
+ * A feature contract is written from the feature's point of view: `emitted` is
34
+ * what the feature sends, `accepted` is what the feature handles. The host
35
+ * channel needs the mirror image — it sends what the feature accepts and
36
+ * accepts what the feature emits — so the two lists are swapped.
37
+ *
38
+ * @param contract - The contract as authored by the feature.
39
+ * @returns A fresh contract oriented to the host side.
40
+ *
41
+ * @example Deriving the host-side contract
42
+ * ```typescript
43
+ * const host = invertFeatureContract({ emitted: [{ type: 'tick' }], accepted: [{ type: 'setTimezone' }] })
44
+ * // => { emitted: [{ type: 'setTimezone' }], accepted: [{ type: 'tick' }] }
45
+ * ```
46
+ */
47
+ function invertFeatureContract(contract) {
48
+ return { emitted: copyActions(contract.accepted), accepted: copyActions(contract.emitted) };
49
+ }
15
50
 
16
51
  // note: Borderless and transparent so the embedded feature blends into the host page.
17
52
  /**
@@ -31,7 +66,7 @@ function resolveContainer(container) {
31
66
  }
32
67
  const found = document.querySelector(container);
33
68
  if (found === null) {
34
- throw index_cjs_js$2.createError(`Shell container not found for selector "${container}".`);
69
+ throw index_cjs_js$3.createError(`Shell container not found for selector "${container}".`);
35
70
  }
36
71
  return found;
37
72
  }
@@ -48,7 +83,7 @@ function resolveContainer(container) {
48
83
  * ```
49
84
  */
50
85
  function createFeatureIframe(url) {
51
- const iframe = index_cjs_js$1.createElement('iframe', {
86
+ const iframe = index_cjs_js$2.createElement('iframe', {
52
87
  inlineStyle: { border: 'none', width: '100%', height: '100%' },
53
88
  }).ref;
54
89
  iframe.src = url;
@@ -87,7 +122,7 @@ function resolveDialogSize() {
87
122
  width = maxWidth;
88
123
  height = width / SIZE_CONFIG.aspectRatio;
89
124
  }
90
- return { width: index_cjs_js$3.round(width), height: index_cjs_js$3.round(height) };
125
+ return { width: index_cjs_js$4.round(width), height: index_cjs_js$4.round(height) };
91
126
  }
92
127
  /**
93
128
  * Dynamic default size for dialog and popup modes, recomputed on each access
@@ -109,7 +144,7 @@ const dialogDefaults = index_cjs_js.freeze({
109
144
  * @param context - Inputs the shell passes to this display mode.
110
145
  * @param context.options - The merged shell options.
111
146
  * @param context.requestClose - Requests the shell close itself.
112
- * @returns The iframe content window and a teardown that unmounts the dialog.
147
+ * @returns The iframe content window, the dialog container as the mounted element, and a teardown that unmounts the dialog.
113
148
  *
114
149
  * @example Mounting a dialog
115
150
  * ```typescript
@@ -117,13 +152,13 @@ const dialogDefaults = index_cjs_js.freeze({
117
152
  * ```
118
153
  */
119
154
  const mountDialog = ({ options, requestClose }) => {
120
- const container = index_cjs_js$1.div({ inlineStyle: { position: 'fixed', inset: '0', zIndex: '2147483647' } });
155
+ const container = index_cjs_js$2.div({ inlineStyle: { position: 'fixed', inset: '0', zIndex: '2147483647' } });
121
156
  if (options.dialogOverlay ?? true) {
122
- const backdrop = index_cjs_js$1.div({ inlineStyle: { position: 'absolute', inset: '0', background: 'rgba(0, 0, 0, 0.5)' } });
157
+ const backdrop = index_cjs_js$2.div({ inlineStyle: { position: 'absolute', inset: '0', background: 'rgba(0, 0, 0, 0.5)' } });
123
158
  backdrop.ref.addEventListener('click', () => requestClose());
124
159
  container.addChild(backdrop);
125
160
  }
126
- const dialog = index_cjs_js$1.div({
161
+ const dialog = index_cjs_js$2.div({
127
162
  inlineStyle: {
128
163
  position: 'absolute',
129
164
  top: '50%',
@@ -134,7 +169,7 @@ const mountDialog = ({ options, requestClose }) => {
134
169
  background: '#fff',
135
170
  },
136
171
  });
137
- const closeButton = index_cjs_js$1.button();
172
+ const closeButton = index_cjs_js$2.button();
138
173
  closeButton.ref.type = 'button';
139
174
  closeButton.ref.textContent = '×';
140
175
  closeButton.ref.setAttribute('aria-label', 'Close');
@@ -153,6 +188,7 @@ const mountDialog = ({ options, requestClose }) => {
153
188
  document.addEventListener('keydown', onKeydown);
154
189
  return {
155
190
  target: iframe.contentWindow,
191
+ element: container.ref,
156
192
  cleanup: () => {
157
193
  document.removeEventListener('keydown', onKeydown);
158
194
  container.ref.remove();
@@ -165,7 +201,7 @@ const mountDialog = ({ options, requestClose }) => {
165
201
  *
166
202
  * @param context - Inputs the shell passes to this display mode.
167
203
  * @param context.options - The merged shell options.
168
- * @returns The iframe content window and a teardown that removes the iframe.
204
+ * @returns The iframe content window, the iframe as the mounted element, and a teardown that removes the iframe.
169
205
  *
170
206
  * @example Mounting embedded
171
207
  * ```typescript
@@ -178,6 +214,7 @@ const mountEmbedded = ({ options }) => {
178
214
  container.appendChild(iframe);
179
215
  return {
180
216
  target: iframe.contentWindow,
217
+ element: iframe,
181
218
  frame: iframe,
182
219
  cleanup: () => iframe.remove(),
183
220
  };
@@ -285,20 +322,20 @@ function createHeartbeatMonitor(onUnresponsive) {
285
322
  let lastBeatAt = null;
286
323
  let timer;
287
324
  const stop = () => {
288
- index_cjs_js$4.clearInterval(timer);
325
+ index_cjs_js$5.clearInterval(timer);
289
326
  timer = undefined;
290
327
  };
291
328
  return {
292
329
  beat() {
293
330
  missed = 0;
294
- lastBeatAt = index_cjs_js$5.dateNow();
331
+ lastBeatAt = index_cjs_js$6.dateNow();
295
332
  },
296
333
  start() {
297
334
  if (timer !== undefined) {
298
335
  return;
299
336
  }
300
337
  missed = 0;
301
- timer = index_cjs_js$4.setInterval(() => {
338
+ timer = index_cjs_js$5.setInterval(() => {
302
339
  missed += 1;
303
340
  if (missed >= MISS_THRESHOLD) {
304
341
  stop();
@@ -350,6 +387,12 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
350
387
  let opened = false;
351
388
  let openCount = 0;
352
389
  let monitor = null;
390
+ let plugins = null;
391
+ let pendingUnmount = null;
392
+ let queuedOpen = null;
393
+ // why: One peer outlives every open/close cycle so handlers registered before the first open (or across reopens) keep answering feature requests.
394
+ const requests = createRequestPeer('host', (type, data) => channel?.send(type, data));
395
+ const emitError = (error) => emitter.emit('error', error);
353
396
  const runCleanup = () => {
354
397
  if (cleanup) {
355
398
  cleanup();
@@ -362,8 +405,45 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
362
405
  monitor = null;
363
406
  }
364
407
  };
365
- const destroy = () => {
366
- stopMonitor();
408
+ const mountPlugins = (registered, element, displayMode) => {
409
+ const context = index_cjs_js.freeze({ element, displayMode });
410
+ const teardowns = [];
411
+ for (const plugin of registered) {
412
+ try {
413
+ const teardown = plugin.onMount?.(context);
414
+ if (teardown) {
415
+ teardowns.push(teardown);
416
+ }
417
+ }
418
+ catch (error) {
419
+ emitError(error);
420
+ }
421
+ }
422
+ plugins = { registered, context, teardowns };
423
+ };
424
+ const startUnmount = (state, finish) => {
425
+ plugins = null;
426
+ let chain = index_cjs_js$7.promiseResolve();
427
+ for (const plugin of [...state.registered].reverse()) {
428
+ chain = chain.then(() => plugin.onUnmount?.(state.context)).catch(emitError);
429
+ }
430
+ pendingUnmount = chain.then(() => {
431
+ for (const teardown of [...state.teardowns].reverse()) {
432
+ try {
433
+ teardown();
434
+ }
435
+ catch (error) {
436
+ emitError(error);
437
+ }
438
+ }
439
+ finish();
440
+ pendingUnmount = null;
441
+ const reopen = queuedOpen;
442
+ queuedOpen = null;
443
+ reopen?.();
444
+ });
445
+ };
446
+ const releaseChannelAndCleanup = () => {
367
447
  if (channel) {
368
448
  channel.destroy();
369
449
  channel = null;
@@ -371,6 +451,20 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
371
451
  opened = false;
372
452
  runCleanup();
373
453
  };
454
+ const destroy = () => {
455
+ requests.rejectAll('The shell was destroyed before the feature responded.');
456
+ queuedOpen = null;
457
+ if (pendingUnmount) {
458
+ return;
459
+ }
460
+ stopMonitor();
461
+ const state = plugins;
462
+ if (state) {
463
+ startUnmount(state, releaseChannelAndCleanup);
464
+ return;
465
+ }
466
+ releaseChannelAndCleanup();
467
+ };
374
468
  const close = () => {
375
469
  if (channel) {
376
470
  channel.disconnect();
@@ -384,20 +478,28 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
384
478
  policy({ missedBeats, lastBeatAt, displayMode: options.displayMode ?? DisplayMode.Embedded, close, destroy });
385
479
  return;
386
480
  }
387
- emitter.emit('error', index_cjs_js$2.createError('Feature became unresponsive.'));
481
+ emitter.emit('error', index_cjs_js$3.createError('Feature became unresponsive.'));
388
482
  if (policy === 'unmount') {
389
483
  destroy();
390
484
  }
391
485
  };
392
486
  const open = (overrides) => {
393
487
  destroy();
488
+ if (pendingUnmount) {
489
+ queuedOpen = () => open(overrides);
490
+ return;
491
+ }
394
492
  const options = { ...baseOptions, ...overrides };
395
- const result = wiring.selectMount(options.displayMode ?? DisplayMode.Embedded)({ options, requestClose: close });
493
+ const displayMode = options.displayMode ?? DisplayMode.Embedded;
494
+ const result = wiring.selectMount(displayMode)({ options, requestClose: close });
396
495
  cleanup = result.cleanup;
397
496
  if (result.target === null) {
398
- emitter.emit('error', index_cjs_js$2.createError('Feature window could not be opened.'));
497
+ emitter.emit('error', index_cjs_js$3.createError('Feature window could not be opened.'));
399
498
  return;
400
499
  }
500
+ if (options.plugins && options.plugins.length > 0) {
501
+ mountPlugins(options.plugins, result.element ?? null, displayMode);
502
+ }
401
503
  channel = broker.addChannel(`feature-${(openCount += 1)}`, result.target, wiring.registerSecurity(broker, options.protocol, options.sharedKey));
402
504
  const sizeFrame = options.embedSizing === 'content' ? result.frame : undefined;
403
505
  const activeMonitor = wiring.createHeartbeatMonitor((missedBeats, lastBeatAt) => applyUnresponsive(options, missedBeats, lastBeatAt));
@@ -411,6 +513,15 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
411
513
  opened = false;
412
514
  emitter.emit('close');
413
515
  stopMonitor();
516
+ requests.rejectAll('The feature channel closed before the feature responded.');
517
+ if (pendingUnmount) {
518
+ return;
519
+ }
520
+ const state = plugins;
521
+ if (state) {
522
+ startUnmount(state, runCleanup);
523
+ return;
524
+ }
414
525
  runCleanup();
415
526
  });
416
527
  channel.on('deny', (data) => emitter.emit('error', data));
@@ -423,6 +534,9 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
423
534
  else if (message.type === ControlType.Size && sizeFrame) {
424
535
  applyContentSize(sizeFrame, message.data);
425
536
  }
537
+ else {
538
+ requests.dispatch(message.type, message.data);
539
+ }
426
540
  return;
427
541
  }
428
542
  emitter.emit(message.type, message.data);
@@ -434,6 +548,8 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
434
548
  close,
435
549
  destroy,
436
550
  send: (type, data) => channel?.send(type, data),
551
+ request: (type, data, options) => channel ? requests.request(type, data, options) : index_cjs_js$7.promiseReject(index_cjs_js$3.createError(`Cannot send request '${type}': the shell is not open.`)),
552
+ handle: requests.handle,
437
553
  on: emitter.on,
438
554
  get isOpen() {
439
555
  return opened;
@@ -458,11 +574,11 @@ function createShellHandle(broker, baseOptions, emitter, wiring) {
458
574
  */
459
575
  function registerSecurity(broker, protocol, sharedKey) {
460
576
  if (protocol === 'v1') {
461
- broker.registerProtocol('v1', index_cjs_js$6.createProtocol(broker.logger));
577
+ broker.registerProtocol('v1', index_cjs_js$8.createProtocol(broker.logger));
462
578
  return { security: { protocol: 'v1' } };
463
579
  }
464
580
  if (protocol === 'v2') {
465
- broker.registerProtocol('v2', index_cjs_js$7.createProtocol(broker.logger, sharedKey ?? ''));
581
+ broker.registerProtocol('v2', index_cjs_js$9.createProtocol(broker.logger, sharedKey ?? ''));
466
582
  return { security: { protocol: 'v2', sharedKey } };
467
583
  }
468
584
  return undefined;
@@ -558,7 +674,10 @@ function deriveShellName(options, sequence) {
558
674
  * Creates a host-side shell for embedding a feature.
559
675
  *
560
676
  * Provisions a nexus broker and returns a handle whose `open` mounts the feature
561
- * in the requested display mode.
677
+ * in the requested display mode. The `contract` option takes the feature's
678
+ * contract exactly as the feature authored it; the shell derives the host-side
679
+ * orientation itself, so the handle sends what the feature accepts and receives
680
+ * what the feature emits.
562
681
  *
563
682
  * @param options - Create-time shell options, overridable per `open` call.
564
683
  * @returns A handle exposing `open`, `close`, `destroy`, `send`, `on`, and `isOpen`.
@@ -572,8 +691,9 @@ function deriveShellName(options, sequence) {
572
691
  */
573
692
  function createShell(options) {
574
693
  const emitter = createEventEmitter();
575
- const contract = withControlContract((options.contract ?? index_cjs_js$8.DEFAULT_CONTRACT));
576
- const broker = index_cjs_js$8.createBroker({ name: deriveShellName(options, (shellCount += 1)), contract });
694
+ // 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.
695
+ const contract = withControlContract(options.contract ? invertFeatureContract(options.contract) : index_cjs_js$a.DEFAULT_CONTRACT);
696
+ const broker = index_cjs_js$a.createBroker({ name: deriveShellName(options, (shellCount += 1)), contract });
577
697
  return createShellHandle(broker, options, emitter, { selectMount, registerSecurity, createHeartbeatMonitor });
578
698
  }
579
699
 
package/host/index.d.ts CHANGED
@@ -1,39 +1,5 @@
1
- import { DisplayMode, ShellOptions, EventHandler } from '..';
2
- export { DisplayMode, EmbedSizing, ShellOptions, UnresponsiveInfo, UnresponsivePolicy } from '..';
3
-
4
- /**
5
- * Context handed to an {@link ExperiencePlugin} around a feature's mount lifecycle.
6
- */
7
- interface ExperiencePluginContext {
8
- /** The root element the display mode mounted (dialog container or embedded frame). */
9
- element: HTMLElement;
10
- /** The display mode the feature was surfaced in. */
11
- displayMode: DisplayMode;
12
- }
13
- /**
14
- * Opt-in extension that decorates a feature's mount lifecycle (e.g. transitions, animations).
15
- *
16
- * Implement this interface to layer experiences onto the built-in display modes,
17
- * then pass the plugin to the host shell. The SDK ships no built-in plugins.
18
- */
19
- interface ExperiencePlugin {
20
- /** Unique plugin name, surfaced in debug logs. */
21
- name: string;
22
- /**
23
- * Runs after the feature mounts; may animate it in and return a teardown.
24
- *
25
- * @param context - The mounted element and its display mode.
26
- * @returns An optional teardown invoked on unmount.
27
- */
28
- onMount?(context: ExperiencePluginContext): void | (() => void);
29
- /**
30
- * Runs before the feature unmounts; may return a promise to defer teardown until an exit animation finishes.
31
- *
32
- * @param context - The mounted element and its display mode.
33
- * @returns Optionally a promise the shell awaits before tearing down.
34
- */
35
- onUnmount?(context: ExperiencePluginContext): void | Promise<void>;
36
- }
1
+ import { ShellOptions, RequestOptions, RequestHandler, EventHandler } from '..';
2
+ export { ActionDescription, DisplayMode, EmbedSizing, ExperiencePlugin, ExperiencePluginContext, FeatureContract, RequestHandler, RequestOptions, SecurityProtocol, ShellOptions, UnresponsiveInfo, UnresponsivePolicy } from '..';
37
3
 
38
4
  /**
39
5
  * Public handle returned by {@link createShell}.
@@ -42,6 +8,9 @@ interface ShellHandle {
42
8
  /**
43
9
  * Mounts the feature using the merged create-time and call-time options.
44
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
+ *
45
14
  * @param options - Per-open overrides layered over the create-time options.
46
15
  */
47
16
  open(options?: Partial<ShellOptions>): void;
@@ -51,6 +20,10 @@ interface ShellHandle {
51
20
  close(): void;
52
21
  /**
53
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.
54
27
  */
55
28
  destroy(): void;
56
29
  /**
@@ -60,6 +33,45 @@ interface ShellHandle {
60
33
  * @param data - Optional payload for the action.
61
34
  */
62
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;
63
75
  /**
64
76
  * Subscribes to feature messages or lifecycle events (`open`, `close`, `error`).
65
77
  *
@@ -78,7 +90,10 @@ interface ShellHandle {
78
90
  * Creates a host-side shell for embedding a feature.
79
91
  *
80
92
  * Provisions a nexus broker and returns a handle whose `open` mounts the feature
81
- * in the requested display mode.
93
+ * in the requested display mode. The `contract` option takes the feature's
94
+ * contract exactly as the feature authored it; the shell derives the host-side
95
+ * orientation itself, so the handle sends what the feature accepts and receives
96
+ * what the feature emits.
82
97
  *
83
98
  * @param options - Create-time shell options, overridable per `open` call.
84
99
  * @returns A handle exposing `open`, `close`, `destroy`, `send`, `on`, and `isOpen`.
@@ -93,4 +108,4 @@ interface ShellHandle {
93
108
  declare function createShell(options: ShellOptions): ShellHandle;
94
109
 
95
110
  export { createShell };
96
- export type { ExperiencePlugin, ExperiencePluginContext, ShellHandle };
111
+ export type { ShellHandle };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACtG,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AAC1E,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../libs/features/src/host/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvE,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AAC1E,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA"}