@lushly-dev/fast-element 3.0.1-preview.0 → 3.0.1-preview.2

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 (250) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/command/command.api.json +7857 -0
  3. package/dist/context/context.api.json +1084 -0
  4. package/dist/declarative/declarative.api.json +8130 -0
  5. package/dist/definition-schema/definition-schema.api.json +2689 -0
  6. package/dist/di/di.api.json +4945 -0
  7. package/dist/dts/arrays.d.ts +2 -0
  8. package/dist/dts/binding/binding.d.ts +59 -0
  9. package/dist/dts/binding/exports.d.ts +9 -0
  10. package/dist/dts/binding/normalize.d.ts +9 -0
  11. package/dist/dts/binding/one-time.d.ts +11 -0
  12. package/dist/dts/binding/one-way.d.ts +20 -0
  13. package/dist/dts/binding/signal.d.ts +36 -0
  14. package/dist/dts/binding/two-way.d.ts +44 -0
  15. package/dist/dts/command/behavior.d.ts +94 -0
  16. package/dist/dts/command/behaviors/confirm-behavior.d.ts +139 -0
  17. package/dist/dts/command/behaviors/undo-behavior.d.ts +178 -0
  18. package/dist/dts/command/command.d.ts +79 -0
  19. package/dist/dts/command/component-registry.d.ts +50 -0
  20. package/dist/dts/command/components/confirm-dialog.d.ts +117 -0
  21. package/dist/dts/command/index.d.ts +21 -0
  22. package/dist/dts/command/registry.d.ts +201 -0
  23. package/dist/dts/command/schema-validation.d.ts +45 -0
  24. package/dist/dts/command/types.d.ts +332 -0
  25. package/dist/dts/command/validation.d.ts +116 -0
  26. package/dist/dts/command/weak-ref.d.ts +11 -0
  27. package/dist/dts/components/attributes.d.ts +153 -0
  28. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  29. package/dist/dts/components/element-controller.d.ts +349 -0
  30. package/dist/dts/components/element-hydration.d.ts +2 -0
  31. package/dist/dts/components/enable-hydration.d.ts +66 -0
  32. package/dist/dts/components/fast-definitions.d.ts +211 -0
  33. package/dist/dts/components/fast-element-registry.d.ts +23 -0
  34. package/dist/dts/components/fast-element.d.ts +91 -0
  35. package/dist/dts/components/hydration-tracker.d.ts +110 -0
  36. package/dist/dts/components/hydration.d.ts +52 -0
  37. package/dist/dts/components/schema.d.ts +205 -0
  38. package/dist/dts/context.d.ts +171 -0
  39. package/dist/dts/debug.d.ts +5 -0
  40. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  41. package/dist/dts/declarative/debug.d.ts +4 -0
  42. package/dist/dts/declarative/index.d.ts +14 -0
  43. package/dist/dts/declarative/interfaces.d.ts +8 -0
  44. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  45. package/dist/dts/declarative/observer-map.d.ts +89 -0
  46. package/dist/dts/declarative/runtime.d.ts +5 -0
  47. package/dist/dts/declarative/syntax.d.ts +21 -0
  48. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  49. package/dist/dts/declarative/template-parser.d.ts +98 -0
  50. package/dist/dts/declarative/template.d.ts +9 -0
  51. package/dist/dts/declarative/utilities.d.ts +358 -0
  52. package/dist/dts/definition-schema/api.d.ts +42 -0
  53. package/dist/dts/definition-schema/cem.d.ts +78 -0
  54. package/dist/dts/definition-schema/extension.d.ts +18 -0
  55. package/dist/dts/definition-schema/extract.d.ts +38 -0
  56. package/dist/dts/definition-schema/index.d.ts +9 -0
  57. package/dist/dts/definition-schema/storage.d.ts +35 -0
  58. package/dist/dts/definition-schema/types.d.ts +176 -0
  59. package/dist/dts/dev-mode.d.ts +13 -0
  60. package/dist/dts/di/di.d.ts +894 -0
  61. package/dist/dts/dom-policy.d.ts +101 -0
  62. package/dist/dts/dom.d.ts +88 -0
  63. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  64. package/dist/dts/hydration/exports.d.ts +18 -0
  65. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  66. package/dist/dts/hydration/messages.d.ts +62 -0
  67. package/dist/dts/hydration/runtime.d.ts +7 -0
  68. package/dist/dts/hydration/target-builder.d.ts +107 -0
  69. package/dist/dts/index.d.ts +51 -0
  70. package/dist/dts/index.debug.d.ts +1 -0
  71. package/dist/dts/index.rollup.d.ts +1 -0
  72. package/dist/dts/index.rollup.debug.d.ts +1 -0
  73. package/dist/dts/interfaces.d.ts +106 -0
  74. package/dist/dts/metadata.d.ts +26 -0
  75. package/dist/dts/observation/arrays.d.ts +248 -0
  76. package/dist/dts/observation/notifier.d.ts +127 -0
  77. package/dist/dts/observation/observable.d.ts +276 -0
  78. package/dist/dts/observation/update-queue.d.ts +40 -0
  79. package/dist/dts/observation/volatile.d.ts +9 -0
  80. package/dist/dts/platform.d.ts +87 -0
  81. package/dist/dts/registry.d.ts +2 -0
  82. package/dist/dts/render.d.ts +7 -0
  83. package/dist/dts/schema.d.ts +1 -0
  84. package/dist/dts/scoped/detection.d.ts +55 -0
  85. package/dist/dts/scoped/index.d.ts +68 -0
  86. package/dist/dts/scoped/mixin.d.ts +118 -0
  87. package/dist/dts/scoped/registry.d.ts +76 -0
  88. package/dist/dts/scoped/types.d.ts +48 -0
  89. package/dist/dts/state/exports.d.ts +3 -0
  90. package/dist/dts/state/reactive.d.ts +8 -0
  91. package/dist/dts/state/state.d.ts +141 -0
  92. package/dist/dts/state/visitor.d.ts +6 -0
  93. package/dist/dts/state/watch.d.ts +10 -0
  94. package/dist/dts/styles/css-directive.d.ts +50 -0
  95. package/dist/dts/styles/css.d.ts +34 -0
  96. package/dist/dts/styles/element-styles.d.ts +61 -0
  97. package/dist/dts/styles/host.d.ts +65 -0
  98. package/dist/dts/styles/style-strategy.d.ts +42 -0
  99. package/dist/dts/templating/children.d.ts +62 -0
  100. package/dist/dts/templating/compiler.d.ts +54 -0
  101. package/dist/dts/templating/html-binding-directive.d.ts +155 -0
  102. package/dist/dts/templating/html-directive.d.ts +218 -0
  103. package/dist/dts/templating/html.d.ts +1 -0
  104. package/dist/dts/templating/hydration-view.d.ts +130 -0
  105. package/dist/dts/templating/markup.d.ts +48 -0
  106. package/dist/dts/templating/node-observation.d.ts +99 -0
  107. package/dist/dts/templating/ref.d.ts +23 -0
  108. package/dist/dts/templating/render.d.ts +318 -0
  109. package/dist/dts/templating/repeat.d.ts +131 -0
  110. package/dist/dts/templating/slotted.d.ts +38 -0
  111. package/dist/dts/templating/template.d.ts +221 -0
  112. package/dist/dts/templating/view.d.ts +268 -0
  113. package/dist/dts/templating/when.d.ts +12 -0
  114. package/dist/dts/testing/exports.d.ts +3 -0
  115. package/dist/dts/testing/fakes.d.ts +16 -0
  116. package/dist/dts/testing/fixture.d.ts +84 -0
  117. package/dist/dts/testing/models.d.ts +20 -0
  118. package/dist/dts/testing/timeout.d.ts +7 -0
  119. package/dist/dts/trust/index.d.ts +8 -0
  120. package/dist/dts/trust/types.d.ts +48 -0
  121. package/dist/dts/trust/utils.d.ts +26 -0
  122. package/dist/dts/tsdoc-metadata.json +11 -0
  123. package/dist/dts/utilities.d.ts +76 -0
  124. package/dist/esm/arrays.js +1 -0
  125. package/dist/esm/binding/binding.js +18 -0
  126. package/dist/esm/binding/exports.js +6 -0
  127. package/dist/esm/binding/normalize.js +17 -0
  128. package/dist/esm/binding/one-time.js +21 -0
  129. package/dist/esm/binding/one-way.js +30 -0
  130. package/dist/esm/binding/signal.js +99 -0
  131. package/dist/esm/binding/two-way.js +109 -0
  132. package/dist/esm/command/behavior.js +155 -0
  133. package/dist/esm/command/behaviors/confirm-behavior.js +225 -0
  134. package/dist/esm/command/behaviors/undo-behavior.js +313 -0
  135. package/dist/esm/command/command.js +104 -0
  136. package/dist/esm/command/component-registry.js +107 -0
  137. package/dist/esm/command/components/confirm-dialog.js +370 -0
  138. package/dist/esm/command/index.js +22 -0
  139. package/dist/esm/command/registry.js +755 -0
  140. package/dist/esm/command/schema-validation.js +194 -0
  141. package/dist/esm/command/types.js +86 -0
  142. package/dist/esm/command/validation.js +508 -0
  143. package/dist/esm/command/weak-ref.js +5 -0
  144. package/dist/esm/components/attributes.js +219 -0
  145. package/dist/esm/components/definition-schema-transforms.js +23 -0
  146. package/dist/esm/components/element-controller.js +840 -0
  147. package/dist/esm/components/element-hydration.js +2 -0
  148. package/dist/esm/components/enable-hydration.js +123 -0
  149. package/dist/esm/components/fast-definitions.js +294 -0
  150. package/dist/esm/components/fast-element-registry.js +67 -0
  151. package/dist/esm/components/fast-element.js +82 -0
  152. package/dist/esm/components/hydration-tracker.js +211 -0
  153. package/dist/esm/components/hydration.js +187 -0
  154. package/dist/esm/components/schema.js +253 -0
  155. package/dist/esm/context.js +185 -0
  156. package/dist/esm/debug.js +71 -0
  157. package/dist/esm/declarative/attribute-map.js +122 -0
  158. package/dist/esm/declarative/debug.js +4 -0
  159. package/dist/esm/declarative/index.js +10 -0
  160. package/dist/esm/declarative/interfaces.js +9 -0
  161. package/dist/esm/declarative/observer-map-utilities.js +565 -0
  162. package/dist/esm/declarative/observer-map.js +216 -0
  163. package/dist/esm/declarative/runtime.js +14 -0
  164. package/dist/esm/declarative/syntax.js +36 -0
  165. package/dist/esm/declarative/template-bridge.js +160 -0
  166. package/dist/esm/declarative/template-parser.js +306 -0
  167. package/dist/esm/declarative/template.js +116 -0
  168. package/dist/esm/declarative/utilities.js +1069 -0
  169. package/dist/esm/definition-schema/api.js +129 -0
  170. package/dist/esm/definition-schema/cem.js +89 -0
  171. package/dist/esm/definition-schema/extension.js +53 -0
  172. package/dist/esm/definition-schema/extract.js +167 -0
  173. package/dist/esm/definition-schema/index.js +8 -0
  174. package/dist/esm/definition-schema/storage.js +58 -0
  175. package/dist/esm/definition-schema/types.js +8 -0
  176. package/dist/esm/dev-mode.js +22 -0
  177. package/dist/esm/di/di.js +1441 -0
  178. package/dist/esm/dom-policy.js +374 -0
  179. package/dist/esm/dom.js +101 -0
  180. package/dist/esm/hydration/diagnostics.js +50 -0
  181. package/dist/esm/hydration/exports.js +6 -0
  182. package/dist/esm/hydration/hydration-debugger.js +112 -0
  183. package/dist/esm/hydration/messages.js +84 -0
  184. package/dist/esm/hydration/runtime.js +33 -0
  185. package/dist/esm/hydration/target-builder.js +264 -0
  186. package/dist/esm/index.debug.js +3 -0
  187. package/dist/esm/index.js +49 -0
  188. package/dist/esm/index.rollup.debug.js +6 -0
  189. package/dist/esm/index.rollup.js +4 -0
  190. package/dist/esm/interfaces.js +55 -0
  191. package/dist/esm/metadata.js +57 -0
  192. package/dist/esm/observation/arrays.js +661 -0
  193. package/dist/esm/observation/notifier.js +162 -0
  194. package/dist/esm/observation/observable.js +324 -0
  195. package/dist/esm/observation/update-queue.js +56 -0
  196. package/dist/esm/observation/volatile.js +16 -0
  197. package/dist/esm/platform.js +98 -0
  198. package/dist/esm/registry.js +2 -0
  199. package/dist/esm/render.js +1 -0
  200. package/dist/esm/schema.js +1 -0
  201. package/dist/esm/scoped/detection.js +102 -0
  202. package/dist/esm/scoped/index.js +69 -0
  203. package/dist/esm/scoped/mixin.js +127 -0
  204. package/dist/esm/scoped/registry.js +125 -0
  205. package/dist/esm/scoped/types.js +4 -0
  206. package/dist/esm/state/exports.js +3 -0
  207. package/dist/esm/state/reactive.js +34 -0
  208. package/dist/esm/state/state.js +148 -0
  209. package/dist/esm/state/visitor.js +28 -0
  210. package/dist/esm/state/watch.js +36 -0
  211. package/dist/esm/styles/css-directive.js +36 -0
  212. package/dist/esm/styles/css.js +56 -0
  213. package/dist/esm/styles/element-styles.js +144 -0
  214. package/dist/esm/styles/host.js +1 -0
  215. package/dist/esm/styles/style-strategy.js +1 -0
  216. package/dist/esm/templating/children.js +67 -0
  217. package/dist/esm/templating/compiler.js +359 -0
  218. package/dist/esm/templating/html-binding-directive.js +288 -0
  219. package/dist/esm/templating/html-directive.js +113 -0
  220. package/dist/esm/templating/html.js +1 -0
  221. package/dist/esm/templating/hydration-view.js +228 -0
  222. package/dist/esm/templating/markup.js +83 -0
  223. package/dist/esm/templating/node-observation.js +84 -0
  224. package/dist/esm/templating/ref.js +21 -0
  225. package/dist/esm/templating/render.js +468 -0
  226. package/dist/esm/templating/repeat.js +434 -0
  227. package/dist/esm/templating/slotted.js +49 -0
  228. package/dist/esm/templating/template.js +206 -0
  229. package/dist/esm/templating/view.js +298 -0
  230. package/dist/esm/templating/when.js +22 -0
  231. package/dist/esm/testing/exports.js +3 -0
  232. package/dist/esm/testing/fakes.js +107 -0
  233. package/dist/esm/testing/fixture.js +86 -0
  234. package/dist/esm/testing/models.js +58 -0
  235. package/dist/esm/testing/timeout.js +24 -0
  236. package/dist/esm/trust/index.js +7 -0
  237. package/dist/esm/trust/types.js +6 -0
  238. package/dist/esm/trust/utils.js +126 -0
  239. package/dist/esm/utilities.js +140 -0
  240. package/dist/fast-element.api.json +26798 -0
  241. package/dist/fast-element.d.ts +3985 -0
  242. package/dist/fast-element.debug.js +7604 -0
  243. package/dist/fast-element.debug.min.js +3 -0
  244. package/dist/fast-element.js +7603 -0
  245. package/dist/fast-element.min.js +3 -0
  246. package/dist/fast-element.untrimmed.d.ts +4103 -0
  247. package/dist/observation/arrays/arrays.api.json +2621 -0
  248. package/dist/scoped/scoped.api.json +891 -0
  249. package/dist/trust/trust.api.json +629 -0
  250. package/package.json +21 -23
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Detection and polyfill loading for Scoped Custom Element Registries.
3
+ */
4
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
6
+ return new (P || (P = Promise))(function (resolve, reject) {
7
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
8
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
9
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
10
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
11
+ });
12
+ };
13
+ import { isDevMode } from "../dev-mode.js";
14
+ /**
15
+ * Robust feature detection for Scoped Custom Element Registries.
16
+ *
17
+ * @remarks
18
+ * Tests the full API surface, not just the constructor existence.
19
+ * This ensures the browser actually supports attaching a custom registry
20
+ * to a shadow root, which is the critical functionality.
21
+ *
22
+ * @returns true if the browser supports scoped custom element registries
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * if (supportsScopedRegistries()) {
27
+ * const registry = new CustomElementRegistry();
28
+ * element.attachShadow({ mode: 'open', customElementRegistry: registry });
29
+ * }
30
+ * ```
31
+ *
32
+ * @public
33
+ */
34
+ export function supportsScopedRegistries() {
35
+ try {
36
+ // Test 1: Can we construct a CustomElementRegistry?
37
+ const reg = new CustomElementRegistry();
38
+ // Test 2: Can we use it with attachShadow?
39
+ const div = document.createElement("div");
40
+ const shadow = div.attachShadow({
41
+ mode: "open",
42
+ customElementRegistry: reg,
43
+ registry: reg,
44
+ });
45
+ const scopedShadow = shadow;
46
+ return (scopedShadow.customElementRegistry === reg || scopedShadow.registry === reg);
47
+ }
48
+ catch (_a) {
49
+ return false;
50
+ }
51
+ }
52
+ /**
53
+ * Idempotency-safe polyfill loading for Scoped Custom Element Registries.
54
+ *
55
+ * @remarks
56
+ * Safe to call from multiple micro-frontend bundles. Uses a global flag
57
+ * (`window.__scopedRegistryPolyfillApplied`) to prevent double-patching
58
+ * which could corrupt the CustomElementRegistry prototype.
59
+ *
60
+ * If native support is detected, returns immediately without loading the polyfill.
61
+ * On polyfill load failure, the flag is reset to allow retry on subsequent calls.
62
+ *
63
+ * @throws Error if the polyfill fails to load (network error, etc.)
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // At app startup
68
+ * await ensureScopedRegistrySupport();
69
+ *
70
+ * // Now safe to use scoped registries
71
+ * const registry = new CustomElementRegistry();
72
+ * ```
73
+ *
74
+ * @public
75
+ */
76
+ export function ensureScopedRegistrySupport() {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ // Check native support first
79
+ if (supportsScopedRegistries()) {
80
+ return;
81
+ }
82
+ const scope = globalThis;
83
+ if (scope.__scopedRegistryPolyfillPromise) {
84
+ return scope.__scopedRegistryPolyfillPromise;
85
+ }
86
+ scope.__scopedRegistryPolyfillPromise = import("@webcomponents/scoped-custom-element-registry")
87
+ .then(() => {
88
+ scope.__scopedRegistryPolyfillApplied = true;
89
+ if (isDevMode()) {
90
+ console.info("[fast-af] Scoped registry polyfill loaded successfully");
91
+ }
92
+ })
93
+ .catch(e => {
94
+ scope.__scopedRegistryPolyfillApplied = false;
95
+ scope.__scopedRegistryPolyfillPromise = void 0;
96
+ const error = new Error("[fast-af] Failed to load scoped registry polyfill");
97
+ error.cause = e;
98
+ throw error;
99
+ });
100
+ return scope.__scopedRegistryPolyfillPromise;
101
+ });
102
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Scoped Custom Element Registries support for FAST-AF.
3
+ *
4
+ * @remarks
5
+ * This module provides utilities for working with Scoped Custom Element Registries,
6
+ * enabling isolated component definitions within shadow roots. This solves namespace
7
+ * collisions for micro-frontends and design system versioning.
8
+ *
9
+ * @example Using ScopedRegistryMixin (recommended)
10
+ * ```typescript
11
+ * import { FASTElement, html } from '@lushly-dev/fast-element';
12
+ * import { ScopedRegistryMixin } from '@lushly-dev/fast-element/scoped.js';
13
+ *
14
+ * class MyButton extends HTMLElement {}
15
+ *
16
+ * class MyCard extends ScopedRegistryMixin(FASTElement) {
17
+ * static elementDefinitions = {
18
+ * 'my-button': MyButton,
19
+ * };
20
+ * }
21
+ *
22
+ * // The shadow root gets the scoped registry, so <my-button> is upgraded without
23
+ * // being defined globally.
24
+ * await FASTElement.define(MyCard, {
25
+ * name: 'my-card',
26
+ * template: html`<my-button>Click me</my-button>`,
27
+ * shadowOptions: MyCard.getShadowOptions(),
28
+ * });
29
+ * ```
30
+ *
31
+ * @example Using createRegistry directly
32
+ * ```typescript
33
+ * import {
34
+ * createRegistry,
35
+ * createRegistryAsync,
36
+ * supportsScopedRegistries,
37
+ * ensureScopedRegistrySupport,
38
+ * } from '@lushly-dev/fast-element/scoped.js';
39
+ *
40
+ * class MyButton extends HTMLElement {}
41
+ * class MyInput extends HTMLElement {}
42
+ *
43
+ * // Check support
44
+ * if (supportsScopedRegistries()) {
45
+ * const registry = createRegistry([
46
+ * [MyButton, 'my-button'],
47
+ * [MyInput, 'my-input'],
48
+ * ]);
49
+ *
50
+ * element.attachShadow({
51
+ * mode: 'open',
52
+ * customElementRegistry: registry,
53
+ * });
54
+ * }
55
+ *
56
+ * // Or use async version with automatic polyfill
57
+ * const registry = await createRegistryAsync([
58
+ * [MyButton, 'my-button'],
59
+ * ]);
60
+ * ```
61
+ *
62
+ * @packageDocumentation
63
+ */
64
+ // Detection
65
+ export { supportsScopedRegistries, ensureScopedRegistrySupport } from "./detection.js";
66
+ // Registry Builder
67
+ export { createRegistry, createRegistryAsync } from "./registry.js";
68
+ // Mixin
69
+ export { ScopedRegistryMixin } from "./mixin.js";
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ScopedRegistryMixin for FAST Elements.
3
+ *
4
+ * @remarks
5
+ * Provides a mixin that enables components to define and use scoped registries,
6
+ * following the Lit labs pattern. Child elements are isolated within the
7
+ * component's shadow root.
8
+ */
9
+ import { isDevMode } from "../dev-mode.js";
10
+ import { createRegistry } from "./registry.js";
11
+ const initializedConstructors = new WeakSet();
12
+ const scopedRegistries = new WeakMap();
13
+ /**
14
+ * Creates a mixin that enables scoped custom element registries for a FASTElement.
15
+ *
16
+ * @remarks
17
+ * The ScopedRegistryMixin provides a way to define child elements that are
18
+ * scoped to the component's shadow root, preventing namespace collisions
19
+ * in micro-frontend scenarios and enabling design system versioning.
20
+ *
21
+ * The mixin adds:
22
+ * - A static `elementDefinitions` property for defining child elements
23
+ * - A static `scopedRegistry` getter that lazily creates the registry
24
+ * - A method to get shadow root options with the scoped registry
25
+ *
26
+ * When scoped registries are not supported by the browser, elements fall back
27
+ * to the global registry with a development warning.
28
+ *
29
+ * @param Base - The FASTElement base class to extend
30
+ * @returns A class that supports scoped element definitions
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { FASTElement, html, css } from '@lushly-dev/fast-element';
35
+ * import { ScopedRegistryMixin } from '@lushly-dev/fast-element/scoped.js';
36
+ *
37
+ * // Children can be plain custom elements. FASTElement children must be composed
38
+ * // first (FASTElementDefinition.compose(MyIcon, { name: 'my-icon' })) so the
39
+ * // registry can reuse their FAST definition; creating an element of an
40
+ * // undefined FASTElement class throws FAST error 1401.
41
+ * class MyButton extends HTMLElement {
42
+ * // Button implementation
43
+ * }
44
+ *
45
+ * class MyCard extends ScopedRegistryMixin(FASTElement) {
46
+ * static elementDefinitions = {
47
+ * 'my-button': MyButton,
48
+ * };
49
+ * }
50
+ *
51
+ * // Attach the scoped registry to the shadow root through getShadowOptions().
52
+ * // The template's <my-button> is created against it and does not need to be
53
+ * // (and here is not) defined in the global registry.
54
+ * await FASTElement.define(MyCard, {
55
+ * name: 'my-card',
56
+ * template: html`<my-button>Click me</my-button>`,
57
+ * shadowOptions: MyCard.getShadowOptions(),
58
+ * });
59
+ * ```
60
+ *
61
+ * @public
62
+ */
63
+ export function ScopedRegistryMixin(Base) {
64
+ return class ScopedRegistryMixinClass extends Base {
65
+ /**
66
+ * Gets the scoped registry for this element class.
67
+ * @remarks
68
+ * Lazily creates the registry from elementDefinitions on first access.
69
+ * Returns undefined if no definitions exist or if scoped registries
70
+ * are not supported (fallback to global).
71
+ */
72
+ static get scopedRegistry() {
73
+ if (!initializedConstructors.has(this)) {
74
+ initializedConstructors.add(this);
75
+ if (this.elementDefinitions) {
76
+ const keys = Object.keys(this.elementDefinitions);
77
+ if (keys.length > 0) {
78
+ // Convert { 'tag-name': Ctor } to [Ctor, 'tag-name'] format
79
+ const elementDefs = [];
80
+ for (let i = 0, len = keys.length; i < len; i++) {
81
+ const tagName = keys[i];
82
+ const ctor = this.elementDefinitions[tagName];
83
+ elementDefs.push([ctor, tagName]);
84
+ }
85
+ const registry = createRegistry(elementDefs);
86
+ scopedRegistries.set(this, registry);
87
+ if (isDevMode() && registry) {
88
+ console.debug("[fast-af] Created scoped registry for " +
89
+ this.name +
90
+ " with " +
91
+ keys.length +
92
+ " element(s)");
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return scopedRegistries.get(this);
98
+ }
99
+ /**
100
+ * Gets shadow root options with the scoped registry configured.
101
+ * @remarks
102
+ * Use this method when defining the element to include the scoped registry.
103
+ * Returns options with customElementRegistry set if a registry was created.
104
+ *
105
+ * @param baseOptions - Optional base shadow root options to extend
106
+ * @returns Shadow root options with customElementRegistry if applicable
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * await FASTElement.define(MyComponent, {
111
+ * name: 'my-component',
112
+ * template: myTemplate,
113
+ * shadowOptions: MyComponent.getShadowOptions({ mode: 'closed' }),
114
+ * });
115
+ * ```
116
+ */
117
+ static getShadowOptions(baseOptions) {
118
+ const registry = this.scopedRegistry;
119
+ const options = Object.assign({ mode: "open" }, baseOptions);
120
+ if (registry) {
121
+ options.customElementRegistry = registry;
122
+ options.registry = registry;
123
+ }
124
+ return options;
125
+ }
126
+ };
127
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Registry builder utilities for Scoped Custom Element Registries.
3
+ */
4
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
6
+ return new (P || (P = Promise))(function (resolve, reject) {
7
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
8
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
9
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
10
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
11
+ });
12
+ };
13
+ import { isDevMode } from "../dev-mode.js";
14
+ import { fastElementRegistry } from "../components/fast-element-registry.js";
15
+ import { ensureScopedRegistrySupport, supportsScopedRegistries } from "./detection.js";
16
+ function defineElements(registry, definitions) {
17
+ for (const [ctor, tagName] of definitions) {
18
+ if (registry.get(tagName)) {
19
+ continue;
20
+ }
21
+ const fastDefinition = fastElementRegistry.getByType(ctor);
22
+ if ((fastDefinition === null || fastDefinition === void 0 ? void 0 : fastDefinition.name) === tagName) {
23
+ fastDefinition.define(registry);
24
+ }
25
+ else {
26
+ registry.define(tagName, ctor, fastDefinition === null || fastDefinition === void 0 ? void 0 : fastDefinition.elementOptions);
27
+ }
28
+ }
29
+ }
30
+ /**
31
+ * Creates a scoped custom element registry with the provided element definitions.
32
+ *
33
+ * @remarks
34
+ * When scoped registries are supported, creates a new CustomElementRegistry
35
+ * and defines all provided elements in it. When not supported, falls back to
36
+ * registering elements on the global `customElements` registry (with a dev warning).
37
+ *
38
+ * In strict mode, throws an error if scoped registries are not supported
39
+ * instead of falling back to the global registry.
40
+ *
41
+ * @param definitions - Array of [Constructor, tagName] tuples to register
42
+ * @param options - Configuration options
43
+ * @returns The created registry, or undefined if fallback to global occurred
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Basic usage
48
+ * const registry = createRegistry([
49
+ * [MyButton, 'my-button'],
50
+ * [MyInput, 'my-input'],
51
+ * ]);
52
+ *
53
+ * if (registry) {
54
+ * element.attachShadow({ mode: 'open', customElementRegistry: registry });
55
+ * }
56
+ * ```
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // Strict mode - throws if not supported
61
+ * const registry = createRegistry(
62
+ * [[MyButton, 'my-button']],
63
+ * { strict: true }
64
+ * );
65
+ * ```
66
+ *
67
+ * @public
68
+ */
69
+ export function createRegistry(definitions, options) {
70
+ if (!supportsScopedRegistries()) {
71
+ if (options === null || options === void 0 ? void 0 : options.strict) {
72
+ throw new Error("[fast-af] Scoped custom element registries are not supported in this browser");
73
+ }
74
+ if (isDevMode()) {
75
+ console.warn("[fast-af] Scoped registries not supported. Falling back to global registry. " +
76
+ "Elements will be globally registered which may cause conflicts in MFE scenarios.");
77
+ }
78
+ // Fallback: register on global registry
79
+ defineElements(customElements, definitions);
80
+ return undefined; // Signal fallback occurred
81
+ }
82
+ const registry = new CustomElementRegistry();
83
+ defineElements(registry, definitions);
84
+ return registry;
85
+ }
86
+ /**
87
+ * Creates a scoped custom element registry with automatic polyfill loading.
88
+ *
89
+ * @remarks
90
+ * This async version first ensures scoped registry support is available
91
+ * (loading the polyfill if necessary), then creates the registry.
92
+ *
93
+ * Unlike `createRegistry()`, this function always operates in strict mode
94
+ * after polyfill loading, as the polyfill should provide support. If the
95
+ * polyfill fails to load, the underlying `ensureScopedRegistrySupport()`
96
+ * will throw.
97
+ *
98
+ * @param definitions - Array of [Constructor, tagName] tuples to register
99
+ * @param options - Configuration options (strict is always true after polyfill)
100
+ * @returns Promise resolving to the created registry
101
+ *
102
+ * @throws Error if polyfill loading fails
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * // Automatically loads polyfill if needed
107
+ * const registry = await createRegistryAsync([
108
+ * [MyButton, 'my-button'],
109
+ * [MyInput, 'my-input'],
110
+ * ]);
111
+ *
112
+ * element.attachShadow({ mode: 'open', customElementRegistry: registry });
113
+ * ```
114
+ *
115
+ * @public
116
+ */
117
+ export function createRegistryAsync(definitions, options) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ yield ensureScopedRegistrySupport();
120
+ // After polyfill loading, we should have support - use strict mode
121
+ const registry = createRegistry(definitions, Object.assign(Object.assign({}, options), { strict: true }));
122
+ // TypeScript knows this won't be undefined because strict: true would throw
123
+ return registry;
124
+ });
125
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for Scoped Custom Element Registries.
3
+ */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ export { reactive } from "./reactive.js";
2
+ export { computedState, ownedState, state, } from "./state.js";
3
+ export { watch } from "./watch.js";
@@ -0,0 +1,34 @@
1
+ import { noop } from "../interfaces.js";
2
+ import { Observable } from "../observation/observable.js";
3
+ import { visitObject } from "./visitor.js";
4
+ const observed = new WeakSet();
5
+ const makeObserverVisitor = {
6
+ visitObject: noop,
7
+ visitArray: noop,
8
+ visitProperty(object, propertyName, value) {
9
+ Reflect.defineProperty(object, propertyName, {
10
+ enumerable: true,
11
+ get() {
12
+ Observable.track(object, propertyName);
13
+ return value;
14
+ },
15
+ set(newValue) {
16
+ if (value !== newValue) {
17
+ value = newValue;
18
+ Observable.notify(object, propertyName);
19
+ }
20
+ },
21
+ });
22
+ },
23
+ };
24
+ /**
25
+ * Converts a plain object to a reactive, observable object.
26
+ * @param object - The object to make reactive.
27
+ * @param deep - Indicates whether or not to deeply convert the oject.
28
+ * @returns The converted object.
29
+ * @beta
30
+ */
31
+ export function reactive(object, deep = false) {
32
+ visitObject(object, deep, makeObserverVisitor, void 0, observed);
33
+ return object;
34
+ }
@@ -0,0 +1,148 @@
1
+ // Inspired by https://www.starbeamjs.com/
2
+ import { isFunction, isString } from "../interfaces.js";
3
+ import { Observable } from "../observation/observable.js";
4
+ import { reactive } from "./reactive.js";
5
+ const defaultStateOptions = {
6
+ deep: false,
7
+ };
8
+ /**
9
+ * Creates a reactive state value.
10
+ * @param value - The initial state value.
11
+ * @param options - Options to customize the state or a friendly name.
12
+ * @returns A State instance.
13
+ * @beta
14
+ */
15
+ export function state(value, options = defaultStateOptions) {
16
+ var _a;
17
+ if (isString(options)) {
18
+ options = { deep: false, name: options };
19
+ }
20
+ const host = reactive({ value }, options.deep);
21
+ const state = (() => host.value);
22
+ Object.defineProperty(state, "current", {
23
+ get: () => host.value,
24
+ set: (value) => (host.value = value),
25
+ });
26
+ Object.defineProperty(state, "name", {
27
+ value: (_a = options.name) !== null && _a !== void 0 ? _a : "SharedState",
28
+ });
29
+ state.set = (value) => (host.value = value);
30
+ state.asReadonly = () => {
31
+ const readonlyState = (() => host.value);
32
+ Object.defineProperty(readonlyState, "current", {
33
+ get: () => host.value,
34
+ });
35
+ Object.defineProperty(readonlyState, "name", {
36
+ value: `${state.name} (Readonly)`,
37
+ });
38
+ return Object.freeze(readonlyState);
39
+ };
40
+ return state;
41
+ }
42
+ /**
43
+ * Creates a reactive state that has its value associated with a specific owner.
44
+ * @param value - The initial value or a factory that provides an initial value for each owner.
45
+ * @param options - Options to customize the state or a friendly name.
46
+ * @returns An OwnedState instance.
47
+ * @beta
48
+ */
49
+ export function ownedState(value, options = defaultStateOptions) {
50
+ var _a;
51
+ if (isString(options)) {
52
+ options = { deep: false, name: options };
53
+ }
54
+ if (!isFunction(value)) {
55
+ const v = value;
56
+ value = () => v;
57
+ }
58
+ const storage = new WeakMap();
59
+ const getHost = (owner) => {
60
+ let host = storage.get(owner);
61
+ if (host === void 0) {
62
+ host = reactive({ value: value() }, options.deep);
63
+ storage.set(owner, host);
64
+ }
65
+ return host;
66
+ };
67
+ const state = ((owner) => getHost(owner).value);
68
+ Object.defineProperty(state, "name", {
69
+ value: (_a = options.name) !== null && _a !== void 0 ? _a : "OwnedState",
70
+ });
71
+ state.set = (owner, value) => (getHost(owner).value = value);
72
+ state.asReadonly = () => {
73
+ const readonlyState = ((owner) => getHost(owner).value);
74
+ Object.defineProperty(readonlyState, "name", {
75
+ value: `${state.name} (Readonly)`,
76
+ });
77
+ return Object.freeze(readonlyState);
78
+ };
79
+ return state;
80
+ }
81
+ /**
82
+ * Creates a ComputedState.
83
+ * @param initialize - The initialization callback.
84
+ * @param name - A friendly name for this computation.
85
+ * @returns A ComputedState
86
+ * @beta
87
+ */
88
+ export function computedState(initialize, name = "ComputedState") {
89
+ let setupCallback = null;
90
+ const builder = {
91
+ on: {
92
+ setup(callback) {
93
+ setupCallback = callback;
94
+ },
95
+ },
96
+ };
97
+ const computer = initialize(builder);
98
+ const host = reactive({ value: null }, false);
99
+ const output = (() => host.value);
100
+ Object.defineProperty(output, "current", {
101
+ get: () => host.value,
102
+ });
103
+ Object.defineProperty(output, "name", {
104
+ value: name,
105
+ });
106
+ // eslint-disable-next-line prefer-const
107
+ let computedNotifier;
108
+ const computedSubscriber = {
109
+ handleChange() {
110
+ host.value = computedNotifier.observe(null);
111
+ },
112
+ };
113
+ computedNotifier = Observable.binding(computer, computedSubscriber);
114
+ computedNotifier.setMode(false);
115
+ let cleanup;
116
+ let setupNotifier;
117
+ if (setupCallback) {
118
+ const setupSubscriber = {
119
+ handleChange() {
120
+ if (cleanup) {
121
+ cleanup();
122
+ }
123
+ cleanup = setupNotifier.observe(null);
124
+ host.value = computer();
125
+ },
126
+ };
127
+ setupNotifier = Observable.binding(setupCallback, setupSubscriber);
128
+ setupNotifier.setMode(false);
129
+ cleanup = setupNotifier.observe(null);
130
+ }
131
+ host.value = computedNotifier.observe(null);
132
+ output.dispose = () => {
133
+ if (cleanup) {
134
+ cleanup();
135
+ }
136
+ if (setupNotifier) {
137
+ setupNotifier.dispose();
138
+ }
139
+ computedNotifier.dispose();
140
+ };
141
+ output.subscribe = (subscriber) => {
142
+ computedNotifier.subscribe(subscriber);
143
+ };
144
+ output.unsubscribe = (subscriber) => {
145
+ computedNotifier.unsubscribe(subscriber);
146
+ };
147
+ return output;
148
+ }
@@ -0,0 +1,28 @@
1
+ function shouldTraverse(value, traversed) {
2
+ return (value !== null &&
3
+ value !== void 0 &&
4
+ typeof value === "object" &&
5
+ !traversed.has(value));
6
+ }
7
+ export function visitObject(object, deep, visitor, data, traversed) {
8
+ if (!shouldTraverse(object, traversed)) {
9
+ return;
10
+ }
11
+ traversed.add(object);
12
+ if (Array.isArray(object)) {
13
+ visitor.visitArray(object, data);
14
+ for (const item of object) {
15
+ visitObject(item, deep, visitor, data, traversed);
16
+ }
17
+ }
18
+ else {
19
+ visitor.visitObject(object, data);
20
+ for (const key in object) {
21
+ const value = object[key];
22
+ visitor.visitProperty(object, key, value, data);
23
+ if (deep) {
24
+ visitObject(value, deep, visitor, data, traversed);
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,36 @@
1
+ import { isFunction, noop } from "../interfaces.js";
2
+ import { ArrayObserver } from "../observation/arrays.js";
3
+ import { Observable } from "../observation/observable.js";
4
+ import { visitObject } from "./visitor.js";
5
+ function watchObject(object, data) {
6
+ const notifier = Observable.getNotifier(object);
7
+ notifier.subscribe(data.subscriber);
8
+ data.notifiers.push(notifier);
9
+ }
10
+ const watchVisitor = {
11
+ visitProperty: noop,
12
+ visitObject: watchObject,
13
+ visitArray: watchObject,
14
+ };
15
+ /**
16
+ * Deeply subscribes to changes in existing observable objects.
17
+ * @param object - The observable object to watch.
18
+ * @param subscriber - The handler to call when changes are made to the object.
19
+ * @returns A disposable that can be used to unsubscribe from change updates.
20
+ * @beta
21
+ */
22
+ export function watch(object, subscriber) {
23
+ const data = {
24
+ notifiers: [],
25
+ subscriber: isFunction(subscriber) ? { handleChange: subscriber } : subscriber,
26
+ };
27
+ ArrayObserver.enable();
28
+ visitObject(object, true, watchVisitor, data, new Set());
29
+ return {
30
+ dispose() {
31
+ for (const n of data.notifiers) {
32
+ n.unsubscribe(data.subscriber);
33
+ }
34
+ },
35
+ };
36
+ }