@universityofmaryland/web-model-library 1.0.7 → 1.1.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 (209) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +1 -1
  3. package/dist/_types.d.ts +43 -0
  4. package/dist/_types.d.ts.map +1 -1
  5. package/dist/_virtual/index.js +1 -1
  6. package/dist/_virtual/postcss.js +1 -1
  7. package/dist/attributes/change-detection.d.ts +21 -0
  8. package/dist/attributes/change-detection.d.ts.map +1 -0
  9. package/dist/attributes/change-detection.js +60 -0
  10. package/dist/attributes/change-detection.js.map +1 -0
  11. package/dist/attributes/checks.d.ts.map +1 -1
  12. package/dist/attributes/checks.js +15 -8
  13. package/dist/attributes/checks.js.map +1 -1
  14. package/dist/attributes/config.d.ts +29 -0
  15. package/dist/attributes/config.d.ts.map +1 -0
  16. package/dist/attributes/config.js +57 -0
  17. package/dist/attributes/config.js.map +1 -0
  18. package/dist/attributes/converters.d.ts +14 -0
  19. package/dist/attributes/converters.d.ts.map +1 -0
  20. package/dist/attributes/converters.js +84 -0
  21. package/dist/attributes/converters.js.map +1 -0
  22. package/dist/attributes/errors.d.ts +16 -0
  23. package/dist/attributes/errors.d.ts.map +1 -0
  24. package/dist/attributes/errors.js +51 -0
  25. package/dist/attributes/errors.js.map +1 -0
  26. package/dist/attributes/index.d.ts +4 -0
  27. package/dist/attributes/index.d.ts.map +1 -1
  28. package/dist/attributes.js +18 -0
  29. package/dist/attributes.js.map +1 -1
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/model/controllers.d.ts +22 -0
  35. package/dist/model/controllers.d.ts.map +1 -0
  36. package/dist/model/controllers.js +53 -0
  37. package/dist/model/controllers.js.map +1 -0
  38. package/dist/model/errors.d.ts +13 -0
  39. package/dist/model/errors.d.ts.map +1 -0
  40. package/dist/model/errors.js +23 -0
  41. package/dist/model/errors.js.map +1 -0
  42. package/dist/model/index.d.ts +54 -3
  43. package/dist/model/index.d.ts.map +1 -1
  44. package/dist/model/registration.d.ts +13 -0
  45. package/dist/model/registration.d.ts.map +1 -0
  46. package/dist/model/registration.js +83 -0
  47. package/dist/model/registration.js.map +1 -0
  48. package/dist/model/update-cycle.d.ts +12 -0
  49. package/dist/model/update-cycle.d.ts.map +1 -0
  50. package/dist/model/update-cycle.js +41 -0
  51. package/dist/model/update-cycle.js.map +1 -0
  52. package/dist/model.js +335 -55
  53. package/dist/model.js.map +1 -1
  54. package/dist/node_modules/.pnpm/nanoid@3.3.11/node_modules/nanoid/non-secure/index.js.map +1 -0
  55. package/dist/node_modules/{picocolors → .pnpm/picocolors@1.1.1/node_modules/picocolors}/picocolors.browser.js +1 -1
  56. package/dist/node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.browser.js.map +1 -0
  57. package/dist/node_modules/.pnpm/postcss-discard-duplicates@5.1.0_postcss@8.5.8/node_modules/postcss-discard-duplicates/src/index.js.map +1 -0
  58. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/at-rule.js.map +1 -0
  59. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/comment.js.map +1 -0
  60. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/container.js.map +1 -0
  61. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/css-syntax-error.js +2 -2
  62. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/css-syntax-error.js.map +1 -0
  63. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/declaration.js.map +1 -0
  64. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/document.js.map +1 -0
  65. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/fromJSON.js.map +1 -0
  66. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/input.js +2 -2
  67. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/input.js.map +1 -0
  68. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/lazy-result.js.map +1 -0
  69. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/list.js.map +1 -0
  70. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/map-generator.js +1 -1
  71. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/map-generator.js.map +1 -0
  72. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/no-work-result.js.map +1 -0
  73. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/node.js.map +1 -0
  74. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/parse.js.map +1 -0
  75. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/parser.js.map +1 -0
  76. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/postcss.js +1 -1
  77. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss.js.map +1 -0
  78. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss2.js.map +1 -0
  79. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/previous-map.js +1 -1
  80. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/previous-map.js.map +1 -0
  81. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/processor.js.map +1 -0
  82. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/result.js.map +1 -0
  83. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/root.js.map +1 -0
  84. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/rule.js.map +1 -0
  85. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/stringifier.js.map +1 -0
  86. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/stringify.js.map +1 -0
  87. package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/symbols.js +1 -1
  88. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/symbols.js.map +1 -0
  89. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/tokenize.js.map +1 -0
  90. package/dist/node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/warning.js.map +1 -0
  91. package/dist/slots/index.d.ts +6 -0
  92. package/dist/slots/index.d.ts.map +1 -1
  93. package/dist/slots/slot-events.d.ts +32 -0
  94. package/dist/slots/slot-events.d.ts.map +1 -0
  95. package/dist/slots/slot-events.js +83 -0
  96. package/dist/slots/slot-events.js.map +1 -0
  97. package/dist/slots/slot-query.d.ts +18 -0
  98. package/dist/slots/slot-query.d.ts.map +1 -0
  99. package/dist/slots/slot-query.js +59 -0
  100. package/dist/slots/slot-query.js.map +1 -0
  101. package/dist/slots/slot-validation.d.ts +5 -0
  102. package/dist/slots/slot-validation.d.ts.map +1 -0
  103. package/dist/slots/slot-validation.js +89 -0
  104. package/dist/slots/slot-validation.js.map +1 -0
  105. package/dist/slots.js +15 -1
  106. package/dist/slots.js.map +1 -1
  107. package/dist/testing/console.d.ts +8 -0
  108. package/dist/testing/console.d.ts.map +1 -0
  109. package/dist/testing/console.js +39 -0
  110. package/dist/testing/console.js.map +1 -0
  111. package/dist/testing/events.d.ts +11 -0
  112. package/dist/testing/events.d.ts.map +1 -0
  113. package/dist/testing/events.js +47 -0
  114. package/dist/testing/events.js.map +1 -0
  115. package/dist/testing/fixture.d.ts +10 -0
  116. package/dist/testing/fixture.d.ts.map +1 -0
  117. package/dist/testing/fixture.js +36 -0
  118. package/dist/testing/fixture.js.map +1 -0
  119. package/dist/testing/index.d.ts +8 -0
  120. package/dist/testing/index.d.ts.map +1 -0
  121. package/dist/testing/shadow.d.ts +6 -0
  122. package/dist/testing/shadow.d.ts.map +1 -0
  123. package/dist/testing/shadow.js +27 -0
  124. package/dist/testing/shadow.js.map +1 -0
  125. package/dist/testing/slots.d.ts +10 -0
  126. package/dist/testing/slots.d.ts.map +1 -0
  127. package/dist/testing/slots.js +62 -0
  128. package/dist/testing/slots.js.map +1 -0
  129. package/dist/testing.d.ts +2 -0
  130. package/dist/testing.js +26 -0
  131. package/dist/testing.js.map +1 -0
  132. package/dist/utilities/debug.d.ts +365 -0
  133. package/dist/utilities/debug.d.ts.map +1 -0
  134. package/dist/utilities/debug.js +71 -0
  135. package/dist/utilities/debug.js.map +1 -0
  136. package/dist/utilities/dom.d.ts +11 -0
  137. package/dist/utilities/dom.d.ts.map +1 -0
  138. package/dist/utilities/dom.js +57 -0
  139. package/dist/utilities/dom.js.map +1 -0
  140. package/dist/utilities/events.d.ts +22 -0
  141. package/dist/utilities/events.d.ts.map +1 -0
  142. package/dist/utilities/events.js +49 -0
  143. package/dist/utilities/events.js.map +1 -0
  144. package/dist/utilities/index.d.ts +7 -0
  145. package/dist/utilities/index.d.ts.map +1 -1
  146. package/dist/utilities/register.d.ts.map +1 -1
  147. package/dist/utilities/register.js +16 -4
  148. package/dist/utilities/register.js.map +1 -1
  149. package/dist/utilities/styles.js +1 -1
  150. package/dist/utilities/types.d.ts +10 -0
  151. package/dist/utilities/types.d.ts.map +1 -0
  152. package/dist/utilities/types.js +45 -0
  153. package/dist/utilities/types.js.map +1 -0
  154. package/dist/utilities.js +27 -1
  155. package/dist/utilities.js.map +1 -1
  156. package/package.json +21 -15
  157. package/dist/node_modules/nanoid/non-secure/index.js.map +0 -1
  158. package/dist/node_modules/picocolors/picocolors.browser.js.map +0 -1
  159. package/dist/node_modules/postcss/lib/at-rule.js.map +0 -1
  160. package/dist/node_modules/postcss/lib/comment.js.map +0 -1
  161. package/dist/node_modules/postcss/lib/container.js.map +0 -1
  162. package/dist/node_modules/postcss/lib/css-syntax-error.js.map +0 -1
  163. package/dist/node_modules/postcss/lib/declaration.js.map +0 -1
  164. package/dist/node_modules/postcss/lib/document.js.map +0 -1
  165. package/dist/node_modules/postcss/lib/fromJSON.js.map +0 -1
  166. package/dist/node_modules/postcss/lib/input.js.map +0 -1
  167. package/dist/node_modules/postcss/lib/lazy-result.js.map +0 -1
  168. package/dist/node_modules/postcss/lib/list.js.map +0 -1
  169. package/dist/node_modules/postcss/lib/map-generator.js.map +0 -1
  170. package/dist/node_modules/postcss/lib/no-work-result.js.map +0 -1
  171. package/dist/node_modules/postcss/lib/node.js.map +0 -1
  172. package/dist/node_modules/postcss/lib/parse.js.map +0 -1
  173. package/dist/node_modules/postcss/lib/parser.js.map +0 -1
  174. package/dist/node_modules/postcss/lib/postcss.js.map +0 -1
  175. package/dist/node_modules/postcss/lib/postcss2.js.map +0 -1
  176. package/dist/node_modules/postcss/lib/previous-map.js.map +0 -1
  177. package/dist/node_modules/postcss/lib/processor.js.map +0 -1
  178. package/dist/node_modules/postcss/lib/result.js.map +0 -1
  179. package/dist/node_modules/postcss/lib/root.js.map +0 -1
  180. package/dist/node_modules/postcss/lib/rule.js.map +0 -1
  181. package/dist/node_modules/postcss/lib/stringifier.js.map +0 -1
  182. package/dist/node_modules/postcss/lib/stringify.js.map +0 -1
  183. package/dist/node_modules/postcss/lib/symbols.js.map +0 -1
  184. package/dist/node_modules/postcss/lib/tokenize.js.map +0 -1
  185. package/dist/node_modules/postcss/lib/warning.js.map +0 -1
  186. package/dist/packages/model/node_modules/postcss-discard-duplicates/src/index.js.map +0 -1
  187. /package/dist/node_modules/{nanoid → .pnpm/nanoid@3.3.11/node_modules/nanoid}/non-secure/index.js +0 -0
  188. /package/dist/{packages/model → node_modules/.pnpm/postcss-discard-duplicates@5.1.0_postcss@8.5.8}/node_modules/postcss-discard-duplicates/src/index.js +0 -0
  189. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/at-rule.js +0 -0
  190. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/comment.js +0 -0
  191. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/container.js +0 -0
  192. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/declaration.js +0 -0
  193. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/document.js +0 -0
  194. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/fromJSON.js +0 -0
  195. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/lazy-result.js +0 -0
  196. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/list.js +0 -0
  197. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/no-work-result.js +0 -0
  198. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/node.js +0 -0
  199. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/parse.js +0 -0
  200. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/parser.js +0 -0
  201. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/postcss2.js +0 -0
  202. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/processor.js +0 -0
  203. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/result.js +0 -0
  204. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/root.js +0 -0
  205. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/rule.js +0 -0
  206. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/stringifier.js +0 -0
  207. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/stringify.js +0 -0
  208. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/tokenize.js +0 -0
  209. /package/dist/node_modules/{postcss → .pnpm/postcss@8.5.8/node_modules/postcss}/lib/warning.js +0 -0
