@craft-ts/component 0.7.0-beta.13

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 (190) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +36 -0
  3. package/src/index.d.ts +40 -0
  4. package/src/index.d.ts.map +1 -0
  5. package/src/index.js +38 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/a11y-control.d.ts +58 -0
  8. package/src/lib/a11y-control.d.ts.map +1 -0
  9. package/src/lib/a11y-control.js +74 -0
  10. package/src/lib/a11y-control.js.map +1 -0
  11. package/src/lib/a11y.d.ts +87 -0
  12. package/src/lib/a11y.d.ts.map +1 -0
  13. package/src/lib/a11y.js +96 -0
  14. package/src/lib/a11y.js.map +1 -0
  15. package/src/lib/ai/ai-context-menu.d.ts +140 -0
  16. package/src/lib/ai/ai-context-menu.d.ts.map +1 -0
  17. package/src/lib/ai/ai-context-menu.js +66 -0
  18. package/src/lib/ai/ai-context-menu.js.map +1 -0
  19. package/src/lib/ai/ai-send-dialog.d.ts +1263 -0
  20. package/src/lib/ai/ai-send-dialog.d.ts.map +1 -0
  21. package/src/lib/ai/ai-send-dialog.js +288 -0
  22. package/src/lib/ai/ai-send-dialog.js.map +1 -0
  23. package/src/lib/ai/send-context-to-ai.d.ts +29 -0
  24. package/src/lib/ai/send-context-to-ai.d.ts.map +1 -0
  25. package/src/lib/ai/send-context-to-ai.js +127 -0
  26. package/src/lib/ai/send-context-to-ai.js.map +1 -0
  27. package/src/lib/assert-defined-input.d.ts +26 -0
  28. package/src/lib/assert-defined-input.d.ts.map +1 -0
  29. package/src/lib/assert-defined-input.js +47 -0
  30. package/src/lib/assert-defined-input.js.map +1 -0
  31. package/src/lib/block.d.ts +36 -0
  32. package/src/lib/block.d.ts.map +1 -0
  33. package/src/lib/block.js +55 -0
  34. package/src/lib/block.js.map +1 -0
  35. package/src/lib/bootstrap.d.ts +32 -0
  36. package/src/lib/bootstrap.d.ts.map +1 -0
  37. package/src/lib/bootstrap.js +60 -0
  38. package/src/lib/bootstrap.js.map +1 -0
  39. package/src/lib/bridge.d.ts +56 -0
  40. package/src/lib/bridge.d.ts.map +1 -0
  41. package/src/lib/bridge.js +81 -0
  42. package/src/lib/bridge.js.map +1 -0
  43. package/src/lib/component.d.ts +46 -0
  44. package/src/lib/component.d.ts.map +1 -0
  45. package/src/lib/component.js +118 -0
  46. package/src/lib/component.js.map +1 -0
  47. package/src/lib/composition.d.ts +17 -0
  48. package/src/lib/composition.d.ts.map +1 -0
  49. package/src/lib/composition.js +28 -0
  50. package/src/lib/composition.js.map +1 -0
  51. package/src/lib/craft-defaults.d.ts +2 -0
  52. package/src/lib/craft-defaults.d.ts.map +1 -0
  53. package/src/lib/craft-defaults.js +41 -0
  54. package/src/lib/craft-defaults.js.map +1 -0
  55. package/src/lib/craft-host-tokens.d.ts +6 -0
  56. package/src/lib/craft-host-tokens.d.ts.map +1 -0
  57. package/src/lib/craft-host-tokens.js +7 -0
  58. package/src/lib/craft-host-tokens.js.map +1 -0
  59. package/src/lib/craft-router-outlet.d.ts +15 -0
  60. package/src/lib/craft-router-outlet.d.ts.map +1 -0
  61. package/src/lib/craft-router-outlet.js +10 -0
  62. package/src/lib/craft-router-outlet.js.map +1 -0
  63. package/src/lib/css-vars.d.ts +31 -0
  64. package/src/lib/css-vars.d.ts.map +1 -0
  65. package/src/lib/css-vars.js +43 -0
  66. package/src/lib/css-vars.js.map +1 -0
  67. package/src/lib/css-vars.type.d.ts +151 -0
  68. package/src/lib/css-vars.type.d.ts.map +1 -0
  69. package/src/lib/css-vars.type.js +1 -0
  70. package/src/lib/css-vars.type.js.map +1 -0
  71. package/src/lib/defer.d.ts +31 -0
  72. package/src/lib/defer.d.ts.map +1 -0
  73. package/src/lib/defer.js +19 -0
  74. package/src/lib/defer.js.map +1 -0
  75. package/src/lib/directive.d.ts +9 -0
  76. package/src/lib/directive.d.ts.map +1 -0
  77. package/src/lib/directive.js +31 -0
  78. package/src/lib/directive.js.map +1 -0
  79. package/src/lib/each-scheduling.d.ts +59 -0
  80. package/src/lib/each-scheduling.d.ts.map +1 -0
  81. package/src/lib/each-scheduling.js +120 -0
  82. package/src/lib/each-scheduling.js.map +1 -0
  83. package/src/lib/each.d.ts +29 -0
  84. package/src/lib/each.d.ts.map +1 -0
  85. package/src/lib/each.js +23 -0
  86. package/src/lib/each.js.map +1 -0
  87. package/src/lib/factory-runtime.d.ts +5 -0
  88. package/src/lib/factory-runtime.d.ts.map +1 -0
  89. package/src/lib/factory-runtime.js +26 -0
  90. package/src/lib/factory-runtime.js.map +1 -0
  91. package/src/lib/field-exception-block.d.ts +96 -0
  92. package/src/lib/field-exception-block.d.ts.map +1 -0
  93. package/src/lib/field-exception-block.js +44 -0
  94. package/src/lib/field-exception-block.js.map +1 -0
  95. package/src/lib/host-runtime.d.ts +60 -0
  96. package/src/lib/host-runtime.d.ts.map +1 -0
  97. package/src/lib/host-runtime.js +16 -0
  98. package/src/lib/host-runtime.js.map +1 -0
  99. package/src/lib/hydrate.d.ts +15 -0
  100. package/src/lib/hydrate.d.ts.map +1 -0
  101. package/src/lib/hydrate.js +79 -0
  102. package/src/lib/hydrate.js.map +1 -0
  103. package/src/lib/hyperscript.d.ts +148 -0
  104. package/src/lib/hyperscript.d.ts.map +1 -0
  105. package/src/lib/hyperscript.js +96 -0
  106. package/src/lib/hyperscript.js.map +1 -0
  107. package/src/lib/if-block.d.ts +8 -0
  108. package/src/lib/if-block.d.ts.map +1 -0
  109. package/src/lib/if-block.js +16 -0
  110. package/src/lib/if-block.js.map +1 -0
  111. package/src/lib/locator.d.ts +170 -0
  112. package/src/lib/locator.d.ts.map +1 -0
  113. package/src/lib/locator.js +151 -0
  114. package/src/lib/locator.js.map +1 -0
  115. package/src/lib/match-block.d.ts +38 -0
  116. package/src/lib/match-block.d.ts.map +1 -0
  117. package/src/lib/match-block.js +29 -0
  118. package/src/lib/match-block.js.map +1 -0
  119. package/src/lib/pending-block.d.ts +125 -0
  120. package/src/lib/pending-block.d.ts.map +1 -0
  121. package/src/lib/pending-block.js +48 -0
  122. package/src/lib/pending-block.js.map +1 -0
  123. package/src/lib/project.d.ts +19 -0
  124. package/src/lib/project.d.ts.map +1 -0
  125. package/src/lib/project.js +56 -0
  126. package/src/lib/project.js.map +1 -0
  127. package/src/lib/render/hydration-metadata.d.ts +3 -0
  128. package/src/lib/render/hydration-metadata.d.ts.map +1 -0
  129. package/src/lib/render/hydration-metadata.js +8 -0
  130. package/src/lib/render/hydration-metadata.js.map +1 -0
  131. package/src/lib/render/hydration.d.ts +24 -0
  132. package/src/lib/render/hydration.d.ts.map +1 -0
  133. package/src/lib/render/hydration.js +181 -0
  134. package/src/lib/render/hydration.js.map +1 -0
  135. package/src/lib/render/interpreter.d.ts +32 -0
  136. package/src/lib/render/interpreter.d.ts.map +1 -0
  137. package/src/lib/render/interpreter.js +3588 -0
  138. package/src/lib/render/interpreter.js.map +1 -0
  139. package/src/lib/render/server-style-collector.d.ts +6 -0
  140. package/src/lib/render/server-style-collector.d.ts.map +1 -0
  141. package/src/lib/render/server-style-collector.js +23 -0
  142. package/src/lib/render/server-style-collector.js.map +1 -0
  143. package/src/lib/render/ssr-coordinator.d.ts +20 -0
  144. package/src/lib/render/ssr-coordinator.d.ts.map +1 -0
  145. package/src/lib/render/ssr-coordinator.js +99 -0
  146. package/src/lib/render/ssr-coordinator.js.map +1 -0
  147. package/src/lib/render/string-dom.d.ts +9 -0
  148. package/src/lib/render/string-dom.d.ts.map +1 -0
  149. package/src/lib/render/string-dom.js +377 -0
  150. package/src/lib/render/string-dom.js.map +1 -0
  151. package/src/lib/render/style-registry.d.ts +14 -0
  152. package/src/lib/render/style-registry.d.ts.map +1 -0
  153. package/src/lib/render/style-registry.js +79 -0
  154. package/src/lib/render/style-registry.js.map +1 -0
  155. package/src/lib/render/style-scope.d.ts +30 -0
  156. package/src/lib/render/style-scope.d.ts.map +1 -0
  157. package/src/lib/render/style-scope.js +261 -0
  158. package/src/lib/render/style-scope.js.map +1 -0
  159. package/src/lib/render/vnode.d.ts +330 -0
  160. package/src/lib/render/vnode.d.ts.map +1 -0
  161. package/src/lib/render/vnode.js +293 -0
  162. package/src/lib/render/vnode.js.map +1 -0
  163. package/src/lib/server-render.d.ts +34 -0
  164. package/src/lib/server-render.d.ts.map +1 -0
  165. package/src/lib/server-render.js +84 -0
  166. package/src/lib/server-render.js.map +1 -0
  167. package/src/lib/start.d.ts +13 -0
  168. package/src/lib/start.d.ts.map +1 -0
  169. package/src/lib/start.js +20 -0
  170. package/src/lib/start.js.map +1 -0
  171. package/src/lib/template-contract.d.ts +347 -0
  172. package/src/lib/template-contract.d.ts.map +1 -0
  173. package/src/lib/template-contract.js +1 -0
  174. package/src/lib/template-contract.js.map +1 -0
  175. package/src/lib/template.d.ts +11 -0
  176. package/src/lib/template.d.ts.map +1 -0
  177. package/src/lib/template.js +21 -0
  178. package/src/lib/template.js.map +1 -0
  179. package/src/lib/testing.d.ts +157 -0
  180. package/src/lib/testing.d.ts.map +1 -0
  181. package/src/lib/testing.js +416 -0
  182. package/src/lib/testing.js.map +1 -0
  183. package/src/lib/types.d.ts +442 -0
  184. package/src/lib/types.d.ts.map +1 -0
  185. package/src/lib/types.js +22 -0
  186. package/src/lib/types.js.map +1 -0
  187. package/testing/public-api.d.ts +3 -0
  188. package/testing/public-api.d.ts.map +1 -0
  189. package/testing/public-api.js +3 -0
  190. package/testing/public-api.js.map +1 -0