@@ -0,0 +1,83 @@
1
+ import { isDev, createLogger } from "../utilities/debug.js";
2
+ import { ComponentRegistrationError, BatchRegistrationError } from "./errors.js";
3
+ const TAG_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
4
+ function registerComponent(tagName, element, options) {
5
+ const registry = options?.registry ?? window.customElements;
6
+ const eager = options?.eager ?? true;
7
+ if (!element || typeof element !== "function") {
8
+ throw new ComponentRegistrationError(
9
+ tagName,
10
+ "missing-constructor",
11
+ "Constructor is required and must be a function."
12
+ );
13
+ }
14
+ if (!TAG_NAME_RE.test(tagName)) {
15
+ throw new ComponentRegistrationError(
16
+ tagName,
17
+ "invalid-name",
18
+ `"${tagName}" is not a valid custom element name. Must match ${TAG_NAME_RE}.`
19
+ );
20
+ }
21
+ const existing = registry.get(tagName);
22
+ if (existing) {
23
+ if (existing === element) {
24
+ return false;
25
+ }
26
+ throw new ComponentRegistrationError(
27
+ tagName,
28
+ "conflict",
29
+ `Already registered with a different constructor.`,
30
+ existing
31
+ );
32
+ }
33
+ if (!eager) {
34
+ const hasElement = document.getElementsByTagName(tagName).length > 0;
35
+ if (!hasElement) {
36
+ if (isDev()) {
37
+ const logger = createLogger(tagName);
38
+ logger.debug("Lazy registration skipped — no DOM elements found.");
39
+ }
40
+ return false;
41
+ }
42
+ }
43
+ registry.define(tagName, element);
44
+ return true;
45
+ }
46
+ function isComponentRegistered(tagName, registry) {
47
+ return (registry ?? window.customElements).get(tagName) !== void 0;
48
+ }
49
+ function whenComponentDefined(tagName, registry) {
50
+ return (registry ?? window.customElements).whenDefined(tagName);
51
+ }
52
+ function getComponentConstructor(tagName, registry) {
53
+ return (registry ?? window.customElements).get(tagName);
54
+ }
55
+ function registerComponents(components, options) {
56
+ const errors = [];
57
+ const succeeded = [];
58
+ for (const { tagName, element } of components) {
59
+ try {
60
+ const result = registerComponent(tagName, element, options);
61
+ if (result) {
62
+ succeeded.push(tagName);
63
+ }
64
+ } catch (error) {
65
+ if (error instanceof ComponentRegistrationError) {
66
+ errors.push(error);
67
+ } else {
68
+ throw error;
69
+ }
70
+ }
71
+ }
72
+ if (errors.length > 0) {
73
+ throw new BatchRegistrationError(errors, succeeded);
74
+ }
75
+ }
76
+ export {
77
+ getComponentConstructor,
78
+ isComponentRegistered,
79
+ registerComponent,
80
+ registerComponents,
81
+ whenComponentDefined
82
+ };
83
+ //# sourceMappingURL=registration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.js","sources":["../../source/model/registration.ts"],"sourcesContent":["/**\n * Component registration utilities.\n *\n * Provides validated registration, idempotent same-constructor handling,\n * conflict detection, batch registration, and query helpers.\n */\n\nimport { isDev, createLogger } from '../utilities/debug';\nimport {\n ComponentRegistrationError,\n BatchRegistrationError,\n} from './errors';\n\nconst TAG_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;\n\nexport interface RegisterOptions {\n /** Register immediately (true, default) or only when DOM elements exist (false). */\n eager?: boolean;\n /** Custom element registry. Defaults to `window.customElements`. */\n registry?: CustomElementRegistry;\n}\n\n/**\n * Validate and register a custom element.\n *\n * - Returns `true` on successful registration.\n * - Returns `false` if the same constructor is already registered (idempotent).\n * - Throws `ComponentRegistrationError` on conflict, invalid name, or missing constructor.\n */\nexport function registerComponent(\n tagName: string,\n element: CustomElementConstructor,\n options?: RegisterOptions,\n): boolean {\n const registry = options?.registry ?? window.customElements;\n const eager = options?.eager ?? true;\n\n if (!element || typeof element !== 'function') {\n throw new ComponentRegistrationError(\n tagName,\n 'missing-constructor',\n 'Constructor is required and must be a function.',\n );\n }\n\n if (!TAG_NAME_RE.test(tagName)) {\n throw new ComponentRegistrationError(\n tagName,\n 'invalid-name',\n `\"${tagName}\" is not a valid custom element name. Must match ${TAG_NAME_RE}.`,\n );\n }\n\n const existing = registry.get(tagName);\n if (existing) {\n if (existing === element) {\n return false;\n }\n throw new ComponentRegistrationError(\n tagName,\n 'conflict',\n `Already registered with a different constructor.`,\n existing,\n );\n }\n\n if (!eager) {\n const hasElement = document.getElementsByTagName(tagName).length > 0;\n if (!hasElement) {\n if (isDev()) {\n const logger = createLogger(tagName);\n logger.debug('Lazy registration skipped — no DOM elements found.');\n }\n return false;\n }\n }\n\n registry.define(tagName, element);\n return true;\n}\n\n/**\n * Check whether a tag name is already registered.\n */\nexport function isComponentRegistered(\n tagName: string,\n registry?: CustomElementRegistry,\n): boolean {\n return (registry ?? window.customElements).get(tagName) !== undefined;\n}\n\n/**\n * Promise that resolves when a tag name is defined.\n */\nexport function whenComponentDefined(\n tagName: string,\n registry?: CustomElementRegistry,\n): Promise<CustomElementConstructor> {\n return (registry ?? window.customElements).whenDefined(tagName);\n}\n\n/**\n * Return the constructor for a registered tag, or `undefined`.\n */\nexport function getComponentConstructor(\n tagName: string,\n registry?: CustomElementRegistry,\n): CustomElementConstructor | undefined {\n return (registry ?? window.customElements).get(tagName);\n}\n\n/**\n * Register multiple components. Attempts all registrations; throws\n * `BatchRegistrationError` if any fail (with per-component errors attached).\n */\nexport function registerComponents(\n components: Array<{ tagName: string; element: CustomElementConstructor }>,\n options?: RegisterOptions,\n): void {\n const errors: ComponentRegistrationError[] = [];\n const succeeded: string[] = [];\n\n for (const { tagName, element } of components) {\n try {\n const result = registerComponent(tagName, element, options);\n if (result) {\n succeeded.push(tagName);\n }\n } catch (error) {\n if (error instanceof ComponentRegistrationError) {\n errors.push(error);\n } else {\n throw error;\n }\n }\n }\n\n if (errors.length > 0) {\n throw new BatchRegistrationError(errors, succeeded);\n }\n}\n"],"names":[],"mappings":";;AAaA,MAAM,cAAc;AAgBb,SAAS,kBACd,SACA,SACA,SACS;AACT,QAAM,WAAW,SAAS,YAAY,OAAO;AAC7C,QAAM,QAAQ,SAAS,SAAS;AAEhC,MAAI,CAAC,WAAW,OAAO,YAAY,YAAY;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,CAAC,YAAY,KAAK,OAAO,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,IAAI,OAAO,oDAAoD,WAAW;AAAA,IAAA;AAAA,EAE9E;AAEA,QAAM,WAAW,SAAS,IAAI,OAAO;AACrC,MAAI,UAAU;AACZ,QAAI,aAAa,SAAS;AACxB,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,CAAC,OAAO;AACV,UAAM,aAAa,SAAS,qBAAqB,OAAO,EAAE,SAAS;AACnE,QAAI,CAAC,YAAY;AACf,UAAI,SAAS;AACX,cAAM,SAAS,aAAa,OAAO;AACnC,eAAO,MAAM,oDAAoD;AAAA,MACnE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,OAAO,SAAS,OAAO;AAChC,SAAO;AACT;AAKO,SAAS,sBACd,SACA,UACS;AACT,UAAQ,YAAY,OAAO,gBAAgB,IAAI,OAAO,MAAM;AAC9D;AAKO,SAAS,qBACd,SACA,UACmC;AACnC,UAAQ,YAAY,OAAO,gBAAgB,YAAY,OAAO;AAChE;AAKO,SAAS,wBACd,SACA,UACsC;AACtC,UAAQ,YAAY,OAAO,gBAAgB,IAAI,OAAO;AACxD;AAMO,SAAS,mBACd,YACA,SACM;AACN,QAAM,SAAuC,CAAA;AAC7C,QAAM,YAAsB,CAAA;AAE5B,aAAW,EAAE,SAAS,QAAA,KAAa,YAAY;AAC7C,QAAI;AACF,YAAM,SAAS,kBAAkB,SAAS,SAAS,OAAO;AAC1D,UAAI,QAAQ;AACV,kBAAU,KAAK,OAAO;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,4BAA4B;AAC/C,eAAO,KAAK,KAAK;AAAA,MACnB,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,uBAAuB,QAAQ,SAAS;AAAA,EACpD;AACF;"}
@@ -0,0 +1,12 @@
1
+ export declare class UpdateScheduler {
2
+ private _pending;
3
+ private _resolve;
4
+ private _promise;
5
+ private _performUpdate;
6
+ private _recursionCount;
7
+ constructor(performUpdate: () => boolean);
8
+ schedule(): void;
9
+ private _flush;
10
+ get updateComplete(): Promise<boolean>;
11
+ }
12
+ //# sourceMappingURL=update-cycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-cycle.d.ts","sourceRoot":"","sources":["../../source/model/update-cycle.ts"],"names":[],"mappings":"AAaA,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAK;gBAEhB,aAAa,EAAE,MAAM,OAAO;IAKxC,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,MAAM;IAsBd,IAAI,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAErC;CACF"}
@@ -0,0 +1,41 @@
1
+ const MAX_RECURSION = 100;
2
+ class UpdateScheduler {
3
+ constructor(performUpdate) {
4
+ this._pending = false;
5
+ this._resolve = null;
6
+ this._recursionCount = 0;
7
+ this._performUpdate = performUpdate;
8
+ this._promise = Promise.resolve(true);
9
+ }
10
+ schedule() {
11
+ if (this._pending) return;
12
+ this._pending = true;
13
+ this._promise = new Promise((resolve) => {
14
+ this._resolve = resolve;
15
+ queueMicrotask(() => this._flush(resolve));
16
+ });
17
+ }
18
+ _flush(resolve) {
19
+ this._pending = false;
20
+ this._recursionCount++;
21
+ if (this._recursionCount > MAX_RECURSION) {
22
+ this._recursionCount = 0;
23
+ resolve(false);
24
+ return;
25
+ }
26
+ const success = this._performUpdate();
27
+ if (this._pending) {
28
+ this._promise.then(resolve);
29
+ } else {
30
+ this._recursionCount = 0;
31
+ resolve(success);
32
+ }
33
+ }
34
+ get updateComplete() {
35
+ return this._promise;
36
+ }
37
+ }
38
+ export {
39
+ UpdateScheduler
40
+ };
41
+ //# sourceMappingURL=update-cycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-cycle.js","sources":["../../source/model/update-cycle.ts"],"sourcesContent":["/**\n * Update Scheduler\n *\n * Batches multiple `schedule()` calls into a single microtask-based update.\n * Inspired by Lit's reactive update cycle.\n *\n * - Deduplicates: multiple `schedule()` calls before the microtask produce one update\n * - Re-entrant safe: if `performUpdate` triggers another `schedule()`, a new microtask is queued\n * - Max recursion guard (100 iterations) to prevent infinite loops\n */\n\nconst MAX_RECURSION = 100;\n\nexport class UpdateScheduler {\n private _pending = false;\n private _resolve: ((value: boolean) => void) | null = null;\n private _promise: Promise<boolean>;\n private _performUpdate: () => boolean;\n private _recursionCount = 0;\n\n constructor(performUpdate: () => boolean) {\n this._performUpdate = performUpdate;\n this._promise = Promise.resolve(true);\n }\n\n schedule(): void {\n if (this._pending) return;\n this._pending = true;\n\n this._promise = new Promise<boolean>((resolve) => {\n this._resolve = resolve;\n queueMicrotask(() => this._flush(resolve));\n });\n }\n\n private _flush(resolve: (value: boolean) => void): void {\n this._pending = false;\n this._recursionCount++;\n\n if (this._recursionCount > MAX_RECURSION) {\n this._recursionCount = 0;\n resolve(false);\n return;\n }\n\n const success = this._performUpdate();\n\n // If performUpdate triggered another schedule(), the new microtask\n // will handle it. Chain this resolve to the new promise.\n if (this._pending) {\n this._promise.then(resolve);\n } else {\n this._recursionCount = 0;\n resolve(success);\n }\n }\n\n get updateComplete(): Promise<boolean> {\n return this._promise;\n }\n}\n"],"names":[],"mappings":"AAWA,MAAM,gBAAgB;AAEf,MAAM,gBAAgB;AAAA,EAO3B,YAAY,eAA8B;AAN1C,SAAQ,WAAW;AACnB,SAAQ,WAA8C;AAGtD,SAAQ,kBAAkB;AAGxB,SAAK,iBAAiB;AACtB,SAAK,WAAW,QAAQ,QAAQ,IAAI;AAAA,EACtC;AAAA,EAEA,WAAiB;AACf,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAEhB,SAAK,WAAW,IAAI,QAAiB,CAAC,YAAY;AAChD,WAAK,WAAW;AAChB,qBAAe,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,SAAyC;AACtD,SAAK,WAAW;AAChB,SAAK;AAEL,QAAI,KAAK,kBAAkB,eAAe;AACxC,WAAK,kBAAkB;AACvB,cAAQ,KAAK;AACb;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,eAAA;AAIrB,QAAI,KAAK,UAAU;AACjB,WAAK,SAAS,KAAK,OAAO;AAAA,IAC5B,OAAO;AACL,WAAK,kBAAkB;AACvB,cAAQ,OAAO;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,IAAI,iBAAmC;AACrC,WAAO,KAAK;AAAA,EACd;AACF;"}
package/dist/model.js CHANGED
@@ -1,4 +1,16 @@
1
1
  import { stylesTemplate } from "./utilities/styles.js";