@@ -0,0 +1,3588 @@
1
+ import { CRAFT_SERVICE_PROVIDER_BRAND, CRAFT_DOM_EVENT_HOOK, CRAFT_NODE_DIRECTIVE, CRAFT_NODE_EFFECT_FACTORY, CRAFT_FIELD_EXCEPTION_BOUNDARY, CRAFT_FIELD_EXCEPTION_SOURCE, CRAFT_TEMPORAL_RUNTIME, COMPONENT_REGISTER, craftEffect, craftLazy, createBrowserDomAdapter, registerCraftSyncTemplateFlush, executeYieldable, HOST_TAG_LIST, executeTemplateTrace, isCraftException, isCraftGenShortCircuit, isCraftNotSettled, ɵwithSettledReadObserver, isGeneratorFunction, isCraftField, isGenerator, markYieldableValue, markYieldableMethod, isYieldableValue, isYieldableMethod, isYieldableReactiveValue, DEEP_YIELDABLE, provideHostName, rawReactiveFacade, rawReactiveValue, toYieldable, yieldableInvocation, ɵfallbackComponentRegister, ɵregisterCraftTarget, ɵcraftInjectorFromHost, fieldExceptionVisibilityMatches, YIELDABLE_VALUE, RealCraftTemporalRuntime, CRAFT_HYDRATION_ID, childCraftRenderIdentity, createCraftRenderIdentity, CRAFT_SSR_POLICY, CRAFT_MATCH, } from '@craft-ts/core';
2
+ import { executeCraftComponentFactory } from '../factory-runtime';
3
+ import { computed, createEnvironmentInjector, ElementRef, EnvironmentInjector, Injector, runInInjectionContext, signal, untracked, } from '../host-runtime';
4
+ import { CRAFT_COMPONENT, CRAFT_DIRECTIVE, CONTENT_DECLARATION_CONTEXT, } from '../types';
5
+ import { cssVarStyles } from '../css-vars';
6
+ import { normalizeChildren, mergeHostProps, withCraftRenderContext, CRAFT_NODE_DIRECTIVES, } from './vnode';
7
+ import { CraftUnhandledPendingError, resolvePendingBlockHandler, } from '../pending-block';
8
+ import { CraftUnhandledExceptionError, resolveCatchBlockHandler, } from '../block';
9
+ import { EACH_SCHEDULER, createEachScheduler, } from '../each-scheduling';
10
+ import { executeCraftComponentFactoryAsync } from '../factory-runtime';
11
+ import { CRAFT_STYLE_REGISTRY, ɵfallbackCraftStyleRegistry, } from './style-registry';
12
+ import { scopeCss, scopeIdFor, validateStyleScope } from './style-scope';
13
+ import { CRAFT_LOCATOR_CONTENT_NAMES, directCraftContentNames, findCraftTemplateLocator, } from '../locator';
14
+ import { HydrationMismatchError } from './hydration';
15
+ import { setNodeHydrationKey } from './hydration-metadata';
16
+ let templateGeneratorDepth = 0;
17
+ function childContext(context, overrides = {}) {
18
+ return { ...context, ...overrides };
19
+ }
20
+ function childNodeContext(context, node, index) {
21
+ const segments = node.kind === 'component'
22
+ ? [node.component[CRAFT_COMPONENT].name, index]
23
+ : node.kind === 'each'
24
+ ? [`each:${node.sourceName ?? index}`]
25
+ : node.kind === 'if'
26
+ ? [`if:${node.conditionName}`]
27
+ : [index];
28
+ return childContext(context, {
29
+ identity: childCraftRenderIdentity(context.identity, ...segments),
30
+ });
31
+ }
32
+ function lexicalContext(context) {
33
+ return context.declarationContext ?? context;
34
+ }
35
+ function preserveContentDeclarationContext(value, context) {
36
+ if (typeof value !== 'function')
37
+ return value;
38
+ if (value[CONTENT_DECLARATION_CONTEXT] !== undefined) {
39
+ return value;
40
+ }
41
+ const wrapped = (...args) => value(...args);
42
+ Object.defineProperty(wrapped, CONTENT_DECLARATION_CONTEXT, {
43
+ value: context,
44
+ enumerable: false,
45
+ configurable: false,
46
+ writable: false,
47
+ });
48
+ return wrapped;
49
+ }
50
+ function renderChildrenCallback(context, callback, args = [], kind = 'callback', name) {
51
+ const phase = tracePhase(context);
52
+ return executeTemplateTrace(context.injector, traceContext(context, kind, phase, name ?? callback.name), () => invokeChildrenCallback(context, callback, args));
53
+ }
54
+ function invokeChildrenCallback(context, callback, args) {
55
+ return runInInjectionContext(context.injector, () => withCraftRenderContext(context, () => callback(...args)));
56
+ }
57
+ function renderBlockChildren(context, name, callback, args = []) {
58
+ return executeTemplateTrace(context.injector, traceContext(context, 'block', tracePhase(context), name), () => invokeChildrenCallback(context, callback, args));
59
+ }
60
+ function renderDeferChildren(context, name, callback, args = []) {
61
+ return executeTemplateTrace(context.injector, traceContext(context, 'defer', tracePhase(context), name), () => invokeChildrenCallback(context, callback, args));
62
+ }
63
+ function tracePhase(context) {
64
+ return (context.traceState?.renderCount ?? 0) <= 1
65
+ ? 'initialRender'
66
+ : 'update';
67
+ }
68
+ function traceContext(context, kind, phase, name) {
69
+ return {
70
+ kind,
71
+ phase,
72
+ ...(context.componentName ? { componentName: context.componentName } : {}),
73
+ ...(name ? { name } : {}),
74
+ ...(context.directiveNames && context.directiveNames.length > 0
75
+ ? { directiveNames: context.directiveNames }
76
+ : {}),
77
+ renderCount: context.traceState?.renderCount ?? 0,
78
+ };
79
+ }
80
+ function traceComponentLifecycle(injector, phase, componentName, renderCount) {
81
+ executeTemplateTrace(injector, {
82
+ kind: 'component',
83
+ phase,
84
+ componentName,
85
+ renderCount,
86
+ }, () => []);
87
+ }
88
+ function eagerlyResolveBrandedProviders(providers, injector, branded = false) {
89
+ const overrides = [];
90
+ const trackers = [];
91
+ for (const provider of providers) {
92
+ if (Array.isArray(provider)) {
93
+ const result = eagerlyResolveBrandedProviders(provider, injector, branded || CRAFT_SERVICE_PROVIDER_BRAND in provider);
94
+ overrides.push(...result.overrides);
95
+ trackers.push(...result.trackers);
96
+ if (result.exception) {
97
+ return { exception: result.exception, overrides, trackers };
98
+ }
99
+ continue;
100
+ }
101
+ if (!branded || typeof provider !== 'object' || provider === null) {
102
+ continue;
103
+ }
104
+ const token = Reflect.get(provider, 'provide');
105
+ const useFactory = Reflect.get(provider, 'useFactory');
106
+ if (token !== undefined && branded && typeof useFactory === 'function') {
107
+ // Resolve Craft providers through a computed so signals read by their
108
+ // factories remain dependencies of the component render effect.
109
+ const resolved = computed(() => runInInjectionContext(injector, useFactory));
110
+ trackers.push(resolved);
111
+ const value = resolved();
112
+ if (isCraftException(value)) {
113
+ return { exception: value, overrides, trackers };
114
+ }
115
+ overrides.push({ provide: token, useValue: value });
116
+ }
117
+ }
118
+ return { overrides, trackers };
119
+ }
120
+ function findResourceException(value, seen = new Set()) {
121
+ if (typeof value !== 'object' || value === null || seen.has(value)) {
122
+ return undefined;
123
+ }
124
+ seen.add(value);
125
+ const candidate = value;
126
+ if (typeof candidate.exceptions === 'function') {
127
+ const exceptions = (isYieldableReactiveValue(candidate.exceptions)
128
+ ? rawReactiveValue(candidate.exceptions)()
129
+ : candidate.exceptions());
130
+ const exception = exceptions.list?.find(isCraftException);
131
+ if (exception)
132
+ return exception;
133
+ }
134
+ if (Array.isArray(value)) {
135
+ for (const child of value) {
136
+ const exception = findResourceException(child, seen);
137
+ if (exception)
138
+ return exception;
139
+ }
140
+ return undefined;
141
+ }
142
+ for (const child of Object.values(value)) {
143
+ const exception = findResourceException(child, seen);
144
+ if (exception)
145
+ return exception;
146
+ }
147
+ return undefined;
148
+ }
149
+ function scopeTokens(parent, own) {
150
+ return [parent, own].filter(Boolean).join(' ');
151
+ }
152
+ function styleValues(styles) {
153
+ return typeof styles === 'string' ? [styles] : (styles ?? []);
154
+ }
155
+ function acquireStyles(context, owners, ownerScope) {
156
+ if (context.serverStyles) {
157
+ return owners.flatMap((owner, index) => {
158
+ const css = styleValues(owner.styles);
159
+ if (!css.length)
160
+ return [];
161
+ const scope = scopeIdFor(owner.definition ?? {}, owner.name);
162
+ const dev = typeof ngDevMode === 'undefined' || ngDevMode;
163
+ if (dev)
164
+ validateStyleScope(scope, css.join('\n'));
165
+ return [
166
+ context.serverStyles.acquire(`craft:${ownerScope}:${scope}`, scopeCss(ownerScope, css.join('\n')), index),
167
+ ];
168
+ });
169
+ }
170
+ if (!context.styleRoot || !context.styles)
171
+ return [];
172
+ const registry = context.styles;
173
+ const releases = [];
174
+ owners.forEach((owner, index) => {
175
+ const css = styleValues(owner.styles);
176
+ if (!css.length)
177
+ return;
178
+ const scope = scopeIdFor(owner.definition ?? {}, owner.name);
179
+ const dev = typeof ngDevMode === 'undefined' || ngDevMode;
180
+ if (dev)
181
+ validateStyleScope(scope, css.join('\n'));
182
+ releases.push(registry.acquire(context.styleRoot, `craft:${ownerScope}:${scope}`, scopeCss(ownerScope, css.join('\n')), index));
183
+ });
184
+ return releases;
185
+ }
186
+ function contentScopeId(ownerScope, slotName) {
187
+ return `${ownerScope}::content::${slotName}`;
188
+ }
189
+ function acquireContentStyle(context, slotName, stylePolicy) {
190
+ if (stylePolicy !== 'allow-container-styles' ||
191
+ !slotName ||
192
+ !context.ownerScope ||
193
+ !context.contentStyles?.[slotName] ||
194
+ (!context.serverStyles && (!context.styleRoot || !context.styles))) {
195
+ return { release: () => undefined };
196
+ }
197
+ const scope = contentScopeId(context.ownerScope, slotName);
198
+ const css = scopeCss(scope, context.contentStyles[slotName], {
199
+ rootAttribute: 'data-craft-content',
200
+ limitSelector: '[data-craft-root]',
201
+ });
202
+ const release = context.serverStyles
203
+ ? context.serverStyles.acquire(`craft:content:${scope}`, css, 1000)
204
+ : context.styles.acquire(context.styleRoot, `craft:content:${scope}`, css, 1000);
205
+ return { scope, release };
206
+ }
207
+ function activeSsrRoute(context) {
208
+ const match = context.injector.get(CRAFT_MATCH, null);
209
+ try {
210
+ return match?.()?.route?.path;
211
+ }
212
+ catch {
213
+ return undefined;
214
+ }
215
+ }
216
+ function createRenderEffect(context, name, effectFn) {
217
+ // One token per binding: a binding that suspends holds it until it next runs
218
+ // to completion. The effect stays subscribed across the suspension — the
219
+ // settled read registered its source's `status` before throwing — so the
220
+ // boundary is released without any polling.
221
+ const token = {};
222
+ // A binding that short-circuited must tell its boundary when it recovers —
223
+ // but only that binding, so a sibling still in exception keeps its fallback.
224
+ let shortCircuited = false;
225
+ const ref = createEffectInInjector(context.injector, name, () => {
226
+ try {
227
+ // A settled read reports its source while it evaluates — that is how the
228
+ // boundary learns about a source that is refetching rather than
229
+ // suspending, since a refetch throws nothing.
230
+ ɵwithSettledReadObserver(context.settledReadObserver, effectFn);
231
+ }
232
+ catch (error) {
233
+ if (isCraftNotSettled(error)) {
234
+ if (!context.pendingBoundary) {
235
+ if (context.ssr) {
236
+ const routePolicy = context.injector.get(CRAFT_SSR_POLICY, null);
237
+ if (routePolicy) {
238
+ context.ssr.suspend(token, error.source, routePolicy.mode, {
239
+ route: activeSsrRoute(context),
240
+ timeoutMs: routePolicy.timeoutMs,
241
+ });
242
+ return;
243
+ }
244
+ context.ssr.unhandled(error.source, activeSsrRoute(context));
245
+ }
246
+ throw new CraftUnhandledPendingError(error.source);
247
+ }
248
+ context.pendingBoundary.suspend(token, error.source);
249
+ return;
250
+ }
251
+ if (!isCraftGenShortCircuit(error))
252
+ throw error;
253
+ // A settled read whose source carries a business exception: route it to
254
+ // the nearest catchBlock, exactly like a component initialization one.
255
+ context.pendingBoundary?.resume(token);
256
+ if (!context.exceptionBoundary?.(error.exception, token)) {
257
+ throw new CraftUnhandledExceptionError(error.exception);
258
+ }
259
+ shortCircuited = true;
260
+ return;
261
+ }
262
+ context.ssr?.resume(token);
263
+ context.pendingBoundary?.resume(token);
264
+ if (shortCircuited) {
265
+ shortCircuited = false;
266
+ context.exceptionBoundaryResolved?.(token);
267
+ }
268
+ });
269
+ return {
270
+ destroy: () => {
271
+ context.ssr?.resume(token);
272
+ context.pendingBoundary?.resume(token);
273
+ if (shortCircuited)
274
+ context.exceptionBoundaryResolved?.(token);
275
+ ref.destroy();
276
+ },
277
+ };
278
+ }
279
+ function createEffectInInjector(injector, name, effectFn) {
280
+ return untracked(() => runInInjectionContext(injector, () => craftEffect(name, effectFn, { manualCleanup: true })));
281
+ }
282
+ function executeTemplateCallback(callback, args, context) {
283
+ const isGeneratorCallback = isGeneratorFunction(callback);
284
+ if (isGeneratorCallback)
285
+ templateGeneratorDepth++;
286
+ try {
287
+ return executeYieldable(callback, args, context.injector);
288
+ }
289
+ finally {
290
+ if (isGeneratorCallback)
291
+ templateGeneratorDepth--;
292
+ }
293
+ }
294
+ function resolveTemplateValue(value, context) {
295
+ return typeof value === 'function'
296
+ ? executeTemplateCallback(value, [], context)
297
+ : value;
298
+ }
299
+ function resolveTemplateContext(context, renderContext) {
300
+ if (typeof context === 'function') {
301
+ return resolveTemplateValue(context, renderContext);
302
+ }
303
+ if (context === null ||
304
+ typeof context !== 'object' ||
305
+ Array.isArray(context)) {
306
+ return context;
307
+ }
308
+ return Object.fromEntries(Object.entries(context).map(([key, value]) => [
309
+ key,
310
+ typeof value === 'function'
311
+ ? resolveTemplateValue(value, renderContext)
312
+ : value,
313
+ ]));
314
+ }
315
+ function resolveProjectionValue(value, renderContext) {
316
+ let current = value;
317
+ for (let depth = 0; depth < 3 && typeof current === 'function'; depth++) {
318
+ current = resolveTemplateValue(current, renderContext);
319
+ }
320
+ return current;
321
+ }
322
+ function projectYieldableTemplateContext(value, seen = new WeakMap()) {
323
+ // Field trees are dynamic proxies: copying their enumerable members would
324
+ // discard lazily-created child fields such as `form.email`.
325
+ if (isCraftField(value))
326
+ return value;
327
+ if (isYieldableValue(value)) {
328
+ const projected = (...args) => {
329
+ const result = Reflect.apply(value, undefined, args);
330
+ if (templateGeneratorDepth === 0)
331
+ return result;
332
+ return toYieldable(() => result)();
333
+ };
334
+ const namedProjected = markYieldableValue(projected, value[YIELDABLE_VALUE]);
335
+ if (typeof value === 'function' &&
336
+ (Object.keys(value).length > 0 || DEEP_YIELDABLE in value)) {
337
+ return new Proxy(namedProjected, {
338
+ get(_target, property) {
339
+ return projectYieldableTemplateContext(Reflect.get(value, property), seen);
340
+ },
341
+ });
342
+ }
343
+ return namedProjected;
344
+ }
345
+ if (typeof value === 'function') {
346
+ if (isYieldableMethod(value)) {
347
+ return (...args) => {
348
+ const result = Reflect.apply(value, undefined, args);
349
+ if (templateGeneratorDepth === 0)
350
+ return result;
351
+ return toYieldable(() => result)();
352
+ };
353
+ }
354
+ // State and resource refs are callable objects with exposed insertions
355
+ // (for example `counter.disabled`). Keep the callable ref intact while
356
+ // projecting its nested yieldable members into the template context.
357
+ if (Object.keys(value).length === 0)
358
+ return value;
359
+ if (seen.has(value))
360
+ return seen.get(value);
361
+ const projected = new Proxy(value, {
362
+ get(target, property, receiver) {
363
+ return projectYieldableTemplateContext(Reflect.get(target, property, receiver), seen);
364
+ },
365
+ });
366
+ seen.set(value, projected);
367
+ return projected;
368
+ }
369
+ if (typeof value !== 'object' || value === null)
370
+ return value;
371
+ // Craft nodes carry runtime objects such as RenderContext and the DOM adapter
372
+ // through their declaration context. Copying them as plain objects would
373
+ // strip prototype methods from those runtime services before projection.
374
+ if ('kind' in value &&
375
+ typeof value.kind === 'string') {
376
+ return value;
377
+ }
378
+ if (seen.has(value))
379
+ return seen.get(value);
380
+ if (Array.isArray(value)) {
381
+ const result = [];
382
+ seen.set(value, result);
383
+ for (const item of value) {
384
+ result.push(projectYieldableTemplateContext(item, seen));
385
+ }
386
+ return result;
387
+ }
388
+ const result = {};
389
+ seen.set(value, result);
390
+ for (const [key, child] of Object.entries(value)) {
391
+ result[key] = projectYieldableTemplateContext(child, seen);
392
+ }
393
+ return result;
394
+ }
395
+ class CraftNodeDirectiveMount {
396
+ inputs;
397
+ environmentInjector;
398
+ cleanup;
399
+ registrationRelease;
400
+ constructor(descriptor, element, context) {
401
+ this.inputs = signal(descriptor.inputs);
402
+ const definition = descriptor.directive[CRAFT_NODE_DIRECTIVE];
403
+ this.environmentInjector = createEnvironmentInjector([
404
+ { provide: ElementRef, useValue: new ElementRef(element) },
405
+ {
406
+ provide: CRAFT_NODE_EFFECT_FACTORY,
407
+ deps: [Injector],
408
+ useFactory: (injector) => (name, effectFn) => createEffectInInjector(injector, `node-directive-${name}`, effectFn),
409
+ },
410
+ ...(context.fieldExceptionBoundary
411
+ ? [
412
+ {
413
+ provide: CRAFT_FIELD_EXCEPTION_BOUNDARY,
414
+ useValue: context.fieldExceptionBoundary,
415
+ },
416
+ ]
417
+ : []),
418
+ ], context.injector.get(EnvironmentInjector), `CraftNodeDirective(${definition.name})`);
419
+ const inputs = this.inputs;
420
+ const nodeContext = {
421
+ element,
422
+ injector: this.environmentInjector,
423
+ renderer: context.renderer,
424
+ get props() {
425
+ return inputs();
426
+ },
427
+ };
428
+ this.registrationRelease = ɵregisterCraftTarget(this.environmentInjector, descriptor.directive, nodeContext, allocateCraftHostName(this.environmentInjector, 'directive', definition.name), false);
429
+ const cleanup = untracked(() => runInInjectionContext(this.environmentInjector, () => definition.mount(nodeContext)));
430
+ this.cleanup = typeof cleanup === 'function' ? cleanup : undefined;
431
+ }
432
+ update(descriptor) {
433
+ this.inputs.set(descriptor.inputs);
434
+ }
435
+ destroy() {
436
+ this.registrationRelease();
437
+ this.cleanup?.();
438
+ this.environmentInjector.destroy();
439
+ }
440
+ }
441
+ function sameCraftDirectives(left, right) {
442
+ return (left.length === right.length &&
443
+ left.every((directive, index) => directive === right[index]));
444
+ }
445
+ function sameCraftNodeDirectives(left, right) {
446
+ return (left.length === right.length &&
447
+ left.every((descriptor, index) => descriptor.directive === right[index]?.directive));
448
+ }
449
+ function insertBefore(renderer, parent, node, before) {
450
+ renderer.insertBefore(parent, node, before);
451
+ }
452
+ function removeNode(renderer, node) {
453
+ const parent = node.parentNode;
454
+ if (parent) {
455
+ renderer.removeChild(parent, node);
456
+ }
457
+ }
458
+ function isElementNode(node) {
459
+ return (typeof node === 'object' &&
460
+ node !== null &&
461
+ node.nodeType === 1);
462
+ }
463
+ function mountComment(parent, value, before, context) {
464
+ if (context.mode === 'hydrate' && context.hydration) {
465
+ try {
466
+ return context.hydration.claimBoundary(context.identity.hydrationKey, value);
467
+ }
468
+ catch (error) {
469
+ if (!(error instanceof HydrationMismatchError))
470
+ throw error;
471
+ context.hydration.recordMismatch(error);
472
+ }
473
+ }
474
+ const comment = context.renderer.createComment(context.emitHydrationKeys
475
+ ? `${value}|hk:${context.identity.hydrationKey}`
476
+ : value);
477
+ insertBefore(context.renderer, parent, comment, before);
478
+ return comment;
479
+ }
480
+ function createElementForRender(tag, parent, before, context) {
481
+ if (context.mode === 'hydrate' && context.hydration) {
482
+ try {
483
+ return {
484
+ element: context.hydration.claimElement(context.identity.hydrationKey, tag),
485
+ context,
486
+ };
487
+ }
488
+ catch (error) {
489
+ if (!(error instanceof HydrationMismatchError))
490
+ throw error;
491
+ context.hydration.recordMismatch(error);
492
+ const actual = error.actualNode;
493
+ const replacementBefore = actual?.parentNode === parent ? actual.nextSibling : before;
494
+ if (actual?.parentNode)
495
+ removeNode(context.renderer, actual);
496
+ const createContext = childContext(context, {
497
+ mode: 'create',
498
+ hydration: undefined,
499
+ });
500
+ const element = createContext.renderer.createElement(tag);
501
+ if (createContext.emitHydrationKeys) {
502
+ createContext.renderer.setAttribute(element, 'data-craft-hk', createContext.identity.hydrationKey);
503
+ }
504
+ insertBefore(createContext.renderer, parent, element, replacementBefore);
505
+ return { element, context: createContext };
506
+ }
507
+ }
508
+ const element = context.renderer.createElement(tag);
509
+ if (context.emitHydrationKeys) {
510
+ context.renderer.setAttribute(element, 'data-craft-hk', context.identity.hydrationKey);
511
+ }
512
+ insertBefore(context.renderer, parent, element, before);
513
+ return { element, context };
514
+ }
515
+ function createTextForRender(value, parent, before, context, compare) {
516
+ if (context.mode === 'hydrate' && context.hydration) {
517
+ try {
518
+ return {
519
+ text: context.hydration.claimText(context.identity.hydrationKey, compare ? value : undefined),
520
+ context,
521
+ };
522
+ }
523
+ catch (error) {
524
+ if (!(error instanceof HydrationMismatchError))
525
+ throw error;
526
+ context.hydration.recordMismatch(error);
527
+ const actual = error.actualNode;
528
+ const replacementBefore = actual?.parentNode === parent ? actual.nextSibling : before;
529
+ if (actual?.parentNode)
530
+ removeNode(context.renderer, actual);
531
+ const createContext = childContext(context, {
532
+ mode: 'create',
533
+ hydration: undefined,
534
+ });
535
+ const text = createContext.renderer.createText(value);
536
+ if (createContext.emitHydrationKeys) {
537
+ setNodeHydrationKey(text, createContext.identity.hydrationKey);
538
+ }
539
+ insertBefore(createContext.renderer, parent, text, replacementBefore);
540
+ return { text, context: createContext };
541
+ }
542
+ }
543
+ const text = context.renderer.createText(value);
544
+ if (context.emitHydrationKeys) {
545
+ setNodeHydrationKey(text, context.identity.hydrationKey);
546
+ }
547
+ insertBefore(context.renderer, parent, text, before);
548
+ return { text, context };
549
+ }
550
+ function patchRenderedChildren(parent, rendered, children, before, context) {
551
+ const nextNodes = normalizeChildren(children);
552
+ const sharedLength = Math.min(rendered.length, nextNodes.length);
553
+ for (let index = 0; index < sharedLength; index += 1) {
554
+ const current = rendered[index];
555
+ const next = nextNodes[index];
556
+ if (!current.patch(next)) {
557
+ const replacement = mountNode(next, parent, current.firstNode(), childNodeContext(context, next, index));
558
+ current.destroy();
559
+ rendered[index] = replacement;
560
+ }
561
+ }
562
+ while (rendered.length > nextNodes.length) {
563
+ rendered.pop()?.destroy();
564
+ }
565
+ for (let index = rendered.length; index < nextNodes.length; index += 1) {
566
+ rendered.push(mountNode(nextNodes[index], parent, before, childNodeContext(context, nextNodes[index], index)));
567
+ }
568
+ return rendered;
569
+ }
570
+ class TextRenderedNode {
571
+ kind = 'text';
572
+ node;
573
+ value;
574
+ renderer;
575
+ constructor(node, value, renderer) {
576
+ this.node = node;
577
+ this.value = value;
578
+ this.renderer = renderer;
579
+ }
580
+ firstNode() {
581
+ return this.node;
582
+ }
583
+ lastNode() {
584
+ return this.node;
585
+ }
586
+ patch(node) {
587
+ if (node.kind !== 'text') {
588
+ return false;
589
+ }
590
+ if (node.value !== this.value) {
591
+ this.renderer.setValue(this.node, node.value);
592
+ this.value = node.value;
593
+ }
594
+ return true;
595
+ }
596
+ destroy() {
597
+ removeNode(this.renderer, this.node);
598
+ }
599
+ }
600
+ class ReactiveTextRenderedNode {
601
+ kind = 'reactive-text';
602
+ binding;
603
+ effectRef;
604
+ value = '';
605
+ node;
606
+ context;
607
+ constructor(node, binding, context) {
608
+ this.node = node;
609
+ this.context = context;
610
+ this.binding = binding;
611
+ this.effectRef = this.createEffect();
612
+ }
613
+ firstNode() {
614
+ return this.node;
615
+ }
616
+ lastNode() {
617
+ return this.node;
618
+ }
619
+ patch(node) {
620
+ if (node.kind !== 'reactive-text') {
621
+ return false;
622
+ }
623
+ if (node.binding !== this.binding) {
624
+ this.effectRef.destroy();
625
+ this.binding = node.binding;
626
+ this.effectRef = this.createEffect();
627
+ }
628
+ return true;
629
+ }
630
+ destroy() {
631
+ this.effectRef.destroy();
632
+ removeNode(this.context.renderer, this.node);
633
+ }
634
+ createEffect() {
635
+ return createRenderEffect(this.context, 'text-binding', () => {
636
+ const resolved = executeTemplateCallback(this.binding, [], this.context);
637
+ const next = resolved === null || resolved === undefined || resolved === false
638
+ ? ''
639
+ : String(resolved);
640
+ if (next !== this.value) {
641
+ this.context.renderer.setValue(this.node, next);
642
+ this.value = next;
643
+ }
644
+ });
645
+ }
646
+ }
647
+ function renderCraftDirectiveNode(node, context) {
648
+ let template = (_componentContext) => [node.node];
649
+ for (const directive of node.directives) {
650
+ template = directive[CRAFT_DIRECTIVE].template(template);
651
+ }
652
+ return renderChildrenCallback(context, template, [context.componentContext], 'callback', 'directive');
653
+ }
654
+ class CraftDirectiveRenderedNode {
655
+ kind = 'directive';
656
+ view;
657
+ descriptor;
658
+ effectRef;
659
+ styleReleases;
660
+ registrationReleases;
661
+ node;
662
+ context;
663
+ constructor(node, parent, before, context) {
664
+ this.node = node;
665
+ this.context = context;
666
+ this.descriptor = signal(node);
667
+ const owners = node.directives.map((directive) => ({
668
+ name: directive[CRAFT_DIRECTIVE].name,
669
+ styles: [
670
+ ...styleValues(directive[CRAFT_DIRECTIVE].meta.styles),
671
+ ...styleValues(directive[CRAFT_DIRECTIVE].meta.stylesUrl),
672
+ ],
673
+ definition: directive[CRAFT_DIRECTIVE],
674
+ }));
675
+ this.styleReleases = acquireStyles(context, owners, context.ownerScope ?? '');
676
+ this.registrationReleases = node.directives.map((directive) => ɵregisterCraftTarget(context.injector, directive, context.componentContext, allocateCraftHostName(context.injector, 'directive', directive[CRAFT_DIRECTIVE].name), false));
677
+ this.view = createFragment(parent, before, context, [], 'craft-directive');
678
+ this.effectRef = createRenderEffect(context, 'craft-directive', () => {
679
+ this.node = this.descriptor();
680
+ this.view.patchChildren(renderCraftDirectiveNode(this.node, context));
681
+ });
682
+ }
683
+ firstNode() {
684
+ return this.view.firstNode();
685
+ }
686
+ lastNode() {
687
+ return this.view.lastNode();
688
+ }
689
+ patch(node) {
690
+ if (node.kind !== 'directive' ||
691
+ !sameCraftDirectives(this.node.directives, node.directives)) {
692
+ return false;
693
+ }
694
+ this.descriptor.set(node);
695
+ return true;
696
+ }
697
+ destroy() {
698
+ this.registrationReleases.forEach((release) => release());
699
+ this.effectRef.destroy();
700
+ this.view.destroy();
701
+ this.styleReleases.forEach((release) => release());
702
+ }
703
+ }
704
+ const EVENT_NAMES = new Set([
705
+ 'abort',
706
+ 'blur',
707
+ 'change',
708
+ 'click',
709
+ 'contextmenu',
710
+ 'dblclick',
711
+ 'error',
712
+ 'focus',
713
+ 'input',
714
+ 'keydown',
715
+ 'keypress',
716
+ 'keyup',
717
+ 'load',
718
+ 'mousedown',
719
+ 'mouseenter',
720
+ 'mouseleave',
721
+ 'mousemove',
722
+ 'mouseout',
723
+ 'mouseover',
724
+ 'mouseup',
725
+ 'pointerdown',
726
+ 'pointerenter',
727
+ 'pointerleave',
728
+ 'pointermove',
729
+ 'pointerup',
730
+ 'reset',
731
+ 'scroll',
732
+ 'submit',
733
+ 'touchend',
734
+ 'touchmove',
735
+ 'touchstart',
736
+ ]);
737
+ const PROPERTY_NAMES = new Set([
738
+ 'checked',
739
+ 'disabled',
740
+ 'multiple',
741
+ 'selected',
742
+ 'value',
743
+ ]);
744
+ function eventNameFor(key, value) {
745
+ if (typeof value !== 'function') {
746
+ return undefined;
747
+ }
748
+ if (EVENT_NAMES.has(key)) {
749
+ return key;
750
+ }
751
+ if (/^on[A-Z]/.test(key)) {
752
+ return `${key[2].toLowerCase()}${key.slice(3)}`;
753
+ }
754
+ return undefined;
755
+ }
756
+ function interactionName(eventName, tag, localName, componentName) {
757
+ return [componentName, tag, localName, eventName]
758
+ .filter((part) => Boolean(part))
759
+ .join(':');
760
+ }
761
+ function executeDomEventHooks(hooks, interaction, action) {
762
+ let current = action;
763
+ for (let index = hooks.length - 1; index >= 0; index -= 1) {
764
+ const hook = hooks[index];
765
+ const next = current;
766
+ current = () => hook(interaction, next);
767
+ }
768
+ return current();
769
+ }
770
+ const HOST_PROPERTY_NAMES = new Set([
771
+ 'accessKey',
772
+ 'autocapitalize',
773
+ 'autofocus',
774
+ 'class',
775
+ 'contentEditable',
776
+ 'dir',
777
+ 'draggable',
778
+ 'hidden',
779
+ 'id',
780
+ 'inert',
781
+ 'lang',
782
+ 'nonce',
783
+ 'slot',
784
+ 'spellcheck',
785
+ 'style',
786
+ 'tabIndex',
787
+ 'title',
788
+ 'translate',
789
+ 'attrs',
790
+ 'directives',
791
+ ]);
792
+ function isHostProperty(key) {
793
+ return (key === 'cssVars' ||
794
+ HOST_PROPERTY_NAMES.has(key) ||
795
+ key.startsWith('data-') ||
796
+ key.startsWith('aria-') ||
797
+ EVENT_NAMES.has(key) ||
798
+ (/^on[A-Z]/.test(key) &&
799
+ EVENT_NAMES.has(`${key[2].toLowerCase()}${key.slice(3)}`)));
800
+ }
801
+ function hostPropsFromComponentProps(props) {
802
+ const hostProps = Object.fromEntries(Object.entries(props).filter(([key]) => key !== 'cssVars' && isHostProperty(key)));
803
+ const vars = cssVarStyles(props['cssVars']);
804
+ return Object.keys(vars).length
805
+ ? mergeHostProps({ style: vars }, hostProps)
806
+ : hostProps;
807
+ }
808
+ function className(value, context) {
809
+ if (typeof value === 'function') {
810
+ return className(resolveTemplateValue(value, context), context);
811
+ }
812
+ if (Array.isArray(value)) {
813
+ return value
814
+ .flatMap((item) => className(item, context))
815
+ .filter(Boolean)
816
+ .join(' ');
817
+ }
818
+ if (typeof value === 'object' && value !== null) {
819
+ return Object.entries(value)
820
+ .filter(([, enabled]) => enabled)
821
+ .map(([name]) => name)
822
+ .join(' ');
823
+ }
824
+ return value == null ? '' : String(value);
825
+ }
826
+ function containsRenderBinding(value, seen = new Set()) {
827
+ if (typeof value === 'function')
828
+ return true;
829
+ if (typeof value !== 'object' || value === null || seen.has(value)) {
830
+ return false;
831
+ }
832
+ seen.add(value);
833
+ return Object.values(value).some((child) => containsRenderBinding(child, seen));
834
+ }
835
+ function resolveStyleBindingValue(value, context) {
836
+ const resolved = resolveTemplateValue(value, context);
837
+ if (typeof resolved !== 'object' || resolved === null)
838
+ return resolved;
839
+ return Object.fromEntries(Object.entries(resolved).map(([key, child]) => [
840
+ key,
841
+ resolveTemplateValue(child, context),
842
+ ]));
843
+ }
844
+ function sameStyleValue(left, right) {
845
+ if (Object.is(left, right))
846
+ return true;
847
+ if (typeof left !== 'object' ||
848
+ left === null ||
849
+ typeof right !== 'object' ||
850
+ right === null) {
851
+ return false;
852
+ }
853
+ const leftEntries = Object.entries(left);
854
+ const rightEntries = Object.entries(right);
855
+ const rightRecord = right;
856
+ return (leftEntries.length === rightEntries.length &&
857
+ leftEntries.every(([key, value]) => Object.is(value, rightRecord[key])));
858
+ }
859
+ function setStyleValue(_renderer, element, key, value, context) {
860
+ value = resolveTemplateValue(value, context);
861
+ const style = element
862
+ .style;
863
+ if (value === null || value === undefined || value === false) {
864
+ if (key.startsWith('--')) {
865
+ style.removeProperty(key);
866
+ }
867
+ else {
868
+ style[key] = '';
869
+ }
870
+ }
871
+ else if (key.startsWith('--')) {
872
+ style.setProperty(key, String(value));
873
+ }
874
+ else {
875
+ style[key] = String(value);
876
+ }
877
+ }
878
+ function applyStyles(renderer, element, previous, next, context) {
879
+ previous = resolveTemplateValue(previous, context);
880
+ next = resolveTemplateValue(next, context);
881
+ if (typeof previous === 'object' && previous !== null) {
882
+ for (const key of Object.keys(previous)) {
883
+ if (typeof next !== 'object' || next === null || !(key in next)) {
884
+ setStyleValue(renderer, element, key, null, context);
885
+ }
886
+ }
887
+ }
888
+ else if (typeof previous === 'string' && previous !== next) {
889
+ renderer.removeAttribute(element, 'style');
890
+ }
891
+ if (typeof next === 'string') {
892
+ renderer.setAttribute(element, 'style', next);
893
+ }
894
+ else if (typeof next === 'object' && next !== null) {
895
+ for (const [key, value] of Object.entries(next)) {
896
+ setStyleValue(renderer, element, key, value, context);
897
+ }
898
+ }
899
+ else {
900
+ renderer.removeAttribute(element, 'style');
901
+ }
902
+ }
903
+ function flattenAttributes(props) {
904
+ const attributes = new Map();
905
+ for (const [key, value] of Object.entries(props)) {
906
+ if (key === 'attrs' && typeof value === 'object' && value !== null) {
907
+ for (const [attribute, attributeValue] of Object.entries(value)) {
908
+ attributes.set(attribute, attributeValue);
909
+ }
910
+ }
911
+ else if (key !== 'class' &&
912
+ key !== 'style' &&
913
+ key !== 'directives' &&
914
+ key !== 'labelledBy' &&
915
+ key !== 'onClose' &&
916
+ !eventNameFor(key, value)) {
917
+ attributes.set(key, value);
918
+ }
919
+ }
920
+ return attributes;
921
+ }
922
+ function applyAttribute(renderer, element, key, value, context) {
923
+ value = resolveTemplateValue(value, context);
924
+ if (key.startsWith('data-craft-')) {
925
+ const dev = typeof ngDevMode === 'undefined' || ngDevMode;
926
+ if (dev) {
927
+ throw new Error('data-craft-* attributes are reserved for the Craft renderer.');
928
+ }
929
+ }
930
+ if (key === 'htmlFor') {
931
+ renderer.setProperty(element, key, value ?? '');
932
+ if (value === null || value === undefined || value === false) {
933
+ renderer.removeAttribute(element, 'for');
934
+ }
935
+ return;
936
+ }
937
+ if (PROPERTY_NAMES.has(key)) {
938
+ renderer.setProperty(element, key, value ?? false);
939
+ if (value === null || value === undefined || value === false) {
940
+ renderer.removeAttribute(element, key);
941
+ }
942
+ return;
943
+ }
944
+ if (value === null || value === undefined) {
945
+ renderer.removeAttribute(element, key);
946
+ }
947
+ else if (key.startsWith('aria-') && typeof value === 'boolean') {
948
+ renderer.setAttribute(element, key, value ? 'true' : 'false');
949
+ }
950
+ else if (value === false) {
951
+ renderer.removeAttribute(element, key);
952
+ }
953
+ else {
954
+ renderer.setAttribute(element, key, value === true ? '' : String(value));
955
+ }
956
+ }
957
+ class ElementRenderedNode {
958
+ kind = 'element';
959
+ children = [];
960
+ props = {};
961
+ listeners = new Map();
962
+ bindings = new Map();
963
+ craftNodeDirectiveTypes = [];
964
+ craftNodeDirectiveMounts = [];
965
+ localName;
966
+ dialogCleanup;
967
+ node;
968
+ tag;
969
+ context;
970
+ constructor(node, tag, context, initial) {
971
+ this.node = node;
972
+ this.tag = tag;
973
+ this.context = context;
974
+ this.patchProperties(initial);
975
+ this.installDialog(initial);
976
+ const nested = this.tag === 'dialog'
977
+ ? childContext(context, {
978
+ rootScope: undefined,
979
+ contentScope: undefined,
980
+ headingLevel: 1,
981
+ })
982
+ : childContext(context, {
983
+ rootScope: undefined,
984
+ contentScope: undefined,
985
+ });
986
+ this.children = patchRenderedChildren(this.node, this.children, initial.children, null, nested);
987
+ this.reapplySelectValue(initial);
988
+ }
989
+ /**
990
+ * A `<select>` only accepts a `value` that matches one of its `<option>`s,
991
+ * and props are applied before children are mounted — so the initial
992
+ * assignment lands on an empty element, is dropped, and the browser falls
993
+ * back to the first option. The control then disagrees with the state it is
994
+ * bound to, silently.
995
+ *
996
+ * Re-assert it now that the options exist. Later updates arrive through the
997
+ * binding, by which point there is nothing to correct.
998
+ */
999
+ reapplySelectValue(node) {
1000
+ if (this.tag !== 'select') {
1001
+ return;
1002
+ }
1003
+ const value = flattenAttributes(node.props).get('value');
1004
+ if (value === null || value === undefined) {
1005
+ return;
1006
+ }
1007
+ const resolved = typeof value === 'function'
1008
+ ? untracked(() => resolveTemplateValue(value, this.context))
1009
+ : value;
1010
+ applyAttribute(this.context.renderer, this.node, 'value', resolved, this.context);
1011
+ }
1012
+ firstNode() {
1013
+ return this.node;
1014
+ }
1015
+ lastNode() {
1016
+ return this.node;
1017
+ }
1018
+ patch(node) {
1019
+ if (node.kind !== 'element' ||
1020
+ node.tag !== this.tag ||
1021
+ node.localName !== this.localName) {
1022
+ return false;
1023
+ }
1024
+ this.patchProperties(node);
1025
+ this.installDialog(node);
1026
+ this.children = patchRenderedChildren(this.node, this.children, node.children, null, childContext(this.context, {
1027
+ rootScope: undefined,
1028
+ contentScope: undefined,
1029
+ }));
1030
+ return true;
1031
+ }
1032
+ patchProperties(nextNode) {
1033
+ const next = nextNode.props;
1034
+ const renderer = this.context.renderer;
1035
+ const previousAttributes = flattenAttributes(this.props);
1036
+ const nextAttributes = flattenAttributes(next);
1037
+ for (const key of previousAttributes.keys()) {
1038
+ if (!nextAttributes.has(key)) {
1039
+ this.destroyBinding(`attribute:${key}`);
1040
+ applyAttribute(renderer, this.node, key, null, this.context);
1041
+ }
1042
+ }
1043
+ for (const [key, value] of nextAttributes) {
1044
+ if (typeof value === 'function') {
1045
+ this.updateBinding(`attribute:${key}`, value, () => resolveTemplateValue(value, this.context), (resolved) => applyAttribute(renderer, this.node, key, resolved, this.context));
1046
+ }
1047
+ else {
1048
+ this.destroyBinding(`attribute:${key}`);
1049
+ }
1050
+ if (typeof value !== 'function' &&
1051
+ (!Object.is(previousAttributes.get(key), value) ||
1052
+ typeof previousAttributes.get(key) === 'function')) {
1053
+ applyAttribute(renderer, this.node, key, value, this.context);
1054
+ }
1055
+ }
1056
+ if (containsRenderBinding(next['class'])) {
1057
+ this.updateBinding('class', next['class'], () => className(next['class'], this.context), (value) => {
1058
+ if (value) {
1059
+ renderer.setAttribute(this.node, 'class', String(value));
1060
+ }
1061
+ else {
1062
+ renderer.removeAttribute(this.node, 'class');
1063
+ }
1064
+ });
1065
+ }
1066
+ else {
1067
+ this.destroyBinding('class');
1068
+ }
1069
+ if (!containsRenderBinding(next['class']) &&
1070
+ (!Object.is(this.props['class'], next['class']) ||
1071
+ containsRenderBinding(this.props['class']))) {
1072
+ const value = className(next['class'], this.context);
1073
+ if (value) {
1074
+ renderer.setAttribute(this.node, 'class', value);
1075
+ }
1076
+ else {
1077
+ renderer.removeAttribute(this.node, 'class');
1078
+ }
1079
+ }
1080
+ if (containsRenderBinding(next['style'])) {
1081
+ this.updateBinding('style', next['style'], () => resolveStyleBindingValue(next['style'], this.context), (value) => {
1082
+ renderer.removeAttribute(this.node, 'style');
1083
+ applyStyles(renderer, this.node, undefined, value, this.context);
1084
+ }, sameStyleValue);
1085
+ }
1086
+ else {
1087
+ this.destroyBinding('style');
1088
+ }
1089
+ if (!containsRenderBinding(next['style']) &&
1090
+ (!Object.is(this.props['style'], next['style']) ||
1091
+ containsRenderBinding(this.props['style']))) {
1092
+ if (containsRenderBinding(this.props['style'])) {
1093
+ renderer.removeAttribute(this.node, 'style');
1094
+ }
1095
+ applyStyles(renderer, this.node, containsRenderBinding(this.props['style'])
1096
+ ? undefined
1097
+ : this.props['style'], next['style'], this.context);
1098
+ }
1099
+ const nextEvents = new Map();
1100
+ for (const [key, value] of Object.entries(next)) {
1101
+ const eventName = eventNameFor(key, value);
1102
+ if (eventName) {
1103
+ nextEvents.set(eventName, value);
1104
+ }
1105
+ }
1106
+ for (const [eventName, dispose] of this.listeners) {
1107
+ if (!nextEvents.has(eventName)) {
1108
+ dispose();
1109
+ this.listeners.delete(eventName);
1110
+ }
1111
+ }
1112
+ for (const [eventName] of nextEvents) {
1113
+ if (!this.listeners.has(eventName)) {
1114
+ this.listeners.set(eventName, renderer.listen(this.node, eventName, (event) => {
1115
+ const listener = Object.entries(this.props).find(([key, value]) => eventNameFor(key, value) === eventName)?.[1];
1116
+ if (!listener) {
1117
+ return undefined;
1118
+ }
1119
+ const interaction = {
1120
+ event,
1121
+ eventName,
1122
+ element: this.node,
1123
+ elementTag: this.tag,
1124
+ elementName: this.localName,
1125
+ componentName: this.context.componentName,
1126
+ interactionName: interactionName(eventName, this.tag, this.localName, this.context.componentName),
1127
+ };
1128
+ const hooks = this.context.injector.get(CRAFT_DOM_EVENT_HOOK);
1129
+ runInInjectionContext(this.context.injector, () => executeDomEventHooks(hooks, interaction, () => executeTemplateCallback(listener, [event], this.context)));
1130
+ return undefined;
1131
+ }));
1132
+ }
1133
+ }
1134
+ const craftNodeDirectives = nextNode[CRAFT_NODE_DIRECTIVES] ?? [];
1135
+ if (!sameCraftNodeDirectives(this.craftNodeDirectiveTypes, craftNodeDirectives)) {
1136
+ this.craftNodeDirectiveMounts.forEach((mount) => mount.destroy());
1137
+ this.craftNodeDirectiveMounts = craftNodeDirectives.map((descriptor) => new CraftNodeDirectiveMount(descriptor, this.node, this.context));
1138
+ }
1139
+ else {
1140
+ this.craftNodeDirectiveMounts.forEach((mount, index) => mount.update(craftNodeDirectives[index]));
1141
+ }
1142
+ this.craftNodeDirectiveTypes = craftNodeDirectives;
1143
+ if (this.localName !== nextNode.localName) {
1144
+ if (nextNode.localName === undefined) {
1145
+ renderer.removeAttribute(this.node, 'data-craft-name');
1146
+ }
1147
+ else {
1148
+ renderer.setAttribute(this.node, 'data-craft-name', nextNode.localName);
1149
+ }
1150
+ this.localName = nextNode.localName;
1151
+ }
1152
+ const contentNames = directCraftContentNames(nextNode.children);
1153
+ if (contentNames.length > 0) {
1154
+ this.node[CRAFT_LOCATOR_CONTENT_NAMES] = contentNames;
1155
+ }
1156
+ else {
1157
+ delete this.node[CRAFT_LOCATOR_CONTENT_NAMES];
1158
+ }
1159
+ this.props = next;
1160
+ }
1161
+ updateBinding(key, source, evaluate, apply, equals = Object.is) {
1162
+ const current = this.bindings.get(key);
1163
+ if (current?.source === source)
1164
+ return;
1165
+ current?.effectRef.destroy();
1166
+ let initialized = false;
1167
+ let previous;
1168
+ const effectRef = createRenderEffect(this.context, `element-${key}-binding`, () => {
1169
+ const value = evaluate();
1170
+ if (!initialized || !equals(previous, value)) {
1171
+ apply(value);
1172
+ previous = value;
1173
+ initialized = true;
1174
+ }
1175
+ });
1176
+ this.bindings.set(key, { source, effectRef });
1177
+ }
1178
+ destroyBinding(key) {
1179
+ this.bindings.get(key)?.effectRef.destroy();
1180
+ this.bindings.delete(key);
1181
+ }
1182
+ installDialog(nextNode) {
1183
+ if (this.tag !== 'dialog')
1184
+ return;
1185
+ const dialog = this.node;
1186
+ const renderer = this.context.renderer;
1187
+ if (!dialog.getAttribute('role')) {
1188
+ renderer.setAttribute(dialog, 'role', 'dialog');
1189
+ }
1190
+ if (!dialog.getAttribute('aria-modal')) {
1191
+ renderer.setAttribute(dialog, 'aria-modal', 'true');
1192
+ }
1193
+ const labelledBy = nextNode.props['labelledBy'];
1194
+ const label = nextNode.props['label'] ?? nextNode.props['aria-label'];
1195
+ if (typeof labelledBy === 'string' && labelledBy) {
1196
+ renderer.setAttribute(dialog, 'aria-labelledby', labelledBy);
1197
+ }
1198
+ else if (typeof label === 'string' && label) {
1199
+ renderer.setAttribute(dialog, 'aria-label', label);
1200
+ }
1201
+ const onClose = nextNode.props['onClose'];
1202
+ this.dialogCleanup?.();
1203
+ const cancel = renderer.listen(dialog, 'cancel', (event) => {
1204
+ event.preventDefault();
1205
+ if (typeof onClose === 'function')
1206
+ onClose(event);
1207
+ if (typeof dialog.close === 'function')
1208
+ dialog.close();
1209
+ });
1210
+ const close = renderer.listen(dialog, 'close', (event) => {
1211
+ if (typeof onClose === 'function')
1212
+ onClose(event);
1213
+ });
1214
+ this.dialogCleanup = () => {
1215
+ cancel();
1216
+ close();
1217
+ };
1218
+ const open = nextNode.props['open'];
1219
+ if (open && typeof dialog.showModal === 'function' && !dialog.open) {
1220
+ dialog.showModal();
1221
+ }
1222
+ else if (open === false &&
1223
+ typeof dialog.close === 'function' &&
1224
+ dialog.open) {
1225
+ dialog.close();
1226
+ }
1227
+ }
1228
+ destroy() {
1229
+ this.dialogCleanup?.();
1230
+ this.bindings.forEach(({ effectRef }) => effectRef.destroy());
1231
+ this.bindings.clear();
1232
+ this.listeners.forEach((dispose) => dispose());
1233
+ this.listeners.clear();
1234
+ this.craftNodeDirectiveMounts.forEach((mount) => mount.destroy());
1235
+ this.craftNodeDirectiveMounts = [];
1236
+ this.children.forEach((child) => child.destroy());
1237
+ removeNode(this.context.renderer, this.node);
1238
+ }
1239
+ }
1240
+ class FragmentRenderedNode {
1241
+ kind = 'fragment';
1242
+ children = [];
1243
+ /**
1244
+ * Where this fragment currently lives. A boundary can detach a range into a
1245
+ * holder fragment to hide it without destroying it, and the range keeps being
1246
+ * reconciled while detached — so every insertion has to follow the markers
1247
+ * rather than the parent captured at construction time.
1248
+ */
1249
+ get liveParent() {
1250
+ return this.start.parentNode ?? this.parent;
1251
+ }
1252
+ parent;
1253
+ start;
1254
+ end;
1255
+ context;
1256
+ constructor(parent, start, end, context, initialChildren) {
1257
+ this.parent = parent;
1258
+ this.start = start;
1259
+ this.end = end;
1260
+ this.context = context;
1261
+ this.children = patchRenderedChildren(parent, this.children, initialChildren, end, context);
1262
+ }
1263
+ firstNode() {
1264
+ return this.start;
1265
+ }
1266
+ lastNode() {
1267
+ return this.end;
1268
+ }
1269
+ patch(_node) {
1270
+ return false;
1271
+ }
1272
+ patchChildren(children) {
1273
+ this.children = patchRenderedChildren(this.liveParent, this.children, children, this.end, this.context);
1274
+ }
1275
+ hasChildren() {
1276
+ return this.children.length > 0;
1277
+ }
1278
+ appendChildren(children) {
1279
+ const nextNodes = normalizeChildren(children);
1280
+ nextNodes.forEach((child) => {
1281
+ this.children.push(mountNode(child, this.liveParent, this.end, this.context));
1282
+ });
1283
+ }
1284
+ prependChildren(children) {
1285
+ const nextNodes = normalizeChildren(children);
1286
+ const before = this.children[0]?.firstNode() ?? this.end;
1287
+ const mounted = [];
1288
+ for (let index = nextNodes.length - 1; index >= 0; index -= 1) {
1289
+ mounted.unshift(mountNode(nextNodes[index], this.liveParent, before, this.context));
1290
+ }
1291
+ this.children.unshift(...mounted);
1292
+ }
1293
+ updateContext(context) {
1294
+ this.context = context;
1295
+ }
1296
+ moveBefore(before) {
1297
+ let current = this.start;
1298
+ const nodes = [];
1299
+ while (current) {
1300
+ nodes.push(current);
1301
+ if (current === this.end) {
1302
+ break;
1303
+ }
1304
+ current = current.nextSibling;
1305
+ }
1306
+ nodes.forEach((node) => insertBefore(this.context.renderer, this.liveParent, node, before));
1307
+ }
1308
+ destroy() {
1309
+ this.children.forEach((child) => child.destroy());
1310
+ removeNode(this.context.renderer, this.start);
1311
+ removeNode(this.context.renderer, this.end);
1312
+ }
1313
+ }
1314
+ class ProjectionRenderedNode {
1315
+ kind = 'projection';
1316
+ view;
1317
+ styleRelease;
1318
+ node;
1319
+ declarationContext;
1320
+ projectionContext;
1321
+ descriptor;
1322
+ effectRef;
1323
+ constructor(node, parent, before, context) {
1324
+ this.node = node;
1325
+ this.descriptor = signal(node);
1326
+ this.declarationContext =
1327
+ node.declarationContext ??
1328
+ lexicalContext(context);
1329
+ const contentStyle = acquireContentStyle(context, node.slotName, node.stylePolicy);
1330
+ this.styleRelease = contentStyle.release;
1331
+ this.projectionContext = childContext(this.declarationContext, {
1332
+ contentScope: contentStyle.scope,
1333
+ });
1334
+ this.view = createFragment(parent, before, this.projectionContext, [], 'craft-projection');
1335
+ this.effectRef = createRenderEffect(this.projectionContext, 'projection', () => {
1336
+ this.node = this.descriptor();
1337
+ this.view.patchChildren(this.children());
1338
+ });
1339
+ }
1340
+ firstNode() {
1341
+ return this.view.firstNode();
1342
+ }
1343
+ lastNode() {
1344
+ return this.view.lastNode();
1345
+ }
1346
+ patch(node) {
1347
+ if (node.kind !== 'projection' ||
1348
+ node.declarationContext !== this.node.declarationContext) {
1349
+ return false;
1350
+ }
1351
+ this.descriptor.set(node);
1352
+ return true;
1353
+ }
1354
+ children() {
1355
+ return executeTemplateTrace(this.projectionContext.injector, traceContext(this.projectionContext, 'projection', tracePhase(this.projectionContext), this.node.slotName), () => runInInjectionContext(this.declarationContext.injector, () => withCraftRenderContext(this.declarationContext, () => resolveProjectionValue(this.node.render(), this.projectionContext))));
1356
+ }
1357
+ destroy() {
1358
+ this.effectRef.destroy();
1359
+ this.view.destroy();
1360
+ this.styleRelease();
1361
+ }
1362
+ }
1363
+ class TemplateRenderedNode {
1364
+ kind = 'template';
1365
+ view;
1366
+ node;
1367
+ declarationContext;
1368
+ descriptor;
1369
+ effectRef;
1370
+ constructor(node, parent, before, context) {
1371
+ this.node = node;
1372
+ this.descriptor = signal(node);
1373
+ this.declarationContext =
1374
+ node.declarationContext ??
1375
+ lexicalContext(context);
1376
+ this.view = createFragment(parent, before, this.declarationContext, [], 'craft-template');
1377
+ this.effectRef = createRenderEffect(this.declarationContext, 'template', () => {
1378
+ this.node = this.descriptor();
1379
+ this.view.patchChildren(this.children());
1380
+ });
1381
+ }
1382
+ firstNode() {
1383
+ return this.view.firstNode();
1384
+ }
1385
+ lastNode() {
1386
+ return this.view.lastNode();
1387
+ }
1388
+ patch(node) {
1389
+ if (node.kind !== 'template' || node.template !== this.node.template) {
1390
+ return false;
1391
+ }
1392
+ this.descriptor.set(node);
1393
+ return true;
1394
+ }
1395
+ children() {
1396
+ return renderChildrenCallback(this.declarationContext, this.node.template, [
1397
+ resolveTemplateContext(this.node.context, this.declarationContext),
1398
+ ]);
1399
+ }
1400
+ destroy() {
1401
+ this.effectRef.destroy();
1402
+ this.view.destroy();
1403
+ }
1404
+ }
1405
+ function createFragment(parent, before, context, children, label) {
1406
+ const start = mountComment(parent, `${label}:start`, before, context);
1407
+ const end = mountComment(parent, `${label}:end`, before, context);
1408
+ return new FragmentRenderedNode(parent, start, end, context, children);
1409
+ }
1410
+ class EachRenderedNode {
1411
+ kind = 'each';
1412
+ entries = new Map();
1413
+ ordered = [];
1414
+ emptyView;
1415
+ descriptor;
1416
+ effectRef;
1417
+ node;
1418
+ parent;
1419
+ start;
1420
+ end;
1421
+ context;
1422
+ scheduler;
1423
+ ownsScheduler = false;
1424
+ schedulePolicy;
1425
+ schedulerConfigured = false;
1426
+ pendingTasks = new Set();
1427
+ reconcileVersion = 0;
1428
+ constructor(node, parent, start, end, context) {
1429
+ this.node = node;
1430
+ this.parent = parent;
1431
+ this.start = start;
1432
+ this.end = end;
1433
+ this.context = context;
1434
+ this.scheduler = createEachScheduler({
1435
+ enabled: false,
1436
+ strategy: 'sync',
1437
+ frameBudgetMs: 4,
1438
+ });
1439
+ this.configureScheduler(node.schedule);
1440
+ this.descriptor = signal(node);
1441
+ this.effectRef = createRenderEffect(context, 'each-block', () => {
1442
+ const previousItemTemplate = this.node.itemTemplate;
1443
+ this.node = this.descriptor();
1444
+ this.configureScheduler(this.node.schedule);
1445
+ this.reconcile(this.node.itemTemplate !== previousItemTemplate);
1446
+ });
1447
+ }
1448
+ firstNode() {
1449
+ return this.start;
1450
+ }
1451
+ lastNode() {
1452
+ return this.end;
1453
+ }
1454
+ patch(node) {
1455
+ if (node.kind !== 'each') {
1456
+ return false;
1457
+ }
1458
+ this.descriptor.set(node);
1459
+ return true;
1460
+ }
1461
+ reconcile(itemTemplateChanged = false) {
1462
+ const items = resolveTemplateValue(this.node.source, this.context);
1463
+ const collection = items ?? [];
1464
+ const keys = collection.map((item, index) => this.node.track(item, index));
1465
+ const seenKeys = new Set();
1466
+ keys.forEach((key) => {
1467
+ if (seenKeys.has(key)) {
1468
+ throw new Error(`each() received the duplicate key "${String(key)}".`);
1469
+ }
1470
+ seenKeys.add(key);
1471
+ });
1472
+ this.reconcileVersion += 1;
1473
+ this.cancelPendingTasks();
1474
+ if (collection.length === 0) {
1475
+ this.entries.forEach((entry) => {
1476
+ entry.renderEffect?.destroy();
1477
+ entry.view.destroy();
1478
+ });
1479
+ this.entries.clear();
1480
+ this.ordered = [];
1481
+ if (this.node.empty) {
1482
+ const emptyContext = childContext(this.context, {
1483
+ identity: childCraftRenderIdentity(this.context.identity, 'empty'),
1484
+ });
1485
+ if (this.emptyView) {
1486
+ this.emptyView.patchChildren(renderBlockChildren(emptyContext, 'empty', this.node.empty));
1487
+ }
1488
+ else {
1489
+ this.emptyView = createFragment(this.parent, this.end, emptyContext, renderBlockChildren(emptyContext, 'empty', this.node.empty), 'craft-empty');
1490
+ }
1491
+ }
1492
+ else {
1493
+ this.emptyView?.destroy();
1494
+ this.emptyView = undefined;
1495
+ }
1496
+ return;
1497
+ }
1498
+ this.emptyView?.destroy();
1499
+ this.emptyView = undefined;
1500
+ const previous = this.entries;
1501
+ const next = new Map();
1502
+ const nextOrdered = [];
1503
+ const entriesToRender = [];
1504
+ collection.forEach((item, index) => {
1505
+ const key = keys[index];
1506
+ let entry = previous.get(key);
1507
+ if (entry) {
1508
+ if (itemTemplateChanged ||
1509
+ !Object.is(entry.item, item) ||
1510
+ entry.index !== index ||
1511
+ !entry.rendered) {
1512
+ entry.item = item;
1513
+ entry.index = index;
1514
+ entriesToRender.push(entry);
1515
+ }
1516
+ }
1517
+ else {
1518
+ const traceState = { renderCount: 0 };
1519
+ const entryContext = childContext(this.context, {
1520
+ identity: childCraftRenderIdentity(this.context.identity, `item:${String(key)}`),
1521
+ });
1522
+ entry = {
1523
+ key,
1524
+ item,
1525
+ index,
1526
+ traceState,
1527
+ rendered: false,
1528
+ view: createFragment(this.parent, this.end, entryContext, [], `craft-each:${String(key)}`),
1529
+ };
1530
+ entriesToRender.push(entry);
1531
+ }
1532
+ next.set(key, entry);
1533
+ nextOrdered.push(entry.view);
1534
+ });
1535
+ previous.forEach((entry, key) => {
1536
+ if (!next.has(key)) {
1537
+ entry.renderEffect?.destroy();
1538
+ entry.view.destroy();
1539
+ }
1540
+ });
1541
+ let before = this.end;
1542
+ for (let index = nextOrdered.length - 1; index >= 0; index -= 1) {
1543
+ const entry = nextOrdered[index];
1544
+ if (entry.lastNode().nextSibling !== before) {
1545
+ entry.moveBefore(before);
1546
+ }
1547
+ before = entry.firstNode();
1548
+ }
1549
+ this.entries = next;
1550
+ this.ordered = nextOrdered;
1551
+ entriesToRender.forEach((entry) => this.scheduleEntryRender(entry));
1552
+ }
1553
+ configureScheduler(policy) {
1554
+ if (this.schedulerConfigured && this.schedulePolicy === policy)
1555
+ return;
1556
+ if (this.ownsScheduler)
1557
+ this.scheduler.destroy?.();
1558
+ this.schedulePolicy = policy;
1559
+ this.schedulerConfigured = true;
1560
+ this.cancelPendingTasks();
1561
+ const injected = this.context.injector.get(EACH_SCHEDULER, null);
1562
+ if (injected) {
1563
+ this.scheduler = injected;
1564
+ this.ownsScheduler = false;
1565
+ return;
1566
+ }
1567
+ this.scheduler = createEachScheduler(policy ?? { enabled: false, strategy: 'sync', frameBudgetMs: 4 });
1568
+ this.ownsScheduler = true;
1569
+ }
1570
+ scheduleEntryRender(entry) {
1571
+ const version = this.reconcileVersion;
1572
+ const scheduled = {
1573
+ completed: false,
1574
+ handle: undefined,
1575
+ };
1576
+ const handle = this.scheduler.schedule(() => {
1577
+ scheduled.completed = true;
1578
+ if (scheduled.handle)
1579
+ this.pendingTasks.delete(scheduled.handle);
1580
+ if (version !== this.reconcileVersion || !this.entries.has(entry.key)) {
1581
+ return;
1582
+ }
1583
+ entry.renderEffect?.destroy();
1584
+ entry.renderEffect = createRenderEffect(this.context, 'each-item', () => entry.view.patchChildren(this.renderItem(entry.traceState, entry.item, entry.index, entry.key)));
1585
+ entry.rendered = true;
1586
+ });
1587
+ scheduled.handle = handle;
1588
+ if (!scheduled.completed)
1589
+ this.pendingTasks.add(handle);
1590
+ }
1591
+ cancelPendingTasks() {
1592
+ this.pendingTasks.forEach((handle) => handle.cancel());
1593
+ this.pendingTasks.clear();
1594
+ }
1595
+ renderItem(traceState, item, index, key) {
1596
+ traceState.renderCount += 1;
1597
+ return renderBlockChildren(childContext(this.context, {
1598
+ traceState,
1599
+ identity: childCraftRenderIdentity(this.context.identity, `item:${String(key)}`),
1600
+ }), 'each', this.node.itemTemplate, [
1601
+ function* () {
1602
+ return item;
1603
+ },
1604
+ index,
1605
+ ]);
1606
+ }
1607
+ destroy() {
1608
+ this.effectRef.destroy();
1609
+ this.reconcileVersion += 1;
1610
+ this.cancelPendingTasks();
1611
+ if (this.ownsScheduler)
1612
+ this.scheduler.destroy?.();
1613
+ this.entries.forEach((entry) => {
1614
+ entry.renderEffect?.destroy();
1615
+ entry.view.destroy();
1616
+ });
1617
+ this.emptyView?.destroy();
1618
+ removeNode(this.context.renderer, this.start);
1619
+ removeNode(this.context.renderer, this.end);
1620
+ }
1621
+ }
1622
+ class IfBlockRenderedNode {
1623
+ kind = 'if';
1624
+ view;
1625
+ active;
1626
+ descriptor;
1627
+ effectRef;
1628
+ node;
1629
+ context;
1630
+ constructor(node, parent, before, context) {
1631
+ this.node = node;
1632
+ this.context = context;
1633
+ this.descriptor = signal(node);
1634
+ this.view = createFragment(parent, before, context, [], `craft-if:${node.conditionName}`);
1635
+ this.effectRef = createRenderEffect(context, 'if-block', () => {
1636
+ this.node = this.descriptor();
1637
+ this.active = this.isTrue();
1638
+ this.view.patchChildren(this.children());
1639
+ });
1640
+ }
1641
+ firstNode() {
1642
+ return this.view.firstNode();
1643
+ }
1644
+ lastNode() {
1645
+ return this.view.lastNode();
1646
+ }
1647
+ patch(node) {
1648
+ if (node.kind !== 'if' || node.conditionName !== this.node.conditionName) {
1649
+ return false;
1650
+ }
1651
+ this.descriptor.set(node);
1652
+ return true;
1653
+ }
1654
+ isTrue() {
1655
+ return Boolean(resolveTemplateValue(this.node.condition, this.context));
1656
+ }
1657
+ children() {
1658
+ return this.active
1659
+ ? renderBlockChildren(this.context, this.node.conditionName, this.node.whenTrue)
1660
+ : this.node.whenFalse
1661
+ ? renderBlockChildren(this.context, this.node.conditionName, this.node.whenFalse)
1662
+ : [];
1663
+ }
1664
+ destroy() {
1665
+ this.effectRef.destroy();
1666
+ this.view.destroy();
1667
+ }
1668
+ }
1669
+ class MatchBlockRenderedNode {
1670
+ kind = 'match-block';
1671
+ view;
1672
+ handledExceptionCode;
1673
+ descriptor;
1674
+ effectRef;
1675
+ node;
1676
+ context;
1677
+ constructor(node, parent, before, context) {
1678
+ this.node = node;
1679
+ this.context = context;
1680
+ this.descriptor = signal(node);
1681
+ this.view = createFragment(parent, before, context, [], 'craft-match-block');
1682
+ this.effectRef = createRenderEffect(context, 'match-block', () => {
1683
+ this.node = this.descriptor();
1684
+ this.view.patchChildren(this.children());
1685
+ });
1686
+ }
1687
+ firstNode() {
1688
+ return this.view.firstNode();
1689
+ }
1690
+ lastNode() {
1691
+ return this.view.lastNode();
1692
+ }
1693
+ patch(node) {
1694
+ if (node.kind !== 'match-block' || node.key !== this.node.key) {
1695
+ return false;
1696
+ }
1697
+ this.descriptor.set(node);
1698
+ return true;
1699
+ }
1700
+ children() {
1701
+ if (this.handledExceptionCode) {
1702
+ this.context.handledResourceExceptionCodes?.delete(this.handledExceptionCode);
1703
+ this.handledExceptionCode = undefined;
1704
+ }
1705
+ const exception = resolveTemplateValue(this.node.source, this.context);
1706
+ if (!exception)
1707
+ return [];
1708
+ const code = exception[this.node.key];
1709
+ // Resource exception buckets use an empty object when no exception is
1710
+ // present. Do not turn that implementation detail into an exception with
1711
+ // an `undefined` discriminator during a later component rerender.
1712
+ if (code === undefined || code === null)
1713
+ return [];
1714
+ const handler = this.node.handlers[String(code)];
1715
+ if (!handler) {
1716
+ throw new CraftUnhandledExceptionError(exception);
1717
+ }
1718
+ this.handledExceptionCode = String(code);
1719
+ this.context.handledResourceExceptionCodes?.add(this.handledExceptionCode);
1720
+ return executeTemplateTrace(this.context.injector, traceContext(this.context, 'block', tracePhase(this.context), String(this.node.key)), () => handler(exception));
1721
+ }
1722
+ destroy() {
1723
+ this.effectRef.destroy();
1724
+ if (this.handledExceptionCode) {
1725
+ this.context.handledResourceExceptionCodes?.delete(this.handledExceptionCode);
1726
+ }
1727
+ this.view.destroy();
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Detaches a mounted DOM range into a holder fragment, keeping every node (and
1732
+ * therefore every binding) alive. Both boundaries hide their subtree this way:
1733
+ * destroying it would take with it the bindings that report recovery.
1734
+ *
1735
+ * The nodes go into a real `DocumentFragment` rather than a plain array so that
1736
+ * a NESTED boundary can still insert relative to its own markers while an outer
1737
+ * boundary holds them detached — the markers keep a parent to insert into.
1738
+ */
1739
+ function detachRange(renderer, first, last) {
1740
+ const holder = renderer.createFragment();
1741
+ let current = first;
1742
+ const nodes = [];
1743
+ while (current) {
1744
+ nodes.push(current);
1745
+ if (current === last)
1746
+ break;
1747
+ current = current.nextSibling;
1748
+ }
1749
+ nodes.forEach((node) => {
1750
+ removeNode(renderer, node);
1751
+ renderer.appendChild(holder, node);
1752
+ });
1753
+ return holder;
1754
+ }
1755
+ /** Re-inserts a detached range before `anchor`, in whatever parent it now has. */
1756
+ function reattachRange(renderer, holder, anchor, fallbackParent) {
1757
+ const parent = anchor.parentNode ?? fallbackParent;
1758
+ while (holder.firstChild) {
1759
+ const node = holder.firstChild;
1760
+ removeNode(renderer, node);
1761
+ insertBefore(renderer, parent, node, anchor);
1762
+ }
1763
+ }
1764
+ /** Stands in for reporters that do not identify themselves (component renders). */
1765
+ const ANONYMOUS_EXCEPTION_REPORTER = {};
1766
+ class CatchBlockRenderedNode {
1767
+ kind = 'catch-block';
1768
+ start;
1769
+ end;
1770
+ sourceView;
1771
+ fallbackView;
1772
+ handling = false;
1773
+ fallbackVisible = false;
1774
+ fallbackPosition;
1775
+ detached;
1776
+ /** Reporters still failing. The fallback stays up until this empties. */
1777
+ failing = new Set();
1778
+ node;
1779
+ parent;
1780
+ context;
1781
+ constructor(node, parent, before, context) {
1782
+ this.node = node;
1783
+ this.parent = parent;
1784
+ this.context = context;
1785
+ this.fallbackPosition = node.position;
1786
+ this.start = mountComment(parent, 'craft-catch-block:start', before, context);
1787
+ this.end = mountComment(parent, 'craft-catch-block:end', before, context);
1788
+ // The fallback renders in the OUTER context: an exception thrown by a
1789
+ // fallback belongs to the next boundary up, never to this one.
1790
+ const createFallback = () => createFragment(parent, this.end, this.context, [], 'craft-catch-fallback');
1791
+ const createSource = () => createFragment(parent, this.end, this.boundaryContext(), [], 'craft-catch-source');
1792
+ if (this.fallbackPosition === 'before') {
1793
+ this.fallbackView = createFallback();
1794
+ this.sourceView = createSource();
1795
+ }
1796
+ else {
1797
+ this.sourceView = createSource();
1798
+ this.fallbackView = createFallback();
1799
+ }
1800
+ try {
1801
+ this.sourceView.patchChildren([this.node.source]);
1802
+ }
1803
+ catch (error) {
1804
+ if (!isCraftGenShortCircuit(error))
1805
+ throw error;
1806
+ this.handle(error.exception);
1807
+ }
1808
+ }
1809
+ firstNode() {
1810
+ return this.start;
1811
+ }
1812
+ lastNode() {
1813
+ return this.end;
1814
+ }
1815
+ patch(node) {
1816
+ if (node.kind !== 'catch-block' || node.position !== this.node.position) {
1817
+ return false;
1818
+ }
1819
+ this.node = node;
1820
+ try {
1821
+ this.sourceView.patchChildren([node.source]);
1822
+ if (this.detached) {
1823
+ // A re-render re-materialises the source range: hide it again.
1824
+ this.detached = undefined;
1825
+ this.hideSource();
1826
+ }
1827
+ }
1828
+ catch (error) {
1829
+ if (!isCraftGenShortCircuit(error))
1830
+ throw error;
1831
+ this.handle(error.exception);
1832
+ }
1833
+ return true;
1834
+ }
1835
+ boundaryContext() {
1836
+ return childContext(this.context, {
1837
+ exceptionBoundary: (exception, token) => this.handle(exception, token),
1838
+ exceptionBoundaryResolved: (token) => this.resolved(token),
1839
+ });
1840
+ }
1841
+ handle(exception, token) {
1842
+ if (this.handling) {
1843
+ return (this.context.exceptionBoundary?.(exception, token) ??
1844
+ this.unhandled(exception));
1845
+ }
1846
+ const handler = this.node.handlers[exception._tag];
1847
+ if (!handler) {
1848
+ return (this.context.exceptionBoundary?.(exception, token) ??
1849
+ this.unhandled(exception));
1850
+ }
1851
+ this.failing.add(token ?? ANONYMOUS_EXCEPTION_REPORTER);
1852
+ this.handling = true;
1853
+ this.fallbackVisible = true;
1854
+ try {
1855
+ const resolved = resolveCatchBlockHandler(handler, exception, true, this.node.position);
1856
+ this.applyPosition(resolved.position);
1857
+ this.fallbackView.patchChildren(hasLiveRegion(resolved.children)
1858
+ ? resolved.children
1859
+ : a11yElement('div', { role: 'alert' }, resolved.children));
1860
+ if (resolved.showSource) {
1861
+ this.restoreSource();
1862
+ }
1863
+ else {
1864
+ this.hideSource();
1865
+ }
1866
+ }
1867
+ finally {
1868
+ this.handling = false;
1869
+ }
1870
+ return true;
1871
+ }
1872
+ resolved(token) {
1873
+ this.failing.delete(token ?? ANONYMOUS_EXCEPTION_REPORTER);
1874
+ if (this.handling || !this.fallbackVisible || this.failing.size > 0)
1875
+ return;
1876
+ this.fallbackVisible = false;
1877
+ this.applyPosition(this.node.position);
1878
+ this.fallbackView.patchChildren([]);
1879
+ this.restoreSource();
1880
+ }
1881
+ applyPosition(position) {
1882
+ if (position === this.fallbackPosition)
1883
+ return;
1884
+ this.fallbackPosition = position;
1885
+ this.fallbackView.moveBefore(position === 'before' ? this.sourceFirstNode() : this.end);
1886
+ }
1887
+ sourceFirstNode() {
1888
+ return this.detached ? this.end : this.sourceView.firstNode();
1889
+ }
1890
+ /**
1891
+ * A hidden source is detached from the DOM but stays mounted. Destroying it
1892
+ * would take its bindings with it — and those bindings are the only channel
1893
+ * that reports the exception is gone, so the fallback could never be removed.
1894
+ */
1895
+ hideSource() {
1896
+ if (this.detached)
1897
+ return;
1898
+ this.detached = detachRange(this.context.renderer, this.sourceView.firstNode(), this.sourceView.lastNode());
1899
+ }
1900
+ restoreSource() {
1901
+ if (!this.detached)
1902
+ return;
1903
+ const anchor = this.fallbackPosition === 'before'
1904
+ ? this.end
1905
+ : this.fallbackView.firstNode();
1906
+ reattachRange(this.context.renderer, this.detached, anchor, this.parent);
1907
+ this.detached = undefined;
1908
+ }
1909
+ unhandled(exception) {
1910
+ throw new CraftUnhandledExceptionError(exception);
1911
+ }
1912
+ destroy() {
1913
+ this.restoreSource();
1914
+ this.fallbackView.destroy();
1915
+ this.sourceView.destroy();
1916
+ removeNode(this.context.renderer, this.start);
1917
+ removeNode(this.context.renderer, this.end);
1918
+ }
1919
+ }
1920
+ const fieldExceptionAccessibility = new WeakMap();
1921
+ let nextFieldExceptionBoundaryId = 0;
1922
+ function componentFieldExceptionSources(value, seen = new WeakSet(), depth = 0) {
1923
+ if ((typeof value !== 'object' || value === null) &&
1924
+ typeof value !== 'function') {
1925
+ return [];
1926
+ }
1927
+ const objectValue = value;
1928
+ if (seen.has(objectValue))
1929
+ return [];
1930
+ seen.add(objectValue);
1931
+ const source = value[CRAFT_FIELD_EXCEPTION_SOURCE];
1932
+ if (source) {
1933
+ return [rawReactiveFacade(source)];
1934
+ }
1935
+ const sources = [];
1936
+ if (depth >= 4)
1937
+ return sources;
1938
+ for (const key of Object.keys(value)) {
1939
+ sources.push(...componentFieldExceptionSources(value[key], seen, depth + 1));
1940
+ }
1941
+ return sources;
1942
+ }
1943
+ function fieldExceptionHandler(handlers, path, code) {
1944
+ const grouped = handlers[path];
1945
+ if (typeof grouped === 'object' && grouped !== null) {
1946
+ return grouped[code];
1947
+ }
1948
+ const local = handlers[code];
1949
+ return typeof local === 'function' ? local : undefined;
1950
+ }
1951
+ function fieldExceptionValidatorName(source, exception) {
1952
+ const branded = exception.__brand;
1953
+ if (typeof branded === 'string')
1954
+ return branded;
1955
+ const byValidator = source.exceptions().byValidator;
1956
+ return (Object.entries(byValidator).find(([, value]) => value === exception)?.[0] ??
1957
+ 'unknown');
1958
+ }
1959
+ function fieldExceptionMessageNode(id, children) {
1960
+ return {
1961
+ kind: 'element',
1962
+ tag: 'div',
1963
+ props: {
1964
+ id,
1965
+ role: 'alert',
1966
+ 'data-field-exception': '',
1967
+ },
1968
+ children,
1969
+ };
1970
+ }
1971
+ function a11yElement(tag, props, children = []) {
1972
+ return { kind: 'element', tag, props, children };
1973
+ }
1974
+ function firstElementChild(value) {
1975
+ const list = Array.isArray(value) ? value : [value];
1976
+ for (const child of list) {
1977
+ if (child &&
1978
+ typeof child === 'object' &&
1979
+ 'kind' in child &&
1980
+ child.kind === 'element') {
1981
+ return child;
1982
+ }
1983
+ }
1984
+ return undefined;
1985
+ }
1986
+ function hasLiveRegion(children) {
1987
+ const element = firstElementChild(children);
1988
+ if (!element)
1989
+ return false;
1990
+ const role = element.props['role'];
1991
+ const live = element.props['aria-live'];
1992
+ return (role === 'alert' ||
1993
+ role === 'status' ||
1994
+ live === 'polite' ||
1995
+ live === 'assertive');
1996
+ }
1997
+ /**
1998
+ * The `pendingBlock` boundary.
1999
+ *
2000
+ * Layout is fixed at construction — a fallback fragment and a source fragment,
2001
+ * ordered by `position` — so showing or hiding the fallback never reconciles
2002
+ * the source subtree.
2003
+ *
2004
+ * While at least one descendant binding holds a pending token, the fallback is
2005
+ * rendered and the source's DOM range is detached (its nodes are kept, not
2006
+ * destroyed). Keeping the subtree alive is what makes resumption work at all:
2007
+ * the suspended bindings stay subscribed to their source's `status`, so they
2008
+ * re-run — and release their token — the moment the data arrives.
2009
+ */
2010
+ class PendingBlockRenderedNode {
2011
+ kind = 'pending-block';
2012
+ start;
2013
+ end;
2014
+ sourceView;
2015
+ fallbackView;
2016
+ position;
2017
+ /** Held tokens, by the binding that suspended, in suspension order. */
2018
+ held = new Map();
2019
+ /** Sources seen below this boundary, by name — the reload watch reads them. */
2020
+ watched = new Map();
2021
+ reloadWatch;
2022
+ reloadingSource;
2023
+ detached;
2024
+ /** Depth of an in-flight render into the source range. */
2025
+ rendering = 0;
2026
+ /** A fallback render asked for while the source range was mid-render. */
2027
+ renderDeferred = false;
2028
+ node;
2029
+ parent;
2030
+ context;
2031
+ constructor(node, parent, before, context) {
2032
+ this.node = node;
2033
+ this.parent = parent;
2034
+ this.context = context;
2035
+ this.position = node.position;
2036
+ if (context.ssr &&
2037
+ node.ssr === 'client' &&
2038
+ !node.handlers &&
2039
+ !node.fallback) {
2040
+ throw new Error('pendingBlock({ ssr: "client" }) requires an explicit fallback or shell.');
2041
+ }
2042
+ this.start = mountComment(parent, 'craft-pending:start', before, context);
2043
+ this.end = mountComment(parent, 'craft-pending:end', before, context);
2044
+ // The fallback renders in the OUTER context: a fallback that suspends in
2045
+ // turn belongs to the next boundary up, never to this one.
2046
+ const createFallback = () => createFragment(parent, this.end, this.context, [], 'craft-pending-fallback');
2047
+ const createSource = () => createFragment(parent, this.end, this.boundaryContext(), [], 'craft-pending-source');
2048
+ // Both ranges are staked out EMPTY first. A binding below this boundary
2049
+ // suspends on its very first run, synchronously, from inside the source
2050
+ // range's own construction — so both views have to exist (and the range has
2051
+ // to be complete) before that first run happens.
2052
+ if (this.position === 'before') {
2053
+ this.fallbackView = createFallback();
2054
+ this.sourceView = createSource();
2055
+ }
2056
+ else {
2057
+ this.sourceView = createSource();
2058
+ this.fallbackView = createFallback();
2059
+ }
2060
+ this.renderSource([node.source]);
2061
+ }
2062
+ /**
2063
+ * Fills the source range, deferring any fallback render that a synchronous
2064
+ * suspension asks for until the range is whole again. Rendering the fallback
2065
+ * mid-pass would detach the range out from under the insertion that is still
2066
+ * walking it.
2067
+ */
2068
+ renderSource(children) {
2069
+ this.rendering += 1;
2070
+ try {
2071
+ this.sourceView.patchChildren(children);
2072
+ }
2073
+ finally {
2074
+ this.rendering -= 1;
2075
+ }
2076
+ if (this.rendering === 0 && this.renderDeferred) {
2077
+ this.renderDeferred = false;
2078
+ this.renderFallback();
2079
+ }
2080
+ }
2081
+ firstNode() {
2082
+ return this.start;
2083
+ }
2084
+ lastNode() {
2085
+ return this.end;
2086
+ }
2087
+ patch(node) {
2088
+ if (node.kind !== 'pending-block') {
2089
+ return false;
2090
+ }
2091
+ const next = node;
2092
+ if (next.position !== this.position) {
2093
+ return false;
2094
+ }
2095
+ this.node = next;
2096
+ // A re-render re-materialises the source range: hide it again.
2097
+ if (this.held.size > 0)
2098
+ this.detached = undefined;
2099
+ this.renderSource([next.source]);
2100
+ if (this.held.size > 0)
2101
+ this.showFallback();
2102
+ return true;
2103
+ }
2104
+ destroy() {
2105
+ this.reloadWatch?.destroy();
2106
+ this.restoreSource();
2107
+ this.fallbackView.destroy();
2108
+ this.sourceView.destroy();
2109
+ removeNode(this.context.renderer, this.start);
2110
+ removeNode(this.context.renderer, this.end);
2111
+ }
2112
+ boundaryContext() {
2113
+ return childContext(this.context, {
2114
+ pendingBoundary: {
2115
+ suspend: (token, source) => this.suspend(token, source),
2116
+ resume: (token) => this.resume(token),
2117
+ },
2118
+ settledReadObserver: (notice) => this.watch(notice),
2119
+ });
2120
+ }
2121
+ /**
2122
+ * Records a source read below this boundary. A refetch throws nothing, so the
2123
+ * boundary cannot learn about it from the suspension channel: it watches the
2124
+ * source's own status instead, on its own effect, which keeps working even
2125
+ * when the binding that first reported it never re-runs.
2126
+ */
2127
+ watch(notice) {
2128
+ if (!this.handles(notice.source)) {
2129
+ this.context.settledReadObserver?.(notice);
2130
+ return;
2131
+ }
2132
+ if (this.watched.has(notice.source))
2133
+ return;
2134
+ this.watched.set(notice.source, notice);
2135
+ this.reloadWatch?.destroy();
2136
+ this.reloadWatch = createEffectInInjector(this.context.injector, 'pending-reload-watch', () => this.onReloadStateChange());
2137
+ }
2138
+ onReloadStateChange() {
2139
+ let reloading;
2140
+ for (const [source, notice] of this.watched) {
2141
+ const status = notice.status();
2142
+ // Refetching with something already on screen — the settled read returns
2143
+ // the stale value, so the subtree stays visible.
2144
+ if (notice.value() !== undefined &&
2145
+ (status === 'loading' || status === 'reloading')) {
2146
+ reloading = source;
2147
+ break;
2148
+ }
2149
+ }
2150
+ if (reloading === this.reloadingSource)
2151
+ return;
2152
+ this.reloadingSource = reloading;
2153
+ // A suspension outranks a reload: it already owns the fallback slot.
2154
+ if (this.held.size === 0)
2155
+ this.requestFallbackRender();
2156
+ }
2157
+ suspend(token, source) {
2158
+ if (this.handles(source)) {
2159
+ const routePolicy = this.context.injector.get(CRAFT_SSR_POLICY, null);
2160
+ this.context.ssr?.suspend(token, source, this.node.ssr ?? routePolicy?.mode, {
2161
+ route: activeSsrRoute(this.context),
2162
+ timeoutMs: routePolicy?.timeoutMs,
2163
+ });
2164
+ const wasIdle = this.held.size === 0;
2165
+ this.held.set(token, source);
2166
+ if (wasIdle)
2167
+ this.showFallback();
2168
+ return;
2169
+ }
2170
+ // Not ours to handle (an `.exhaustive` boundary that does not list this
2171
+ // source): delegate outwards.
2172
+ if (!this.context.pendingBoundary) {
2173
+ if (this.context.ssr) {
2174
+ this.context.ssr.unhandled(source, activeSsrRoute(this.context));
2175
+ }
2176
+ throw new CraftUnhandledPendingError(source);
2177
+ }
2178
+ this.context.pendingBoundary.suspend(token, source);
2179
+ }
2180
+ resume(token) {
2181
+ if (!this.held.delete(token)) {
2182
+ this.context.pendingBoundary?.resume(token);
2183
+ return;
2184
+ }
2185
+ this.context.ssr?.resume(token);
2186
+ if (this.held.size === 0)
2187
+ this.hideFallback();
2188
+ }
2189
+ /** The catch-all form handles every source; `.exhaustive` only what it lists. */
2190
+ handles(source) {
2191
+ return !this.node.handlers || source in this.node.handlers;
2192
+ }
2193
+ fallbackChildren() {
2194
+ const handlers = this.node.handlers;
2195
+ const suspended = this.held.size > 0;
2196
+ // Several listed sources may be pending at once — the first to suspend wins.
2197
+ const [firstSuspended] = this.held.values();
2198
+ const source = suspended ? firstSuspended : this.reloadingSource;
2199
+ if (!source)
2200
+ return [];
2201
+ if (!handlers) {
2202
+ return this.announceFallback(suspended
2203
+ ? (this.node.fallback?.() ?? [])
2204
+ : (this.node.reloading?.() ?? []), true);
2205
+ }
2206
+ const handler = handlers[source];
2207
+ const inner = handler
2208
+ ? resolvePendingBlockHandler(handler, suspended ? 'pending' : 'reloading')
2209
+ : [];
2210
+ return this.announceFallback(inner, true);
2211
+ }
2212
+ announceFallback(children, busy) {
2213
+ if (!children || (Array.isArray(children) && children.length === 0)) {
2214
+ return [];
2215
+ }
2216
+ return a11yElement('span', {
2217
+ 'aria-live': 'polite',
2218
+ 'aria-atomic': 'true',
2219
+ 'aria-busy': busy ? 'true' : 'false',
2220
+ }, children);
2221
+ }
2222
+ /** Renders whatever the current state calls for: a suspension, a reload, or nothing. */
2223
+ renderFallback() {
2224
+ if (this.held.size > 0) {
2225
+ this.detachSource();
2226
+ }
2227
+ else {
2228
+ // A reload keeps the stale subtree on screen next to the indicator.
2229
+ this.restoreSource();
2230
+ }
2231
+ this.fallbackView.patchChildren(this.fallbackChildren());
2232
+ }
2233
+ showFallback() {
2234
+ this.requestFallbackRender();
2235
+ }
2236
+ hideFallback() {
2237
+ this.requestFallbackRender();
2238
+ }
2239
+ requestFallbackRender() {
2240
+ if (this.rendering > 0) {
2241
+ this.renderDeferred = true;
2242
+ return;
2243
+ }
2244
+ this.renderFallback();
2245
+ }
2246
+ focused;
2247
+ detachSource() {
2248
+ if (this.detached)
2249
+ return;
2250
+ const documentRef = this.parent.ownerDocument;
2251
+ const active = documentRef?.activeElement ?? null;
2252
+ if (active && this.sourceContains(active)) {
2253
+ this.focused = active;
2254
+ }
2255
+ this.detached = detachRange(this.context.renderer, this.sourceView.firstNode(), this.sourceView.lastNode());
2256
+ }
2257
+ sourceContains(node) {
2258
+ let current = this.sourceView.firstNode();
2259
+ const end = this.sourceView.lastNode();
2260
+ while (current) {
2261
+ if (current === node)
2262
+ return true;
2263
+ if (isElementNode(current) && current.contains(node))
2264
+ return true;
2265
+ if (current === end)
2266
+ break;
2267
+ current = current.nextSibling;
2268
+ }
2269
+ return false;
2270
+ }
2271
+ restoreSource() {
2272
+ if (!this.detached)
2273
+ return;
2274
+ // Back before the marker that follows the source range, so the original
2275
+ // order (fallback / source) is preserved.
2276
+ const anchor = this.position === 'before' ? this.end : this.fallbackView.firstNode();
2277
+ reattachRange(this.context.renderer, this.detached, anchor, this.parent);
2278
+ this.detached = undefined;
2279
+ if (this.focused &&
2280
+ 'focus' in this.focused &&
2281
+ typeof this.focused.focus === 'function') {
2282
+ this.focused.focus();
2283
+ }
2284
+ this.focused = undefined;
2285
+ }
2286
+ }
2287
+ class FieldExceptionBlockRenderedNode {
2288
+ kind = 'field-exception-block';
2289
+ boundaryId = nextFieldExceptionBoundaryId++;
2290
+ descriptor;
2291
+ sourcesVersion = signal(0);
2292
+ sources = new Set();
2293
+ componentSourceCleanups;
2294
+ view;
2295
+ effectRef;
2296
+ node;
2297
+ context;
2298
+ constructor(node, parent, before, context) {
2299
+ this.node = node;
2300
+ this.context = context;
2301
+ this.descriptor = signal(node);
2302
+ this.view = createFragment(parent, before, this.boundaryContext(), [node.source], 'craft-field-exception-block');
2303
+ this.componentSourceCleanups = componentFieldExceptionSources(context.componentContext).map((source) => this.register(source));
2304
+ this.effectRef = createRenderEffect(context, 'field-exception-block', () => {
2305
+ this.node = this.descriptor();
2306
+ this.sourcesVersion();
2307
+ const fallbacks = this.renderFallbacks();
2308
+ this.view.patchChildren(this.node.options.position === 'before'
2309
+ ? [fallbacks, this.node.source]
2310
+ : [this.node.source, fallbacks]);
2311
+ });
2312
+ }
2313
+ firstNode() {
2314
+ return this.view.firstNode();
2315
+ }
2316
+ lastNode() {
2317
+ return this.view.lastNode();
2318
+ }
2319
+ patch(node) {
2320
+ if (node.kind !== 'field-exception-block')
2321
+ return false;
2322
+ this.descriptor.set(node);
2323
+ return true;
2324
+ }
2325
+ boundaryContext() {
2326
+ return childContext(this.context, {
2327
+ fieldExceptionBoundary: {
2328
+ register: (source, element) => this.register(source, element),
2329
+ },
2330
+ });
2331
+ }
2332
+ register(source, element) {
2333
+ const rawSource = rawReactiveFacade(source);
2334
+ const unregisterParent = this.context.fieldExceptionBoundary?.register(rawSource, element);
2335
+ const registered = {
2336
+ source: rawSource,
2337
+ element,
2338
+ messageIds: new Map(),
2339
+ };
2340
+ if (element) {
2341
+ this.accessibilityState(element).registrations.add(this.boundaryId);
2342
+ }
2343
+ this.sources.add(registered);
2344
+ untracked(() => this.sourcesVersion.update((value) => value + 1));
2345
+ return () => {
2346
+ this.releaseAccessibility(registered);
2347
+ this.sources.delete(registered);
2348
+ untracked(() => this.sourcesVersion.update((value) => value + 1));
2349
+ unregisterParent?.();
2350
+ };
2351
+ }
2352
+ orderedSources() {
2353
+ const ordered = [...this.sources].sort((left, right) => {
2354
+ if (!left.element)
2355
+ return 1;
2356
+ if (!right.element)
2357
+ return -1;
2358
+ if (left.element === right.element)
2359
+ return 0;
2360
+ const relation = left.element.compareDocumentPosition(right.element);
2361
+ return relation & 4 /* Node.DOCUMENT_POSITION_FOLLOWING */ ? -1 : 1;
2362
+ });
2363
+ const seen = new Set();
2364
+ return ordered.filter(({ source }) => {
2365
+ if (seen.has(source))
2366
+ return false;
2367
+ seen.add(source);
2368
+ return true;
2369
+ });
2370
+ }
2371
+ activeExceptions(source) {
2372
+ const visibility = this.node.options.visibility ?? 'visibleExceptions';
2373
+ if (visibility === 'visibleExceptions') {
2374
+ if (this.node.options.mode === 'first') {
2375
+ const first = source.visibleFirstLeftFailedValidation();
2376
+ if (first && this.handles(source, first))
2377
+ return [first];
2378
+ return source
2379
+ .visibleExceptions()
2380
+ .list.filter((exception) => this.handles(source, exception))
2381
+ .slice(0, 1);
2382
+ }
2383
+ return source.visibleExceptions().list;
2384
+ }
2385
+ if (!fieldExceptionVisibilityMatches(visibility, {
2386
+ field: source.field,
2387
+ hasAttemptedSubmit: source.hasAttemptedSubmit,
2388
+ })) {
2389
+ return [];
2390
+ }
2391
+ if (this.node.options.mode === 'first') {
2392
+ const first = source.firstLeftFailedValidation();
2393
+ if (first && this.handles(source, first))
2394
+ return [first];
2395
+ return source
2396
+ .exceptions()
2397
+ .list.filter((exception) => this.handles(source, exception))
2398
+ .slice(0, 1);
2399
+ }
2400
+ return source.exceptions().list;
2401
+ }
2402
+ handles(source, exception) {
2403
+ return Boolean(fieldExceptionHandler(this.node.handlers, source.path, exception._tag));
2404
+ }
2405
+ renderFallbacks() {
2406
+ const children = [];
2407
+ for (const registered of this.orderedSources()) {
2408
+ const { source } = registered;
2409
+ const activeMessageIds = [];
2410
+ const exceptions = this.activeExceptions(source);
2411
+ exceptions.forEach((exception, index) => {
2412
+ const handler = fieldExceptionHandler(this.node.handlers, source.path, exception._tag);
2413
+ if (!handler)
2414
+ return;
2415
+ const validatorName = fieldExceptionValidatorName(source, exception);
2416
+ const messageKey = `${validatorName}:${exception._tag}:${index}`;
2417
+ let id = registered.messageIds.get(messageKey);
2418
+ if (!id) {
2419
+ id = `craft-field-exception-${this.boundaryId}-${registered.messageIds.size}`;
2420
+ registered.messageIds.set(messageKey, id);
2421
+ }
2422
+ activeMessageIds.push(id);
2423
+ children.push(fieldExceptionMessageNode(id, handler({
2424
+ field: source.field,
2425
+ path: source.path,
2426
+ runtimePath: source.runtimePath,
2427
+ validatorName,
2428
+ exception,
2429
+ })));
2430
+ });
2431
+ this.applyAccessibility(registered, activeMessageIds);
2432
+ }
2433
+ return children;
2434
+ }
2435
+ applyAccessibility(registered, messageIds) {
2436
+ if (!registered.element)
2437
+ return;
2438
+ if (messageIds.length) {
2439
+ this.accessibilityState(registered.element).messagesByBoundary.set(this.boundaryId, messageIds);
2440
+ }
2441
+ else {
2442
+ this.accessibilityState(registered.element).messagesByBoundary.delete(this.boundaryId);
2443
+ }
2444
+ this.reconcileAccessibility(registered.element);
2445
+ }
2446
+ accessibilityState(element) {
2447
+ const existing = fieldExceptionAccessibility.get(element);
2448
+ if (existing)
2449
+ return existing;
2450
+ const created = {
2451
+ originalAriaInvalid: element.getAttribute('aria-invalid'),
2452
+ originalAriaDescribedBy: element.getAttribute('aria-describedby'),
2453
+ registrations: new Set(),
2454
+ messagesByBoundary: new Map(),
2455
+ };
2456
+ fieldExceptionAccessibility.set(element, created);
2457
+ return created;
2458
+ }
2459
+ reconcileAccessibility(element) {
2460
+ const state = this.accessibilityState(element);
2461
+ const renderer = this.context.renderer;
2462
+ const messageIds = [...state.messagesByBoundary.values()].flat();
2463
+ if (messageIds.length) {
2464
+ renderer.setAttribute(element, 'aria-invalid', 'true');
2465
+ }
2466
+ else {
2467
+ if (state.originalAriaInvalid === null) {
2468
+ renderer.removeAttribute(element, 'aria-invalid');
2469
+ }
2470
+ else {
2471
+ renderer.setAttribute(element, 'aria-invalid', state.originalAriaInvalid);
2472
+ }
2473
+ }
2474
+ const describedBy = [
2475
+ ...(state.originalAriaDescribedBy?.split(/\s+/).filter(Boolean) ?? []),
2476
+ ...messageIds,
2477
+ ];
2478
+ if (describedBy.length) {
2479
+ renderer.setAttribute(element, 'aria-describedby', [...new Set(describedBy)].join(' '));
2480
+ }
2481
+ else {
2482
+ renderer.removeAttribute(element, 'aria-describedby');
2483
+ }
2484
+ }
2485
+ releaseAccessibility(registered) {
2486
+ if (!registered.element)
2487
+ return;
2488
+ const state = this.accessibilityState(registered.element);
2489
+ state.messagesByBoundary.delete(this.boundaryId);
2490
+ state.registrations.delete(this.boundaryId);
2491
+ this.reconcileAccessibility(registered.element);
2492
+ if (!state.registrations.size) {
2493
+ fieldExceptionAccessibility.delete(registered.element);
2494
+ }
2495
+ }
2496
+ destroy() {
2497
+ this.effectRef.destroy();
2498
+ this.componentSourceCleanups.forEach((cleanup) => cleanup());
2499
+ this.view.destroy();
2500
+ this.sources.forEach((source) => this.releaseAccessibility(source));
2501
+ this.sources.clear();
2502
+ }
2503
+ }
2504
+ function componentHostName(injector, name) {
2505
+ const tags = injector.get(HOST_TAG_LIST, []);
2506
+ const current = tags[tags.length - 1];
2507
+ if (current?.startsWith('component:')) {
2508
+ return current;
2509
+ }
2510
+ return allocateCraftHostName(injector, 'component', name);
2511
+ }
2512
+ function allocateCraftHostName(injector, kind, name) {
2513
+ const register = injector.get(COMPONENT_REGISTER, null) ?? ɵfallbackComponentRegister;
2514
+ return `${kind}:${name}#${register.next()}`;
2515
+ }
2516
+ class ComponentRenderedNode {
2517
+ kind = 'component';
2518
+ environmentInjector;
2519
+ propKeys;
2520
+ propSources;
2521
+ hostPropsSource;
2522
+ view;
2523
+ effectRef;
2524
+ composedTemplateEffect;
2525
+ componentExceptionEffect;
2526
+ syncTemplateFlushRelease;
2527
+ componentFallbackVisible = false;
2528
+ componentFallbackException;
2529
+ styleReleases;
2530
+ providerTrackers = [];
2531
+ templateOnly;
2532
+ traceState;
2533
+ componentRenderContext;
2534
+ traceCreated = false;
2535
+ factoryContext;
2536
+ latestTemplate = [];
2537
+ registrationReleases = [];
2538
+ hostBindings;
2539
+ component;
2540
+ context;
2541
+ constructor(component, props, parent, before, context, hostTarget, templateContext, additionalProviders = [], declarationContext) {
2542
+ this.component = component;
2543
+ this.context = context;
2544
+ this.templateOnly = templateContext !== undefined;
2545
+ const definition = component[CRAFT_COMPONENT];
2546
+ const composition = definition.composition;
2547
+ const ownScope = scopeIdFor(definition.scopeDefinition, definition.name);
2548
+ this.traceState = { renderCount: 0 };
2549
+ this.styleReleases = acquireStyles(context, definition.styleOwners, ownScope);
2550
+ const componentRenderContext = childContext(context, {
2551
+ ownerScope: ownScope,
2552
+ rootScope: scopeTokens(context.rootScope, ownScope),
2553
+ componentName: definition.name,
2554
+ contentStyles: definition.meta.contentStyles,
2555
+ contentScope: undefined,
2556
+ declarationContext: declarationContext ?? context,
2557
+ directiveNames: definition.styleOwners
2558
+ .slice(1)
2559
+ .map((owner) => owner.name),
2560
+ traceState: this.traceState,
2561
+ });
2562
+ this.componentRenderContext = componentRenderContext;
2563
+ this.hostBindings = hostTarget
2564
+ ? new HostPropertyBindings(hostTarget, context)
2565
+ : undefined;
2566
+ const componentElement = hostTarget ?? (isElementNode(parent) ? parent : parent.parentElement);
2567
+ // Angular's runtime accepts any Injector as the R3Injector parent even
2568
+ // though the public helper narrows the type to EnvironmentInjector. Keep
2569
+ // the immediate parent here: unwrapping it through
2570
+ // `get(EnvironmentInjector)` would skip route-scoped providers such as
2571
+ // ActivatedRoute and ChildrenOutletContexts.
2572
+ const parentInjector = context.injector;
2573
+ if (!composition) {
2574
+ this.environmentInjector = createEnvironmentInjector([
2575
+ ...provideHostName(`component:${definition.name}`),
2576
+ {
2577
+ provide: CRAFT_HYDRATION_ID,
2578
+ useValue: componentRenderContext.identity,
2579
+ },
2580
+ ...(definition.meta.providers ?? []),
2581
+ {
2582
+ provide: ElementRef,
2583
+ useValue: new ElementRef(componentElement),
2584
+ },
2585
+ ...additionalProviders,
2586
+ ], parentInjector, 'CraftComponent');
2587
+ this.traceCreated = true;
2588
+ traceComponentLifecycle(this.environmentInjector, 'create', definition.name, this.traceState.renderCount);
2589
+ }
2590
+ this.propKeys = this.templateOnly
2591
+ ? []
2592
+ : Object.keys(props).filter((key) => !isHostProperty(key));
2593
+ this.propSources = this.propKeys.map((key) => signal(props[key]));
2594
+ this.hostPropsSource = signal(hostPropsFromComponentProps(props));
2595
+ const inputShells = this.propSources.map((source) => {
2596
+ const input = markYieldableMethod((...callbackArgs) => {
2597
+ const current = source();
2598
+ if (callbackArgs.length > 0 && typeof current === 'function') {
2599
+ return current(...callbackArgs);
2600
+ }
2601
+ const value = typeof current === 'function'
2602
+ ? current()
2603
+ : current;
2604
+ return isGenerator(value)
2605
+ ? value
2606
+ : yieldableInvocation(value);
2607
+ });
2608
+ // Content inputs are object-shaped at the call site but component
2609
+ // factories receive callable input shells. Expose object properties on
2610
+ // the shell so the factory can use `content.header` without eagerly
2611
+ // evaluating a slot.
2612
+ return new Proxy(input, {
2613
+ get(target, property, receiver) {
2614
+ const current = source();
2615
+ if (typeof current === 'object' &&
2616
+ current !== null &&
2617
+ property in current) {
2618
+ return Reflect.get(current, property);
2619
+ }
2620
+ return Reflect.get(target, property, receiver);
2621
+ },
2622
+ has(target, property) {
2623
+ const current = source();
2624
+ return ((typeof current === 'object' &&
2625
+ current !== null &&
2626
+ property in current) ||
2627
+ property in target);
2628
+ },
2629
+ ownKeys(target) {
2630
+ const current = source();
2631
+ return [
2632
+ ...new Set([
2633
+ ...Reflect.ownKeys(target),
2634
+ ...(typeof current === 'object' && current !== null
2635
+ ? Reflect.ownKeys(current)
2636
+ : []),
2637
+ ]),
2638
+ ];
2639
+ },
2640
+ getOwnPropertyDescriptor(target, property) {
2641
+ const current = source();
2642
+ if (typeof current === 'object' &&
2643
+ current !== null &&
2644
+ property in current) {
2645
+ return {
2646
+ configurable: true,
2647
+ enumerable: true,
2648
+ value: Reflect.get(current, property),
2649
+ };
2650
+ }
2651
+ return Reflect.getOwnPropertyDescriptor(target, property);
2652
+ },
2653
+ });
2654
+ });
2655
+ // A one-argument logic factory may model its complete input as one typed
2656
+ // object. The callable shell keeps the historical Input<T> invocation
2657
+ // while exposing the object's properties for the contract-based API.
2658
+ const args = !this.templateOnly && definition.factory.length === 1
2659
+ ? [
2660
+ new Proxy(((...callbackArgs) => inputShells[0](...callbackArgs)), {
2661
+ get: (target, property, receiver) => {
2662
+ const index = this.propKeys.indexOf(String(property));
2663
+ if (index !== -1) {
2664
+ return preserveContentDeclarationContext(this.propSources[index](), declarationContext ?? context);
2665
+ }
2666
+ return Reflect.get(target, property, receiver);
2667
+ },
2668
+ has: (target, property) => this.propKeys.includes(String(property)) ||
2669
+ Reflect.has(target, property),
2670
+ ownKeys: (target) => [
2671
+ ...new Set([...Reflect.ownKeys(target), ...this.propKeys]),
2672
+ ],
2673
+ getOwnPropertyDescriptor: (target, property) => {
2674
+ const index = this.propKeys.indexOf(String(property));
2675
+ return index === -1
2676
+ ? Reflect.getOwnPropertyDescriptor(target, property)
2677
+ : {
2678
+ configurable: true,
2679
+ enumerable: true,
2680
+ value: preserveContentDeclarationContext(this.propSources[index](), declarationContext ?? context),
2681
+ };
2682
+ },
2683
+ }),
2684
+ ]
2685
+ : inputShells;
2686
+ const factoryContext = composition
2687
+ ? undefined
2688
+ : this.templateOnly
2689
+ ? templateContext?.value
2690
+ : untracked(() => executeCraftComponentFactory(definition.factory, args, this.environmentInjector));
2691
+ if (!composition &&
2692
+ !this.templateOnly &&
2693
+ typeof factoryContext === 'object' &&
2694
+ factoryContext !== null &&
2695
+ 'then' in factoryContext) {
2696
+ throw new Error('Async component factories are not renderable directly. Move asynchronous work behind defer().');
2697
+ }
2698
+ this.factoryContext = factoryContext;
2699
+ if (!composition) {
2700
+ this.registerRuntimeTargets(definition, factoryContext, this.environmentInjector);
2701
+ }
2702
+ this.view = createFragment(parent, before, childContext(componentRenderContext, {
2703
+ injector: composition ? parentInjector : this.environmentInjector,
2704
+ componentContext: factoryContext,
2705
+ }), composition ? [] : [], 'craft-component');
2706
+ this.effectRef = untracked(() => runInInjectionContext(composition ? parentInjector : this.environmentInjector, () => craftEffect('component-render', composition
2707
+ ? (onCleanup) => {
2708
+ // Composition providers and public inputs are the only
2709
+ // dependencies that should recreate the component scope.
2710
+ // Template signals (for example a query status) are tracked
2711
+ // by the dedicated template effect created by
2712
+ // refreshComposedComponent().
2713
+ this.propSources.forEach((source) => source());
2714
+ this.hostPropsSource();
2715
+ untracked(() => this.refreshComposedComponent(definition, args, componentElement, parentInjector, additionalProviders, hostTarget, context, componentRenderContext, onCleanup));
2716
+ this.providerTrackers.forEach((tracker) => tracker());
2717
+ }
2718
+ : () => {
2719
+ try {
2720
+ const callSiteHostProps = this.hostPropsSource();
2721
+ const hostProps = mergeHostProps(definition.meta.host ?? {}, callSiteHostProps);
2722
+ const renderContext = childContext(componentRenderContext, {
2723
+ injector: this.environmentInjector,
2724
+ componentContext: this.factoryContext,
2725
+ });
2726
+ this.traceState.renderCount += 1;
2727
+ this.latestTemplate = executeTemplateTrace(renderContext.injector, traceContext(renderContext, 'component', this.traceState.renderCount === 1
2728
+ ? 'initialRender'
2729
+ : 'update', definition.name), () => withCraftRenderContext(renderContext, () => definition.template(projectYieldableTemplateContext(this.factoryContext), callSiteHostProps)));
2730
+ this.view.patchChildren(this.latestTemplate);
2731
+ this.hostBindings?.patch(hostProps);
2732
+ context.exceptionBoundaryResolved?.();
2733
+ }
2734
+ catch (error) {
2735
+ if (isCraftGenShortCircuit(error) &&
2736
+ context.exceptionBoundary?.(error.exception)) {
2737
+ this.view.patchChildren([]);
2738
+ return;
2739
+ }
2740
+ if (isCraftGenShortCircuit(error)) {
2741
+ throw new CraftUnhandledExceptionError(error.exception);
2742
+ }
2743
+ throw error;
2744
+ }
2745
+ })));
2746
+ }
2747
+ firstNode() {
2748
+ return this.view.firstNode();
2749
+ }
2750
+ refreshComposedComponent(definition, args, componentElement, parentInjector, additionalProviders, hostTarget, context, componentRenderContext, onCleanup) {
2751
+ const composition = definition.composition;
2752
+ if (!composition) {
2753
+ return;
2754
+ }
2755
+ this.view.patchChildren([]);
2756
+ this.environmentInjector = createEnvironmentInjector([
2757
+ ...provideHostName(`component:${definition.name}`),
2758
+ {
2759
+ provide: CRAFT_HYDRATION_ID,
2760
+ useValue: componentRenderContext.identity,
2761
+ },
2762
+ ...(definition.meta.providers ?? []),
2763
+ ...(composition.providers ?? []),
2764
+ {
2765
+ provide: ElementRef,
2766
+ useValue: new ElementRef(componentElement),
2767
+ },
2768
+ ...additionalProviders,
2769
+ ], parentInjector, 'CraftComponent');
2770
+ if (!this.traceCreated) {
2771
+ this.traceCreated = true;
2772
+ traceComponentLifecycle(this.environmentInjector, 'create', definition.name, this.traceState.renderCount);
2773
+ }
2774
+ const environmentInjector = this.environmentInjector;
2775
+ let renderInjector = environmentInjector;
2776
+ onCleanup(() => {
2777
+ this.registrationReleases.forEach((release) => release());
2778
+ this.registrationReleases = [];
2779
+ this.composedTemplateEffect?.destroy();
2780
+ this.composedTemplateEffect = undefined;
2781
+ this.syncTemplateFlushRelease?.();
2782
+ this.syncTemplateFlushRelease = undefined;
2783
+ this.componentExceptionEffect?.destroy();
2784
+ this.componentExceptionEffect = undefined;
2785
+ this.componentFallbackVisible = false;
2786
+ this.componentFallbackException = undefined;
2787
+ this.view.patchChildren([]);
2788
+ renderInjector.destroy();
2789
+ if (renderInjector !== environmentInjector) {
2790
+ environmentInjector.destroy();
2791
+ }
2792
+ this.providerTrackers = [];
2793
+ if (this.environmentInjector === renderInjector) {
2794
+ this.environmentInjector = undefined;
2795
+ this.factoryContext = undefined;
2796
+ }
2797
+ });
2798
+ let factoryContext;
2799
+ let renderContext;
2800
+ const handledResourceExceptionCodes = new Set();
2801
+ const componentBoundary = composition.catchBlockPosition
2802
+ ? (exception) => {
2803
+ if (!composition.catchHandlers?.[exception._tag]) {
2804
+ return context.exceptionBoundary?.(exception) ?? false;
2805
+ }
2806
+ this.renderComposedException(definition, exception, renderInjector, renderContext, context, hostTarget);
2807
+ return true;
2808
+ }
2809
+ : undefined;
2810
+ renderContext = childContext(componentRenderContext, {
2811
+ injector: environmentInjector,
2812
+ handledResourceExceptionCodes,
2813
+ ...(componentBoundary
2814
+ ? {
2815
+ exceptionBoundary: componentBoundary,
2816
+ }
2817
+ : {}),
2818
+ });
2819
+ try {
2820
+ const providerResolution = eagerlyResolveBrandedProviders(composition.providers ?? [], environmentInjector);
2821
+ this.providerTrackers = providerResolution.trackers;
2822
+ if (providerResolution.exception) {
2823
+ renderContext = childContext(componentRenderContext, {
2824
+ injector: environmentInjector,
2825
+ });
2826
+ this.view.updateContext(renderContext);
2827
+ this.renderComposedException(definition, providerResolution.exception, environmentInjector, renderContext, context, hostTarget);
2828
+ return;
2829
+ }
2830
+ if (providerResolution.overrides.length > 0) {
2831
+ renderInjector = createEnvironmentInjector([...providerResolution.overrides], environmentInjector, 'CraftComponentProviders');
2832
+ this.environmentInjector = renderInjector;
2833
+ }
2834
+ renderContext = childContext(componentRenderContext, {
2835
+ injector: renderInjector,
2836
+ handledResourceExceptionCodes,
2837
+ });
2838
+ this.view.updateContext(renderContext);
2839
+ factoryContext = untracked(() => executeCraftComponentFactory(definition.factory, args, renderInjector));
2840
+ if (typeof factoryContext === 'object' &&
2841
+ factoryContext !== null &&
2842
+ 'then' in factoryContext) {
2843
+ throw new Error('Async component factories are not renderable directly. Move asynchronous work behind defer().');
2844
+ }
2845
+ this.registerRuntimeTargets(definition, factoryContext, renderInjector);
2846
+ }
2847
+ catch (error) {
2848
+ if (!isCraftGenShortCircuit(error)) {
2849
+ throw error;
2850
+ }
2851
+ if (this.context.exceptionBoundary?.(error.exception)) {
2852
+ return;
2853
+ }
2854
+ this.renderComposedException(definition, error.exception, renderInjector, renderContext, context, hostTarget);
2855
+ return;
2856
+ }
2857
+ if (isCraftException(factoryContext)) {
2858
+ this.renderComposedException(definition, factoryContext, renderInjector, renderContext, context, hostTarget);
2859
+ return;
2860
+ }
2861
+ this.composedTemplateEffect = untracked(() => runInInjectionContext(renderInjector, () => craftEffect('component-template', () => {
2862
+ this.renderComposedTemplate(definition, factoryContext, renderInjector, renderContext, context, hostTarget);
2863
+ if (composition.catchBlockPosition &&
2864
+ !this.componentExceptionEffect) {
2865
+ this.installComponentExceptionEffect(definition, factoryContext, renderInjector, renderContext, context, hostTarget);
2866
+ }
2867
+ })));
2868
+ this.syncTemplateFlushRelease?.();
2869
+ this.syncTemplateFlushRelease = registerCraftSyncTemplateFlush(() => {
2870
+ this.renderComposedTemplate(definition, factoryContext, renderInjector, renderContext, context, hostTarget);
2871
+ });
2872
+ }
2873
+ installComponentExceptionEffect(definition, factoryContext, renderInjector, renderContext, context, hostTarget) {
2874
+ this.componentExceptionEffect = untracked(() => runInInjectionContext(renderInjector, () => craftEffect('component-catch-block-resource-exceptions', () => {
2875
+ const exception = findResourceException(factoryContext);
2876
+ if (exception) {
2877
+ if (renderContext.handledResourceExceptionCodes?.has(exception._tag)) {
2878
+ if (this.componentFallbackVisible) {
2879
+ this.componentFallbackVisible = false;
2880
+ this.componentFallbackException = undefined;
2881
+ untracked(() => this.renderComposedTemplate(definition, factoryContext, renderInjector, renderContext, context, hostTarget));
2882
+ }
2883
+ return;
2884
+ }
2885
+ if (this.componentFallbackVisible &&
2886
+ this.componentFallbackException === exception) {
2887
+ return;
2888
+ }
2889
+ this.componentFallbackVisible = true;
2890
+ this.componentFallbackException = exception;
2891
+ untracked(() => this.renderComposedException(definition, exception, renderInjector, renderContext, context, hostTarget, true));
2892
+ }
2893
+ else if (this.componentFallbackVisible) {
2894
+ this.componentFallbackVisible = false;
2895
+ this.componentFallbackException = undefined;
2896
+ untracked(() => this.renderComposedTemplate(definition, factoryContext, renderInjector, renderContext, context, hostTarget));
2897
+ }
2898
+ }, { manualCleanup: true })));
2899
+ }
2900
+ registerRuntimeTargets(definition, factoryContext, injector) {
2901
+ this.registrationReleases.forEach((release) => release());
2902
+ this.registrationReleases = [
2903
+ ɵregisterCraftTarget(injector, this.component, factoryContext, componentHostName(injector, definition.name)),
2904
+ ...definition.styleOwners.flatMap((owner) => owner.registrationTarget === undefined
2905
+ ? []
2906
+ : [
2907
+ ɵregisterCraftTarget(injector, owner.registrationTarget, factoryContext, allocateCraftHostName(injector, 'directive', owner.name)),
2908
+ ]),
2909
+ ];
2910
+ }
2911
+ renderComposedTemplate(definition, factoryContext, renderInjector, renderContext, context, hostTarget) {
2912
+ if (this.componentFallbackVisible)
2913
+ return;
2914
+ try {
2915
+ this.traceState.renderCount += 1;
2916
+ const rendered = this.composedTemplateChildren(definition, factoryContext, renderContext);
2917
+ const children = this.withComponentFieldExceptionBlock(definition, rendered.children);
2918
+ this.latestTemplate = children;
2919
+ this.view.patchChildren(children);
2920
+ this.hostBindings?.patch(rendered.hostProps);
2921
+ }
2922
+ catch (error) {
2923
+ if (!isCraftGenShortCircuit(error))
2924
+ throw error;
2925
+ if (renderContext.exceptionBoundary?.(error.exception))
2926
+ return;
2927
+ this.renderComposedException(definition, error.exception, renderInjector, renderContext, context, hostTarget);
2928
+ return;
2929
+ }
2930
+ renderContext.exceptionBoundaryResolved?.();
2931
+ }
2932
+ composedTemplateChildren(definition, factoryContext, renderContext) {
2933
+ this.factoryContext = factoryContext;
2934
+ this.view.updateContext(childContext(renderContext, { componentContext: factoryContext }));
2935
+ const callSiteHostProps = this.hostPropsSource();
2936
+ const hostProps = mergeHostProps(definition.meta.host ?? {}, callSiteHostProps);
2937
+ return {
2938
+ children: executeTemplateTrace(renderContext.injector, traceContext(renderContext, 'component', this.traceState.renderCount === 1 ? 'initialRender' : 'update', definition.name), () => withCraftRenderContext(renderContext, () => definition.template(projectYieldableTemplateContext(factoryContext), callSiteHostProps))),
2939
+ hostProps,
2940
+ };
2941
+ }
2942
+ withComponentFieldExceptionBlock(definition, children) {
2943
+ const handlers = definition.composition?.fieldExceptionHandlers;
2944
+ const options = definition.composition?.fieldExceptionOptions;
2945
+ if (!handlers || !options)
2946
+ return children;
2947
+ return {
2948
+ kind: 'field-exception-block',
2949
+ source: children,
2950
+ handlers,
2951
+ options,
2952
+ };
2953
+ }
2954
+ renderComposedException(definition, exception, environmentInjector, renderContext, context, hostTarget, preserveFactoryContext = false) {
2955
+ const blockHandler = definition.composition?.catchHandlers?.[exception._tag];
2956
+ const tagHandler = definition.composition?.catchTagHandlers?.[exception._tag];
2957
+ if (!blockHandler && !tagHandler) {
2958
+ if (this.context.exceptionBoundary?.(exception)) {
2959
+ return;
2960
+ }
2961
+ throw new CraftUnhandledExceptionError(exception);
2962
+ }
2963
+ if (!preserveFactoryContext) {
2964
+ this.factoryContext = undefined;
2965
+ }
2966
+ this.view.updateContext(renderContext);
2967
+ const hostProps = mergeHostProps(definition.meta.host ?? {}, this.hostPropsSource());
2968
+ if (tagHandler) {
2969
+ try {
2970
+ executeCraftComponentFactory(function* () {
2971
+ return yield* tagHandler(exception);
2972
+ }, [], environmentInjector);
2973
+ }
2974
+ catch (error) {
2975
+ if (!isCraftGenShortCircuit(error))
2976
+ throw error;
2977
+ if (context.exceptionBoundary?.(error.exception))
2978
+ return;
2979
+ throw new CraftUnhandledExceptionError(error.exception);
2980
+ }
2981
+ this.view.patchChildren([]);
2982
+ }
2983
+ else {
2984
+ const resolved = runInInjectionContext(environmentInjector, () => resolveCatchBlockHandler(blockHandler, exception, false, definition.composition?.catchBlockPosition ?? 'after'));
2985
+ if (preserveFactoryContext && resolved.showSource) {
2986
+ try {
2987
+ const source = untracked(() => this.composedTemplateChildren(definition, this.factoryContext, renderContext));
2988
+ const children = resolved.position === 'before'
2989
+ ? [
2990
+ resolved.children,
2991
+ this.withComponentFieldExceptionBlock(definition, source.children),
2992
+ ]
2993
+ : [
2994
+ this.withComponentFieldExceptionBlock(definition, source.children),
2995
+ resolved.children,
2996
+ ];
2997
+ this.view.patchChildren(children);
2998
+ this.hostBindings?.patch(source.hostProps);
2999
+ }
3000
+ catch (error) {
3001
+ if (!isCraftGenShortCircuit(error))
3002
+ throw error;
3003
+ this.view.patchChildren(resolved.children);
3004
+ }
3005
+ }
3006
+ else {
3007
+ this.view.patchChildren(resolved.children);
3008
+ }
3009
+ }
3010
+ this.hostBindings?.patch(hostProps);
3011
+ }
3012
+ lastNode() {
3013
+ return this.view.lastNode();
3014
+ }
3015
+ patch(node) {
3016
+ if (node.kind !== 'component' ||
3017
+ node.component !== this.component ||
3018
+ (node.injector !== undefined && node.injector !== this.context.injector)) {
3019
+ return false;
3020
+ }
3021
+ const props = node.props;
3022
+ this.propKeys.forEach((key, index) => {
3023
+ this.propSources[index].set(props[key]);
3024
+ });
3025
+ this.hostPropsSource.set(hostPropsFromComponentProps(props));
3026
+ return true;
3027
+ }
3028
+ updateProps(props) {
3029
+ if (this.templateOnly) {
3030
+ return;
3031
+ }
3032
+ this.patch({
3033
+ kind: 'component',
3034
+ component: this.component,
3035
+ props,
3036
+ });
3037
+ }
3038
+ updateContext(context) {
3039
+ if (!this.templateOnly) {
3040
+ throw new Error('Cannot update the context of a logic-backed component.');
3041
+ }
3042
+ this.factoryContext = context;
3043
+ const definition = this.component[CRAFT_COMPONENT];
3044
+ const callSiteHostProps = this.hostPropsSource();
3045
+ const hostProps = mergeHostProps(definition.meta.host ?? {}, callSiteHostProps);
3046
+ this.traceState.renderCount += 1;
3047
+ this.latestTemplate = executeTemplateTrace(this.environmentInjector ?? this.context.injector, traceContext(this.componentRenderContext, 'component', this.traceState.renderCount === 1 ? 'initialRender' : 'update', definition.name), () => definition.template(projectYieldableTemplateContext(context), callSiteHostProps));
3048
+ this.view.patchChildren(this.latestTemplate);
3049
+ this.hostBindings?.patch(hostProps);
3050
+ }
3051
+ locator(host, tag, criteria) {
3052
+ return findCraftTemplateLocator(host, this.latestTemplate, tag, criteria);
3053
+ }
3054
+ destroy() {
3055
+ if (this.traceCreated) {
3056
+ traceComponentLifecycle(this.environmentInjector ?? this.context.injector, 'destroy', this.componentRenderContext.componentName ?? 'unknown', this.traceState.renderCount);
3057
+ this.traceCreated = false;
3058
+ }
3059
+ this.effectRef.destroy();
3060
+ this.hostBindings?.destroy();
3061
+ this.view.destroy();
3062
+ this.styleReleases.forEach((release) => release());
3063
+ this.environmentInjector?.destroy();
3064
+ }
3065
+ }
3066
+ class DeferRenderedNode {
3067
+ kind = 'defer';
3068
+ view;
3069
+ state = 'placeholder';
3070
+ destroyed = false;
3071
+ triggerCleanup;
3072
+ triggerTimer;
3073
+ loadedValue;
3074
+ loadError;
3075
+ node;
3076
+ parent;
3077
+ context;
3078
+ constructor(node, parent, before, context) {
3079
+ this.node = node;
3080
+ this.parent = parent;
3081
+ this.context = context;
3082
+ this.view = createFragment(parent, before, context, node.placeholder
3083
+ ? renderDeferChildren(context, 'placeholder', node.placeholder)
3084
+ : [], 'craft-defer');
3085
+ this.installTrigger();
3086
+ }
3087
+ firstNode() {
3088
+ return this.view.firstNode();
3089
+ }
3090
+ lastNode() {
3091
+ return this.view.lastNode();
3092
+ }
3093
+ patch(node) {
3094
+ if (node.kind !== 'defer' || node.loader !== this.node.loader) {
3095
+ return false;
3096
+ }
3097
+ this.node = node;
3098
+ if (this.state === 'loaded') {
3099
+ this.view.patchChildren(renderDeferChildren(this.context, 'resolve', this.node.resolve, [
3100
+ this.loadedValue,
3101
+ ]));
3102
+ }
3103
+ else if (this.state === 'error') {
3104
+ this.view.patchChildren(this.node.error
3105
+ ? renderDeferChildren(this.context, 'error', this.node.error, [
3106
+ this.loadError,
3107
+ ])
3108
+ : []);
3109
+ }
3110
+ else if (this.state === 'loading') {
3111
+ this.view.patchChildren(this.node.loading
3112
+ ? renderDeferChildren(this.context, 'loading', this.node.loading)
3113
+ : this.node.placeholder
3114
+ ? renderDeferChildren(this.context, 'placeholder', this.node.placeholder)
3115
+ : []);
3116
+ }
3117
+ else {
3118
+ this.view.patchChildren(this.node.placeholder
3119
+ ? renderDeferChildren(this.context, 'placeholder', this.node.placeholder)
3120
+ : []);
3121
+ }
3122
+ return true;
3123
+ }
3124
+ installTrigger() {
3125
+ if (this.node.trigger === 'immediate') {
3126
+ this.startLoad();
3127
+ return;
3128
+ }
3129
+ if (this.node.trigger === 'idle') {
3130
+ const windowRef = this.parent.ownerDocument?.defaultView;
3131
+ const idleWindow = windowRef;
3132
+ if (idleWindow?.requestIdleCallback) {
3133
+ const handle = idleWindow.requestIdleCallback(() => this.startLoad());
3134
+ this.triggerCleanup = () => idleWindow.cancelIdleCallback?.(handle);
3135
+ }
3136
+ else {
3137
+ this.triggerTimer = this.context.injector
3138
+ .get(CRAFT_TEMPORAL_RUNTIME, new RealCraftTemporalRuntime())
3139
+ .schedule(() => this.startLoad(), 0, {
3140
+ kind: 'defer-trigger',
3141
+ owner: this.context.componentName,
3142
+ });
3143
+ this.triggerCleanup = () => this.triggerTimer?.cancel();
3144
+ }
3145
+ return;
3146
+ }
3147
+ if (this.node.trigger === 'viewport' &&
3148
+ typeof IntersectionObserver !== 'undefined') {
3149
+ const target = this.firstElementInView();
3150
+ if (target) {
3151
+ const observer = new IntersectionObserver((entries) => {
3152
+ if (entries.some((entry) => entry.isIntersecting)) {
3153
+ observer.disconnect();
3154
+ this.startLoad();
3155
+ }
3156
+ });
3157
+ observer.observe(target);
3158
+ this.triggerCleanup = () => observer.disconnect();
3159
+ return;
3160
+ }
3161
+ }
3162
+ if (this.node.trigger === 'interaction') {
3163
+ const target = this.firstElementInView() ?? this.parent;
3164
+ const start = (event) => {
3165
+ if (event && event.type === 'keydown') {
3166
+ const key = event.key;
3167
+ if (key !== 'Enter' && key !== ' ')
3168
+ return;
3169
+ event.preventDefault();
3170
+ }
3171
+ this.startLoad();
3172
+ };
3173
+ if (isElementNode(target)) {
3174
+ const tag = target.tagName.toLowerCase();
3175
+ const interactive = tag === 'button' ||
3176
+ tag === 'a' ||
3177
+ tag === 'input' ||
3178
+ tag === 'select' ||
3179
+ tag === 'textarea';
3180
+ if (!interactive) {
3181
+ if (!target.hasAttribute('tabindex')) {
3182
+ this.context.renderer.setAttribute(target, 'tabindex', '0');
3183
+ }
3184
+ if (!target.getAttribute('role')) {
3185
+ this.context.renderer.setAttribute(target, 'role', 'button');
3186
+ }
3187
+ }
3188
+ }
3189
+ const clickCleanup = this.context.renderer.listen(target, 'click', start);
3190
+ const keyCleanup = this.context.renderer.listen(target, 'keydown', start);
3191
+ this.triggerCleanup = () => {
3192
+ clickCleanup();
3193
+ keyCleanup();
3194
+ };
3195
+ return;
3196
+ }
3197
+ this.triggerTimer = this.context.injector
3198
+ .get(CRAFT_TEMPORAL_RUNTIME, new RealCraftTemporalRuntime())
3199
+ .schedule(() => this.startLoad(), 0, {
3200
+ kind: 'defer-trigger',
3201
+ owner: this.context.componentName,
3202
+ });
3203
+ this.triggerCleanup = () => this.triggerTimer?.cancel();
3204
+ }
3205
+ firstElementInView() {
3206
+ let current = this.view.firstNode().nextSibling;
3207
+ while (current && current !== this.view.lastNode()) {
3208
+ if (current.nodeType === 1) {
3209
+ return current;
3210
+ }
3211
+ current = current.nextSibling;
3212
+ }
3213
+ return undefined;
3214
+ }
3215
+ setBusy(busy) {
3216
+ const target = this.firstElementInView();
3217
+ if (!target)
3218
+ return;
3219
+ if (busy) {
3220
+ this.context.renderer.setAttribute(target, 'aria-busy', 'true');
3221
+ }
3222
+ else {
3223
+ this.context.renderer.removeAttribute(target, 'aria-busy');
3224
+ }
3225
+ }
3226
+ startLoad() {
3227
+ if (this.state !== 'placeholder' || this.destroyed) {
3228
+ return;
3229
+ }
3230
+ this.state = 'loading';
3231
+ this.triggerCleanup?.();
3232
+ this.triggerCleanup = undefined;
3233
+ this.setBusy(true);
3234
+ this.view.patchChildren(this.node.loading
3235
+ ? renderDeferChildren(this.context, 'loading', this.node.loading)
3236
+ : this.node.placeholder
3237
+ ? renderDeferChildren(this.context, 'placeholder', this.node.placeholder)
3238
+ : []);
3239
+ const loader = this.node.loader;
3240
+ executeCraftComponentFactoryAsync(function* () {
3241
+ return yield* craftLazy(loader);
3242
+ }, [], this.context.injector)
3243
+ .then((settled) => {
3244
+ if (this.destroyed) {
3245
+ return;
3246
+ }
3247
+ if (settled.kind === 'shortCircuit') {
3248
+ this.state = 'error';
3249
+ this.loadError = settled.exception;
3250
+ this.view.patchChildren(this.node.error
3251
+ ? renderDeferChildren(this.context, 'error', this.node.error, [
3252
+ settled.exception,
3253
+ ])
3254
+ : []);
3255
+ this.setBusy(false);
3256
+ return;
3257
+ }
3258
+ this.state = 'loaded';
3259
+ this.loadedValue = settled.value;
3260
+ this.view.patchChildren(renderDeferChildren(this.context, 'resolve', this.node.resolve, [
3261
+ settled.value,
3262
+ ]));
3263
+ this.setBusy(false);
3264
+ })
3265
+ .catch((error) => {
3266
+ if (!this.destroyed) {
3267
+ this.state = 'error';
3268
+ this.loadError = error;
3269
+ this.view.patchChildren(this.node.error
3270
+ ? renderDeferChildren(this.context, 'error', this.node.error, [
3271
+ error,
3272
+ ])
3273
+ : []);
3274
+ this.setBusy(false);
3275
+ }
3276
+ });
3277
+ }
3278
+ destroy() {
3279
+ this.destroyed = true;
3280
+ this.triggerCleanup?.();
3281
+ this.view.destroy();
3282
+ }
3283
+ }
3284
+ class HeadingSectionRenderedNode {
3285
+ kind = 'heading-section';
3286
+ view;
3287
+ constructor(node, parent, before, context) {
3288
+ const level = node.reset ? 1 : Math.min(6, (context.headingLevel ?? 1) + 1);
3289
+ this.view = createFragment(parent, before, childContext(context, { headingLevel: level }), node.children, 'craft-heading-section');
3290
+ }
3291
+ firstNode() {
3292
+ return this.view.firstNode();
3293
+ }
3294
+ lastNode() {
3295
+ return this.view.lastNode();
3296
+ }
3297
+ patch(node) {
3298
+ if (node.kind !== 'heading-section')
3299
+ return false;
3300
+ this.view.patchChildren(node.children);
3301
+ return true;
3302
+ }
3303
+ destroy() {
3304
+ this.view.destroy();
3305
+ }
3306
+ }
3307
+ class HeadingRenderedNode {
3308
+ kind = 'heading';
3309
+ elementView;
3310
+ tag;
3311
+ constructor(node, parent, before, context) {
3312
+ const level = Math.min(6, Math.max(1, context.headingLevel ?? 1));
3313
+ this.tag = `h${level}`;
3314
+ const created = createElementForRender(this.tag, parent, before, context);
3315
+ this.elementView = new ElementRenderedNode(created.element, this.tag, created.context, {
3316
+ kind: 'element',
3317
+ tag: this.tag,
3318
+ props: node.props,
3319
+ children: node.children,
3320
+ });
3321
+ }
3322
+ firstNode() {
3323
+ return this.elementView.firstNode();
3324
+ }
3325
+ lastNode() {
3326
+ return this.elementView.lastNode();
3327
+ }
3328
+ patch(node) {
3329
+ if (node.kind !== 'heading')
3330
+ return false;
3331
+ return this.elementView.patch({
3332
+ kind: 'element',
3333
+ tag: this.tag,
3334
+ props: node.props,
3335
+ children: node.children,
3336
+ });
3337
+ }
3338
+ destroy() {
3339
+ this.elementView.destroy();
3340
+ }
3341
+ }
3342
+ function mountNode(node, parent, before, context) {
3343
+ switch (node.kind) {
3344
+ case 'text': {
3345
+ const created = createTextForRender(node.value, parent, before, context, true);
3346
+ return new TextRenderedNode(created.text, node.value, created.context.renderer);
3347
+ }
3348
+ case 'reactive-text': {
3349
+ const created = createTextForRender('', parent, before, context, false);
3350
+ return new ReactiveTextRenderedNode(created.text, node.binding, created.context);
3351
+ }
3352
+ case 'element': {
3353
+ const created = createElementForRender(node.tag, parent, before, context);
3354
+ if (created.context.rootScope) {
3355
+ created.context.renderer.setAttribute(created.element, 'data-craft-root', created.context.rootScope);
3356
+ }
3357
+ if (created.context.contentScope) {
3358
+ created.context.renderer.setAttribute(created.element, 'data-craft-content', created.context.contentScope);
3359
+ }
3360
+ return new ElementRenderedNode(created.element, node.tag, created.context, node);
3361
+ }
3362
+ case 'component':
3363
+ return new ComponentRenderedNode(node.component, node.props, parent, before, node.injector
3364
+ ? childContext(context, { injector: node.injector })
3365
+ : context, undefined, undefined, [], node.declarationContext);
3366
+ case 'directive':
3367
+ return new CraftDirectiveRenderedNode(node, parent, before, context);
3368
+ case 'each': {
3369
+ const start = mountComment(parent, 'craft-each:start', before, context);
3370
+ const end = mountComment(parent, 'craft-each:end', before, context);
3371
+ return new EachRenderedNode(node, parent, start, end, context);
3372
+ }
3373
+ case 'if': {
3374
+ return new IfBlockRenderedNode(node, parent, before, context);
3375
+ }
3376
+ case 'heading': {
3377
+ return new HeadingRenderedNode(node, parent, before, context);
3378
+ }
3379
+ case 'heading-section': {
3380
+ return new HeadingSectionRenderedNode(node, parent, before, context);
3381
+ }
3382
+ case 'pending-block': {
3383
+ return new PendingBlockRenderedNode(node, parent, before, context);
3384
+ }
3385
+ case 'catch-block': {
3386
+ return new CatchBlockRenderedNode(node, parent, before, context);
3387
+ }
3388
+ case 'field-exception-block': {
3389
+ return new FieldExceptionBlockRenderedNode(node, parent, before, context);
3390
+ }
3391
+ case 'match-block': {
3392
+ return new MatchBlockRenderedNode(node, parent, before, context);
3393
+ }
3394
+ case 'defer':
3395
+ return new DeferRenderedNode(node, parent, before, context);
3396
+ case 'projection':
3397
+ return new ProjectionRenderedNode(node, parent, before, context);
3398
+ case 'template':
3399
+ return new TemplateRenderedNode(node, parent, before, context);
3400
+ }
3401
+ }
3402
+ class HostPropertyBindings {
3403
+ props = {};
3404
+ bindings = new Map();
3405
+ host;
3406
+ context;
3407
+ constructor(host, context) {
3408
+ this.host = host;
3409
+ this.context = context;
3410
+ }
3411
+ patch(next) {
3412
+ const renderer = this.context.renderer;
3413
+ const previousAttributes = flattenAttributes(this.props);
3414
+ const nextAttributes = flattenAttributes(next);
3415
+ for (const key of previousAttributes.keys()) {
3416
+ if (!nextAttributes.has(key)) {
3417
+ this.destroyBinding(`attribute:${key}`);
3418
+ applyAttribute(renderer, this.host, key, null, this.context);
3419
+ }
3420
+ }
3421
+ for (const [key, value] of nextAttributes) {
3422
+ if (typeof value === 'function') {
3423
+ this.updateBinding(`attribute:${key}`, value, () => resolveTemplateValue(value, this.context), (resolved) => applyAttribute(renderer, this.host, key, resolved, this.context));
3424
+ }
3425
+ else {
3426
+ this.destroyBinding(`attribute:${key}`);
3427
+ if (!Object.is(previousAttributes.get(key), value) ||
3428
+ typeof previousAttributes.get(key) === 'function') {
3429
+ applyAttribute(renderer, this.host, key, value, this.context);
3430
+ }
3431
+ }
3432
+ }
3433
+ if (containsRenderBinding(next['class'])) {
3434
+ this.updateBinding('class', next['class'], () => className(next['class'], this.context), (value) => {
3435
+ if (value) {
3436
+ renderer.setAttribute(this.host, 'class', String(value));
3437
+ }
3438
+ else {
3439
+ renderer.removeAttribute(this.host, 'class');
3440
+ }
3441
+ });
3442
+ }
3443
+ else {
3444
+ this.destroyBinding('class');
3445
+ if (!Object.is(this.props['class'], next['class']) ||
3446
+ containsRenderBinding(this.props['class'])) {
3447
+ const value = className(next['class'], this.context);
3448
+ if (value) {
3449
+ renderer.setAttribute(this.host, 'class', value);
3450
+ }
3451
+ else {
3452
+ renderer.removeAttribute(this.host, 'class');
3453
+ }
3454
+ }
3455
+ }
3456
+ if (containsRenderBinding(next['style'])) {
3457
+ this.updateBinding('style', next['style'], () => resolveStyleBindingValue(next['style'], this.context), (value) => {
3458
+ renderer.removeAttribute(this.host, 'style');
3459
+ applyStyles(renderer, this.host, undefined, value, this.context);
3460
+ }, sameStyleValue);
3461
+ }
3462
+ else {
3463
+ this.destroyBinding('style');
3464
+ if (!Object.is(this.props['style'], next['style']) ||
3465
+ containsRenderBinding(this.props['style'])) {
3466
+ if (containsRenderBinding(this.props['style'])) {
3467
+ renderer.removeAttribute(this.host, 'style');
3468
+ }
3469
+ applyStyles(renderer, this.host, containsRenderBinding(this.props['style'])
3470
+ ? undefined
3471
+ : this.props['style'], next['style'], this.context);
3472
+ }
3473
+ }
3474
+ this.props = next;
3475
+ }
3476
+ destroy() {
3477
+ this.bindings.forEach(({ effectRef }) => effectRef.destroy());
3478
+ this.bindings.clear();
3479
+ }
3480
+ updateBinding(key, source, evaluate, apply, equals = Object.is) {
3481
+ const current = this.bindings.get(key);
3482
+ if (current?.source === source)
3483
+ return;
3484
+ current?.effectRef.destroy();
3485
+ let initialized = false;
3486
+ let previous;
3487
+ const effectRef = createRenderEffect(this.context, `host-${key}-binding`, () => {
3488
+ const value = evaluate();
3489
+ if (!initialized || !equals(previous, value)) {
3490
+ apply(value);
3491
+ previous = value;
3492
+ initialized = true;
3493
+ }
3494
+ });
3495
+ this.bindings.set(key, { source, effectRef });
3496
+ }
3497
+ destroyBinding(key) {
3498
+ this.bindings.get(key)?.effectRef.destroy();
3499
+ this.bindings.delete(key);
3500
+ }
3501
+ }
3502
+ export function mountInterpretedComponent(component, host, injector, props) {
3503
+ const craftInjector = ɵcraftInjectorFromHost(injector);
3504
+ const renderer = createBrowserDomAdapter(host.ownerDocument);
3505
+ const rootNode = host.getRootNode();
3506
+ const styleRoot = (typeof Document !== 'undefined' && rootNode instanceof Document) ||
3507
+ (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot)
3508
+ ? rootNode
3509
+ : (host.ownerDocument ?? document);
3510
+ const styles = craftInjector.get(CRAFT_STYLE_REGISTRY, ɵfallbackCraftStyleRegistry);
3511
+ return mountInterpretedComponentWithOptions(component, host, craftInjector, props, { renderer, styleRoot, styles });
3512
+ }
3513
+ export function mountInterpretedComponentWithOptions(component, host, injector, props, options) {
3514
+ const craftInjector = ɵcraftInjectorFromHost(injector);
3515
+ const identity = options.identity ??
3516
+ createCraftRenderIdentity([component[CRAFT_COMPONENT].name, 0]);
3517
+ let instance;
3518
+ try {
3519
+ instance = new ComponentRenderedNode(component, props, host, null, {
3520
+ renderer: options.renderer,
3521
+ injector: craftInjector,
3522
+ mode: options.mode ?? 'create',
3523
+ identity,
3524
+ hydration: options.hydration,
3525
+ emitHydrationKeys: options.emitHydrationKeys,
3526
+ styleRoot: options.styleRoot,
3527
+ styles: options.styles,
3528
+ ssr: options.ssr,
3529
+ serverStyles: options.serverStyles,
3530
+ }, host);
3531
+ }
3532
+ catch (error) {
3533
+ if (isCraftGenShortCircuit(error)) {
3534
+ throw new CraftUnhandledExceptionError(error.exception);
3535
+ }
3536
+ throw error;
3537
+ }
3538
+ return {
3539
+ updateProps(nextProps) {
3540
+ instance.updateProps(nextProps);
3541
+ },
3542
+ destroy() {
3543
+ instance.destroy();
3544
+ },
3545
+ };
3546
+ }
3547
+ export function mountInterpretedComponentTemplate(component, host, injector, context, additionalProviders = []) {
3548
+ const craftInjector = ɵcraftInjectorFromHost(injector);
3549
+ const renderer = createBrowserDomAdapter(host.ownerDocument);
3550
+ const rootNode = host.getRootNode();
3551
+ const styleRoot = (typeof Document !== 'undefined' && rootNode instanceof Document) ||
3552
+ (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot)
3553
+ ? rootNode
3554
+ : (host.ownerDocument ?? document);
3555
+ const styles = craftInjector.get(CRAFT_STYLE_REGISTRY, ɵfallbackCraftStyleRegistry);
3556
+ let instance;
3557
+ try {
3558
+ instance = new ComponentRenderedNode(component, {}, host, null, {
3559
+ renderer,
3560
+ injector: craftInjector,
3561
+ styleRoot,
3562
+ styles,
3563
+ mode: 'create',
3564
+ identity: createCraftRenderIdentity([
3565
+ component[CRAFT_COMPONENT].name,
3566
+ 0,
3567
+ ]),
3568
+ }, host, { value: context }, additionalProviders);
3569
+ }
3570
+ catch (error) {
3571
+ if (isCraftGenShortCircuit(error)) {
3572
+ throw new CraftUnhandledExceptionError(error.exception);
3573
+ }
3574
+ throw error;
3575
+ }
3576
+ return {
3577
+ updateContext(nextContext) {
3578
+ instance.updateContext(nextContext);
3579
+ },
3580
+ locator(tag, criteria) {
3581
+ return instance.locator(host, tag, criteria);
3582
+ },
3583
+ destroy() {
3584
+ instance.destroy();
3585
+ },
3586
+ };
3587
+ }
3588
+ //# sourceMappingURL=interpreter.js.map