2
+ import { createLogger } from "./utilities/debug.js";
3
+ import { defineObservedAttributes, resolveAttributeConfigs } from "./attributes/config.js";
4
+ import { ChangeDetector } from "./attributes/change-detection.js";
5
+ import { AttributeTypeError, AttributeValidationError } from "./attributes/errors.js";
6
+ import { UpdateScheduler } from "./model/update-cycle.js";
7
+ import { registerComponent } from "./model/registration.js";
8
+ import { getComponentConstructor, isComponentRegistered, registerComponents, whenComponentDefined } from "./model/registration.js";
9
+ import { validateAllSlots, validateSlotElements } from "./slots/slot-validation.js";
10
+ import { createSlotchangeHandler } from "./slots/slot-events.js";
11
+ import { querySlottedElements, querySlottedElement, hasSlottedContent } from "./slots/slot-query.js";
12
+ import { IntersectionController, MediaQueryController } from "./model/controllers.js";
13
+ import { BatchRegistrationError, ComponentRegistrationError } from "./model/errors.js";
2
14
  var __create = Object.create;
3
15
  var __defProp = Object.defineProperty;
4
16
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -40,6 +52,17 @@ class BaseComponent extends HTMLElement {
40
52
  constructor() {
41
53
  super();
42
54
  this.elementRef = null;
55
+ this._resolvedReactiveAttrs = [];
56
+ this._changeDetector = new ChangeDetector();
57
+ this._reactiveValues = /* @__PURE__ */ new Map();
58
+ this._isReflecting = false;
59
+ this._hasConnected = false;
60
+ this._hasFirstUpdated = false;
61
+ this._changedProperties = /* @__PURE__ */ new Map();
62
+ this._isUpdating = false;
63
+ this._pendingPreUpgrade = null;
64
+ this._slotCleanups = [];
65
+ this._controllers = /* @__PURE__ */ new Set();
43
66
  const constructor = this.constructor;
44
67
  this.config = constructor.componentConfig;
45
68
  if (!this.config) {
@@ -47,14 +70,24 @@ class BaseComponent extends HTMLElement {
47
70
  "Component config not found. Did you forget to use the @Component decorator?"
48
71
  );
49
72
  }
73
+ this._logger = createLogger(this.config.tagName);
74
+ this._updateScheduler = new UpdateScheduler(() => this._performUpdate());
50
75
  this.shadow = this.attachShadow({ mode: "open" });
51
76
  this.validateConfig();
77
+ this.setupReactiveAttributes();
52
78
  }
53
79
  static get observedAttributes() {
54
- return this.componentConfig?.attributes?.map((attr) => attr.name) || [];
80
+ const handlerNames = this.componentConfig?.attributes?.map((attr) => attr.name) || [];
81
+ const reactiveNames = this.componentConfig?.reactiveAttributes ? defineObservedAttributes(this.componentConfig.reactiveAttributes) : [];
82
+ return [.../* @__PURE__ */ new Set([...handlerNames, ...reactiveNames])];
55
83
  }
56
84
  connectedCallback() {
57
85
  try {
86
+ this.applyPreUpgradeProperties();
87
+ if (!this._hasConnected) {
88
+ this._hasConnected = true;
89
+ this.executeFirstConnected();
90
+ }
58
91
  this.initializeComponent();
59
92
  } catch (error) {
60
93
  this.handleError("Failed to initialize component", error);
@@ -74,6 +107,10 @@ class BaseComponent extends HTMLElement {
74
107
  initializeComponent() {
75
108
  try {
76
109
  this.validateSlots();
110
+ if (!this._hasFirstUpdated) {
111
+ this._hasFirstUpdated = true;
112
+ this.executeWillFirstUpdate();
113
+ }
77
114
  const componentRef = this.config.createComponent(this);
78
115
  if (!componentRef || !componentRef.element) {
79
116
  throw new Error(
@@ -82,6 +119,11 @@ class BaseComponent extends HTMLElement {
82
119
  }
83
120
  this.elementRef = componentRef;
84
121
  this.setupShadowDom(componentRef);
122
+ this.setupSlotObservers();
123
+ this.notifyControllers("hostConnected");
124
+ if (this._changedProperties.size > 0) {
125
+ this._updateScheduler.schedule();
126
+ }
85
127
  this.afterInit();
86
128
  } catch (error) {
87
129
  this.handleError("Failed to initialize component", error);
@@ -125,15 +167,218 @@ class BaseComponent extends HTMLElement {
125
167
  });
126
168
  }
127
169
  handleAttributeChange(name, oldValue, newValue) {
128
- if (!this.elementRef) return;
129
- const handler = this.config.attributes?.find(
130
- (attr) => attr.name === name
131
- )?.handler;
132
- if (handler) {
133
- handler(this.elementRef, oldValue, newValue);
170
+ if (this.elementRef) {
171
+ const handler = this.config.attributes?.find(
172
+ (attr) => attr.name === name
173
+ )?.handler;
174
+ if (handler) {
175
+ handler(this.elementRef, oldValue, newValue);
176
+ }
177
+ }
178
+ if (!this._isReflecting) {
179
+ const resolved = this._resolvedReactiveAttrs.find(
180
+ (r) => r.attributeName === name
181
+ );
182
+ if (resolved) {
183
+ let converted;
184
+ try {
185
+ converted = resolved.converter.fromAttribute(newValue, name);
186
+ } catch (err) {
187
+ throw new AttributeTypeError(
188
+ name,
189
+ resolved.type ?? "unknown",
190
+ newValue,
191
+ this.config.tagName
192
+ );
193
+ }
194
+ const value = converted !== void 0 ? converted : resolved.defaultValue;
195
+ if (value !== void 0 && resolved.validate) {
196
+ const error = resolved.validate(value);
197
+ if (error) {
198
+ throw new AttributeValidationError(name, error, this.config.tagName);
199
+ }
200
+ }
201
+ const oldValue2 = this._reactiveValues.get(resolved.propertyName);
202
+ const equalityOverride = resolved.hasChanged ? (prev, next) => !resolved.hasChanged(next, prev) : void 0;
203
+ const changed = this._changeDetector.set(resolved.propertyName, value, equalityOverride);
204
+ this._reactiveValues.set(resolved.propertyName, value);
205
+ if (changed) {
206
+ if (resolved.onChange) {
207
+ resolved.onChange(this, value, oldValue2);
208
+ }
209
+ this.requestUpdate(resolved.propertyName, oldValue2);
210
+ }
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Capture a property value set on the instance before upgrade.
216
+ * Deletes the instance property so the class accessor takes precedence,
217
+ * then returns the captured value for re-application.
218
+ */
219
+ capturePreUpgradeProperty(propertyName) {
220
+ if (this.hasOwnProperty(propertyName)) {
221
+ const value = this[propertyName];
222
+ delete this[propertyName];
223
+ return { value, captured: true };
224
+ }
225
+ return { value: void 0, captured: false };
226
+ }
227
+ /**
228
+ * Apply pre-upgrade property values through reactive setters.
229
+ * Called in connectedCallback after all attributeChangedCallback
230
+ * calls have completed during upgrade.
231
+ */
232
+ applyPreUpgradeProperties() {
233
+ if (!this._pendingPreUpgrade) return;
234
+ const pending = this._pendingPreUpgrade;
235
+ this._pendingPreUpgrade = null;
236
+ for (const [propertyName, value] of pending) {
237
+ this[propertyName] = value;
238
+ }
239
+ }
240
+ setupReactiveAttributes() {
241
+ if (!this.config.reactiveAttributes) return;
242
+ this._resolvedReactiveAttrs = resolveAttributeConfigs(
243
+ this.config.reactiveAttributes
244
+ );
245
+ for (const resolved of this._resolvedReactiveAttrs) {
246
+ const { propertyName, defaultValue } = resolved;
247
+ const preUpgrade = this.capturePreUpgradeProperty(propertyName);
248
+ if (resolved.attributeName !== false) {
249
+ const raw = this.getAttribute(resolved.attributeName);
250
+ if (raw !== null) {
251
+ const converted = resolved.converter.fromAttribute(
252
+ raw,
253
+ resolved.attributeName
254
+ );
255
+ this._reactiveValues.set(
256
+ propertyName,
257
+ converted !== void 0 ? converted : defaultValue
258
+ );
259
+ } else if (defaultValue !== void 0) {
260
+ this._reactiveValues.set(propertyName, defaultValue);
261
+ }
262
+ } else if (defaultValue !== void 0) {
263
+ this._reactiveValues.set(propertyName, defaultValue);
264
+ }
265
+ Object.defineProperty(this, propertyName, {
266
+ get: () => this._reactiveValues.get(propertyName),
267
+ set: (value) => {
268
+ if (value !== void 0 && resolved.validate) {
269
+ const error = resolved.validate(value);
270
+ if (error) {
271
+ const attrName = resolved.attributeName !== false ? resolved.attributeName : propertyName;
272
+ throw new AttributeValidationError(attrName, error, this.config.tagName);
273
+ }
274
+ }
275
+ const oldValue = this._reactiveValues.get(propertyName);
276
+ const equalityOverride = resolved.hasChanged ? (prev, next) => !resolved.hasChanged(next, prev) : void 0;
277
+ const changed = this._changeDetector.set(propertyName, value, equalityOverride);
278
+ if (!changed) return;
279
+ this._reactiveValues.set(propertyName, value);
280
+ if (resolved.reflect && resolved.attributeName !== false) {
281
+ this._isReflecting = true;
282
+ try {
283
+ const attrValue = resolved.converter.toAttribute(value);
284
+ if (attrValue === null) {
285
+ this.removeAttribute(resolved.attributeName);
286
+ } else {
287
+ this.setAttribute(resolved.attributeName, attrValue);
288
+ }
289
+ } finally {
290
+ this._isReflecting = false;
291
+ }
292
+ }
293
+ if (resolved.onChange) {
294
+ resolved.onChange(this, value, oldValue);
295
+ }
296
+ this.requestUpdate(propertyName, oldValue);
297
+ },
298
+ configurable: true,
299
+ enumerable: true
300
+ });
301
+ if (preUpgrade.captured) {
302
+ if (!this._pendingPreUpgrade) {
303
+ this._pendingPreUpgrade = /* @__PURE__ */ new Map();
304
+ }
305
+ this._pendingPreUpgrade.set(propertyName, preUpgrade.value);
306
+ }
307
+ }
308
+ }
309
+ addController(controller) {
310
+ this._controllers.add(controller);
311
+ if (this.isConnected && this.elementRef) {
312
+ controller.hostConnected?.();
313
+ }
314
+ }
315
+ removeController(controller) {
316
+ this._controllers.delete(controller);
317
+ }
318
+ requestUpdate(name, oldValue) {
319
+ if (name !== void 0 && !this._changedProperties.has(name)) {
320
+ this._changedProperties.set(name, oldValue);
321
+ }
322
+ if (!this._hasConnected) return;
323
+ this._updateScheduler.schedule();
324
+ }
325
+ get updateComplete() {
326
+ return this._updateScheduler.updateComplete;
327
+ }
328
+ willUpdate(_changedProperties) {
329
+ this.config.willUpdate?.(this, _changedProperties);
330
+ }
331
+ update(_changedProperties) {
332
+ this.config.update?.(this, _changedProperties);
333
+ }
334
+ updated(_changedProperties) {
335
+ this.config.updated?.(this, _changedProperties);
336
+ }
337
+ _performUpdate() {
338
+ if (this._isUpdating) return true;
339
+ this._isUpdating = true;
340
+ const changedProperties = this._changedProperties;
341
+ this._changedProperties = /* @__PURE__ */ new Map();
342
+ try {
343
+ for (const controller of this._controllers) {
344
+ controller.hostUpdate?.();
345
+ }
346
+ this.willUpdate(changedProperties);
347
+ this.update(changedProperties);
348
+ this.updated(changedProperties);
349
+ for (const controller of this._controllers) {
350
+ controller.hostUpdated?.();
351
+ }
352
+ return true;
353
+ } catch (error) {
354
+ this.handleError("Error during update cycle", error);
355
+ return false;
356
+ } finally {
357
+ this._isUpdating = false;
358
+ }
359
+ }
360
+ notifyControllers(method) {
361
+ for (const controller of this._controllers) {
362
+ try {
363
+ controller[method]?.();
364
+ } catch (error) {
365
+ this.handleError(`Controller ${method} failed`, error);
366
+ }
134
367
  }
135
368
  }
369
+ querySlot(slotName, selector) {
370
+ return querySlottedElements(this, slotName, { selector, flatten: true });
371
+ }
372
+ querySlotElement(slotName, selector) {
373
+ return querySlottedElement(this, slotName, selector);
374
+ }
375
+ hasSlotContent(slotName) {
376
+ return hasSlottedContent(this, slotName);
377
+ }
136
378
  cleanup() {
379
+ this.notifyControllers("hostDisconnected");
380
+ for (const fn of this._slotCleanups) fn();
381
+ this._slotCleanups = [];
137
382
  this.elementRef = null;
138
383
  }
139
384
  /**
@@ -155,7 +400,7 @@ class BaseComponent extends HTMLElement {
155
400
  );
156
401
  }
157
402
  handleError(message, error) {
158
- console.error(`[${this.config.tagName}]`, message, error);
403
+ this._logger.error(message, error);
159
404
  const errorType = this.elementRef ? "runtime" : message.toLowerCase().includes("validation") ? "validation" : "initialization";
160
405
  this.dispatchComponentEvent("umdComponent:error", {
161
406
  type: errorType,
@@ -165,55 +410,44 @@ class BaseComponent extends HTMLElement {
165
410
  }
166
411
  validateSlots() {
167
412
  if (!this.config.slots) return;
168
- const errors = [];
169
- Object.entries(this.config.slots).forEach(([name, config]) => {
170
- const type = name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, "");
171
- if (config.required) {
172
- const slotElement = this.querySelector(`[slot="${type}"]`);
173
- if (!slotElement) {
174
- errors.push({
175
- slot: name,
176
- error: "missing",
177
- message: `Required slot "${type}" is missing`
178
- });
179
- }
180
- }
181
- if (config.deprecated) {
182
- const slotElement = this.querySelector(`[slot="${type}"]`);
183
- if (slotElement) {
184
- errors.push({
185
- slot: name,
186
- error: "deprecated",
187
- message: `Slot "${type}" is deprecated. ${config.deprecated}`
188
- });
189
- }
190
- }
191
- if (config.allowedElements) {
192
- const slotElements = Array.from(
193
- this.querySelectorAll(`[slot="${type}"]`)
194
- );
195
- const invalidElements = slotElements.filter(
196
- (element) => !config.allowedElements.includes(element.tagName.toLowerCase())
197
- );
198
- if (invalidElements.length > 0) {
199
- errors.push({
200
- slot: name,
201
- error: "invalid-elements",
202
- message: `Slot "${type}" contains invalid elements. Allowed: ${config.allowedElements.join(
203
- ", "
204
- )}`,
205
- invalidElements
206
- });
207
- }
413
+ const result = validateAllSlots(this, this.config.slots);
414
+ this.reportSlotErrors(result);
415
+ }
416
+ reportSlotErrors(result) {
417
+ if (result.isValid) return;
418
+ this.handleError("Slot validation failed", result.errors);
419
+ if (result.errors.length === 1) {
420
+ this._logger.warn(result.errors[0].message);
421
+ } else {
422
+ this._logger.group(`${result.errors.length} slot validation warnings`);
423
+ for (const error of result.errors) {
424
+ this._logger.warn(error.message);
208
425
  }
209
- });
210
- if (errors.length > 0) {
211
- this.handleError("Slot validation failed", errors);
212
- errors.forEach((error) => {
213
- console.warn(`[${this.config.tagName}]`, error.message, error);
214
- });
426
+ this._logger.groupEnd();
215
427
  }
216
428
  }
429
+ setupSlotObservers() {
430
+ if (!this.config.slots) return;
431
+ const configKeys = Object.keys(this.config.slots);
432
+ const kebabToConfig = /* @__PURE__ */ new Map();
433
+ for (const key of configKeys) {
434
+ const kebab = key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, "");
435
+ kebabToConfig.set(kebab, { key, config: this.config.slots[key] });
436
+ }
437
+ const handler = createSlotchangeHandler(
438
+ this,
439
+ (event) => {
440
+ const slotName = event.slotName ?? "";
441
+ const entry = kebabToConfig.get(slotName);
442
+ if (!entry) return;
443
+ const result = validateSlotElements(entry.key, event.elements, entry.config);
444
+ this.reportSlotErrors(result);
445
+ },
446
+ { flatten: true }
447
+ );
448
+ handler.connect();
449
+ this._slotCleanups.push(() => handler.disconnect());
450
+ }
217
451
  async executeLifecycleCallbacks() {
218
452
  const lifecycleMethods = [
219
453
  "beforeConnect",
@@ -238,6 +472,33 @@ class BaseComponent extends HTMLElement {
238
472
  getRef() {
239
473
  return this.elementRef;
240
474
  }
475
+ getShadowRoot() {
476
+ return this.shadow;
477
+ }
478
+ executeFirstConnected() {
479
+ try {
480
+ if (this.config.firstConnected) {
481
+ this.config.firstConnected(this, this.shadow);
482
+ }
483
+ this.firstConnected();
484
+ } catch (error) {
485
+ this.handleError("Failed to execute firstConnected hook", error);
486
+ }
487
+ }
488
+ firstConnected() {
489
+ }
490
+ executeWillFirstUpdate() {
491
+ try {
492
+ if (this.config.willFirstUpdate) {
493
+ this.config.willFirstUpdate(this, this.shadow);
494
+ }
495
+ this.willFirstUpdate();
496
+ } catch (error) {
497
+ this.handleError("Failed to execute willFirstUpdate hook", error);
498
+ }
499
+ }
500
+ willFirstUpdate() {
501
+ }
241
502
  }
242
503
  const createCustomElement = (config) => {
243
504
  var _Component_decorators, _init, _a;
@@ -252,8 +513,27 @@ const createCustomElement = (config) => {
252
513
  __runInitializers(_init, 1, Component);
253
514
  return Component;
254
515
  };
516
+ const defineComponent = (config, options) => {
517
+ let element = null;
518
+ return () => {
519
+ if (!element) {
520
+ element = createCustomElement(config);
521
+ }
522
+ registerComponent(config.tagName, element, options);
523
+ };
524
+ };
255
525
  export {
256
526
  BaseComponent,
257
- createCustomElement
527
+ BatchRegistrationError,
528
+ ComponentRegistrationError,
529
+ IntersectionController,
530
+ MediaQueryController,
531
+ createCustomElement,
532
+ defineComponent,
533
+ getComponentConstructor,
534
+ isComponentRegistered,
535
+ registerComponent,
536
+ registerComponents,
537
+ whenComponentDefined
258
538
  };
259
539
  //# sourceMappingURL=model.js.map