1const RUNTIME_STUB: &str = r#"(() => {
2 "use strict";
3
4 const MANIFEST_ELEMENT_ID = "presolve-template-manifest";
5 const COMPUTED_ARTIFACT_ELEMENT_ID = "presolve-computed-runtime";
6 const EFFECT_ARTIFACT_ELEMENT_ID = "presolve-effect-runtime";
7 const CONTEXT_ARTIFACT_ELEMENT_ID = "presolve-context-runtime";
8 const COMPONENT_ARTIFACT_ELEMENT_ID = "presolve-component-runtime";
9 const FORMS_ARTIFACT_ELEMENT_ID = "presolve-forms-runtime";
10 const RESOURCES_ARTIFACT_ELEMENT_ID = "presolve-resources-runtime";
11 const OPAQUE_ARTIFACT_ELEMENT_ID = "presolve-opaque-runtime";
12 const RESUME_MANIFEST_ELEMENT_ID = "presolve-resume-runtime";
13 const RESUME_SNAPSHOT_ELEMENT_ID = "presolve-resume-snapshot";
14 const PRODUCTION_RUNTIME_ELEMENT_ID = "presolve-production-runtime";
15 const RUNTIME_VERSION = "0.0.0";
16 const SUPPORTED_SCHEMA_VERSION = 5;
17 const ACTION_MANIFEST_SCHEMA_VERSION = 2;
18 const FORMS_MANIFEST_SCHEMA_VERSION = 3;
19 const LEGACY_MANIFEST_SCHEMA_VERSION = 1;
20 const SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION = 12;
21 const SUPPORTED_EFFECT_ARTIFACT_SCHEMA_VERSION = __EZ_EFFECT_SCHEMA_VERSION__;
22 const SUPPORTED_CONTEXT_ARTIFACT_SCHEMA_VERSION = 2;
23 const SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION = __EZ_COMPONENT_SCHEMA_VERSION__;
24 const LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION = 2;
25 const SUPPORTED_FORMS_ARTIFACT_SCHEMA_VERSION = 2;
26 const SUPPORTED_RESOURCES_ARTIFACT_SCHEMA_VERSION = 3;
27 const SUPPORTED_OPAQUE_ARTIFACT_SCHEMA_VERSION = 1;
28 const SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION = 7;
29 const SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION = 2;
30 const SUPPORTED_RESUME_RUNTIME_PROTOCOL_VERSION = 1;
31 const RESUME_REGISTRY_CONTRACT_VERSION = 1;
32
33 class PresolveBootError extends Error {
34 constructor(code) {
35 super(code);
36 this.name = "PresolveBootError";
37 this.code = code;
38 }
39 }
40
41 function createDiagnostic(code, message, detail, fatal = false) {
42 return {
43 code,
44 message,
45 detail,
46 fatal
47 };
48 }
49
50 function reportDiagnostic(diagnostics, code, message, detail, fatal = false) {
51 const diagnostic = createDiagnostic(code, message, detail, fatal);
52 diagnostics.push(diagnostic);
53 console.error(`[Presolve] ${code}`, diagnostic);
54 return diagnostic;
55 }
56
57 class ResumeBootError extends Error {
58 constructor(failure, detail = {}) {
59 super(failure);
60 this.name = "ResumeBootError";
61 this.failure = failure;
62 this.detail = detail;
63 }
64 }
65
66 const resumeBootstrapState = {
67 phase: "idle",
68 manifest: null,
69 registry: null,
70 result: null,
71 debug: []
72 };
73
74 function exactObjectKeys(value, expected) {
75 if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
76 const actual = Object.keys(value).sort();
77 const canonical = [...expected].sort();
78 return actual.length === canonical.length
79 && actual.every((key, index) => key === canonical[index]);
80 }
81
82 function readResumeManifest(diagnostics) {
83 const element = document.getElementById(RESUME_MANIFEST_ELEMENT_ID);
84 if (!(element instanceof HTMLScriptElement)) {
85 reportDiagnostic(diagnostics, "PSR_RESUME_MANIFEST_MISSING", "Resume manifest v7 is missing", { artifactElementId: RESUME_MANIFEST_ELEMENT_ID });
86 throw new ResumeBootError("ManifestVersionMismatch");
87 }
88 try {
89 return JSON.parse(element.textContent ?? "");
90 } catch (error) {
91 reportDiagnostic(diagnostics, "PSR_RESUME_MANIFEST_PARSE", "Resume manifest v7 could not be parsed", { message: error instanceof Error ? error.message : String(error) });
92 throw new ResumeBootError("ManifestVersionMismatch");
93 }
94 }
95
96 function readProductionRuntimeArtifact() {
97 const element = document.getElementById(PRODUCTION_RUNTIME_ELEMENT_ID);
98 if (element === null) return null;
99 if (!(element instanceof HTMLScriptElement)) throw new ResumeBootError("ProductionArtifactMismatch");
100 let artifact;
101 try { artifact = JSON.parse(element.textContent ?? ""); } catch (_) { throw new ResumeBootError("ProductionArtifactMismatch"); }
102 if (artifact === null || typeof artifact !== "object"
103 || artifact.schemaVersion !== 1 || artifact.runtimeProtocolVersion !== 1
104 || !Array.isArray(artifact.tables?.tables) || !Array.isArray(artifact.chunks)) {
105 throw new ResumeBootError("ProductionArtifactMismatch");
106 }
107 return artifact;
108 }
109
110 function productionOrdinalIndexes(artifact) {
111 if (artifact === null) return null;
112 const indexes = new Map();
113 for (const table of artifact.tables.tables) {
114 if (typeof table.table_kind !== "string" || !Array.isArray(table.mappings)) {
115 throw new ResumeBootError("ProductionArtifactMismatch");
116 }
117 const values = new Map();
118 for (const mapping of table.mappings) {
119 if (typeof mapping.canonical_id !== "string" || !Number.isInteger(mapping.ordinal)
120 || mapping.ordinal < 0 || values.has(mapping.canonical_id)) {
121 throw new ResumeBootError("ProductionArtifactMismatch");
122 }
123 values.set(mapping.canonical_id, mapping.ordinal);
124 }
125 indexes.set(table.table_kind, values);
126 }
127 for (const required of ["anchors", "events", "activations", "activation_roots"]) {
128 if (!indexes.has(required)) throw new ResumeBootError("ProductionArtifactMismatch");
129 }
130 return indexes;
131 }
132
133 function readOptionalResumeSnapshot(diagnostics, explicitSnapshot) {
134 if (explicitSnapshot !== undefined) return explicitSnapshot;
135 if (window.__PRESOLVE_RESUME_SNAPSHOT__ !== undefined) {
136 return window.__PRESOLVE_RESUME_SNAPSHOT__;
137 }
138 const element = document.getElementById(RESUME_SNAPSHOT_ELEMENT_ID);
139 if (element === null) return null;
140 if (!(element instanceof HTMLScriptElement)) {
141 reportDiagnostic(diagnostics, "PSR_RESUME_SNAPSHOT_PARSE", "Resume snapshot was not stored in a JSON script element", { artifactElementId: RESUME_SNAPSHOT_ELEMENT_ID });
142 throw new ResumeBootError("SnapshotParseFailure");
143 }
144 try {
145 return JSON.parse(element.textContent ?? "");
146 } catch (error) {
147 reportDiagnostic(diagnostics, "PSR_RESUME_SNAPSHOT_PARSE", "Resume snapshot v2 could not be parsed", { message: error instanceof Error ? error.message : String(error) });
148 throw new ResumeBootError("SnapshotParseFailure");
149 }
150 }
151
152 function uniqueRecordIndex(records, key, failure = "DuplicateIdentity") {
153 if (!Array.isArray(records)) throw new ResumeBootError("ResumeArtifactMismatch");
154 const index = new Map();
155 for (const record of records) {
156 const id = record?.[key];
157 if (typeof id !== "string" || index.has(id)) throw new ResumeBootError(failure, { id });
158 index.set(id, record);
159 }
160 return index;
161 }
162
163 function validateResumeManifest(manifest) {
164 const topLevelKeys = [
165 "schema_version", "build_id", "snapshot_schema_version", "runtime_protocol_version",
166 "application_root_boundary_id", "boundaries", "slot_schemas", "capture_programs",
167 "restore_programs", "chunks", "activations", "anchors", "events",
168 "phase_i_component_resume_records", "phase_i_form_resume_records"
169 ];
170 if (!exactObjectKeys(manifest, topLevelKeys)) throw new ResumeBootError("ResumeArtifactMismatch");
171 if (manifest.schema_version !== SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION) {
172 throw new ResumeBootError("ManifestVersionMismatch");
173 }
174 if (manifest.snapshot_schema_version !== SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION) {
175 throw new ResumeBootError("SnapshotSchemaMismatch");
176 }
177 if (manifest.runtime_protocol_version !== SUPPORTED_RESUME_RUNTIME_PROTOCOL_VERSION) {
178 throw new ResumeBootError("RuntimeProtocolMismatch");
179 }
180 if (typeof manifest.build_id !== "string" || !/^resume-build:[0-9a-f]{64}$/.test(manifest.build_id)) {
181 throw new ResumeBootError("ResumeArtifactMismatch");
182 }
183 const boundaries = uniqueRecordIndex(manifest.boundaries, "boundary_id");
184 const slots = uniqueRecordIndex(manifest.slot_schemas, "slot_id");
185 const capturePrograms = uniqueRecordIndex(manifest.capture_programs, "program_id");
186 const restorePrograms = uniqueRecordIndex(manifest.restore_programs, "program_id");
187 const chunks = uniqueRecordIndex(manifest.chunks, "chunk_id");
188 const activations = uniqueRecordIndex(manifest.activations, "activation_id");
189 const anchors = uniqueRecordIndex(manifest.anchors, "anchor_id");
190 const events = uniqueRecordIndex(manifest.events, "resume_event_id");
191 if (!boundaries.has(manifest.application_root_boundary_id)) {
192 throw new ResumeBootError("UnknownBoundary");
193 }
194 for (const boundary of boundaries.values()) {
195 if (!capturePrograms.has(boundary.capture_program_id)
196 || !restorePrograms.has(boundary.restore_program_id)
197 || (boundary.parent_boundary_id !== undefined && !boundaries.has(boundary.parent_boundary_id))
198 || !boundary.child_boundary_ids.every((id) => boundaries.has(id))
199 || !boundary.anchor_ids.every((id) => anchors.has(id))
200 || !boundary.event_ids.every((id) => events.has(id))) {
201 throw new ResumeBootError("ResumeArtifactMismatch", { boundary: boundary.boundary_id });
202 }
203 }
204 for (const slot of slots.values()) {
205 if (!boundaries.has(slot.owner_boundary_id)) throw new ResumeBootError("UnknownBoundary");
206 }
207 for (const activation of activations.values()) {
208 if (!boundaries.has(activation.boundary_id)
209 || !chunks.has(activation.chunk_id)
210 || !activation.prerequisite_boundary_ids.every((id) => boundaries.has(id))
211 || (activation.event_id !== undefined && !events.has(activation.event_id))) {
212 throw new ResumeBootError("ResumeArtifactMismatch", { activation: activation.activation_id });
213 }
214 }
215 for (const anchor of anchors.values()) {
216 if (!boundaries.has(anchor.boundary_id)) throw new ResumeBootError("UnknownBoundary");
217 }
218 for (const event of events.values()) {
219 if (!anchors.has(event.exact_target_anchor_id)
220 || !boundaries.has(event.owner_boundary_id)
221 || !chunks.has(event.chunk_id)) {
222 throw new ResumeBootError("ResumeArtifactMismatch", { event: event.resume_event_id });
223 }
224 }
225 return { boundaries, slots, capturePrograms, restorePrograms, chunks, activations, anchors, events };
226 }
227
228 function validateResumeSnapshot(snapshot, manifest, definitions) {
229 if (!exactObjectKeys(snapshot, ["schemaVersion", "buildId", "snapshotId", "manifestVersion", "capturedAt", "boundaries", "structuralOccurrences"])) {
230 throw new ResumeBootError("SnapshotSchemaMismatch");
231 }
232 if (snapshot.schemaVersion !== SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION
233 || snapshot.manifestVersion !== SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION
234 || snapshot.capturedAt !== null
235 || typeof snapshot.snapshotId !== "string") {
236 throw new ResumeBootError("SnapshotSchemaMismatch");
237 }
238 if (snapshot.buildId !== manifest.build_id) throw new ResumeBootError("BuildIdMismatch");
239 const seenBoundaries = new Set();
240 const seenSlots = new Set();
241 if (!Array.isArray(snapshot.boundaries) || !Array.isArray(snapshot.structuralOccurrences)) {
242 throw new ResumeBootError("SnapshotSchemaMismatch");
243 }
244 for (const boundary of snapshot.boundaries) {
245 if (!exactObjectKeys(boundary, ["boundaryId", "schemaId", "values"])) {
246 throw new ResumeBootError("SnapshotSchemaMismatch");
247 }
248 const definition = definitions.boundaries.get(boundary.boundaryId);
249 if (definition === undefined) throw new ResumeBootError("UnknownBoundary");
250 if (seenBoundaries.has(boundary.boundaryId)) throw new ResumeBootError("DuplicateIdentity");
251 if (boundary.schemaId !== definition.schema_id || !Array.isArray(boundary.values)) {
252 throw new ResumeBootError("ResumeArtifactMismatch");
253 }
254 seenBoundaries.add(boundary.boundaryId);
255 for (const value of boundary.values) {
256 if (!exactObjectKeys(value, ["valueRecordId", "slotId", "value"])) {
257 throw new ResumeBootError("SnapshotSchemaMismatch");
258 }
259 const slot = definitions.slots.get(value.slotId);
260 if (slot === undefined) throw new ResumeBootError("UnknownSlot");
261 if (slot.owner_boundary_id !== boundary.boundaryId || seenSlots.has(value.slotId)) {
262 throw new ResumeBootError("DuplicateIdentity");
263 }
264 seenSlots.add(value.slotId);
265 }
266 }
267 const structuralOccurrences = new Map();
268 for (const occurrence of snapshot.structuralOccurrences) {
269 if (!exactObjectKeys(occurrence, ["occurrenceIdentity", "templateInstance", "parentScope", "structuralRegion", "localOccurrence", "state"])
270 || typeof occurrence.occurrenceIdentity !== "string"
271 || typeof occurrence.templateInstance !== "string"
272 || typeof occurrence.parentScope !== "string"
273 || typeof occurrence.structuralRegion !== "string"
274 || typeof occurrence.localOccurrence !== "string"
275 || !Array.isArray(occurrence.state)
276 || structuralOccurrences.has(occurrence.occurrenceIdentity)) {
277 throw new ResumeBootError("SnapshotSchemaMismatch");
278 }
279 const stateSlots = new Set();
280 for (const state of occurrence.state) {
281 if (!exactObjectKeys(state, ["slotId", "value"])
282 || typeof state.slotId !== "string"
283 || stateSlots.has(state.slotId)) {
284 throw new ResumeBootError("SnapshotSchemaMismatch");
285 }
286 stateSlots.add(state.slotId);
287 }
288 structuralOccurrences.set(occurrence.occurrenceIdentity, occurrence);
289 }
290 return { seenBoundaries, seenSlots, structuralOccurrences };
291 }
292
293 function allocateResumeRegistry(manifest, definitions) {
294 return {
295 contract_version: RESUME_REGISTRY_CONTRACT_VERSION,
296 build_id: manifest.build_id,
297 definitions,
298 boundary_records: new Map(),
299 slot_values: new Map(),
300 context_bindings: new Map(),
301 component_records: new Map(),
302 form_records: new Map(),
303 structural_records: new Map(),
304 structural_occurrences: new Map(),
305 effect_subscriptions: new Map(),
306 activation_states: new Map(),
307 debug: []
308 };
309 }
310
311 function resumeDebugEvidence(result) {
312 return {
313 contract_version: RESUME_REGISTRY_CONTRACT_VERSION,
314 mode: result.mode,
315 failure: result.failure ?? null,
316 build_id: resumeBootstrapState.manifest?.build_id ?? null,
317 boundary_ids: resumeBootstrapState.registry === null
318 ? []
319 : [...resumeBootstrapState.registry.definitions.boundaries.keys()],
320 slot_ids: resumeBootstrapState.registry === null
321 ? []
322 : [...resumeBootstrapState.registry.definitions.slots.keys()]
323 };
324 }
325
326 async function bootstrapResume(input = {}) {
327 if (resumeBootstrapState.phase !== "idle") {
328 throw new ResumeBootError("DoubleBootstrap");
329 }
330 resumeBootstrapState.phase = "booting";
331 const diagnostics = input.diagnostics ?? [];
332 let snapshot = null;
333 let coldAttempted = false;
334 try {
335 const manifest = input.resumeManifest ?? readResumeManifest(diagnostics);
336 const definitions = validateResumeManifest(manifest);
337 snapshot = readOptionalResumeSnapshot(diagnostics, input.snapshot);
338 resumeBootstrapState.manifest = manifest;
339 if (snapshot === null) {
340 coldAttempted = true;
341 const cold = await input.coldBoot?.("NoSnapshot");
342 const result = { mode: "cold", failure: null, cold };
343 resumeBootstrapState.phase = "ready";
344 resumeBootstrapState.result = result;
345 resumeBootstrapState.debug = [resumeDebugEvidence(result)];
346 return result;
347 }
348 validateResumeSnapshot(snapshot, manifest, definitions);
349 resumeBootstrapState.registry = allocateResumeRegistry(manifest, definitions);
350 const resume = await input.resumeBoot?.({
351 manifest,
352 snapshot,
353 registry: resumeBootstrapState.registry
354 });
355 const result = {
356 mode: "resume",
357 failure: null,
358 registry: resumeBootstrapState.registry,
359 resume
360 };
361 resumeBootstrapState.phase = "ready";
362 resumeBootstrapState.result = result;
363 resumeBootstrapState.debug = [resumeDebugEvidence(result)];
364 return result;
365 } catch (error) {
366 if (coldAttempted) {
367 resumeBootstrapState.phase = "failed";
368 throw error;
369 }
370 const failure = error instanceof ResumeBootError ? error.failure : "RestoreInstructionFailure";
371 resumeBootstrapState.registry = null;
372 resumeBootstrapState.debug = [{ contract_version: RESUME_REGISTRY_CONTRACT_VERSION, mode: "cold", failure }];
373 try {
374 coldAttempted = true;
375 const cold = await input.coldBoot?.(failure);
376 const result = { mode: "cold", failure, cold };
377 resumeBootstrapState.phase = "ready";
378 resumeBootstrapState.result = result;
379 return result;
380 } catch (coldError) {
381 resumeBootstrapState.phase = "failed";
382 throw coldError;
383 }
384 }
385 }
386
387 function captureSnapshot() {
388 if (resumeBootstrapState.phase !== "ready" || resumeBootstrapState.manifest === null) {
389 return { ok: false, failure: "NotQuiescent" };
390 }
391 return { ok: false, failure: "NotQuiescent" };
392 }
393
394 async function activateBoundary(boundaryId) {
395 const registry = resumeBootstrapState.registry;
396 if (registry === null || !registry.definitions.boundaries.has(boundaryId)) {
397 throw new ResumeBootError("UnknownBoundary", { boundaryId });
398 }
399 const activation = [...registry.definitions.activations.values()]
400 .find((record) => record.boundary_id === boundaryId);
401 if (activation === undefined) throw new ResumeBootError("ActivationFailure", { boundaryId });
402 const prior = registry.activation_states.get(activation.activation_id);
403 if (prior?.status === "active") return prior.result;
404 if (prior?.status === "failed") throw new ResumeBootError("ActivationFailure", { boundaryId });
405 if (prior?.promise !== undefined) return prior.promise;
406 const chunk = registry.definitions.chunks.get(activation.chunk_id);
407 if (chunk === undefined || typeof chunk.module_path !== "string") {
408 throw new ResumeBootError("ActivationFailure", { boundaryId });
409 }
410 const promise = import(new URL(chunk.module_path, document.baseURI).href)
411 .then(() => {
412 const result = { boundary_id: boundaryId, activation_id: activation.activation_id, chunk_id: activation.chunk_id, status: "active" };
413 registry.activation_states.set(activation.activation_id, { status: "active", result });
414 return result;
415 })
416 .catch(() => {
417 registry.activation_states.set(activation.activation_id, { status: "failed" });
418 throw new ResumeBootError("ActivationFailure", { boundaryId });
419 });
420 registry.activation_states.set(activation.activation_id, { status: "loading", promise });
421 return promise;
422 }
423
424 async function activateByEvent(resumeEventId) {
425 const registry = resumeBootstrapState.registry;
426 if (registry === null || !registry.definitions.events.has(resumeEventId)) {
427 throw new ResumeBootError("ActivationFailure", { resumeEventId });
428 }
429 const activation = [...registry.definitions.activations.values()]
430 .find((record) => record.event_id === resumeEventId);
431 if (activation === undefined) throw new ResumeBootError("ActivationFailure", { resumeEventId });
432 return activateBoundary(activation.boundary_id);
433 }
434
435 function resumeEventMarker(target) {
436 let current = target instanceof Element ? target : target?.parentElement;
437 while (current !== null && current !== undefined) {
438 const marker = current.getAttribute("data-presolve-e");
439 if (marker !== null) return marker;
440 current = current.parentElement;
441 }
442 return null;
443 }
444
445 function installResumeActivationListeners(registry, store) {
446 const events = new Map([...registry.definitions.events.values()].map((event) => [event.resume_event_id, event]));
447 const eventTypes = new Set([...events.values()].map((event) => event.event_type));
448 for (const eventType of eventTypes) {
449 document.addEventListener(eventType, async (event) => {
450 const target = event.target instanceof Element ? event.target : event.target?.parentElement;
451 const type = event.type;
452 const marker = resumeEventMarker(target);
453 if (marker === null) return;
454 const record = events.get(marker);
455 if (record === undefined || record.event_type !== type) return;
456 try {
457 await activateByEvent(marker);
458 dispatchOrdinaryInstanceEvent(store, { target, type });
459 } catch (error) {
460 registry.debug.push({ event_id: marker, failure: error instanceof ResumeBootError ? error.failure : "ActivationFailure" });
461 }
462 });
463 }
464 }
465
466 function readManifest(diagnostics) {
467 const element = document.getElementById(MANIFEST_ELEMENT_ID);
468
469 if (!(element instanceof HTMLScriptElement)) {
470 reportDiagnostic(
471 diagnostics,
472 "PSR_MISSING_MANIFEST",
473 `Missing template manifest script #${MANIFEST_ELEMENT_ID}`,
474 { manifestElementId: MANIFEST_ELEMENT_ID },
475 true
476 );
477 throw new PresolveBootError("PSR_MISSING_MANIFEST");
478 }
479
480 try {
481 return JSON.parse(element.textContent ?? "");
482 } catch (error) {
483 reportDiagnostic(
484 diagnostics,
485 "PSR_INVALID_MANIFEST_JSON",
486 "Template manifest JSON could not be parsed",
487 { message: error instanceof Error ? error.message : String(error) },
488 true
489 );
490 throw new PresolveBootError("PSR_INVALID_MANIFEST_JSON");
491 }
492 }
493
494 function effectArtifactHasActionPlans(effectArtifact) {
495 return (effectArtifact?.effects ?? []).some((effect) =>
496 Array.isArray(effect.action_batch_triggers) && effect.action_batch_triggers.length > 0
497 );
498 }
499
500 function legacyActionBinding(action) {
501 return action?.method_id === undefined
502 && action?.action_batch_id === undefined
503 && typeof action?.method === "string"
504 && action.method.length > 0;
505 }
506
507 function legacyEventBinding(event) {
508 return event?.kind === undefined
509 && event?.method_id === undefined
510 && event?.action_batch_id === undefined
511 && /^this\.[A-Za-z_$][\w$]*$/.test(String(event?.handler ?? ""));
512 }
513
514 function legacyHandlerMethod(reference) {
515 return String(reference ?? "").replace(/^this\./, "");
516 }
517
518 function legacyComponentMethodKey(componentName, method) {
519 return `${componentName}:${method}`;
520 }
521
522 function validateManifestActionBindings(manifest, opaqueArtifact, diagnostics) {
523 const opaqueMethods = new Set((opaqueArtifact?.activations ?? []).map((activation) => activation.method));
524 for (const component of manifest.components ?? []) {
525 const actionsByMethod = new Map();
526 const legacyMethods = new Set();
527
528 for (const action of component.actions ?? []) {
529 if (legacyActionBinding(action)) {
530 legacyMethods.add(action.method);
531 continue;
532 }
533 if (typeof action.method_id !== "string"
534 || typeof action.action_batch_id !== "string"
535 || (manifest.schema_version === SUPPORTED_SCHEMA_VERSION && typeof action.storage_id !== "string")) {
536 reportDiagnostic(
537 diagnostics,
538 "PSR_INVALID_ACTION_BINDING",
539 "Schema-v2 template action was missing compiler action identities",
540 { component: component.name, action },
541 true
542 );
543 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
544 }
545 actionsByMethod.set(action.method_id, action.action_batch_id);
546 }
547
548 for (const event of component.template?.events ?? []) {
549 if (legacyEventBinding(event)) {
550 const method = legacyHandlerMethod(event.handler);
551 if (legacyMethods.has(method)) continue;
552 reportDiagnostic(
553 diagnostics,
554 "PSR_INVALID_ACTION_BINDING",
555 "Legacy template action binding did not match its compiler action implementation",
556 { component: component.name, event },
557 true
558 );
559 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
560 }
561 if (event.kind !== "action") {
562 reportDiagnostic(
563 diagnostics,
564 "PSR_INVALID_ACTION_BINDING",
565 "Schema-v2 template event was not an explicit action binding",
566 { component: component.name, event },
567 true
568 );
569 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
570 }
571 if (typeof event.method_id !== "string" || typeof event.action_batch_id !== "string") {
572 reportDiagnostic(
573 diagnostics,
574 "PSR_INVALID_ACTION_BINDING",
575 "Schema-v2 template action binding was missing an action batch identity",
576 { component: component.name, event },
577 true
578 );
579 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
580 }
581 if (actionsByMethod.get(event.method_id) !== event.action_batch_id
582 && !opaqueMethods.has(event.method_id)) {
583 reportDiagnostic(
584 diagnostics,
585 "PSR_INVALID_ACTION_BINDING",
586 "Template action binding did not match its compiler action implementation",
587 { component: component.name, event },
588 true
589 );
590 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
591 }
592 }
593 }
594 }
595
596 function validateManifestSchema(manifest, effectArtifact, componentArtifact, opaqueArtifact, diagnostics) {
597 if (
598 manifest?.schema_version !== SUPPORTED_SCHEMA_VERSION &&
599 manifest?.schema_version !== FORMS_MANIFEST_SCHEMA_VERSION &&
600 manifest?.schema_version !== ACTION_MANIFEST_SCHEMA_VERSION &&
601 manifest?.schema_version !== LEGACY_MANIFEST_SCHEMA_VERSION
602 ) {
603 reportDiagnostic(
604 diagnostics,
605 "PSR_UNSUPPORTED_SCHEMA",
606 `Unsupported template manifest schema version ${String(manifest?.schema_version)}`,
607 {
608 schema_version: manifest?.schema_version,
609 supported_schema_version: SUPPORTED_SCHEMA_VERSION
610 },
611 true
612 );
613 throw new PresolveBootError("PSR_UNSUPPORTED_SCHEMA");
614 }
615
616 const isOrdinaryInstancePair = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
617 && componentArtifact?.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION;
618 const isLegacyColdPair = manifest.schema_version === FORMS_MANIFEST_SCHEMA_VERSION
619 && componentArtifact?.schema_version === LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION;
620 if (!isOrdinaryInstancePair && !isLegacyColdPair) {
621 reportDiagnostic(
622 diagnostics,
623 "PSR_UNSUPPORTED_SCHEMA",
624 "Template manifest and component artifact are not an exact runtime contract pair",
625 { manifest_schema_version: manifest.schema_version, component_schema_version: componentArtifact?.schema_version },
626 true
627 );
628 throw new PresolveBootError("PSR_UNSUPPORTED_SCHEMA");
629 }
630
631 if (isOrdinaryInstancePair) {
632 for (const program of componentArtifact.structural_programs ?? []) {
633 const component = (manifest.components ?? []).find(
634 (candidate) => candidate.component_id === program.host_component
635 );
636 const host = component?.template?.nodes?.find((node) => node.id === program.host_node);
637 if (component === undefined || host === undefined
638 || (host.kind !== "conditional" && host.kind !== "list")) {
639 reportDiagnostic(
640 diagnostics,
641 "PSR_INVALID_COMPONENT_ARTIFACT",
642 "Structural component metadata did not resolve to an emitted conditional or keyed-list host",
643 { region: program.region, host_component: program.host_component, host_node: program.host_node },
644 true
645 );
646 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
647 }
648 if (host.kind === "list" && program.conditional_host_fragments.length !== 0) {
649 reportDiagnostic(
650 diagnostics,
651 "PSR_INVALID_COMPONENT_ARTIFACT",
652 "Conditional host fragments were attached to a keyed-list host",
653 { region: program.region, host_component: program.host_component, host_node: program.host_node },
654 true
655 );
656 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
657 }
658 if (host.kind === "conditional" && program.keyed_host_fragments.length !== 0) {
659 reportDiagnostic(
660 diagnostics,
661 "PSR_INVALID_COMPONENT_ARTIFACT",
662 "Keyed host fragments were attached to a conditional host",
663 { region: program.region, host_component: program.host_component, host_node: program.host_node },
664 true
665 );
666 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
667 }
668 }
669 }
670
671 if (
672 manifest.schema_version === LEGACY_MANIFEST_SCHEMA_VERSION &&
673 effectArtifactHasActionPlans(effectArtifact)
674 ) {
675 reportDiagnostic(
676 diagnostics,
677 "PSR_LEGACY_MANIFEST_EFFECT_ACTIONS",
678 "A legacy template manifest cannot activate compiler-generated effect action batches",
679 { schema_version: manifest.schema_version },
680 true
681 );
682 throw new PresolveBootError("PSR_LEGACY_MANIFEST_EFFECT_ACTIONS");
683 }
684
685 if (manifest.schema_version >= ACTION_MANIFEST_SCHEMA_VERSION) {
686 validateManifestActionBindings(manifest, opaqueArtifact, diagnostics);
687 }
688 }
689
690 function readFormsArtifact(diagnostics) {
691 const element = document.getElementById(FORMS_ARTIFACT_ELEMENT_ID);
692 if (element === null) return null;
693 if (!(element instanceof HTMLScriptElement)) {
694 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Forms runtime metadata was not stored in a script element", { artifactElementId: FORMS_ARTIFACT_ELEMENT_ID }, true);
695 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
696 }
697 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
698 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Forms runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
699 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
700 }
701 }
702
703 function readResourcesArtifact(diagnostics) {
704 const element = document.getElementById(RESOURCES_ARTIFACT_ELEMENT_ID);
705 if (element === null) return null;
706 if (!(element instanceof HTMLScriptElement)) {
707 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource runtime metadata was not stored in a script element", { artifactElementId: RESOURCES_ARTIFACT_ELEMENT_ID }, true);
708 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
709 }
710 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
711 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
712 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
713 }
714 }
715
716 function readOpaqueArtifact(diagnostics) {
717 const element = document.getElementById(OPAQUE_ARTIFACT_ELEMENT_ID);
718 if (element === null) return null;
719 if (!(element instanceof HTMLScriptElement)) {
720 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal metadata was not stored in a script element", { artifactElementId: OPAQUE_ARTIFACT_ELEMENT_ID }, true);
721 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
722 }
723 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
724 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
725 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
726 }
727 }
728
729 function validateOpaqueArtifact(opaqueArtifact, diagnostics) {
730 if (opaqueArtifact === null) return;
731 if (opaqueArtifact.schema_version !== SUPPORTED_OPAQUE_ARTIFACT_SCHEMA_VERSION
732 || !Array.isArray(opaqueArtifact.activations)) {
733 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_OPAQUE_ARTIFACT_SCHEMA", "Opaque terminal metadata did not match the compiler artifact contract", { schema_version: opaqueArtifact.schema_version }, true);
734 throw new PresolveBootError("PSR_UNSUPPORTED_OPAQUE_ARTIFACT_SCHEMA");
735 }
736 const ids = new Set();
737 const methods = new Set();
738 for (const activation of opaqueArtifact.activations) {
739 if (typeof activation?.id !== "string" || ids.has(activation.id)
740 || typeof activation?.method !== "string" || methods.has(activation.method)
741 || typeof activation?.owner_component !== "string"
742 || typeof activation?.package !== "string" || typeof activation?.version !== "string"
743 || typeof activation?.integrity !== "string" || typeof activation?.export !== "string"
744 || typeof activation?.runtime_module !== "string" || typeof activation?.runtime_location !== "string"
745 || activation?.type_signature !== "() -> void"
746 || activation?.execution_boundary !== "client" || activation?.resume_policy !== "cold_fallback") {
747 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal activation did not retain one exact callable client boundary", { activation }, true);
748 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
749 }
750 ids.add(activation.id);
751 methods.add(activation.method);
752 }
753 }
754
755 function validateResourcesArtifact(resourcesArtifact, diagnostics) {
756 if (resourcesArtifact === null) return;
757 if (resourcesArtifact.schema_version !== SUPPORTED_RESOURCES_ARTIFACT_SCHEMA_VERSION
758 || !Array.isArray(resourcesArtifact.declarations)
759 || !Array.isArray(resourcesArtifact.activations)) {
760 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_RESOURCES_ARTIFACT_SCHEMA", "Resource runtime metadata did not match the compiler artifact contract", { schema_version: resourcesArtifact.schema_version }, true);
761 throw new PresolveBootError("PSR_UNSUPPORTED_RESOURCES_ARTIFACT_SCHEMA");
762 }
763 const declarations = new Set();
764 for (const declaration of resourcesArtifact.declarations) {
765 const endpoint = declaration?.endpoint;
766 if (typeof declaration?.id !== "string" || declarations.has(declaration.id)
767 || typeof endpoint?.package !== "string" || typeof endpoint?.version !== "string"
768 || typeof endpoint?.integrity !== "string" || typeof endpoint?.export !== "string"
769 || typeof endpoint?.runtime_module !== "string" || typeof endpoint?.runtime_location !== "string"
770 || !isValidResourceValueCodec(declaration?.data_codec)
771 || !isValidResourceValueCodec(declaration?.error_codec)) {
772 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource declaration did not retain one exact executable endpoint", { declaration }, true);
773 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
774 }
775 declarations.add(declaration.id);
776 }
777 const activations = new Set();
778 for (const activation of resourcesArtifact.activations) {
779 const generationRequired = ["pending", "ready", "failed", "cancelled"].includes(activation?.state);
780 if (typeof activation?.id !== "string" || activations.has(activation.id)
781 || !declarations.has(activation?.declaration)
782 || generationRequired !== Number.isInteger(activation?.generation)
783 || !hasExactResourceResumeSlots(activation)) {
784 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource activation did not retain canonical lifecycle linkage", { activation }, true);
785 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
786 }
787 activations.add(activation.id);
788 }
789 }
790
791 function hasExactResourceResumeSlots(activation) {
792 if (typeof activation?.id !== "string"
793 || typeof activation?.state_slot !== "string"
794 || typeof activation?.data_slot !== "string"
795 || typeof activation?.error_slot !== "string") return false;
796 const prefix = `${activation.id}/resource-slot:`;
797 return activation.state_slot === `${prefix}state`
798 && activation.data_slot === `${prefix}data`
799 && activation.error_slot === `${prefix}error`;
800 }
801
802 function isValidResourceValueCodec(codec, depth = 0) {
803 if (depth > 32 || codec === null || typeof codec !== "object" || Array.isArray(codec)) return false;
804 switch (codec.kind) {
805 case "null_codec":
806 case "boolean_codec":
807 case "number_codec":
808 case "string_codec":
809 return exactObjectKeys(codec, ["kind"]);
810 case "array_codec":
811 case "nullable_codec":
812 return exactObjectKeys(codec, ["kind", "value"])
813 && isValidResourceValueCodec(codec.value, depth + 1);
814 case "object_codec": {
815 if (!exactObjectKeys(codec, ["kind", "value"]) || !Array.isArray(codec.value)) return false;
816 const names = new Set();
817 return codec.value.every((property) => property !== null
818 && typeof property === "object" && !Array.isArray(property)
819 && exactObjectKeys(property, ["name", "codec"])
820 && typeof property.name === "string" && property.name.length > 0
821 && !names.has(property.name) && names.add(property.name)
822 && isValidResourceValueCodec(property.codec, depth + 1));
823 }
824 default:
825 return false;
826 }
827 }
828
829 function validateFormsArtifact(formsArtifact, manifest, diagnostics) {
830 if (formsArtifact === null) {
831 if (manifest.schema_version >= 3) {
832 reportDiagnostic(diagnostics, "PSR_MISSING_FORMS_ARTIFACT", "A schema-v3 template manifest requires Forms runtime metadata", {}, true);
833 throw new PresolveBootError("PSR_MISSING_FORMS_ARTIFACT");
834 }
835 return;
836 }
837 if (formsArtifact.schema_version !== SUPPORTED_FORMS_ARTIFACT_SCHEMA_VERSION || !Array.isArray(formsArtifact.forms) || !Array.isArray(formsArtifact.instances) || !Array.isArray(formsArtifact.hosts)) {
838 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_FORMS_ARTIFACT_SCHEMA", "Forms runtime metadata did not match the compiler artifact contract", { schema_version: formsArtifact.schema_version }, true);
839 throw new PresolveBootError("PSR_UNSUPPORTED_FORMS_ARTIFACT_SCHEMA");
840 }
841 for (const form of formsArtifact.forms) {
842 const paths = new Set();
843 for (const field of form?.fields ?? []) {
844 if (!Array.isArray(field?.path) || field.path.length === 0 || field.path.length > 16
845 || !field.path.every((segment) => typeof segment === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(segment))) {
846 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Form Field path was not compiler-canonical", { field }, true);
847 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
848 }
849 const path = field.path.join(".");
850 if ([...paths].some((other) => path === other || path.startsWith(`${other}.`) || other.startsWith(`${path}.`))) {
851 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Form Field paths conflicted", { form: form.id, path }, true);
852 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
853 }
854 paths.add(path);
855 }
856 }
857 const hasForms = formsArtifact.forms.length > 0 || formsArtifact.instances.length > 0;
858 if (hasForms && manifest.schema_version < 3) {
859 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms runtime metadata requires a schema-v3 template manifest", { schema_version: manifest.schema_version }, true);
860 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
861 }
862 const instances = new Set(formsArtifact.instances.map((instance) => instance.id));
863 for (const binding of manifest.form_bindings ?? []) {
864 if (!instances.has(binding.form_instance_id)) {
865 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest bridge referenced an unknown Form instance", { binding }, true);
866 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
867 }
868 }
869 const artifactHosts = new Map(formsArtifact.hosts.map((host) => [`${host.host_anchor}|${host.form_instance}`, host]));
870 for (const host of manifest.form_hosts ?? []) {
871 const artifact = artifactHosts.get(`${host.host_anchor}|${host.form_instance_id}`);
872 if (artifact === undefined || artifact.id !== host.submission_host_id || artifact.event !== host.event || artifact.submit_action !== host.submit_action || artifact.action_batch !== host.action_batch || artifact.serialization_plan !== host.serialization_plan || artifact.prevent_default !== host.prevent_default) {
873 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest host bridge did not match an exact compiler host record", { host }, true);
874 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
875 }
876 }
877 }
878
879 function readComputedArtifact(diagnostics) {
880 const element = document.getElementById(COMPUTED_ARTIFACT_ELEMENT_ID);
881
882 if (element === null) {
883 return null;
884 }
885
886 if (!(element instanceof HTMLScriptElement)) {
887 reportDiagnostic(
888 diagnostics,
889 "PSR_INVALID_COMPUTED_ARTIFACT",
890 "Computed runtime metadata was not stored in a script element",
891 { artifactElementId: COMPUTED_ARTIFACT_ELEMENT_ID },
892 true
893 );
894 throw new PresolveBootError("PSR_INVALID_COMPUTED_ARTIFACT");
895 }
896
897 try {
898 return JSON.parse(element.textContent ?? "");
899 } catch (error) {
900 reportDiagnostic(
901 diagnostics,
902 "PSR_INVALID_COMPUTED_ARTIFACT",
903 "Computed runtime metadata JSON could not be parsed",
904 { message: error instanceof Error ? error.message : String(error) },
905 true
906 );
907 throw new PresolveBootError("PSR_INVALID_COMPUTED_ARTIFACT");
908 }
909 }
910
911 function validateComputedArtifactSchema(artifact, diagnostics) {
912 if (artifact === null) {
913 return;
914 }
915
916 if (artifact.schema_version !== SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION) {
917 reportDiagnostic(
918 diagnostics,
919 "PSR_UNSUPPORTED_COMPUTED_ARTIFACT_SCHEMA",
920 `Unsupported computed runtime metadata schema version ${String(artifact.schema_version)}`,
921 {
922 schema_version: artifact.schema_version,
923 supported_schema_version: SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION
924 },
925 true
926 );
927 throw new PresolveBootError("PSR_UNSUPPORTED_COMPUTED_ARTIFACT_SCHEMA");
928 }
929 }
930
931 function readEffectArtifact(diagnostics) {
932 const element = document.getElementById(EFFECT_ARTIFACT_ELEMENT_ID);
933
934 if (element === null) {
935 return null;
936 }
937
938 if (!(element instanceof HTMLScriptElement)) {
939 reportDiagnostic(
940 diagnostics,
941 "PSR_INVALID_EFFECT_ARTIFACT",
942 "Effect runtime metadata was not stored in a script element",
943 { artifactElementId: EFFECT_ARTIFACT_ELEMENT_ID },
944 true
945 );
946 throw new PresolveBootError("PSR_INVALID_EFFECT_ARTIFACT");
947 }
948
949 try {
950 return JSON.parse(element.textContent ?? "");
951 } catch (error) {
952 reportDiagnostic(
953 diagnostics,
954 "PSR_INVALID_EFFECT_ARTIFACT",
955 "Effect runtime metadata JSON could not be parsed",
956 { message: error instanceof Error ? error.message : String(error) },
957 true
958 );
959 throw new PresolveBootError("PSR_INVALID_EFFECT_ARTIFACT");
960 }
961 }
962
963 function validateEffectArtifactSchema(artifact, diagnostics) {
964 if (artifact === null) {
965 return;
966 }
967
968 if (artifact.schema_version !== SUPPORTED_EFFECT_ARTIFACT_SCHEMA_VERSION) {
969 reportDiagnostic(
970 diagnostics,
971 "PSR_UNSUPPORTED_EFFECT_ARTIFACT_SCHEMA",
972 `Unsupported effect runtime metadata schema version ${String(artifact.schema_version)}`,
973 {
974 schema_version: artifact.schema_version,
975 supported_schema_version: SUPPORTED_EFFECT_ARTIFACT_SCHEMA_VERSION
976 },
977 true
978 );
979 throw new PresolveBootError("PSR_UNSUPPORTED_EFFECT_ARTIFACT_SCHEMA");
980 }
981 }
982
983 function validateEffectArtifactInstances(effectArtifact, componentArtifact, diagnostics) {
984 if (effectArtifact === null) return;
985 if (!Array.isArray(effectArtifact.instances) || !Array.isArray(effectArtifact.structural_templates)) {
986 reportDiagnostic(diagnostics, "PSR_INVALID_EFFECT_INSTANCE_ARTIFACT", "Effect runtime metadata omitted instance ownership records", {}, true);
987 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
988 }
989 if (effectArtifact.instances.length === 0 && effectArtifact.structural_templates.length === 0) return;
990 if (componentArtifact === null) {
991 reportDiagnostic(diagnostics, "PSR_INVALID_EFFECT_INSTANCE_ARTIFACT", "Instance-owned effects require a component runtime artifact", {}, true);
992 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
993 }
994 const declarations = new Set((effectArtifact.effects ?? []).map((effect) => effect.effect));
995 const instances = new Map((componentArtifact.instances ?? []).map((instance) => [instance.instance, instance]));
996 const identities = new Set();
997 for (const record of effectArtifact.instances) {
998 const component = instances.get(record.component_instance);
999 if (typeof record.effect_instance !== "string" || identities.has(record.effect_instance)
1000 || !declarations.has(record.effect) || component === undefined
1001 || record.parent_instance !== component.parent || record.depth !== component.depth
1002 || !Number.isInteger(record.declaration_order) || record.declaration_order < 0) {
1003 reportDiagnostic(diagnostics, "PSR_INVALID_EFFECT_INSTANCE_ARTIFACT", "Effect instance ownership did not match compiler component metadata", { effect_instance: record.effect_instance }, true);
1004 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
1005 }
1006 identities.add(record.effect_instance);
1007 }
1008 const regions = new Map((componentArtifact.structural_programs ?? []).map((program) => [program.region, program]));
1009 const occurrences = new Map();
1010 for (const program of regions.values()) {
1011 for (const occurrence of program.template_occurrences ?? []) {
1012 if (occurrences.has(occurrence.template_instance)) {
1013 reportDiagnostic(diagnostics, "PSR_INVALID_EFFECT_INSTANCE_ARTIFACT", "Structural effect templates did not have unique compiler occurrence ownership", { template_instance: occurrence.template_instance }, true);
1014 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
1015 }
1016 occurrences.set(occurrence.template_instance, { region: program.region, component: occurrence.component });
1017 }
1018 }
1019 const templateIdentities = new Set();
1020 for (const record of effectArtifact.structural_templates) {
1021 const occurrence = occurrences.get(record.template_instance);
1022 const expectedEffectInstance = `${String(record.template_instance ?? "")}/effect-instance:${String(record.effect ?? "")}`;
1023 if (typeof record.effect_instance !== "string" || templateIdentities.has(record.effect_instance)
1024 || record.effect_instance !== expectedEffectInstance
1025 || occurrence === undefined || record.structural_region !== occurrence.region
1026 || record.component !== occurrence.component || !declarations.has(record.effect)
1027 || !Number.isInteger(record.depth) || record.depth < 0
1028 || !Number.isInteger(record.declaration_order) || record.declaration_order < 0) {
1029 reportDiagnostic(diagnostics, "PSR_INVALID_EFFECT_INSTANCE_ARTIFACT", "Structural effect template metadata did not match compiler component metadata", { effect_instance: record.effect_instance }, true);
1030 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
1031 }
1032 templateIdentities.add(record.effect_instance);
1033 }
1034 }
1035
1036 function readComponentArtifact(diagnostics) {
1037 const element = document.getElementById(COMPONENT_ARTIFACT_ELEMENT_ID);
1038 if (element === null) return null;
1039 if (!(element instanceof HTMLScriptElement)) {
1040 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata was not stored in a script element", { artifactElementId: COMPONENT_ARTIFACT_ELEMENT_ID }, true);
1041 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1042 }
1043 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
1044 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
1045 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1046 }
1047 }
1048
1049 function canonicalStateSlotId(componentInstanceId, storageId) {
1050 const encoded = [...new TextEncoder().encode(storageId)].map((byte) => {
1051 const unreserved = (byte >= 65 && byte <= 90)
1052 || (byte >= 97 && byte <= 122)
1053 || (byte >= 48 && byte <= 57)
1054 || byte === 45 || byte === 46 || byte === 95 || byte === 126;
1055 return unreserved ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
1056 }).join("");
1057 return `${componentInstanceId}/state-slot:${encoded}`;
1058 }
1059
1060 function validateComponentArtifactSchema(artifact, diagnostics) {
1061 if (artifact === null) return;
1062 if ((artifact.schema_version !== SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION && artifact.schema_version !== LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION) || !Array.isArray(artifact.instances) || !Array.isArray(artifact.initialization_batches)) {
1063 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata did not match the compiler artifact contract", { schema_version: artifact.schema_version }, true);
1064 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1065 }
1066 const instances = new Set(artifact.instances.map((instance) => instance.instance));
1067 const instancesById = new Map(artifact.instances.map((instance) => [instance.instance, instance]));
1068 const structuralTemplates = new Set(
1069 (artifact.structural_programs ?? [])
1070 .flatMap((program) => program.template_instances ?? [])
1071 );
1072 const structuralTemplateComponents = new Map(
1073 (artifact.structural_programs ?? [])
1074 .flatMap((program) => (program.template_occurrences ?? []).map((occurrence) => [
1075 occurrence.template_instance,
1076 occurrence.component
1077 ]))
1078 );
1079 const stateSlots = new Set();
1080 const statePairs = new Set();
1081 const structuralStateSlots = new Set();
1082 const structuralStatePairs = new Set();
1083 const structuralComputedCacheSlots = new Set();
1084 const structuralComputedDirtySlots = new Set();
1085 const structuralComputedPairs = new Set();
1086 for (const instance of artifact.instances) if (
1087 instance.parent !== null
1088 && instance.parent !== undefined
1089 && !instances.has(instance.parent)
1090 && !structuralTemplates.has(instance.parent)
1091 ) {
1092 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata referenced an unknown parent instance", { instance: instance.instance, parent: instance.parent }, true);
1093 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1094 }
1095 if (artifact.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION) {
1096 if (!Array.isArray(artifact.structural_programs)) {
1097 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1098 }
1099 if (!Array.isArray(artifact.ordinary_template_targets)
1100 || !Array.isArray(artifact.ordinary_template_bindings)
1101 || !Array.isArray(artifact.ordinary_template_events)) {
1102 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1103 }
1104 const structuralRegions = new Set();
1105 const structuralHosts = new Set();
1106 const slotBindingCallees = new Map((artifact.slot_binding_programs ?? []).map((binding) => [binding.binding, binding.callee_instance]));
1107 for (const program of artifact.structural_programs) {
1108 const host = `${String(program.host_component)}\u001f${String(program.host_node)}`;
1109 if (typeof program.region !== "string" || program.region.length === 0
1110 || typeof program.host_component !== "string" || program.host_component.length === 0
1111 || typeof program.host_node !== "string" || program.host_node.length === 0
1112 || typeof program.host_template_entity !== "string" || program.host_template_entity.length === 0
1113 || structuralRegions.has(program.region) || structuralHosts.has(host)
1114 || !Array.isArray(program.conditional_host_fragments)
1115 || !Array.isArray(program.keyed_host_fragments)
1116 || !Array.isArray(program.template_instances)
1117 || !Array.isArray(program.template_occurrences)
1118 || program.template_occurrences.length !== program.template_instances.length
1119 || new Set(program.conditional_host_fragments.map((fragments) => fragments?.host_instance)).size !== program.conditional_host_fragments.length
1120 || program.conditional_host_fragments.some((fragments) => typeof fragments?.host_instance !== "string"
1121 || typeof fragments.host_scope !== "string"
1122 || (fragments.host_scope === "static-instance"
1123 ? instancesById.get(fragments.host_instance)?.component !== program.host_component
1124 : fragments.host_scope === "structural-occurrence"
1125 ? structuralTemplateComponents.get(fragments.host_instance) !== program.host_component
1126 : true)
1127 || typeof fragments.when_true_html !== "string" || fragments.when_true_html.length === 0
1128 || typeof fragments.when_false_html !== "string" || fragments.when_false_html.length === 0
1129 || !Array.isArray(fragments.when_true_invocations)
1130 || !Array.isArray(fragments.when_false_invocations)
1131 || !Array.isArray(fragments.slot_projection_bindings)
1132 || !Array.isArray(fragments.slot_projection_programs)
1133 || fragments.slot_projection_programs.length !== fragments.slot_projection_bindings.length
1134 || new Set(fragments.slot_projection_bindings).size !== fragments.slot_projection_bindings.length
1135 || fragments.slot_projection_bindings.some((binding) => slotBindingCallees.get(binding) !== fragments.host_instance)
1136 || fragments.slot_projection_programs.some((projection) => typeof projection?.binding !== "string"
1137 || !fragments.slot_projection_bindings.includes(projection.binding)
1138 || typeof projection.caller_instance !== "string" || typeof projection.content_owner_instance !== "string"
1139 || !Array.isArray(projection.target_ids) || !Array.isArray(projection.binding_ids)
1140 || !Array.isArray(projection.event_ids) || !Array.isArray(projection.nested_invocations))
1141 || new Set(fragments.when_true_invocations).size !== fragments.when_true_invocations.length
1142 || new Set(fragments.when_false_invocations).size !== fragments.when_false_invocations.length
1143 || [...fragments.when_true_invocations, ...fragments.when_false_invocations].some((invocation) =>
1144 !program.template_occurrences.some((occurrence) => occurrence.invocation === invocation)
1145 ))
1146 || new Set(program.keyed_host_fragments.map((fragments) => fragments?.host_instance)).size !== program.keyed_host_fragments.length
1147 || program.keyed_host_fragments.some((fragments) => typeof fragments?.host_instance !== "string"
1148 || typeof fragments.host_scope !== "string"
1149 || (fragments.host_scope === "static-instance"
1150 ? instancesById.get(fragments.host_instance)?.component !== program.host_component
1151 : fragments.host_scope === "structural-occurrence"
1152 ? structuralTemplateComponents.get(fragments.host_instance) !== program.host_component
1153 : true)
1154 || typeof fragments.item_template_html !== "string" || fragments.item_template_html.length === 0
1155 || !Array.isArray(fragments.item_invocations)
1156 || !Array.isArray(fragments.slot_projection_bindings)
1157 || !Array.isArray(fragments.slot_projection_programs)
1158 || fragments.slot_projection_programs.length !== fragments.slot_projection_bindings.length
1159 || new Set(fragments.slot_projection_bindings).size !== fragments.slot_projection_bindings.length
1160 || fragments.slot_projection_bindings.some((binding) => slotBindingCallees.get(binding) !== fragments.host_instance)
1161 || fragments.slot_projection_programs.some((projection) => typeof projection?.binding !== "string"
1162 || !fragments.slot_projection_bindings.includes(projection.binding)
1163 || typeof projection.caller_instance !== "string" || typeof projection.content_owner_instance !== "string"
1164 || !Array.isArray(projection.target_ids) || !Array.isArray(projection.binding_ids)
1165 || !Array.isArray(projection.event_ids) || !Array.isArray(projection.nested_invocations))
1166 || new Set(fragments.item_invocations).size !== fragments.item_invocations.length
1167 || fragments.item_invocations.some((invocation) =>
1168 !program.template_occurrences.some((occurrence) => occurrence.invocation === invocation)
1169 ))
1170 || program.template_occurrences.some((occurrence, index) => typeof occurrence?.template_instance !== "string"
1171 || occurrence.template_instance !== program.template_instances[index]
1172 || typeof occurrence.invocation !== "string" || typeof occurrence.invocation_template_entity !== "string"
1173 || occurrence.invocation_template_entity.length === 0 || typeof occurrence.component !== "string"
1174 || typeof occurrence.template_html !== "string" || occurrence.template_html.length === 0
1175 || !Array.isArray(occurrence.nested_invocations)
1176 || new Set(occurrence.nested_invocations).size !== occurrence.nested_invocations.length
1177 || occurrence.nested_invocations.some((invocation) => !program.template_occurrences.some((candidate) => candidate.invocation === invocation))
1178 || !Array.isArray(occurrence.state_slots)
1179 || !Array.isArray(occurrence.computed_slots)
1180 || !Array.isArray(occurrence.ordinary_template_targets)
1181 || !Array.isArray(occurrence.ordinary_template_bindings)
1182 || !Array.isArray(occurrence.ordinary_template_events)
1183 || occurrence.state_slots.some((slot) => {
1184 const pair = `${occurrence.template_instance}|${slot?.storage_id}`;
1185 return typeof slot?.slot_id !== "string"
1186 || slot.slot_id !== canonicalStateSlotId(occurrence.template_instance, slot.storage_id)
1187 || typeof slot.state_id !== "string"
1188 || slot.state_id.length === 0
1189 || typeof slot.storage_id !== "string"
1190 || slot.storage_id !== `storage:${slot.state_id}`
1191 || typeof slot.semantic_type !== "string"
1192 || slot.semantic_type.length === 0
1193 || typeof slot.serializable !== "boolean"
1194 || structuralStateSlots.has(slot.slot_id)
1195 || structuralStatePairs.has(pair)
1196 || !structuralStateSlots.add(slot.slot_id)
1197 || !structuralStatePairs.add(pair);
1198 })
1199 || occurrence.computed_slots.some((slot) => {
1200 const pair = `${occurrence.template_instance}|${slot?.computed_id}`;
1201 return typeof slot?.computed_id !== "string"
1202 || slot.computed_id.length === 0
1203 || typeof slot?.cache_slot_id !== "string"
1204 || typeof slot?.dirty_slot_id !== "string"
1205 || !slot.cache_slot_id.startsWith(`${occurrence.template_instance}/computed-cache:`)
1206 || !slot.dirty_slot_id.startsWith(`${occurrence.template_instance}/computed-dirty:`)
1207 || typeof slot.dirty_initial_value !== "boolean"
1208 || structuralComputedCacheSlots.has(slot.cache_slot_id)
1209 || structuralComputedDirtySlots.has(slot.dirty_slot_id)
1210 || structuralComputedPairs.has(pair)
1211 || !structuralComputedCacheSlots.add(slot.cache_slot_id)
1212 || !structuralComputedDirtySlots.add(slot.dirty_slot_id)
1213 || !structuralComputedPairs.add(pair);
1214 })
1215 || JSON.stringify(occurrence.ordinary_template_targets) !== JSON.stringify(
1216 artifact.ordinary_template_targets
1217 .filter((target) => target.component_instance_id === occurrence.template_instance)
1218 .map((target) => target.id)
1219 )
1220 || JSON.stringify(occurrence.ordinary_template_bindings) !== JSON.stringify(
1221 artifact.ordinary_template_bindings
1222 .filter((binding) => binding.component_instance_id === occurrence.template_instance)
1223 .map((binding) => binding.id)
1224 )
1225 || JSON.stringify(occurrence.ordinary_template_events) !== JSON.stringify(
1226 artifact.ordinary_template_events
1227 .filter((event) => event.component_instance_id === occurrence.template_instance)
1228 .map((event) => event.declaration_event_id)
1229 ))
1230 || JSON.stringify(program.create_order) !== JSON.stringify(program.template_instances)
1231 || JSON.stringify([...(program.destroy_order ?? [])].reverse()) !== JSON.stringify(program.template_instances)) {
1232 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1233 }
1234 structuralRegions.add(program.region);
1235 structuralHosts.add(host);
1236 }
1237 for (const instance of artifact.instances) {
1238 if (!Array.isArray(instance.state_slots)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1239 for (const slot of instance.state_slots) {
1240 const pair = `${instance.instance}|${slot.storage_id}`;
1241 if (
1242 typeof slot.slot_id !== "string"
1243 || slot.slot_id !== canonicalStateSlotId(instance.instance, slot.storage_id)
1244 || typeof slot.state_id !== "string"
1245 || typeof slot.storage_id !== "string"
1246 || slot.storage_id !== `storage:${slot.state_id}`
1247 || stateSlots.has(slot.slot_id)
1248 || statePairs.has(pair)
1249 ) {
1250 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1251 }
1252 stateSlots.add(slot.slot_id);
1253 statePairs.add(pair);
1254 }
1255 }
1256 }
1257 }
1258
1259 function readContextArtifact(diagnostics) {
1260 const element = document.getElementById(CONTEXT_ARTIFACT_ELEMENT_ID);
1261 if (!(element instanceof HTMLScriptElement)) {
1262 reportDiagnostic(diagnostics, "PSR_INVALID_CONTEXT_ARTIFACT", "Context runtime metadata was not stored in a script element", { artifactElementId: CONTEXT_ARTIFACT_ELEMENT_ID }, true);
1263 throw new PresolveBootError("PSR_INVALID_CONTEXT_ARTIFACT");
1264 }
1265 try { return JSON.parse(element.textContent ?? ""); }
1266 catch (error) {
1267 reportDiagnostic(diagnostics, "PSR_INVALID_CONTEXT_ARTIFACT", "Context runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
1268 throw new PresolveBootError("PSR_INVALID_CONTEXT_ARTIFACT");
1269 }
1270 }
1271
1272 function validateContextArtifactSchema(artifact, diagnostics) {
1273 if (artifact.schema_version !== SUPPORTED_CONTEXT_ARTIFACT_SCHEMA_VERSION) {
1274 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_CONTEXT_ARTIFACT_SCHEMA", `Unsupported Context runtime metadata schema version ${String(artifact.schema_version)}`, { schema_version: artifact.schema_version, supported_schema_version: SUPPORTED_CONTEXT_ARTIFACT_SCHEMA_VERSION }, true);
1275 throw new PresolveBootError("PSR_UNSUPPORTED_CONTEXT_ARTIFACT_SCHEMA");
1276 }
1277 }
1278
1279 function fieldNameFromThisMember(expression) {
1280 const match = /^this\.([A-Za-z_$][\w$]*)$/.exec(String(expression ?? ""));
1281 return match === null ? null : match[1];
1282 }
1283
1284 function collectBindingAnchors() {
1285 const anchors = new Map();
1286 const walker = document.createTreeWalker(
1287 document.body,
1288 NodeFilter.SHOW_COMMENT
1289 );
1290
1291 while (walker.nextNode()) {
1292 const value = (walker.currentNode.nodeValue ?? "").trim();
1293 const match = /^presolve-binding:([^:]+):(.*)$/.exec(value);
1294
1295 if (match !== null) {
1296 anchors.set(match[1], {
1297 id: match[1],
1298 expression: match[2],
1299 marker: walker.currentNode
1300 });
1301 }
1302 }
1303
1304 return anchors;
1305 }
1306
1307 function collectConditionalAnchors() {
1308 const starts = new Map();
1309 const ends = new Map();
1310 const walker = document.createTreeWalker(
1311 document.body,
1312 NodeFilter.SHOW_COMMENT
1313 );
1314
1315 while (walker.nextNode()) {
1316 const value = (walker.currentNode.nodeValue ?? "").trim();
1317 const startMatch = /^presolve-conditional-start:([^:]+):(.*)$/.exec(value);
1318
1319 if (startMatch !== null) {
1320 starts.set(startMatch[1], {
1321 id: startMatch[1],
1322 condition: startMatch[2],
1323 marker: walker.currentNode
1324 });
1325 continue;
1326 }
1327
1328 const endMatch = /^presolve-conditional-end:([^:]+)$/.exec(value);
1329
1330 if (endMatch !== null) {
1331 ends.set(endMatch[1], {
1332 id: endMatch[1],
1333 marker: walker.currentNode
1334 });
1335 }
1336 }
1337
1338 return {
1339 starts,
1340 ends
1341 };
1342 }
1343
1344 function collectListAnchors() {
1345 const starts = new Map();
1346 const ends = new Map();
1347 const walker = document.createTreeWalker(
1348 document.body,
1349 NodeFilter.SHOW_COMMENT
1350 );
1351
1352 while (walker.nextNode()) {
1353 const value = (walker.currentNode.nodeValue ?? "").trim();
1354 const startMatch = /^presolve-list-start:([^:]+):(.*)$/.exec(value);
1355
1356 if (startMatch !== null) {
1357 starts.set(startMatch[1], {
1358 id: startMatch[1],
1359 iterable: startMatch[2],
1360 marker: walker.currentNode
1361 });
1362 continue;
1363 }
1364
1365 const endMatch = /^presolve-list-end:([^:]+)$/.exec(value);
1366
1367 if (endMatch !== null) {
1368 ends.set(endMatch[1], {
1369 id: endMatch[1],
1370 marker: walker.currentNode
1371 });
1372 }
1373 }
1374
1375 return {
1376 starts,
1377 ends
1378 };
1379 }
1380
1381 function collectElementAnchors() {
1382 const elementsByNode = new Map();
1383
1384 for (const element of document.querySelectorAll("[data-presolve-node]")) {
1385 elementsByNode.set(element.dataset.presolveNode, element);
1386 }
1387
1388 return elementsByNode;
1389 }
1390
1391 function collectMissingAnchors(
1392 manifest,
1393 bindingAnchors,
1394 conditionalAnchors,
1395 listAnchors,
1396 elementsByNode
1397 ) {
1398 const missing = [];
1399
1400 for (const component of manifest.components ?? []) {
1401 for (const node of component.template?.nodes ?? []) {
1402 if (node.kind === "element") {
1403 if (!elementsByNode.has(node.id)) {
1404 missing.push({
1405 component: component.name,
1406 id: node.id,
1407 kind: node.kind,
1408 code: "PSR_MISSING_ELEMENT_ANCHOR"
1409 });
1410 }
1411 }
1412
1413 if (
1414 node.kind === "binding" &&
1415 node.target !== "attribute" &&
1416 !bindingAnchors.has(node.id)
1417 ) {
1418 missing.push({
1419 component: component.name,
1420 id: node.id,
1421 kind: node.kind,
1422 code: "PSR_MISSING_BINDING_ANCHOR"
1423 });
1424 }
1425
1426 if (node.kind === "conditional") {
1427 if (!conditionalAnchors.starts.has(node.start)) {
1428 missing.push({
1429 component: component.name,
1430 id: node.start,
1431 kind: node.kind,
1432 code: "PSR_MISSING_CONDITIONAL_ANCHOR"
1433 });
1434 }
1435
1436 if (!conditionalAnchors.ends.has(node.end)) {
1437 missing.push({
1438 component: component.name,
1439 id: node.end,
1440 kind: node.kind,
1441 code: "PSR_MISSING_CONDITIONAL_ANCHOR"
1442 });
1443 }
1444 }
1445
1446 if (node.kind === "list") {
1447 if (!listAnchors.starts.has(node.start)) {
1448 missing.push({
1449 component: component.name,
1450 id: node.start,
1451 kind: node.kind,
1452 code: "PSR_MISSING_LIST_ANCHOR"
1453 });
1454 }
1455
1456 if (!listAnchors.ends.has(node.end)) {
1457 missing.push({
1458 component: component.name,
1459 id: node.end,
1460 kind: node.kind,
1461 code: "PSR_MISSING_LIST_ANCHOR"
1462 });
1463 }
1464 }
1465 }
1466 }
1467
1468 return missing;
1469 }
1470
1471 function buildActionsByMethod(component) {
1472 const actionsByMethod = new Map();
1473 const legacyActionsByMethod = new Map();
1474
1475 for (const action of component.actions ?? []) {
1476 if (legacyActionBinding(action)) {
1477 const actions = legacyActionsByMethod.get(action.method) ?? [];
1478 actions.push(action);
1479 legacyActionsByMethod.set(action.method, actions);
1480 continue;
1481 }
1482 const record = actionsByMethod.get(action.method_id) ?? {
1483 action_batch_id: action.action_batch_id,
1484 actions: []
1485 };
1486 record.actions.push(action);
1487 actionsByMethod.set(action.method_id, record);
1488 }
1489
1490 return { actionsByMethod, legacyActionsByMethod };
1491 }
1492
1493 function componentFieldKey(componentName, field) {
1494 return `${componentName}:${field}`;
1495 }
1496
1497 function formatBindingValue(value) {
1498 return value === null ? "" : String(value);
1499 }
1500
1501 function isBooleanAttribute(attribute) {
1502 return new Set([
1503 "allowfullscreen",
1504 "async",
1505 "autofocus",
1506 "autoplay",
1507 "checked",
1508 "controls",
1509 "default",
1510 "defer",
1511 "disabled",
1512 "formnovalidate",
1513 "hidden",
1514 "inert",
1515 "loop",
1516 "multiple",
1517 "muted",
1518 "nomodule",
1519 "novalidate",
1520 "open",
1521 "readonly",
1522 "required",
1523 "reversed",
1524 "selected"
1525 ]).has(String(attribute).toLowerCase());
1526 }
1527
1528 function isPropertyAttribute(attribute) {
1529 return new Set([
1530 "checked",
1531 "disabled",
1532 "selected",
1533 "value"
1534 ]).has(String(attribute).toLowerCase());
1535 }
1536
1537 function updateAttributeBinding(element, attribute, value) {
1538 const normalizedAttribute = String(attribute);
1539
1540 if (isBooleanAttribute(normalizedAttribute)) {
1541 const enabled = Boolean(value);
1542 element.toggleAttribute(normalizedAttribute, enabled);
1543
1544 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1545 element[normalizedAttribute] = enabled;
1546 }
1547
1548 return;
1549 }
1550
1551 if (value === null || value === undefined) {
1552 element.removeAttribute(normalizedAttribute);
1553
1554 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1555 element[normalizedAttribute] = "";
1556 }
1557
1558 return;
1559 }
1560
1561 const text = formatBindingValue(value);
1562 element.setAttribute(normalizedAttribute, text);
1563
1564 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1565 element[normalizedAttribute] = text;
1566 }
1567 }
1568
1569 function replaceConditionalBranch(store, startMarker, endMarker, html) {
1570 if (
1571 startMarker.parentNode === null ||
1572 endMarker.parentNode === null ||
1573 startMarker.parentNode !== endMarker.parentNode
1574 ) {
1575 reportDiagnostic(
1576 store.diagnostics,
1577 "PSR_MISSING_CONDITIONAL_ANCHOR",
1578 "Conditional anchor range was not contiguous in one parent",
1579 {}
1580 );
1581 return;
1582 }
1583
1584 let current = startMarker.nextSibling;
1585
1586 while (current !== null && current !== endMarker) {
1587 const next = current.nextSibling;
1588 current.remove();
1589 current = next;
1590 }
1591
1592 const template = document.createElement("template");
1593 template.innerHTML = String(html ?? "");
1594 endMarker.parentNode.insertBefore(template.content, endMarker);
1595 store.elementsByNode = collectElementAnchors();
1596 }
1597
1598 function listItems(value) {
1599 return Array.isArray(value) ? value : [];
1600 }
1601
1602 function normalizeListKey(value) {
1603 return String(value).replaceAll("--", "—");
1604 }
1605
1606 function listItemMemberPath(node, expression) {
1607 const prefix = `${String(node.item_variable ?? "")}.`;
1608 const value = String(expression ?? "");
1609
1610 if (!value.startsWith(prefix)) {
1611 return null;
1612 }
1613
1614 const path = value.slice(prefix.length).split(".");
1615 return path.length === 0 || path.some((member) => member === "") ? null : path;
1616 }
1617
1618 function listItemMemberValue(node, item, expression) {
1619 const path = listItemMemberPath(node, expression);
1620
1621 if (path === null) {
1622 return undefined;
1623 }
1624
1625 let value = item;
1626
1627 for (const member of path) {
1628 if (
1629 value === null ||
1630 typeof value !== "object" ||
1631 Array.isArray(value) ||
1632 !Object.prototype.hasOwnProperty.call(value, member)
1633 ) {
1634 return undefined;
1635 }
1636
1637 value = value[member];
1638 }
1639
1640 return value;
1641 }
1642
1643 function listItemBindingValue(node, item, index, expression) {
1644 if (expression === node.item_variable) {
1645 return item;
1646 }
1647
1648 if (expression === node.index_variable) {
1649 return index;
1650 }
1651
1652 if (listItemMemberPath(node, expression) !== null) {
1653 return listItemMemberValue(node, item, expression);
1654 }
1655
1656 return undefined;
1657 }
1658
1659 function isListKeyPrimitive(value) {
1660 return value === null || (typeof value !== "object" && typeof value !== "undefined");
1661 }
1662
1663 function listItemKey(node, item, index) {
1664 if (node.key_expression === node.item_variable) {
1665 return isListKeyPrimitive(item) ? normalizeListKey(item) : String(index);
1666 }
1667
1668 if (listItemMemberPath(node, node.key_expression) !== null) {
1669 const value = listItemMemberValue(node, item, node.key_expression);
1670 return isListKeyPrimitive(value) ? normalizeListKey(value) : String(index);
1671 }
1672
1673 if (node.index_variable === node.key_expression) {
1674 return String(index);
1675 }
1676
1677 return String(index);
1678 }
1679
1680 const STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX = "presolve-structural-occurrence:v1:";
1681
1682 function structuralOccurrenceHex(value) {
1683 if (typeof value !== "string" || value.length === 0) {
1684 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1685 }
1686 return [...new TextEncoder().encode(value)]
1687 .map((byte) => byte.toString(16).toUpperCase().padStart(2, "0"))
1688 .join("");
1689 }
1690
1691 function structuralOccurrenceIdentity(parentScope, region, templateInstance, localOccurrence) {
1692 return `${STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX}${structuralOccurrenceHex(parentScope)}.${structuralOccurrenceHex(region)}.${structuralOccurrenceHex(templateInstance)}.${structuralOccurrenceHex(localOccurrence)}`;
1693 }
1694
1695 function decodeStructuralOccurrenceIdentity(value) {
1696 if (typeof value !== "string" || !value.startsWith(STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX)) {
1697 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1698 }
1699 const fields = value.slice(STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX.length).split(".");
1700 if (fields.length !== 4 || fields.some((field) => field.length === 0 || field.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(field))) {
1701 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1702 }
1703 let decoded;
1704 try {
1705 decoded = fields.map((field) => new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(
1706 field.match(/../g).map((pair) => Number.parseInt(pair, 16))
1707 )));
1708 } catch (_) {
1709 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1710 }
1711 if (decoded.some((field) => field.length === 0)) {
1712 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1713 }
1714 return Object.freeze({
1715 parent_scope: decoded[0],
1716 region: decoded[1],
1717 template_instance: decoded[2],
1718 local_occurrence: decoded[3]
1719 });
1720 }
1721
1722 function instantiateStructuralTemplateSlots(occurrence, occurrenceIdentity) {
1723 decodeStructuralOccurrenceIdentity(occurrenceIdentity);
1724 const templateInstance = String(occurrence?.template_instance ?? "");
1725 const prefix = `${templateInstance}/`;
1726 if (templateInstance.length === 0
1727 || !Array.isArray(occurrence?.state_slots)
1728 || !Array.isArray(occurrence?.computed_slots)) {
1729 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1730 }
1731 const ids = new Set();
1732 const stateSlots = occurrence.state_slots.map((slot) => {
1733 if (typeof slot?.slot_id !== "string" || !slot.slot_id.startsWith(`${prefix}state-slot:`)
1734 || typeof slot.storage_id !== "string" || !slot.storage_id.startsWith("storage:")) {
1735 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1736 }
1737 const slotId = `${occurrenceIdentity}/${slot.slot_id.slice(prefix.length)}`;
1738 if (ids.has(slotId)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1739 ids.add(slotId);
1740 return Object.freeze({ ...slot, slot_id: slotId });
1741 });
1742 const computedSlots = occurrence.computed_slots.map((slot) => {
1743 if (typeof slot?.cache_slot_id !== "string" || !slot.cache_slot_id.startsWith(`${prefix}computed-cache:`)
1744 || typeof slot.dirty_slot_id !== "string" || !slot.dirty_slot_id.startsWith(`${prefix}computed-dirty:`)) {
1745 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1746 }
1747 const cacheSlotId = `${occurrenceIdentity}/${slot.cache_slot_id.slice(prefix.length)}`;
1748 const dirtySlotId = `${occurrenceIdentity}/${slot.dirty_slot_id.slice(prefix.length)}`;
1749 if (ids.has(cacheSlotId) || ids.has(dirtySlotId)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1750 ids.add(cacheSlotId);
1751 ids.add(dirtySlotId);
1752 return Object.freeze({ ...slot, cache_slot_id: cacheSlotId, dirty_slot_id: dirtySlotId });
1753 });
1754 return Object.freeze({ state_slots: stateSlots, computed_slots: computedSlots });
1755 }
1756
1757 function rewriteStructuralTemplateIdentity(templateInstance, occurrenceIdentity, value) {
1758 if (typeof templateInstance !== "string" || templateInstance.length === 0
1759 || typeof occurrenceIdentity !== "string" || typeof value !== "string") {
1760 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1761 }
1762 const prefix = `${templateInstance}/`;
1763 if (!value.startsWith(prefix)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1764 return `${occurrenceIdentity}/${value.slice(prefix.length)}`;
1765 }
1766
1767 function deriveStructuralOccurrenceRecords(templateRecord, occurrenceIdentity) {
1768 const decoded = decodeStructuralOccurrenceIdentity(occurrenceIdentity);
1769 const occurrence = templateRecord?.occurrence;
1770 const templateInstance = String(occurrence?.template_instance ?? "");
1771 if (templateInstance.length === 0
1772 || decoded.template_instance !== templateInstance
1773 || typeof templateRecord?.structural_region !== "string"
1774 || decoded.region !== templateRecord.structural_region
1775 || !Array.isArray(templateRecord?.targets)
1776 || !Array.isArray(templateRecord?.bindings)
1777 || !Array.isArray(templateRecord?.events)) {
1778 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1779 }
1780 const slots = instantiateStructuralTemplateSlots(occurrence, occurrenceIdentity);
1781 const targetIds = new Set();
1782 const bindingIds = new Set();
1783 const eventKeys = new Set();
1784 const targets = templateRecord.targets.map((pair) => {
1785 const artifact = pair?.artifact;
1786 const manifest = pair?.manifest;
1787 const id = rewriteStructuralTemplateIdentity(templateInstance, occurrenceIdentity, artifact?.id);
1788 if (manifest?.id !== artifact?.id || targetIds.has(id)) {
1789 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1790 }
1791 targetIds.add(id);
1792 return Object.freeze({
1793 artifact: Object.freeze({ ...artifact, id, component_instance_id: occurrenceIdentity }),
1794 manifest: Object.freeze({ ...manifest, id, component_instance_id: occurrenceIdentity })
1795 });
1796 });
1797 const bindings = templateRecord.bindings.map((pair) => {
1798 const artifact = pair?.artifact;
1799 const manifest = pair?.manifest;
1800 const id = rewriteStructuralTemplateIdentity(templateInstance, occurrenceIdentity, artifact?.id);
1801 const targetId = rewriteStructuralTemplateIdentity(templateInstance, occurrenceIdentity, artifact?.target_id);
1802 if (manifest?.instance_binding_id !== artifact?.id
1803 || manifest?.instance_target_id !== artifact?.target_id
1804 || !targetIds.has(targetId)
1805 || bindingIds.has(id)) {
1806 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1807 }
1808 bindingIds.add(id);
1809 return Object.freeze({
1810 artifact: Object.freeze({ ...artifact, id, target_id: targetId, component_instance_id: occurrenceIdentity }),
1811 manifest: Object.freeze({ ...manifest, instance_binding_id: id, instance_target_id: targetId, component_instance_id: occurrenceIdentity })
1812 });
1813 });
1814 const events = templateRecord.events.map((pair) => {
1815 const artifact = pair?.artifact;
1816 const manifest = pair?.manifest;
1817 const targetId = rewriteStructuralTemplateIdentity(templateInstance, occurrenceIdentity, artifact?.target_id);
1818 const key = ordinaryEventKey(targetId, artifact?.event_type);
1819 if (manifest?.instance_target_id !== artifact?.target_id
1820 || !targetIds.has(targetId)
1821 || eventKeys.has(key)) {
1822 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1823 }
1824 eventKeys.add(key);
1825 return Object.freeze({
1826 artifact: Object.freeze({ ...artifact, target_id: targetId, component_instance_id: occurrenceIdentity }),
1827 manifest: Object.freeze({ ...manifest, instance_target_id: targetId, component_instance_id: occurrenceIdentity })
1828 });
1829 });
1830 return Object.freeze({
1831 occurrence_identity: occurrenceIdentity,
1832 parent_scope: decoded.parent_scope,
1833 structural_region: templateRecord.structural_region,
1834 template_instance: templateInstance,
1835 occurrence,
1836 component: occurrence.component,
1837 definition: templateRecord.definition,
1838 state_slots: slots.state_slots,
1839 computed_slots: slots.computed_slots,
1840 targets: Object.freeze(targets),
1841 bindings: Object.freeze(bindings),
1842 events: Object.freeze(events)
1843 });
1844 }
1845
1846 function stageStructuralOccurrenceRecords(store, records) {
1847 const instanceId = String(records?.occurrence_identity ?? "");
1848 const componentId = String(records?.component ?? "");
1849 const definition = records?.definition;
1850 if (instanceId.length === 0 || componentId.length === 0 || definition === undefined
1851 || store.components.has(instanceId) || store.componentInstances.has(instanceId)) {
1852 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1853 }
1854 const statePairs = [];
1855 const computedPairs = [];
1856 let component = null;
1857 let staged = false;
1858 const rollback = () => {
1859 if (!staged) return;
1860 for (const [pair, slot] of statePairs) {
1861 store.stateSlotsByInstanceStorage.delete(pair);
1862 store.storageValues.delete(slot.slot_id);
1863 store.bindingsByStateSlot.delete(slot.slot_id);
1864 }
1865 for (const [pair, slot] of computedPairs) {
1866 store.computedSlotsByInstanceComputed.delete(pair);
1867 store.computedDirtySlots.delete(slot.dirty_slot_id);
1868 store.computedCaches.delete(slot.cache_slot_id);
1869 store.bindingsByInstanceComputed.delete(pair);
1870 }
1871 store.components.delete(instanceId);
1872 store.componentInstances.delete(instanceId);
1873 staged = false;
1874 };
1875 try {
1876 for (const slot of records.state_slots ?? []) {
1877 const pair = `${instanceId}|${slot.storage_id}`;
1878 if (store.stateSlotsByInstanceStorage.has(pair) || store.storageValues.has(slot.slot_id)) {
1879 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1880 }
1881 store.stateSlotsByInstanceStorage.set(pair, slot);
1882 store.storageValues.set(slot.slot_id, initialStateSlotValue(slot));
1883 statePairs.push([pair, slot]);
1884 }
1885 for (const slot of records.computed_slots ?? []) {
1886 const pair = `${instanceId}|${slot.computed_id}`;
1887 if (store.computedSlotsByInstanceComputed.has(pair)
1888 || store.computedDirtySlots.has(slot.dirty_slot_id)
1889 || store.computedCaches.has(slot.cache_slot_id)) {
1890 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1891 }
1892 store.computedSlotsByInstanceComputed.set(pair, slot);
1893 store.computedDirtySlots.set(slot.dirty_slot_id, slot.dirty_initial_value === true);
1894 computedPairs.push([pair, slot]);
1895 }
1896 component = { instance_id: instanceId, name: definition.name, manifest: definition, state: {} };
1897 for (const state of store.computedArtifact?.state ?? []) {
1898 if (state.component !== definition.name) continue;
1899 const slot = store.stateSlotsByInstanceStorage.get(`${instanceId}|${state.storage}`);
1900 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1901 component.state[state.field] = store.storageValues.get(slot.slot_id);
1902 }
1903 store.components.set(instanceId, component);
1904 store.componentInstances.set(instanceId, {
1905 instance: instanceId,
1906 component: componentId,
1907 parent: records.parent_scope,
1908 structural_region: records.structural_region,
1909 status: "staged"
1910 });
1911 registerActions(store, component, definition);
1912 staged = true;
1913 } catch (error) {
1914 staged = true;
1915 rollback();
1916 throw error;
1917 }
1918 return Object.freeze({ ...records, rollback });
1919 }
1920
1921 function renderStructuralOccurrenceTemplate(records) {
1922 const occurrenceIdentity = String(records?.occurrence_identity ?? "");
1923 const templateHtml = records?.occurrence?.template_html;
1924 decodeStructuralOccurrenceIdentity(occurrenceIdentity);
1925 if (typeof templateHtml !== "string" || templateHtml.length === 0) {
1926 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1927 }
1928 const html = templateHtml.replaceAll("__PRESOLVE_STRUCTURAL_OCCURRENCE__", occurrenceIdentity);
1929 if (html.includes("__PRESOLVE_STRUCTURAL_OCCURRENCE__")) {
1930 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1931 }
1932 const template = document.createElement("template");
1933 template.innerHTML = html;
1934 if (!template.content.hasChildNodes()) {
1935 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1936 }
1937 return template.content;
1938 }
1939
1940 function attachStructuralOccurrenceFragment(marker, invocation, fragment) {
1941 if (!(marker instanceof Element)
1942 || marker.getAttribute("data-presolve-structural-invocation") !== invocation
1943 || marker.parentNode === null
1944 || !(fragment instanceof DocumentFragment)
1945 || !fragment.hasChildNodes()) {
1946 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1947 }
1948 const parent = marker.parentNode;
1949 const nextSibling = marker.nextSibling;
1950 const nodes = [...fragment.childNodes];
1951 marker.replaceWith(fragment);
1952 let attached = true;
1953 const rollback = () => {
1954 if (!attached) return;
1955 for (const node of nodes) {
1956 if (node.parentNode !== null) node.remove();
1957 }
1958 if (nextSibling !== null && nextSibling.parentNode === parent) {
1959 parent.insertBefore(marker, nextSibling);
1960 } else {
1961 parent.appendChild(marker);
1962 }
1963 attached = false;
1964 };
1965 return Object.freeze({ nodes: Object.freeze(nodes), rollback });
1966 }
1967
1968 function registerStructuralOccurrenceRecords(store, staged) {
1969 if (!(store.templateTargetsById instanceof Map)
1970 || !(store.ordinaryBindingsById instanceof Map)
1971 || !(store.ordinaryEventsByTargetAndType instanceof Map)) {
1972 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1973 }
1974 const anchors = collectOrdinaryTargetAnchors();
1975 for (const pair of staged.bindings) {
1976 const binding = pair.manifest;
1977 if (binding.kind !== "text" || anchors.targets.has(binding.instance_target_id)) continue;
1978 const text = ordinaryTextBindingNode(binding.instance_binding_id);
1979 if (text !== null) anchors.targets.set(binding.instance_target_id, text);
1980 }
1981 const targetIds = [];
1982 const bindingIds = [];
1983 const eventKeys = [];
1984 const unsubscribes = [];
1985 let registered = false;
1986 const rollback = () => {
1987 if (!registered) return;
1988 for (const unsubscribe of unsubscribes.reverse()) unsubscribe();
1989 for (const key of eventKeys) store.ordinaryEventsByTargetAndType.delete(key);
1990 for (const id of bindingIds) store.ordinaryBindingsById.delete(id);
1991 for (const id of targetIds) store.templateTargetsById.delete(id);
1992 registered = false;
1993 };
1994 try {
1995 for (const pair of staged.targets) {
1996 const target = pair.manifest;
1997 if (anchors.duplicates.has(target.id) || !anchors.targets.has(target.id)
1998 || store.templateTargetsById.has(target.id)) {
1999 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2000 }
2001 store.templateTargetsById.set(target.id, anchors.targets.get(target.id));
2002 targetIds.push(target.id);
2003 }
2004 for (const pair of staged.bindings) {
2005 const binding = pair.manifest;
2006 if (store.ordinaryBindingsById.has(binding.instance_binding_id)) {
2007 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2008 }
2009 store.ordinaryBindingsById.set(binding.instance_binding_id, {
2010 ...binding,
2011 execution_context: { component_instance_id: binding.component_instance_id }
2012 });
2013 bindingIds.push(binding.instance_binding_id);
2014 unsubscribes.push(registerOrdinaryBinding(store, binding, pair.artifact));
2015 }
2016 for (const pair of staged.events) {
2017 const event = pair.manifest;
2018 const key = ordinaryEventKey(event.instance_target_id, event.event_type);
2019 if (store.ordinaryEventsByTargetAndType.has(key)) {
2020 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2021 }
2022 store.ordinaryEventsByTargetAndType.set(key, event);
2023 eventKeys.push(key);
2024 }
2025 executeComputedPlan(store, staged.occurrence_identity);
2026 installOrdinaryInstanceEventListeners(store);
2027 registered = true;
2028 } catch (error) {
2029 registered = true;
2030 rollback();
2031 throw error;
2032 }
2033 return Object.freeze({ rollback });
2034 }
2035
2036 function structuralEffectInstances(store, records) {
2037 const templateInstance = String(records?.template_instance ?? "");
2038 const occurrenceIdentity = String(records?.occurrence_identity ?? "");
2039 const structuralRegion = String(records?.structural_region ?? "");
2040 const component = String(records?.component ?? "");
2041 if (templateInstance.length === 0 || occurrenceIdentity.length === 0
2042 || structuralRegion.length === 0 || component.length === 0) {
2043 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
2044 }
2045 const effects = new Set((store.effectArtifact?.effects ?? []).map((effect) => effect.effect));
2046 const identities = new Set();
2047 const instances = (store.effectArtifact?.structural_templates ?? [])
2048 .filter((record) => record.template_instance === templateInstance)
2049 .map((record) => {
2050 if (record.structural_region !== structuralRegion || record.component !== component
2051 || !effects.has(record.effect)) {
2052 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
2053 }
2054 const effectInstance = rewriteStructuralTemplateIdentity(
2055 templateInstance,
2056 occurrenceIdentity,
2057 record.effect_instance
2058 );
2059 if (identities.has(effectInstance) || store.activeEffectInstances.has(effectInstance)) {
2060 throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
2061 }
2062 identities.add(effectInstance);
2063 return Object.freeze({
2064 effect_instance: effectInstance,
2065 effect: record.effect,
2066 component_instance: occurrenceIdentity,
2067 parent_instance: records.parent_scope,
2068 depth: record.depth,
2069 declaration_order: record.declaration_order,
2070 structural: true
2071 });
2072 })
2073 .sort((left, right) => left.declaration_order - right.declaration_order
2074 || left.effect_instance.localeCompare(right.effect_instance));
2075 return Object.freeze(instances);
2076 }
2077
2078 function activateStructuralEffectInstances(store, records, resumeOnly = false) {
2079 const instances = structuralEffectInstances(store, records);
2080 const effects = new Map((store.effectArtifact?.effects ?? []).map((effect) => [effect.effect, effect]));
2081 const activated = [];
2082 try {
2083 for (const instance of instances) {
2084 const effect = effects.get(instance.effect);
2085 if (effect === undefined) throw new PresolveBootError("PSR_INVALID_EFFECT_INSTANCE_ARTIFACT");
2086 if (resumeOnly && effect.run_on_resume !== true) continue;
2087 store.activeEffectInstances.set(instance.effect_instance, instance);
2088 activated.push(instance);
2089 const evidence = {
2090 effect: effect.effect,
2091 effect_instance: instance.effect_instance,
2092 structural_region: records.structural_region,
2093 occurrence_identity: records.occurrence_identity,
2094 capability_operations: []
2095 };
2096 runEffect(store, effect, evidence, instance);
2097 store.structuralEffectRuns.push(evidence);
2098 }
2099 return Object.freeze({ instances, dispose: () => disposeEffectInstances(store, instances) });
2100 } catch (error) {
2101 disposeEffectInstances(store, activated);
2102 throw error;
2103 }
2104 }
2105
2106 function materializeStructuralOccurrence(store, marker, parentScope, localOccurrence) {
2107 let materializationPhase = "invocation";
2108 const invocation = marker?.getAttribute?.("data-presolve-structural-invocation");
2109 const template = store.structuralOccurrenceTemplatesByInvocation?.get(invocation);
2110 if (typeof invocation !== "string" || template === undefined
2111 || typeof parentScope !== "string" || parentScope.length === 0
2112 || typeof localOccurrence !== "string" || localOccurrence.length === 0) {
2113 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2114 }
2115 const identity = structuralOccurrenceIdentity(
2116 parentScope,
2117 template.structural_region,
2118 template.occurrence.template_instance,
2119 localOccurrence
2120 );
2121 materializationPhase = "record-derivation";
2122 const records = deriveStructuralOccurrenceRecords(template, identity);
2123 let staged = null;
2124 let attachment = null;
2125 let registration = null;
2126 let effects = null;
2127 const children = [];
2128 try {
2129 materializationPhase = "record-staging";
2130 staged = stageStructuralOccurrenceRecords(store, records);
2131 materializationPhase = "fragment-rendering";
2132 const program = store.componentRegions?.get(records.structural_region);
2133 const fragment = renderStructuralOccurrenceTemplate(records);
2134 materializationPhase = "nested-anchor-validation";
2135 const anchors = compilerFragmentInvocationAnchors(
2136 fragment,
2137 program,
2138 records.occurrence.nested_invocations
2139 );
2140 attachment = attachStructuralOccurrenceFragment(
2141 marker,
2142 invocation,
2143 fragment
2144 );
2145 materializationPhase = "ordinary-record-registration";
2146 registration = registerStructuralOccurrenceRecords(store, staged);
2147 materializationPhase = "effect-activation";
2148 effects = activateStructuralEffectInstances(store, staged);
2149 materializationPhase = "nested-materialization";
2150 for (const anchor of anchors) {
2151 children.push(materializeStructuralOccurrence(store, anchor.marker, identity, localOccurrence));
2152 }
2153 return Object.freeze({ ...staged, attachment, registration, dispose: () => {
2154 for (const child of [...children].reverse()) child.dispose();
2155 effects?.dispose();
2156 registration.rollback();
2157 attachment.rollback();
2158 staged.rollback();
2159 }});
2160 } catch (error) {
2161 for (const child of [...children].reverse()) child.dispose();
2162 effects?.dispose();
2163 registration?.rollback();
2164 attachment?.rollback();
2165 staged?.rollback();
2166 reportDiagnostic(
2167 store.diagnostics,
2168 error instanceof PresolveBootError ? error.code : "PSR_RUNTIME_BOOT_FAILED",
2169 "Structural occurrence materialization failed",
2170 { invocation, structural_region: template?.structural_region ?? null, phase: materializationPhase, message: error instanceof Error ? error.message : String(error) },
2171 true
2172 );
2173 throw error;
2174 }
2175 }
2176
2177 function structuralConditionalHostFragment(store, component, node) {
2178 const programs = [...(store.componentRegions?.values() ?? [])].filter((program) =>
2179 program.host_component === component?.manifest?.component_id
2180 && program.host_node === node?.id
2181 );
2182 if (programs.length === 0) return null;
2183 if (programs.length !== 1 || typeof component?.instance_id !== "string") {
2184 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2185 }
2186 const fragments = programs[0].conditional_host_fragments.filter((fragment) =>
2187 structuralHostScopeMatches(fragment, component)
2188 );
2189 if (fragments.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2190 return fragments[0];
2191 }
2192
2193 function structuralKeyedHostFragment(store, component, node) {
2194 const programs = [...(store.componentRegions?.values() ?? [])].filter((program) =>
2195 program.host_component === component?.manifest?.component_id
2196 && program.host_node === node?.id
2197 );
2198 if (programs.length === 0) return null;
2199 if (programs.length !== 1 || typeof component?.instance_id !== "string") {
2200 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2201 }
2202 const fragments = programs[0].keyed_host_fragments.filter((fragment) =>
2203 structuralHostScopeMatches(fragment, component)
2204 );
2205 if (fragments.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2206 return fragments[0];
2207 }
2208
2209 function structuralHostScopeMatches(fragment, component) {
2210 if (typeof component?.instance_id !== "string" || typeof fragment?.host_instance !== "string") {
2211 return false;
2212 }
2213 if (fragment.host_scope === "static-instance") {
2214 return fragment.host_instance === component.instance_id;
2215 }
2216 if (fragment.host_scope === "structural-occurrence") {
2217 return decodeStructuralOccurrenceIdentity(component.instance_id).template_instance === fragment.host_instance;
2218 }
2219 return false;
2220 }
2221
2222 function compilerFragmentInvocationAnchors(fragment, program, expectedInvocations) {
2223 if (!(fragment instanceof DocumentFragment)
2224 || !Array.isArray(program?.template_occurrences)
2225 || !Array.isArray(program?.create_order)
2226 || !Array.isArray(expectedInvocations)
2227 || program.create_order.length !== program.template_occurrences.length) {
2228 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2229 }
2230 const occurrencesByInvocation = new Map();
2231 const occurrencesByTemplate = new Map();
2232 for (const occurrence of program.template_occurrences) {
2233 if (typeof occurrence?.invocation !== "string" || typeof occurrence?.template_instance !== "string"
2234 || occurrencesByInvocation.has(occurrence.invocation)
2235 || occurrencesByTemplate.has(occurrence.template_instance)) {
2236 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2237 }
2238 occurrencesByInvocation.set(occurrence.invocation, occurrence);
2239 occurrencesByTemplate.set(occurrence.template_instance, occurrence);
2240 }
2241 if (new Set(program.create_order).size !== program.create_order.length
2242 || program.create_order.some((templateInstance) => !occurrencesByTemplate.has(templateInstance))) {
2243 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2244 }
2245 if (new Set(expectedInvocations).size !== expectedInvocations.length
2246 || expectedInvocations.some((invocation) => !occurrencesByInvocation.has(invocation))) {
2247 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2248 }
2249 const anchorsByInvocation = new Map();
2250 const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_ELEMENT);
2251 while (walker.nextNode()) {
2252 const marker = walker.currentNode;
2253 const invocation = marker.getAttribute("data-presolve-structural-invocation");
2254 if (invocation === null) continue;
2255 if (!occurrencesByInvocation.has(invocation) || anchorsByInvocation.has(invocation)) {
2256 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2257 }
2258 anchorsByInvocation.set(invocation, marker);
2259 }
2260 if (anchorsByInvocation.size !== expectedInvocations.length
2261 || expectedInvocations.some((invocation) => !anchorsByInvocation.has(invocation))) {
2262 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2263 }
2264 return Object.freeze(program.create_order
2265 .map((templateInstance) => occurrencesByTemplate.get(templateInstance))
2266 .filter((occurrence) => expectedInvocations.includes(occurrence.invocation))
2267 .map((occurrence) => Object.freeze({
2268 invocation: occurrence.invocation,
2269 marker: anchorsByInvocation.get(occurrence.invocation)
2270 })));
2271 }
2272
2273 function replaceStructuralConditionalBranch(store, target, component, node, fragmentRecord, value) {
2274 if (!structuralHostScopeMatches(fragmentRecord, component)
2275 || target?.start?.parentNode === null
2276 || target?.end?.parentNode === null
2277 || target.start.parentNode !== target.end.parentNode) {
2278 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2279 }
2280 const program = [...(store.componentRegions?.values() ?? [])].filter((candidate) =>
2281 candidate.host_component === component.manifest?.component_id && candidate.host_node === node?.id
2282 );
2283 if (program.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2284 const branch = value === true ? "true" : "false";
2285 const html = branch === "true" ? fragmentRecord.when_true_html : fragmentRecord.when_false_html;
2286 if (typeof html !== "string" || html.length === 0) {
2287 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2288 }
2289 const template = document.createElement("template");
2290 template.innerHTML = html;
2291 if (!template.content.hasChildNodes()) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2292 const expectedInvocations = branch === "true"
2293 ? fragmentRecord.when_true_invocations
2294 : fragmentRecord.when_false_invocations;
2295 const anchors = compilerFragmentInvocationAnchors(template.content, program[0], expectedInvocations);
2296 const previous = document.createDocumentFragment();
2297 let current = target.start.nextSibling;
2298 while (current !== null && current !== target.end) {
2299 const next = current.nextSibling;
2300 previous.appendChild(current);
2301 current = next;
2302 }
2303 const prior = target.structural_occurrences ?? [];
2304 const priorProjection = target.structural_projection_registration ?? null;
2305 const priorBranch = target.structural_branch ?? null;
2306 const priorHtml = priorBranch === "true" ? fragmentRecord.when_true_html
2307 : priorBranch === "false" ? fragmentRecord.when_false_html
2308 : null;
2309 priorProjection?.rollback();
2310 const created = [];
2311 let projectionRegistration = null;
2312 try {
2313 target.end.parentNode.insertBefore(template.content, target.end);
2314 const projection = structuralHostProjectionRecords(store, fragmentRecord, component, html);
2315 projectionRegistration = registerStructuralOccurrenceRecords(store, {
2316 ...projection,
2317 occurrence_identity: component.instance_id
2318 });
2319 for (const anchor of anchors) {
2320 created.push(materializeStructuralOccurrence(
2321 store,
2322 anchor.marker,
2323 component.instance_id,
2324 `conditional:${branch}`
2325 ));
2326 }
2327 for (const occurrence of [...prior].reverse()) occurrence.dispose();
2328 target.structural_occurrences = Object.freeze(created);
2329 target.structural_projection_registration = projectionRegistration;
2330 target.structural_branch = branch;
2331 store.elementsByNode = collectElementAnchors();
2332 } catch (error) {
2333 for (const occurrence of [...created].reverse()) occurrence.dispose();
2334 projectionRegistration?.rollback();
2335 current = target.start.nextSibling;
2336 while (current !== null && current !== target.end) {
2337 const next = current.nextSibling;
2338 current.remove();
2339 current = next;
2340 }
2341 target.end.parentNode.insertBefore(previous, target.end);
2342 if (priorProjection !== null) {
2343 if (typeof priorHtml !== "string") throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2344 const projection = structuralHostProjectionRecords(store, fragmentRecord, component, priorHtml);
2345 target.structural_projection_registration = registerStructuralOccurrenceRecords(store, {
2346 ...projection,
2347 occurrence_identity: component.instance_id
2348 });
2349 }
2350 throw error;
2351 }
2352 }
2353
2354 function structuralOccurrenceTemplateRegistry(manifest, componentArtifact, computedArtifact) {
2355 const components = new Map((manifest.components ?? []).map((component) => [component.component_id, component]));
2356 if (components.size !== (manifest.components ?? []).length) {
2357 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2358 }
2359 const targetRecords = new Map((componentArtifact.ordinary_template_targets ?? []).map((record) => [record.id, record]));
2360 const bindingRecords = new Map((componentArtifact.ordinary_template_bindings ?? []).map((record) => [record.id, record]));
2361 const manifestTargets = new Map((manifest.ordinary_targets ?? []).map((record) => [record.id, record]));
2362 const manifestBindings = new Map((manifest.ordinary_bindings ?? []).map((record) => [record.instance_binding_id, record]));
2363 if (targetRecords.size !== (componentArtifact.ordinary_template_targets ?? []).length
2364 || bindingRecords.size !== (componentArtifact.ordinary_template_bindings ?? []).length
2365 || manifestTargets.size !== (manifest.ordinary_targets ?? []).length
2366 || manifestBindings.size !== (manifest.ordinary_bindings ?? []).length) {
2367 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2368 }
2369 const recordsFor = (ids, table, templateInstance) => {
2370 if (!Array.isArray(ids)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2371 const records = ids.map((id) => table.get(id));
2372 if (new Set(ids).size !== ids.length || records.some((record) => record === undefined
2373 || record.component_instance_id !== templateInstance)) {
2374 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2375 }
2376 return records;
2377 };
2378 const registry = new Map();
2379 for (const program of componentArtifact.structural_programs ?? []) {
2380 for (const occurrence of program.template_occurrences ?? []) {
2381 if (registry.has(occurrence.invocation)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2382 const definition = components.get(occurrence.component);
2383 if (definition === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2384 const targets = recordsFor(occurrence.ordinary_template_targets, targetRecords, occurrence.template_instance);
2385 const bindings = recordsFor(occurrence.ordinary_template_bindings, bindingRecords, occurrence.template_instance);
2386 const events = (occurrence.ordinary_template_events ?? []).map((declarationEventId) => {
2387 const matching = (componentArtifact.ordinary_template_events ?? []).filter((record) =>
2388 record.component_instance_id === occurrence.template_instance
2389 && record.declaration_event_id === declarationEventId
2390 );
2391 if (matching.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2392 return matching[0];
2393 });
2394 if (new Set(occurrence.ordinary_template_events ?? []).size !== events.length) {
2395 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2396 }
2397 const targetPairs = targets.map((record) => {
2398 const manifestRecord = manifestTargets.get(record.id);
2399 if (manifestRecord === undefined
2400 || manifestRecord.component_instance_id !== occurrence.template_instance
2401 || manifestRecord.template_entity_id !== record.template_entity_id
2402 || manifestRecord.kind !== record.kind) {
2403 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2404 }
2405 return Object.freeze({ artifact: record, manifest: manifestRecord });
2406 });
2407 const bindingPairs = bindings.map((record) => {
2408 const manifestRecord = manifestBindings.get(record.id);
2409 if (manifestRecord === undefined
2410 || manifestRecord.component_instance_id !== occurrence.template_instance
2411 || manifestRecord.instance_target_id !== record.target_id
2412 || manifestRecord.declaration_binding_id !== record.declaration_binding_id
2413 || manifestRecord.kind !== record.kind
2414 || manifestRecord.program_id !== record.program_id
2415 || manifestRecord.expression !== record.expression
2416 || manifestRecord.attribute_name !== record.attribute_name) {
2417 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2418 }
2419 return Object.freeze({ artifact: record, manifest: manifestRecord });
2420 });
2421 const eventPairs = events.map((record) => {
2422 const matching = (manifest.ordinary_events ?? []).filter((manifestRecord) =>
2423 manifestRecord.component_instance_id === occurrence.template_instance
2424 && manifestRecord.declaration_event_id === record.declaration_event_id
2425 );
2426 if (matching.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2427 const manifestRecord = matching[0];
2428 if (manifestRecord.component_id !== record.component_id
2429 || manifestRecord.instance_target_id !== record.target_id
2430 || manifestRecord.event_type !== record.event_type
2431 || manifestRecord.handler_method_id !== record.handler_method_id
2432 || manifestRecord.action_batch_id !== record.action_batch_id
2433 || JSON.stringify(manifestRecord.arguments ?? []) !== JSON.stringify(record.arguments ?? [])
2434 || manifestRecord.program_id !== record.program_id) {
2435 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2436 }
2437 return Object.freeze({ artifact: record, manifest: manifestRecord });
2438 });
2439 const expectedStateStorage = (computedArtifact?.state ?? [])
2440 .filter((state) => state.component === definition.name)
2441 .map((state) => state.storage);
2442 const expectedComputed = (computedArtifact?.evaluations ?? [])
2443 .filter((evaluation) => evaluation.component === definition.name)
2444 .map((evaluation) => evaluation.computed);
2445 if (JSON.stringify(occurrence.state_slots.map((slot) => slot.storage_id)) !== JSON.stringify(expectedStateStorage)
2446 || JSON.stringify(occurrence.computed_slots.map((slot) => slot.computed_id)) !== JSON.stringify(expectedComputed)) {
2447 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2448 }
2449 registry.set(occurrence.invocation, Object.freeze({
2450 structural_region: program.region,
2451 occurrence,
2452 definition,
2453 targets: Object.freeze(targetPairs),
2454 bindings: Object.freeze(bindingPairs),
2455 events: Object.freeze(eventPairs)
2456 }));
2457 }
2458 }
2459 return registry;
2460 }
2461
2462 function structuralSlotProjectionRegistry(manifest, componentArtifact) {
2463 const targets = new Map((componentArtifact?.ordinary_template_targets ?? []).map((record) => [record.id, record]));
2464 const bindings = new Map((componentArtifact?.ordinary_template_bindings ?? []).map((record) => [record.id, record]));
2465 const manifestTargets = new Map((manifest?.ordinary_targets ?? []).map((record) => [record.id, record]));
2466 const manifestBindings = new Map((manifest?.ordinary_bindings ?? []).map((record) => [record.instance_binding_id, record]));
2467 const slotBindings = new Map((componentArtifact?.slot_binding_programs ?? []).map((record) => [record.binding, record]));
2468 if (targets.size !== (componentArtifact?.ordinary_template_targets ?? []).length
2469 || bindings.size !== (componentArtifact?.ordinary_template_bindings ?? []).length
2470 || manifestTargets.size !== (manifest?.ordinary_targets ?? []).length
2471 || manifestBindings.size !== (manifest?.ordinary_bindings ?? []).length
2472 || slotBindings.size !== (componentArtifact?.slot_binding_programs ?? []).length) {
2473 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2474 }
2475 const registry = new Map();
2476 for (const program of componentArtifact?.structural_programs ?? []) {
2477 for (const fragment of [...(program.conditional_host_fragments ?? []), ...(program.keyed_host_fragments ?? [])]) {
2478 const programs = fragment?.slot_projection_programs;
2479 if (!Array.isArray(programs) || !Array.isArray(fragment.slot_projection_bindings)
2480 || programs.length !== fragment.slot_projection_bindings.length) {
2481 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2482 }
2483 for (const projection of programs) {
2484 const slotBinding = slotBindings.get(projection?.binding);
2485 if (slotBinding === undefined || slotBinding.binding !== projection.binding
2486 || slotBinding.callee_instance !== fragment.host_instance
2487 || slotBinding.caller_instance !== projection.caller_instance
2488 || slotBinding.content_owner_instance !== projection.content_owner_instance
2489 || !fragment.slot_projection_bindings.includes(projection.binding)
2490 || !Array.isArray(projection.target_ids) || !Array.isArray(projection.binding_ids)
2491 || !Array.isArray(projection.event_ids) || !Array.isArray(projection.nested_invocations)
2492 || new Set(projection.target_ids).size !== projection.target_ids.length
2493 || new Set(projection.binding_ids).size !== projection.binding_ids.length
2494 || new Set(projection.event_ids).size !== projection.event_ids.length
2495 || new Set(projection.nested_invocations).size !== projection.nested_invocations.length
2496 // This amendment activates ordinary caller-owned projection
2497 // members. Projected component invocation cloning needs its own
2498 // State/Effect identity contract and therefore remains fail-closed.
2499 || projection.nested_invocations.length !== 0) {
2500 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2501 }
2502 const targetPairs = projection.target_ids.map((id) => {
2503 const artifact = targets.get(id); const manifestRecord = manifestTargets.get(id);
2504 if (artifact === undefined || manifestRecord === undefined
2505 || artifact.component_instance_id !== projection.caller_instance
2506 || manifestRecord.component_instance_id !== projection.caller_instance) {
2507 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2508 }
2509 return Object.freeze({ artifact, manifest: manifestRecord });
2510 });
2511 const targetIds = new Set(projection.target_ids);
2512 const bindingPairs = projection.binding_ids.map((id) => {
2513 const artifact = bindings.get(id); const manifestRecord = manifestBindings.get(id);
2514 if (artifact === undefined || manifestRecord === undefined
2515 || artifact.component_instance_id !== projection.caller_instance
2516 || !targetIds.has(artifact.target_id) || manifestRecord.instance_target_id !== artifact.target_id) {
2517 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2518 }
2519 return Object.freeze({ artifact, manifest: manifestRecord });
2520 });
2521 const eventPairs = projection.event_ids.map((id) => {
2522 const matches = (componentArtifact?.ordinary_template_events ?? []).filter((event) =>
2523 event.component_instance_id === projection.caller_instance && event.declaration_event_id === id
2524 );
2525 const manifestMatches = (manifest?.ordinary_events ?? []).filter((event) =>
2526 event.component_instance_id === projection.caller_instance && event.declaration_event_id === id
2527 );
2528 if (matches.length !== 1 || manifestMatches.length !== 1 || !targetIds.has(matches[0].target_id)
2529 || manifestMatches[0].instance_target_id !== matches[0].target_id) {
2530 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2531 }
2532 return Object.freeze({ artifact: matches[0], manifest: manifestMatches[0] });
2533 });
2534 const record = Object.freeze({
2535 binding: projection.binding,
2536 caller_instance: projection.caller_instance,
2537 targets: Object.freeze(targetPairs), bindings: Object.freeze(bindingPairs), events: Object.freeze(eventPairs),
2538 nested_invocations: Object.freeze([...projection.nested_invocations])
2539 });
2540 // A binding may be listed by more than one host fragment while only
2541 // one exact outlet is emitted by a given conditional branch or keyed
2542 // item template. Key the preflight result by the compiler program
2543 // object, preserving fragment-local membership without merging it.
2544 registry.set(projection, record);
2545 }
2546 }
2547 }
2548 return registry;
2549 }
2550
2551 function structuralSlotProjectionTargetIds(componentArtifact) {
2552 const ids = new Set();
2553 for (const program of componentArtifact?.structural_programs ?? []) {
2554 for (const fragment of [...(program.conditional_host_fragments ?? []), ...(program.keyed_host_fragments ?? [])]) {
2555 for (const projection of fragment?.slot_projection_programs ?? []) {
2556 for (const id of projection?.target_ids ?? []) {
2557 if (typeof id !== "string" || id.length === 0) {
2558 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2559 }
2560 ids.add(id);
2561 }
2562 }
2563 }
2564 }
2565 return ids;
2566 }
2567
2568 function structuralProjectionOwnerScope(store, component, callerInstance) {
2569 const parent = store.componentInstances?.get(component?.instance_id)?.parent;
2570 if (typeof parent !== "string" || parent.length === 0 || typeof callerInstance !== "string") {
2571 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2572 }
2573 if (callerInstance === parent) return parent;
2574 if (parent.startsWith(STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX)
2575 && decodeStructuralOccurrenceIdentity(parent).template_instance === callerInstance) return parent;
2576 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2577 }
2578
2579 function structuralHostProjectionRecords(store, fragment, component, compilerHtml, itemKey = null) {
2580 if (typeof compilerHtml !== "string") throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2581 const records = [];
2582 const ids = new Set();
2583 for (const projection of fragment?.slot_projection_programs ?? []) {
2584 const source = store.structuralSlotProjectionPrograms?.get(projection);
2585 if (source === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2586 const emitted = [...source.targets, ...source.bindings].some((pair) => compilerHtml.includes(pair.artifact.id));
2587 if (!emitted) continue;
2588 const owner = structuralProjectionOwnerScope(store, component, source.caller_instance);
2589 const rewrite = (value) => {
2590 const base = owner === source.caller_instance ? value
2591 : rewriteStructuralTemplateIdentity(source.caller_instance, owner, value);
2592 return itemKey === null ? base : `${base}:${itemKey}`;
2593 };
2594 const targets = source.targets.map((pair) => Object.freeze({
2595 artifact: Object.freeze({ ...pair.artifact, id: rewrite(pair.artifact.id), component_instance_id: owner }),
2596 manifest: Object.freeze({ ...pair.manifest, id: rewrite(pair.manifest.id), component_instance_id: owner })
2597 }));
2598 const targetIds = new Set(targets.map((pair) => pair.manifest.id));
2599 const bindings = source.bindings.map((pair) => Object.freeze({
2600 artifact: Object.freeze({ ...pair.artifact, id: rewrite(pair.artifact.id), target_id: rewrite(pair.artifact.target_id), component_instance_id: owner }),
2601 manifest: Object.freeze({ ...pair.manifest, instance_binding_id: rewrite(pair.manifest.instance_binding_id), instance_target_id: rewrite(pair.manifest.instance_target_id), component_instance_id: owner })
2602 }));
2603 const events = source.events.map((pair) => Object.freeze({
2604 artifact: Object.freeze({ ...pair.artifact, target_id: rewrite(pair.artifact.target_id), component_instance_id: owner }),
2605 manifest: Object.freeze({ ...pair.manifest, instance_target_id: rewrite(pair.manifest.instance_target_id), component_instance_id: owner })
2606 }));
2607 for (const target of targets) if (ids.has(target.manifest.id) || !ids.add(target.manifest.id)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2608 if (bindings.some((pair) => !targetIds.has(pair.manifest.instance_target_id))) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2609 records.push(...targets, ...bindings, ...events);
2610 }
2611 const targets = records.filter((pair) => pair.artifact?.template_entity_id !== undefined);
2612 const bindings = records.filter((pair) => pair.artifact?.declaration_binding_id !== undefined);
2613 const events = records.filter((pair) => pair.artifact?.declaration_event_id !== undefined);
2614 return Object.freeze({ targets, bindings, events });
2615 }
2616
2617 function escapeHtmlText(value) {
2618 return String(value)
2619 .replaceAll("&", "&")
2620 .replaceAll("<", "<")
2621 .replaceAll(">", ">");
2622 }
2623
2624 function escapeHtmlAttribute(value) {
2625 return escapeHtmlText(value).replaceAll('"', """);
2626 }
2627
2628 function renderListItemHtml(node, item, index, key) {
2629 return String(node.item_template_html ?? "")
2630 .replaceAll("__ez_list_key__", escapeHtmlAttribute(key))
2631 .replaceAll("__ez_list_item__", escapeHtmlText(formatBindingValue(item)))
2632 .replaceAll("__ez_list_index__", String(index));
2633 }
2634
2635 function qualifyStructuralSlotProjectionItemHtml(fragment, html, key) {
2636 let qualified = html;
2637 for (const projection of fragment?.slot_projection_programs ?? []) {
2638 for (const id of [...(projection.target_ids ?? []), ...(projection.binding_ids ?? [])]) {
2639 if (typeof id !== "string" || id.length === 0) {
2640 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2641 }
2642 qualified = qualified.replaceAll(id, `${id}:${escapeHtmlAttribute(key)}`);
2643 }
2644 }
2645 return qualified;
2646 }
2647
2648 function populateListItemMemberBindings(node, item, fragment) {
2649 const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_COMMENT);
2650 const markers = [];
2651
2652 while (walker.nextNode()) {
2653 markers.push(walker.currentNode);
2654 }
2655
2656 const memberPrefix = `:${String(node.item_variable ?? "")}.`;
2657
2658 for (const marker of markers) {
2659 const comment = String(marker.nodeValue ?? "");
2660 const expressionStart = comment.lastIndexOf(memberPrefix);
2661
2662 if (expressionStart < 0) {
2663 continue;
2664 }
2665
2666 const expression = comment.slice(expressionStart + 1);
2667 const value = listItemMemberValue(node, item, expression);
2668 marker.after(document.createTextNode(value === undefined ? "" : formatBindingValue(value)));
2669 }
2670 }
2671
2672 function listItemBindingExpression(node, comment) {
2673 const value = String(comment ?? "");
2674 const memberPrefix = `:${String(node.item_variable ?? "")}.`;
2675 const memberStart = value.lastIndexOf(memberPrefix);
2676
2677 if (memberStart >= 0) {
2678 return value.slice(memberStart + 1);
2679 }
2680
2681 const itemSuffix = `:${String(node.item_variable ?? "")}`;
2682 if (value.endsWith(itemSuffix)) {
2683 return String(node.item_variable ?? "");
2684 }
2685
2686 const indexSuffix = `:${String(node.index_variable ?? "")}`;
2687 if (node.index_variable !== null && node.index_variable !== undefined && value.endsWith(indexSuffix)) {
2688 return String(node.index_variable);
2689 }
2690
2691 return null;
2692 }
2693
2694 function updateListItemTextBindings(node, instance) {
2695 const walker = document.createTreeWalker(instance.element, NodeFilter.SHOW_COMMENT);
2696
2697 while (walker.nextNode()) {
2698 const marker = walker.currentNode;
2699 const expression = listItemBindingExpression(node, marker.nodeValue);
2700
2701 if (expression === null) {
2702 continue;
2703 }
2704
2705 const textNode = marker.nextSibling;
2706 const value = listItemBindingValue(node, instance.item, instance.index, expression);
2707 const text = value === undefined ? "" : formatBindingValue(value);
2708
2709 if (textNode instanceof Text) {
2710 textNode.textContent = text;
2711 } else if (
2712 textNode instanceof Comment &&
2713 String(textNode.nodeValue ?? "").startsWith("presolve-list-binding-end:")
2714 ) {
2715 textNode.before(document.createTextNode(text));
2716 }
2717 }
2718 }
2719
2720 function listItemElements(root) {
2721 return [root, ...root.querySelectorAll("[data-presolve-list-bindings]")];
2722 }
2723
2724 function updateListItemAttributes(node, instance) {
2725 for (const element of listItemElements(instance.element)) {
2726 const bindings = String(element.dataset.presolveListBindings ?? "");
2727
2728 for (const binding of bindings.split(";")) {
2729 const separator = binding.indexOf("=");
2730
2731 if (separator < 1) {
2732 continue;
2733 }
2734
2735 const attribute = binding.slice(0, separator);
2736 const expression = binding.slice(separator + 1);
2737 const value = listItemBindingValue(node, instance.item, instance.index, expression);
2738 updateAttributeBinding(element, attribute, value);
2739 }
2740 }
2741 }
2742
2743 function listItemEventElements(root) {
2744 return [root, ...root.querySelectorAll("[data-presolve-on-click]")];
2745 }
2746
2747 function registerListItemEvents(store, component, instance) {
2748 for (const element of listItemEventElements(instance.element)) {
2749 const node = element.dataset.presolveNode;
2750 const handler = element.dataset.presolveOnClick;
2751
2752 if (node !== undefined && handler !== undefined) {
2753 registerEvent(store, component, { node, event: "click", handler });
2754 }
2755 }
2756 }
2757
2758 function unregisterListItemEvents(store, instance) {
2759 const eventsByNode = store.eventsByType.get("click");
2760
2761 if (eventsByNode === undefined) {
2762 return;
2763 }
2764
2765 for (const element of listItemEventElements(instance.element)) {
2766 const node = element.dataset.presolveNode;
2767
2768 if (node !== undefined) {
2769 eventsByNode.delete(node);
2770 }
2771 }
2772 }
2773
2774 function renderListItemElement(node, item, index, key) {
2775 const template = document.createElement("template");
2776 template.innerHTML = renderListItemHtml(node, item, index, key);
2777 populateListItemMemberBindings(node, item, template.content);
2778 return template.content.firstElementChild;
2779 }
2780
2781 function renderStructuralKeyedListItem(store, component, node, fragmentRecord, item, index, key) {
2782 if (!structuralHostScopeMatches(fragmentRecord, component)
2783 || typeof fragmentRecord.item_template_html !== "string") {
2784 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2785 }
2786 const programs = [...(store.componentRegions?.values() ?? [])].filter((program) =>
2787 program.host_component === component.manifest?.component_id && program.host_node === node?.id
2788 );
2789 if (programs.length !== 1) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2790 const template = document.createElement("template");
2791 template.innerHTML = qualifyStructuralSlotProjectionItemHtml(
2792 fragmentRecord,
2793 renderListItemHtml({ item_template_html: fragmentRecord.item_template_html }, item, index, key),
2794 key
2795 );
2796 populateListItemMemberBindings(node, item, template.content);
2797 const anchors = compilerFragmentInvocationAnchors(template.content, programs[0], fragmentRecord.item_invocations);
2798 const element = template.content.firstElementChild;
2799 if (element === null || template.content.childElementCount !== 1
2800 || [...template.content.childNodes].some((child) => child !== element)) {
2801 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2802 }
2803 return Object.freeze({ element, anchors });
2804 }
2805
2806 function disposeStructuralKeyedListItem(store, instance) {
2807 for (const occurrence of [...(instance.structural_occurrences ?? [])].reverse()) occurrence.dispose();
2808 instance.structural_projection_registration?.rollback();
2809 unregisterListItemEvents(store, instance);
2810 instance.element.remove();
2811 }
2812
2813 function reconcileStructuralKeyedList(store, component, node, fragmentRecord, startMarker, endMarker, instances, value) {
2814 if (startMarker.parentNode === null || endMarker.parentNode === null || startMarker.parentNode !== endMarker.parentNode) {
2815 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
2816 }
2817 const parent = startMarker.parentNode;
2818 const nextInstances = new Map();
2819 const ordered = [];
2820 for (const [index, item] of listItems(value).entries()) {
2821 const key = listItemKey(node, item, index);
2822 if (nextInstances.has(key)) {
2823 reportDiagnostic(store.diagnostics, "PSR_DUPLICATE_LIST_KEY", "List update produced a duplicate key", { id: node.id, key });
2824 continue;
2825 }
2826 let instance = instances.get(key);
2827 if (instance === undefined || !Array.isArray(instance.structural_occurrences)) {
2828 const prior = instance;
2829 const rendered = renderStructuralKeyedListItem(store, component, node, fragmentRecord, item, index, key);
2830 const created = [];
2831 let projectionRegistration = null;
2832 try {
2833 parent.insertBefore(rendered.element, endMarker);
2834 const projection = structuralHostProjectionRecords(store, fragmentRecord, component, fragmentRecord.item_template_html, key);
2835 projectionRegistration = registerStructuralOccurrenceRecords(store, {
2836 ...projection,
2837 occurrence_identity: component.instance_id
2838 });
2839 for (const anchor of rendered.anchors) {
2840 created.push(materializeStructuralOccurrence(store, anchor.marker, component.instance_id, `keyed:${key}`));
2841 }
2842 instance = { element: rendered.element, item, index, key, structural_occurrences: Object.freeze(created), structural_projection_registration: projectionRegistration };
2843 registerListItemEvents(store, component, instance);
2844 if (prior !== undefined) {
2845 unregisterListItemEvents(store, prior);
2846 prior.element.remove();
2847 }
2848 } catch (error) {
2849 for (const occurrence of [...created].reverse()) occurrence.dispose();
2850 projectionRegistration?.rollback();
2851 rendered.element.remove();
2852 throw error;
2853 }
2854 }
2855 instance.item = item;
2856 instance.index = index;
2857 updateListItemTextBindings(node, instance);
2858 updateListItemAttributes(node, instance);
2859 nextInstances.set(key, instance);
2860 ordered.push(instance);
2861 }
2862 for (const [key, instance] of instances) {
2863 if (!nextInstances.has(key)) disposeStructuralKeyedListItem(store, instance);
2864 }
2865 let cursor = startMarker.nextSibling;
2866 for (const instance of ordered) {
2867 if (instance.element !== cursor) parent.insertBefore(instance.element, cursor);
2868 cursor = instance.element.nextSibling;
2869 }
2870 store.elementsByNode = collectElementAnchors();
2871 return nextInstances;
2872 }
2873
2874 function initialListInstances(store, component, node, items) {
2875 const instances = new Map();
2876
2877 for (const [index, item] of items.entries()) {
2878 const key = listItemKey(node, item, index);
2879 const element = store.elementsByNode.get(`${node.item_root}:${key}`);
2880
2881 if (element !== undefined) {
2882 const restored = store.restoredStructuralOccurrencesByParentLocal?.get(
2883 `${component?.instance_id ?? ""}\u001fkeyed:${key}`
2884 );
2885 const instance = { element, item, index, key };
2886 if (restored !== undefined) instance.structural_occurrences = Object.freeze(restored);
2887 instances.set(key, instance);
2888 }
2889 }
2890
2891 return instances;
2892 }
2893
2894 function reconcileKeyedList(store, component, node, startMarker, endMarker, instances, value) {
2895 if (
2896 startMarker.parentNode === null ||
2897 endMarker.parentNode === null ||
2898 startMarker.parentNode !== endMarker.parentNode
2899 ) {
2900 reportDiagnostic(
2901 store.diagnostics,
2902 "PSR_MISSING_LIST_ANCHOR",
2903 "List anchor range was not contiguous in one parent",
2904 {}
2905 );
2906 return instances;
2907 }
2908
2909 const parent = startMarker.parentNode;
2910 const nextInstances = new Map();
2911 const ordered = [];
2912
2913 for (const [index, item] of listItems(value).entries()) {
2914 const key = listItemKey(node, item, index);
2915
2916 if (nextInstances.has(key)) {
2917 reportDiagnostic(
2918 store.diagnostics,
2919 "PSR_DUPLICATE_LIST_KEY",
2920 "List update produced a duplicate key",
2921 { id: node.id, key }
2922 );
2923 continue;
2924 }
2925
2926 let instance = instances.get(key);
2927
2928 if (instance === undefined) {
2929 const element = renderListItemElement(node, item, index, key);
2930
2931 if (element === null) {
2932 reportDiagnostic(
2933 store.diagnostics,
2934 "PSR_INVALID_LIST_TEMPLATE",
2935 "List item template did not produce a root element",
2936 node
2937 );
2938 continue;
2939 }
2940
2941 parent.insertBefore(element, endMarker);
2942 instance = { element, item, index, key };
2943 registerListItemEvents(store, component, instance);
2944 }
2945
2946 instance.item = item;
2947 instance.index = index;
2948 updateListItemTextBindings(node, instance);
2949 updateListItemAttributes(node, instance);
2950 nextInstances.set(key, instance);
2951 ordered.push(instance);
2952 }
2953
2954 for (const [key, instance] of instances) {
2955 if (!nextInstances.has(key)) {
2956 unregisterListItemEvents(store, instance);
2957 instance.element.remove();
2958 }
2959 }
2960
2961 let cursor = startMarker.nextSibling;
2962
2963 for (const instance of ordered) {
2964 if (instance.element !== cursor) {
2965 parent.insertBefore(instance.element, cursor);
2966 }
2967 cursor = instance.element.nextSibling;
2968 }
2969
2970 store.elementsByNode = collectElementAnchors();
2971 return nextInstances;
2972 }
2973
2974 function createRuntimeStore(elementsByNode, diagnostics, computedArtifact, contextArtifact, effectArtifact, componentArtifact, opaqueArtifact) {
2975 const computedEvaluations = new Map();
2976 const storageValues = new Map();
2977 const storageByComponentField = new Map();
2978 const instanceQualifiedState = componentArtifact?.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION;
2979 const invalidationsByStorage = new Map();
2980 const resourceInvalidationsByDeclaration = new Map();
2981 const computedDirty = new Map();
2982
2983 if (!instanceQualifiedState) {
2984 for (const state of computedArtifact?.state ?? []) {
2985 storageValues.set(state.storage, state.initial_value);
2986 storageByComponentField.set(
2987 componentFieldKey(state.component, state.field),
2988 state.storage
2989 );
2990 }
2991 }
2992
2993 for (const invalidation of computedArtifact?.invalidations ?? []) {
2994 invalidationsByStorage.set(invalidation.storage, invalidation.dependents ?? []);
2995 }
2996
2997 for (const invalidation of computedArtifact?.resource_invalidations ?? []) {
2998 resourceInvalidationsByDeclaration.set(invalidation.declaration, invalidation.dependents ?? []);
2999 }
3000
3001 for (const evaluation of computedArtifact?.evaluations ?? []) {
3002 computedEvaluations.set(evaluation.computed, evaluation);
3003 computedDirty.set(evaluation.computed, evaluation.dirty_flag?.initial_value === true);
3004 }
3005
3006 return {
3007 components: new Map(),
3008 bindingsByField: new Map(),
3009 bindingsByStateSlot: new Map(),
3010 bindingsByInstanceComputed: new Map(),
3011 ordinaryEventListenerTypes: new Set(),
3012 actionsByMethod: new Map(),
3013 legacyActionsByComponentMethod: new Map(),
3014 opaqueTerminalsByMethod: new Map((opaqueArtifact?.activations ?? []).map((activation) => [activation.method, activation])),
3015 opaqueActivations: [],
3016 eventsByType: new Map(),
3017 elementsByNode,
3018 diagnostics,
3019 computedArtifact,
3020 contextArtifact,
3021 effectArtifact,
3022 contextSlots: new Map(),
3023 contextConsumerBindings: new Map(),
3024 contextInitialSourceRuns: [],
3025 contextUpdateSourceRuns: [],
3026 contextFailures: [],
3027 computedEvaluations,
3028 computedDirty,
3029 computedValues: new Map(),
3030 computedCaches: new Map(),
3031 computedSlotsByInstanceComputed: new Map(),
3032 computedDirtySlots: new Map(),
3033 storageValues,
3034 storageByComponentField,
3035 stateSlotsByInstanceStorage: new Map(),
3036 instanceQualifiedState,
3037 invalidationsByStorage,
3038 resourceInvalidationsByDeclaration,
3039 computedUpdateRuns: 0,
3040 initialEffectRuns: [],
3041 completedActionEffectRuns: [],
3042 structuralEffectRuns: [],
3043 effectCleanupRuns: [],
3044 effectSubscriptions: new Map(),
3045 effectCleanups: new Map(),
3046 activeEffectInstances: new Map((effectArtifact?.instances ?? []).map((record) => [
3047 record.effect_instance,
3048 Object.freeze({ ...record, structural: false })
3049 ])),
3050 resources: new Map(),
3051 resourceActivationsByInstanceDeclaration: new Map(),
3052 resourceSlots: new Map(),
3053 activeActionBatch: null
3054 };
3055 }
3056
3057 function computedSlotForExecution(store, computed) {
3058 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
3059 if (componentInstanceId === undefined) return undefined;
3060 const slot = store.computedSlotsByInstanceComputed.get(`${componentInstanceId}|${computed}`);
3061 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3062 return slot;
3063 }
3064
3065 function stateSlotForInstanceStorage(store, componentInstanceId, storage) {
3066 const slot = store.stateSlotsByInstanceStorage.get(`${componentInstanceId}|${storage}`);
3067 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3068 return slot;
3069 }
3070
3071 function stateValueForStorage(store, storage) {
3072 if (!store.instanceQualifiedState) return store.storageValues.get(storage);
3073 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
3074 if (componentInstanceId === undefined) throw new PresolveBootError("PSR_MISSING_EXECUTION_CONTEXT");
3075 return store.storageValues.get(
3076 stateSlotForInstanceStorage(store, componentInstanceId, storage).slot_id
3077 );
3078 }
3079
3080 function isComputedDirty(store, computed) {
3081 const slot = computedSlotForExecution(store, computed);
3082 return slot === undefined
3083 ? store.computedDirty.get(computed) === true
3084 : store.computedDirtySlots.get(slot.dirty_slot_id) === true;
3085 }
3086
3087 function setComputedDirty(store, computed, value) {
3088 const slot = computedSlotForExecution(store, computed);
3089 if (slot === undefined) store.computedDirty.set(computed, value);
3090 else store.computedDirtySlots.set(slot.dirty_slot_id, value);
3091 }
3092
3093 function computedValue(store, computed) {
3094 const slot = computedSlotForExecution(store, computed);
3095 return slot === undefined
3096 ? store.computedValues.get(computed)
3097 : store.computedCaches.get(slot.cache_slot_id);
3098 }
3099
3100 function computedValueForInstance(store, componentInstanceId, computed) {
3101 const slot = store.computedSlotsByInstanceComputed.get(`${componentInstanceId}|${computed}`);
3102 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3103 return store.computedCaches.get(slot.cache_slot_id);
3104 }
3105
3106 function notifyComputed(store, computed, value) {
3107 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
3108 if (componentInstanceId === undefined) return;
3109 for (const updateBinding of store.bindingsByInstanceComputed.get(`${componentInstanceId}|${computed}`) ?? []) {
3110 updateBinding(value);
3111 }
3112 }
3113
3114 function registerComputedBinding(store, componentInstanceId, computed, updateBinding) {
3115 const key = `${componentInstanceId}|${computed}`;
3116 const bindings = store.bindingsByInstanceComputed.get(key) ?? [];
3117 bindings.push(updateBinding);
3118 store.bindingsByInstanceComputed.set(key, bindings);
3119 return () => {
3120 const active = store.bindingsByInstanceComputed.get(key);
3121 if (active === undefined) return;
3122 const index = active.indexOf(updateBinding);
3123 if (index < 0) return;
3124 active.splice(index, 1);
3125 if (active.length === 0) store.bindingsByInstanceComputed.delete(key);
3126 };
3127 }
3128
3129 function storeComputedValue(store, evaluation, value) {
3130 const slot = computedSlotForExecution(store, evaluation.computed);
3131 if (slot === undefined) {
3132 store.computedValues.set(evaluation.computed, value);
3133 store.computedCaches.set(evaluation.cache_slot, value);
3134 } else {
3135 store.computedCaches.set(slot.cache_slot_id, value);
3136 }
3137 notifyComputed(store, evaluation.computed, value);
3138 }
3139
3140 function computedOperandValue(store, values, operand) {
3141 if (operand?.kind === "value") {
3142 return values.get(operand.value);
3143 }
3144
3145 if (operand?.kind === "constant") {
3146 return operand.value;
3147 }
3148
3149 if (operand?.kind === "storage") {
3150 return stateValueForStorage(store, operand.storage);
3151 }
3152
3153 return undefined;
3154 }
3155
3156 function computedBinary(operation, left, right) {
3157 switch (operation) {
3158 case "add": return left + right;
3159 case "subtract": return left - right;
3160 case "multiply": return left * right;
3161 case "divide": return left / right;
3162 case "remainder": return left % right;
3163 case "equal": return left === right;
3164 case "not-equal": return left !== right;
3165 case "less-than": return left < right;
3166 case "less-than-or-equal": return left <= right;
3167 case "greater-than": return left > right;
3168 case "greater-than-or-equal": return left >= right;
3169 case "and": return left && right;
3170 case "or": return left || right;
3171 case "nullish-coalesce": return left ?? right;
3172 case "min": return typeof left === "number" && typeof right === "number" ? Math.min(left, right) : undefined;
3173 case "max": return typeof left === "number" && typeof right === "number" ? Math.max(left, right) : undefined;
3174 default: return undefined;
3175 }
3176 }
3177
3178 function computedUnary(operation, value) {
3179 switch (operation) {
3180 case "not": return !value;
3181 case "identity": return +value;
3182 case "negate": return -value;
3183 case "abs": return typeof value === "number" ? Math.abs(value) : undefined;
3184 case "floor": return typeof value === "number" ? Math.floor(value) : undefined;
3185 case "ceil": return typeof value === "number" ? Math.ceil(value) : undefined;
3186 case "round": return typeof value === "number" ? Math.round(value) : undefined;
3187 default: return undefined;
3188 }
3189 }
3190
3191 function executePureProgramInstruction(store, values, instruction, subject) {
3192 if (instruction.kind === "constant") {
3193 values.set(instruction.result, instruction.value);
3194 return true;
3195 }
3196
3197 if (instruction.kind === "load-state") {
3198 values.set(instruction.result, stateValueForStorage(store, instruction.storage));
3199 return true;
3200 }
3201
3202 if (instruction.kind === "load-computed") {
3203 if (isComputedDirty(store, instruction.computed)) {
3204 reportDiagnostic(
3205 store.diagnostics,
3206 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
3207 "Compiler program depended on a value not yet evaluated by the compiler plan",
3208 { subject, dependency: instruction.computed }
3209 );
3210 values.set(instruction.result, undefined);
3211 } else {
3212 values.set(instruction.result, computedValue(store, instruction.computed));
3213 }
3214 return true;
3215 }
3216
3217 if (instruction.kind === "load-resource") {
3218 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
3219 const activationId = typeof componentInstanceId === "string"
3220 ? store.resourceActivationsByInstanceDeclaration.get(`${componentInstanceId}\u001f${instruction.declaration}`)
3221 : undefined;
3222 const resource = activationId === undefined ? undefined : store.resources.get(activationId);
3223 if (resource === undefined) {
3224 reportDiagnostic(
3225 store.diagnostics,
3226 "PSR_RESOURCE_ACTIVATION_MISSING",
3227 "Computed Resource projection lacked exactly one compiler-selected activation",
3228 { subject, component_instance_id: componentInstanceId, declaration: instruction.declaration },
3229 true
3230 );
3231 throw new PresolveBootError("PSR_RESOURCE_ACTIVATION_MISSING");
3232 }
3233 values.set(instruction.result, { data: resource.data, error: resource.error, state: resource.state });
3234 return true;
3235 }
3236
3237 if (instruction.kind === "get-member") {
3238 const object = computedOperandValue(store, values, instruction.object);
3239 const value = object !== null && typeof object === "object"
3240 && Object.prototype.hasOwnProperty.call(object, instruction.property)
3241 ? object[instruction.property]
3242 : undefined;
3243 values.set(instruction.result, value);
3244 return true;
3245 }
3246
3247 if (instruction.kind === "get-index") {
3248 const object = computedOperandValue(store, values, instruction.object);
3249 const index = computedOperandValue(store, values, instruction.index);
3250 const key = typeof index === "string" || (typeof index === "number" && Number.isInteger(index) && index >= 0)
3251 ? String(index)
3252 : null;
3253 const value = key !== null && object !== null && typeof object === "object"
3254 && Object.prototype.hasOwnProperty.call(object, key)
3255 ? object[key]
3256 : undefined;
3257 values.set(instruction.result, value);
3258 return true;
3259 }
3260
3261 if (instruction.kind === "select") {
3262 const condition = computedOperandValue(store, values, instruction.condition);
3263 const value = condition === true
3264 ? computedOperandValue(store, values, instruction.when_true)
3265 : computedOperandValue(store, values, instruction.when_false);
3266 values.set(instruction.result, value);
3267 return true;
3268 }
3269
3270 if (instruction.kind === "template") {
3271 const quasis = instruction.quasis ?? [];
3272 const expressions = instruction.expressions ?? [];
3273 if (quasis.length !== expressions.length + 1) {
3274 reportDiagnostic(
3275 store.diagnostics,
3276 "PSR_INVALID_TEMPLATE_PROGRAM",
3277 "Compiler artifact contained an invalid template interpolation program",
3278 { subject }
3279 );
3280 values.set(instruction.result, undefined);
3281 } else {
3282 let value = quasis[0];
3283 for (let index = 0; index < expressions.length; index += 1) {
3284 value += String(values.get(expressions[index]));
3285 value += quasis[index + 1];
3286 }
3287 values.set(instruction.result, value);
3288 }
3289 return true;
3290 }
3291
3292 if (instruction.kind === "binary") {
3293 values.set(
3294 instruction.result,
3295 computedBinary(
3296 instruction.operation,
3297 computedOperandValue(store, values, instruction.left),
3298 computedOperandValue(store, values, instruction.right)
3299 )
3300 );
3301 return true;
3302 }
3303
3304 if (instruction.kind === "unary") {
3305 values.set(
3306 instruction.result,
3307 computedUnary(
3308 instruction.operation,
3309 computedOperandValue(store, values, instruction.operand)
3310 )
3311 );
3312 return true;
3313 }
3314
3315 if (instruction.kind === "pure-package-call") {
3316 if (instruction.operation === "identity" && instruction.arguments?.length === 1) {
3317 values.set(instruction.result, values.get(instruction.arguments[0]));
3318 } else {
3319 reportDiagnostic(
3320 store.diagnostics,
3321 "PSR_INVALID_PURE_PACKAGE_OPERATION",
3322 "Compiler artifact contained an unsupported pure package operation",
3323 { subject, package: instruction.package, export: instruction.export, operation: instruction.operation }
3324 );
3325 values.set(instruction.result, undefined);
3326 }
3327 return true;
3328 }
3329
3330 return false;
3331 }
3332
3333 function executeComputedProgram(store, evaluation) {
3334 const values = new Map();
3335
3336 for (const instruction of evaluation.program?.instructions ?? []) {
3337 executePureProgramInstruction(store, values, instruction, evaluation.computed);
3338 }
3339
3340 return values.get(evaluation.program?.result);
3341 }
3342
3343 function initialEffectBatches(effectArtifact) {
3344 const batches = new Map();
3345
3346 for (const effect of effectArtifact?.effects ?? []) {
3347 const trigger = effect.initial_trigger;
3348
3349 if (trigger === null || trigger === undefined) {
3350 continue;
3351 }
3352
3353 const effects = batches.get(trigger.effect_batch_index) ?? [];
3354 effects.push(effect);
3355 batches.set(trigger.effect_batch_index, effects);
3356 }
3357
3358 return [...batches.entries()]
3359 .sort(([left], [right]) => left - right)
3360 .map(([batchIndex, effects]) => [
3361 batchIndex,
3362 effects.sort((left, right) =>
3363 (left.declaration_order ?? Number.MAX_SAFE_INTEGER)
3364 - (right.declaration_order ?? Number.MAX_SAFE_INTEGER)
3365 || left.effect.localeCompare(right.effect)
3366 )
3367 ]);
3368 }
3369
3370 function dispatchEffectCapability(store, effect, instruction, values, evidence) {
3371 const runtimeLowering = instruction.runtime_lowering;
3372 const capabilityArguments = (instruction.arguments ?? []).map((operand) =>
3373 computedOperandValue(store, values, operand)
3374 );
3375 const value = computedOperandValue(store, values, instruction.value);
3376
3377 switch (runtimeLowering) {
3378 case "builtin.browser.document.title.assign":
3379 document.title = value;
3380 break;
3381 case "builtin.browser.console.log":
3382 console.log(...capabilityArguments);
3383 break;
3384 case "builtin.browser.console.info":
3385 console.info(...capabilityArguments);
3386 break;
3387 case "builtin.browser.console.warn":
3388 console.warn(...capabilityArguments);
3389 break;
3390 case "builtin.browser.console.error":
3391 console.error(...capabilityArguments);
3392 break;
3393 case "builtin.browser.local_storage.set_item":
3394 localStorage.setItem(...capabilityArguments);
3395 break;
3396 case "builtin.browser.local_storage.remove_item":
3397 localStorage.removeItem(...capabilityArguments);
3398 break;
3399 case "builtin.browser.session_storage.set_item":
3400 sessionStorage.setItem(...capabilityArguments);
3401 break;
3402 case "builtin.browser.session_storage.remove_item":
3403 sessionStorage.removeItem(...capabilityArguments);
3404 break;
3405 default:
3406 reportDiagnostic(
3407 store.diagnostics,
3408 "PSR_UNSUPPORTED_EFFECT_CAPABILITY",
3409 "Effect program referenced an unsupported compiler runtime lowering",
3410 { effect: effect.effect, runtime_lowering: runtimeLowering }
3411 );
3412 return;
3413 }
3414
3415 evidence.capability_operations.push({
3416 operation: instruction.operation,
3417 runtime_lowering: runtimeLowering
3418 });
3419 }
3420
3421 function effectInstanceTargets(store, effect, actionInstanceId = null) {
3422 const records = [...store.activeEffectInstances.values()]
3423 .filter((record) => record.effect === effect.effect)
3424 .filter((record) => actionInstanceId === null || record.component_instance === actionInstanceId)
3425 .sort((left, right) => left.depth - right.depth
3426 || left.declaration_order - right.declaration_order
3427 || left.effect_instance.localeCompare(right.effect_instance));
3428 const hasOwnership = (store.effectArtifact?.instances ?? []).some(
3429 (record) => record.effect === effect.effect
3430 ) || (store.effectArtifact?.structural_templates ?? []).some(
3431 (record) => record.effect === effect.effect
3432 );
3433 return records.length === 0 && !hasOwnership ? [null] : records;
3434 }
3435
3436 function initialEffectInstanceTargets(store, effect) {
3437 const records = [...store.activeEffectInstances.values()]
3438 .filter((record) => record.structural !== true && record.effect === effect.effect)
3439 .sort((left, right) => left.depth - right.depth
3440 || left.declaration_order - right.declaration_order
3441 || left.effect_instance.localeCompare(right.effect_instance));
3442 const hasOwnership = (store.effectArtifact?.instances ?? []).some(
3443 (record) => record.effect === effect.effect
3444 ) || (store.effectArtifact?.structural_templates ?? []).some(
3445 (record) => record.effect === effect.effect
3446 );
3447 return records.length === 0 && !hasOwnership ? [null] : records;
3448 }
3449
3450 function executeEffectProgram(store, effect, evidence, program = effect.program) {
3451 const values = new Map();
3452
3453 for (const instruction of program?.instructions ?? []) {
3454 if (executePureProgramInstruction(store, values, instruction, effect.effect)) {
3455 continue;
3456 }
3457
3458 if (instruction.kind === "capability-call" || instruction.kind === "capability-assign") {
3459 dispatchEffectCapability(store, effect, instruction, values, evidence);
3460 continue;
3461 }
3462
3463 reportDiagnostic(
3464 store.diagnostics,
3465 "PSR_UNSUPPORTED_EFFECT_INSTRUCTION",
3466 "Effect program contained an unsupported compiler instruction",
3467 { effect: effect.effect, kind: instruction.kind }
3468 );
3469 }
3470 }
3471
3472 function runEffect(store, effect, evidence, instance = null) {
3473 const key = instance?.effect_instance ?? effect.effect;
3474 const priorExecutionContext = store.activeExecutionContext;
3475 if (instance !== null) store.activeExecutionContext = { component_instance_id: instance.component_instance };
3476 try {
3477 const cleanup = store.effectCleanups.get(key);
3478 if (cleanup !== undefined) {
3479 const cleanupEvidence = { capability_operations: [] };
3480 executeEffectProgram(store, effect, cleanupEvidence, cleanup);
3481 evidence.cleanup_capability_operations = cleanupEvidence.capability_operations;
3482 store.effectCleanups.delete(key);
3483 }
3484 executeEffectProgram(store, effect, evidence);
3485 if (effect.cleanup_program !== null && effect.cleanup_program !== undefined) {
3486 store.effectCleanups.set(key, effect.cleanup_program);
3487 }
3488 } finally {
3489 store.activeExecutionContext = priorExecutionContext;
3490 }
3491 }
3492
3493 function disposeEffectInstances(store, selected = null) {
3494 const effects = new Map((store.effectArtifact?.effects ?? []).map((effect) => [effect.effect, effect]));
3495 const instances = [...(selected ?? store.activeEffectInstances.values())]
3496 .sort((left, right) => right.depth - left.depth
3497 || right.declaration_order - left.declaration_order
3498 || right.effect_instance.localeCompare(left.effect_instance));
3499 for (const instance of instances) {
3500 const cleanup = store.effectCleanups.get(instance.effect_instance);
3501 const effect = effects.get(instance.effect);
3502 if (cleanup === undefined || effect === undefined) {
3503 store.effectCleanups.delete(instance.effect_instance);
3504 store.activeEffectInstances.delete(instance.effect_instance);
3505 continue;
3506 }
3507 const priorExecutionContext = store.activeExecutionContext;
3508 store.activeExecutionContext = { component_instance_id: instance.component_instance };
3509 try {
3510 const evidence = {
3511 effect: effect.effect,
3512 effect_instance: instance.effect_instance,
3513 capability_operations: []
3514 };
3515 executeEffectProgram(store, effect, evidence, cleanup);
3516 store.effectCleanupRuns.push(evidence);
3517 store.effectCleanups.delete(instance.effect_instance);
3518 } finally {
3519 store.activeExecutionContext = priorExecutionContext;
3520 }
3521 store.activeEffectInstances.delete(instance.effect_instance);
3522 }
3523 }
3524
3525 function installEffectDisposal(store) {
3526 window.addEventListener("pagehide", () => disposeEffectInstances(store), { once: true });
3527 }
3528
3529 function executeInitialEffects(store) {
3530 for (const [effectBatchIndex, effects] of initialEffectBatches(store.effectArtifact)) {
3531 for (const effect of effects) {
3532 for (const instance of initialEffectInstanceTargets(store, effect)) {
3533 const evidence = {
3534 effect: effect.effect,
3535 effect_instance: instance?.effect_instance,
3536 effect_batch_index: effectBatchIndex,
3537 capability_operations: []
3538 };
3539 runEffect(store, effect, evidence, instance);
3540 store.initialEffectRuns.push(evidence);
3541 }
3542 }
3543 }
3544 }
3545
3546 // Resume is an explicit V2 lifecycle phase. It intentionally excludes the
3547 // legacy decorator effects whose restore boundary is frozen by the existing
3548 // resumability contract.
3549 function executeResumeEffects(store) {
3550 for (const [effectBatchIndex, effects] of initialEffectBatches(store.effectArtifact)) {
3551 for (const effect of effects) {
3552 if (effect.run_on_resume !== true) continue;
3553 for (const instance of initialEffectInstanceTargets(store, effect)) {
3554 const evidence = {
3555 effect: effect.effect,
3556 effect_instance: instance?.effect_instance,
3557 effect_batch_index: effectBatchIndex,
3558 capability_operations: []
3559 };
3560 runEffect(store, effect, evidence, instance);
3561 store.initialEffectRuns.push(evidence);
3562 }
3563 }
3564 }
3565 }
3566
3567 function actionEffectBatches(effectArtifact, actionBatchId) {
3568 const batches = new Map();
3569
3570 for (const effect of effectArtifact?.effects ?? []) {
3571 const trigger = (effect.action_batch_triggers ?? []).find(
3572 (candidate) => candidate.action_batch === actionBatchId
3573 );
3574
3575 if (trigger === undefined) {
3576 continue;
3577 }
3578
3579 const effects = batches.get(trigger.effect_batch_index) ?? [];
3580 effects.push(effect);
3581 batches.set(trigger.effect_batch_index, effects);
3582 }
3583
3584 return [...batches.entries()]
3585 .sort(([left], [right]) => left - right)
3586 .map(([batchIndex, effects]) => [
3587 batchIndex,
3588 effects.sort((left, right) =>
3589 (left.declaration_order ?? Number.MAX_SAFE_INTEGER)
3590 - (right.declaration_order ?? Number.MAX_SAFE_INTEGER)
3591 || left.effect.localeCompare(right.effect)
3592 )
3593 ]);
3594 }
3595
3596 function executeCompletedActionEffects(store, actionBatchId) {
3597 for (const [effectBatchIndex, effects] of actionEffectBatches(
3598 store.effectArtifact,
3599 actionBatchId
3600 )) {
3601 for (const effect of effects) {
3602 const actionInstanceId = store.activeExecutionContext?.component_instance_id ?? null;
3603 for (const instance of effectInstanceTargets(store, effect, actionInstanceId)) {
3604 const evidence = {
3605 action_batch_id: actionBatchId,
3606 effect: effect.effect,
3607 effect_instance: instance?.effect_instance,
3608 effect_batch_index: effectBatchIndex,
3609 capability_operations: []
3610 };
3611 runEffect(store, effect, evidence, instance);
3612 store.completedActionEffectRuns.push(evidence);
3613 }
3614 }
3615 }
3616 }
3617
3618 function executeComputedPlan(store, componentInstanceId = null) {
3619 if (store.computedArtifact === null) {
3620 return;
3621 }
3622
3623 const priorExecutionContext = store.activeExecutionContext;
3624 if (componentInstanceId !== null) {
3625 store.activeExecutionContext = { component_instance_id: componentInstanceId };
3626 }
3627
3628 try {
3629 for (const computed of store.computedArtifact.evaluation_order ?? []) {
3630 const evaluation = store.computedEvaluations.get(computed);
3631
3632 if (evaluation === undefined) {
3633 reportDiagnostic(
3634 store.diagnostics,
3635 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
3636 "Compiler plan referenced a missing computed evaluation",
3637 { computed }
3638 );
3639 continue;
3640 }
3641
3642 if (!isComputedDirty(store, computed)) continue;
3643
3644 storeComputedValue(store, evaluation, executeComputedProgram(store, evaluation));
3645 setComputedDirty(store, computed, false);
3646 }
3647 } finally {
3648 store.activeExecutionContext = priorExecutionContext;
3649 }
3650 }
3651
3652 function executeInitialContext(store) {
3653 const sources = new Map((store.contextArtifact?.sources ?? []).map((source) => [source.source, source]));
3654 for (const batch of store.contextArtifact?.initial_batches ?? []) {
3655 for (const sourceId of batch.sources ?? []) {
3656 const source = sources.get(sourceId);
3657 if (source === undefined) { continue; }
3658 const unavailable = (source.required_computed ?? []).some((computed) => store.computedDirty.get(computed) === true);
3659 if (unavailable) {
3660 store.contextFailures.push({ source: source.source, failure: "unavailable-computed-prerequisite" });
3661 continue;
3662 }
3663 const values = new Map();
3664 let initialized = false;
3665 for (const instruction of source.program?.instructions ?? []) {
3666 if (executePureProgramInstruction(store, values, instruction, source.source)) { continue; }
3667 if (instruction.kind === "initialize_context_slot") {
3668 store.contextSlots.set(instruction.slot, computedOperandValue(store, values, instruction.value));
3669 initialized = true;
3670 continue;
3671 }
3672 store.contextFailures.push({ source: source.source, failure: `unsupported-instruction:${String(instruction.kind)}` });
3673 break;
3674 }
3675 if (initialized) { store.contextInitialSourceRuns.push(source.source); }
3676 }
3677 }
3678 for (const consumer of store.contextArtifact?.consumers ?? []) {
3679 store.contextConsumerBindings.set(consumer.consumer, consumer.slot);
3680 if (!store.contextSlots.has(consumer.slot)) {
3681 store.contextFailures.push({ consumer: consumer.consumer, failure: "source-slot-unavailable" });
3682 }
3683 }
3684 }
3685
3686 function executeContextUpdates(store, actionBatchId) {
3687 const update = (store.contextArtifact?.action_updates ?? []).find(
3688 (candidate) => candidate.action_batch === actionBatchId
3689 );
3690 if (update === undefined || update.invalidated_sources.length === 0) { return; }
3691 const sources = new Map((store.contextArtifact?.sources ?? []).map((source) => [source.source, source]));
3692 for (const sourceId of update.invalidated_sources) {
3693 const source = sources.get(sourceId);
3694 if (source === undefined) { continue; }
3695 const values = new Map();
3696 let initialized = false;
3697 for (const instruction of source.program?.instructions ?? []) {
3698 if (executePureProgramInstruction(store, values, instruction, source.source)) { continue; }
3699 if (instruction.kind === "initialize_context_slot") {
3700 store.contextSlots.set(instruction.slot, computedOperandValue(store, values, instruction.value));
3701 initialized = true;
3702 continue;
3703 }
3704 store.contextFailures.push({ action_batch: actionBatchId, source: source.source, failure: `unsupported-update-instruction:${String(instruction.kind)}` });
3705 break;
3706 }
3707 if (initialized) { store.contextUpdateSourceRuns.push({ action_batch: actionBatchId, source: source.source }); }
3708 }
3709 }
3710
3711 function executeComputedUpdateBatches(store) {
3712 if (store.computedArtifact === null) {
3713 return;
3714 }
3715
3716 let executed = false;
3717
3718 for (const batch of store.computedArtifact.update_batches ?? []) {
3719 for (const computed of batch) {
3720 if (!isComputedDirty(store, computed)) {
3721 continue;
3722 }
3723
3724 const evaluation = store.computedEvaluations.get(computed);
3725
3726 if (evaluation === undefined) {
3727 reportDiagnostic(
3728 store.diagnostics,
3729 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
3730 "Compiler update batch referenced a missing computed evaluation",
3731 { computed }
3732 );
3733 continue;
3734 }
3735
3736 storeComputedValue(store, evaluation, executeComputedProgram(store, evaluation));
3737 setComputedDirty(store, computed, false);
3738 executed = true;
3739 }
3740 }
3741
3742 if (executed) {
3743 store.computedUpdateRuns += 1;
3744 }
3745 }
3746
3747 function invalidateResourceComputeds(store, activation) {
3748 const priorExecutionContext = store.activeExecutionContext;
3749 store.activeExecutionContext = { component_instance_id: activation.component_instance };
3750 try {
3751 for (const computed of store.resourceInvalidationsByDeclaration.get(activation.declaration) ?? []) {
3752 setComputedDirty(store, computed, true);
3753 }
3754 executeComputedUpdateBatches(store);
3755 } finally {
3756 store.activeExecutionContext = priorExecutionContext;
3757 }
3758 }
3759
3760 function readField(store, component, field, storageId = null) {
3761 if (store.instanceQualifiedState) {
3762 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
3763 throw new PresolveBootError("PSR_INVALID_STATE_OPERATION");
3764 }
3765 return store.storageValues.get(
3766 stateSlotForInstanceStorage(store, component.instance_id, storageId).slot_id
3767 );
3768 }
3769 if (!(field in component.state)) {
3770 reportDiagnostic(
3771 store.diagnostics,
3772 "PSR_INVALID_STATE_OPERATION",
3773 "Action referenced a missing state field",
3774 { component: component.name, field }
3775 );
3776 return undefined;
3777 }
3778
3779 return component.state[field];
3780 }
3781
3782 function writeField(store, component, field, value, storageId = null) {
3783 if (store.instanceQualifiedState) {
3784 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
3785 throw new PresolveBootError("PSR_INVALID_STATE_OPERATION");
3786 }
3787 const slot = stateSlotForInstanceStorage(store, component.instance_id, storageId);
3788 store.storageValues.set(slot.slot_id, value);
3789 component.state[field] = value;
3790 for (const computed of store.invalidationsByStorage.get(storageId) ?? []) {
3791 setComputedDirty(store, computed, true);
3792 }
3793 notifyField(store, component, field, slot.slot_id);
3794 return;
3795 }
3796 if (!(field in component.state)) {
3797 reportDiagnostic(
3798 store.diagnostics,
3799 "PSR_INVALID_STATE_OPERATION",
3800 "Action referenced a missing state field",
3801 { component: component.name, field }
3802 );
3803 return;
3804 }
3805
3806 component.state[field] = value;
3807 const storage = store.storageByComponentField.get(
3808 componentFieldKey(component.name, field)
3809 );
3810
3811 if (storage !== undefined) {
3812 store.storageValues.set(storage, value);
3813 for (const computed of store.invalidationsByStorage.get(storage) ?? []) {
3814 setComputedDirty(store, computed, true);
3815 }
3816 }
3817 notifyField(store, component, field, null);
3818 }
3819
3820 function notifyField(store, component, field, stateSlotId) {
3821 const bindings = stateSlotId === null
3822 ? store.bindingsByField.get(componentFieldKey(component.name, field))
3823 : store.bindingsByStateSlot.get(stateSlotId);
3824
3825 if (bindings === undefined) {
3826 reportDiagnostic(
3827 store.diagnostics,
3828 "PSR_MISSING_BINDING_ANCHOR",
3829 "State field has no registered binding anchor",
3830 { component: component.name, field }
3831 );
3832 return;
3833 }
3834
3835 for (const updateBinding of bindings) {
3836 updateBinding(stateSlotId === null ? component.state[field] : store.storageValues.get(stateSlotId));
3837 }
3838 }
3839
3840 function registerBinding(store, component, field, updateBinding, storageId = null) {
3841 if (store.instanceQualifiedState) {
3842 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
3843 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
3844 }
3845 const slot = stateSlotForInstanceStorage(store, component.instance_id, storageId);
3846 const bindings = store.bindingsByStateSlot.get(slot.slot_id) ?? [];
3847 bindings.push(updateBinding);
3848 store.bindingsByStateSlot.set(slot.slot_id, bindings);
3849 return () => {
3850 const active = store.bindingsByStateSlot.get(slot.slot_id);
3851 if (active === undefined) return;
3852 const index = active.indexOf(updateBinding);
3853 if (index < 0) return;
3854 active.splice(index, 1);
3855 if (active.length === 0) store.bindingsByStateSlot.delete(slot.slot_id);
3856 };
3857 }
3858 const key = componentFieldKey(component.name, field);
3859 const bindings = store.bindingsByField.get(key) ?? [];
3860 bindings.push(updateBinding);
3861 store.bindingsByField.set(key, bindings);
3862 return () => {
3863 const active = store.bindingsByField.get(key);
3864 if (active === undefined) return;
3865 const index = active.indexOf(updateBinding);
3866 if (index < 0) return;
3867 active.splice(index, 1);
3868 if (active.length === 0) store.bindingsByField.delete(key);
3869 };
3870 }
3871
3872 function registerActions(store, component, manifestComponent) {
3873 const { actionsByMethod, legacyActionsByMethod } = buildActionsByMethod(manifestComponent);
3874
3875 for (const [methodId, actionRecord] of actionsByMethod) {
3876 store.actionsByMethod.set(methodId, actionRecord);
3877 }
3878 for (const [method, actions] of legacyActionsByMethod) {
3879 store.legacyActionsByComponentMethod.set(
3880 legacyComponentMethodKey(component.name, method),
3881 actions
3882 );
3883 }
3884 }
3885
3886 function registerEvent(store, component, event) {
3887 if (event.event !== "click") {
3888 reportDiagnostic(
3889 store.diagnostics,
3890 "PSR_UNRESOLVED_EVENT",
3891 "Unsupported event type in template manifest",
3892 event
3893 );
3894 return;
3895 }
3896
3897 if (legacyEventBinding(event)) {
3898 const method = legacyHandlerMethod(event.handler);
3899 const actions = store.legacyActionsByComponentMethod.get(
3900 legacyComponentMethodKey(component.name, method)
3901 );
3902
3903 if (actions === undefined) {
3904 reportDiagnostic(
3905 store.diagnostics,
3906 "PSR_UNRESOLVED_ACTION",
3907 "Legacy event handler did not resolve to a compiler action",
3908 event
3909 );
3910 return;
3911 }
3912
3913 const eventsByNode = store.eventsByType.get(event.event) ?? new Map();
3914 if (eventsByNode.has(event.node)) {
3915 reportDiagnostic(
3916 store.diagnostics,
3917 "PSR_UNRESOLVED_EVENT",
3918 "Duplicate event registration for template node",
3919 event
3920 );
3921 return;
3922 }
3923
3924 eventsByNode.set(event.node, {
3925 component,
3926 method_id: null,
3927 action_batch_id: null,
3928 actions,
3929 arguments: Array.isArray(event.arguments) ? event.arguments : []
3930 });
3931 store.eventsByType.set(event.event, eventsByNode);
3932 return;
3933 }
3934
3935 const actionRecord = store.actionsByMethod.get(event.method_id)
3936 ?? (store.opaqueTerminalsByMethod.has(event.method_id)
3937 ? { action_batch_id: event.action_batch_id, actions: [] }
3938 : undefined);
3939
3940 if (
3941 actionRecord === undefined ||
3942 actionRecord.action_batch_id !== event.action_batch_id
3943 ) {
3944 reportDiagnostic(
3945 store.diagnostics,
3946 "PSR_UNRESOLVED_ACTION",
3947 "Event handler did not resolve to a compiler action",
3948 event
3949 );
3950 return;
3951 }
3952
3953 const eventsByNode = store.eventsByType.get(event.event) ?? new Map();
3954
3955 if (eventsByNode.has(event.node)) {
3956 reportDiagnostic(
3957 store.diagnostics,
3958 "PSR_UNRESOLVED_EVENT",
3959 "Duplicate event registration for template node",
3960 event
3961 );
3962 return;
3963 }
3964
3965 eventsByNode.set(event.node, {
3966 component,
3967 method_id: event.method_id,
3968 action_batch_id: event.action_batch_id,
3969 actions: actionRecord.actions,
3970 arguments: Array.isArray(event.arguments) ? event.arguments : []
3971 });
3972 store.eventsByType.set(event.event, eventsByNode);
3973 }
3974
3975 function initializeComponentRuntime(
3976 store,
3977 manifestComponent,
3978 bindingAnchors,
3979 conditionalAnchors,
3980 listAnchors
3981 ) {
3982 const component = {
3983 name: manifestComponent.name,
3984 manifest: manifestComponent,
3985 state: {}
3986 };
3987
3988 store.components.set(component.name, component);
3989 registerActions(store, component, manifestComponent);
3990
3991 for (const node of manifestComponent.template?.nodes ?? []) {
3992 if (node.kind === "list") {
3993 const field = fieldNameFromThisMember(node.iterable);
3994
3995 if (field === null) {
3996 continue;
3997 }
3998
3999 if (component.state[field] === undefined) {
4000 component.state[field] = node.initial_value;
4001 }
4002
4003 const start = listAnchors.starts.get(node.start);
4004 const end = listAnchors.ends.get(node.end);
4005
4006 if (start === undefined || end === undefined) {
4007 continue;
4008 }
4009
4010 let instances = initialListInstances(
4011 store,
4012 component,
4013 node,
4014 listItems(component.state[field])
4015 );
4016 for (const instance of instances.values()) {
4017 registerListItemEvents(store, component, instance);
4018 }
4019 registerBinding(store, component, field, (value) => {
4020 instances = reconcileKeyedList(
4021 store,
4022 component,
4023 node,
4024 start.marker,
4025 end.marker,
4026 instances,
4027 value
4028 );
4029 });
4030 continue;
4031 }
4032
4033 if (node.kind === "conditional") {
4034 const field = fieldNameFromThisMember(node.condition);
4035
4036 if (field === null) {
4037 continue;
4038 }
4039
4040 if (component.state[field] === undefined) {
4041 component.state[field] = node.initial_value;
4042 }
4043
4044 const start = conditionalAnchors.starts.get(node.start);
4045 const end = conditionalAnchors.ends.get(node.end);
4046
4047 if (start === undefined || end === undefined) {
4048 continue;
4049 }
4050
4051 registerBinding(store, component, field, (value) => {
4052 replaceConditionalBranch(
4053 store,
4054 start.marker,
4055 end.marker,
4056 value === true ? node.when_true_html : node.when_false_html
4057 );
4058 });
4059 continue;
4060 }
4061
4062 if (node.kind !== "binding") {
4063 continue;
4064 }
4065
4066 const field = fieldNameFromThisMember(node.expression);
4067
4068 if (field === null) {
4069 continue;
4070 }
4071
4072 if (component.state[field] === undefined) {
4073 component.state[field] = node.initial_value;
4074 }
4075
4076 if (node.target === "attribute") {
4077 const element = store.elementsByNode.get(node.element);
4078
4079 if (element === undefined) {
4080 continue;
4081 }
4082
4083 updateAttributeBinding(element, node.attribute, component.state[field]);
4084 registerBinding(store, component, field, (value) => {
4085 updateAttributeBinding(element, node.attribute, value);
4086 });
4087 continue;
4088 }
4089
4090 const anchor = bindingAnchors.get(node.id);
4091
4092 if (anchor === undefined) {
4093 continue;
4094 }
4095
4096 const textNode = anchor.marker.nextSibling;
4097
4098 if (!(textNode instanceof Text)) {
4099 reportDiagnostic(
4100 store.diagnostics,
4101 "PSR_MISSING_BINDING_ANCHOR",
4102 "Binding anchor was not followed by a text node",
4103 node
4104 );
4105 continue;
4106 }
4107
4108 registerBinding(store, component, field, (value) => {
4109 textNode.textContent = formatBindingValue(value);
4110 });
4111 }
4112
4113 return component;
4114 }
4115
4116 function actionDelta(store, action) {
4117 if (action.operation === "increment") {
4118 return 1;
4119 }
4120
4121 if (action.operation === "decrement") {
4122 return -1;
4123 }
4124
4125 if (action.operation === "add_assign" || action.operation === "subtract_assign") {
4126 const operand = Number(action.operand);
4127
4128 if (Number.isNaN(operand)) {
4129 reportDiagnostic(
4130 store.diagnostics,
4131 "PSR_INVALID_STATE_OPERATION",
4132 "Numeric state operation had a non-numeric operand",
4133 action
4134 );
4135 return null;
4136 }
4137
4138 return action.operation === "add_assign" ? operand : -operand;
4139 }
4140
4141 return null;
4142 }
4143
4144 function executeAction(store, component, action, executionContext = null) {
4145 if (
4146 action.operation !== "increment" &&
4147 action.operation !== "decrement" &&
4148 action.operation !== "add_assign" &&
4149 action.operation !== "subtract_assign" &&
4150 action.operation !== "assign" &&
4151 action.operation !== "assign_parameter" &&
4152 action.operation !== "toggle"
4153 ) {
4154 reportDiagnostic(
4155 store.diagnostics,
4156 "PSR_INVALID_STATE_OPERATION",
4157 "Action used an unsupported state operation",
4158 action
4159 );
4160 return;
4161 }
4162
4163 if (action.operation === "toggle") {
4164 const current = readField(store, component, action.field, action.storage_id);
4165
4166 if (typeof current !== "boolean") {
4167 reportDiagnostic(
4168 store.diagnostics,
4169 "PSR_INVALID_STATE_OPERATION",
4170 "Toggle action requires a boolean state field",
4171 action
4172 );
4173 return;
4174 }
4175
4176 writeField(store, component, action.field, !current, action.storage_id);
4177 return;
4178 }
4179
4180 if (action.operation === "assign") {
4181 writeField(store, component, action.field, action.operand, action.storage_id);
4182 return;
4183 }
4184
4185 if (action.operation === "assign_parameter") {
4186 const parameterIndex = Number(action.operand);
4187 const value = executionContext?.arguments?.[parameterIndex];
4188 if (value === undefined || !Number.isInteger(parameterIndex)) {
4189 reportDiagnostic(store.diagnostics, "PSR_INVALID_STATE_OPERATION", "Action parameter assignment had no compiler-projected argument", action);
4190 return;
4191 }
4192 writeField(store, component, action.field, value, action.storage_id);
4193 return;
4194 }
4195
4196 const current = Number(readField(store, component, action.field, action.storage_id));
4197
4198 if (Number.isNaN(current)) {
4199 reportDiagnostic(
4200 store.diagnostics,
4201 "PSR_INVALID_STATE_OPERATION",
4202 "Numeric state operation requires a numeric state field",
4203 action
4204 );
4205 return;
4206 }
4207
4208 const delta = actionDelta(store, action);
4209
4210 if (delta === null) {
4211 return;
4212 }
4213
4214 writeField(store, component, action.field, current + delta, action.storage_id);
4215 }
4216
4217 function executeActions(store, component, actionBatchId, actions, executionContext = null) {
4218 store.activeActionBatch = actionBatchId;
4219 store.activeExecutionContext = executionContext;
4220
4221 try {
4222 for (const action of actions) {
4223 executeAction(store, component, action, executionContext);
4224 }
4225
4226 executeComputedUpdateBatches(store);
4227 executeContextUpdates(store, actionBatchId);
4228 executeCompletedActionEffects(store, actionBatchId);
4229 const methodId = executionContext?.method_id;
4230 if (typeof methodId === "string") executeOpaqueTerminal(store, methodId);
4231 } finally {
4232 store.activeActionBatch = null;
4233 store.activeExecutionContext = null;
4234 refreshComputedDebugState(store);
4235 }
4236 }
4237
4238 function executeOpaqueTerminal(store, methodId) {
4239 const terminal = store.opaqueTerminalsByMethod.get(methodId);
4240 if (terminal === undefined) return;
4241 const evidence = { activation: terminal.id, method: methodId, status: "loading" };
4242 store.opaqueActivations.push(evidence);
4243 import(new URL(terminal.runtime_location, document.baseURI).href)
4244 .then((module) => {
4245 const callable = module?.[terminal.export];
4246 if (typeof callable !== "function") throw new Error("declared export is not callable");
4247 return callable();
4248 })
4249 .then(() => { evidence.status = "complete"; })
4250 .catch((error) => {
4251 evidence.status = "failed";
4252 reportDiagnostic(store.diagnostics, "PSR_OPAQUE_TERMINAL_FAILURE", "A compiler-authorized opaque terminal failed", { activation: terminal.id, message: error instanceof Error ? error.message : String(error) });
4253 });
4254 }
4255
4256 function registerComponentEvents(store, component) {
4257 for (const event of component.manifest.template?.events ?? []) {
4258 registerEvent(store, component, event);
4259 }
4260 }
4261
4262 function registerLegacyComponentEvents(store, component) {
4263 for (const event of component.manifest.template?.events ?? []) {
4264 if (legacyEventBinding(event)) registerEvent(store, component, event);
4265 }
4266 }
4267
4268 function delegatedEventRecord(store, eventType, target) {
4269 const eventsByNode = store.eventsByType.get(eventType);
4270
4271 if (eventsByNode === undefined) {
4272 return null;
4273 }
4274
4275 let current = target instanceof Element ? target : target?.parentElement;
4276
4277 while (current !== null && current !== undefined) {
4278 const nodeId = current.dataset?.presolveNode;
4279
4280 if (nodeId !== undefined) {
4281 const record = eventsByNode.get(nodeId);
4282
4283 if (record !== undefined) {
4284 return record;
4285 }
4286 }
4287
4288 current = current.parentElement;
4289 }
4290
4291 return null;
4292 }
4293
4294 function dispatchDelegatedEvent(store, event) {
4295 const record = delegatedEventRecord(store, event.type, event.target);
4296
4297 if (record === null) {
4298 return;
4299 }
4300
4301 executeActions(store, record.component, record.action_batch_id, record.actions, { arguments: record.arguments, method_id: record.method_id });
4302 }
4303
4304 function installDelegatedEventListeners(store) {
4305 for (const eventType of store.eventsByType.keys()) {
4306 document.addEventListener(eventType, (event) => {
4307 dispatchDelegatedEvent(store, event);
4308 });
4309 }
4310 }
4311
4312 function collectOrdinaryTargetAnchors() {
4313 const targets = new Map();
4314 const duplicates = new Set();
4315 const register = (id, target) => {
4316 if (targets.has(id)) duplicates.add(id);
4317 targets.set(id, target);
4318 };
4319 for (const element of document.querySelectorAll("[data-presolve-ti]")) {
4320 const id = element.getAttribute("data-presolve-ti");
4321 if (id === null) continue;
4322 register(id, element);
4323 }
4324 const conditionalStarts = new Map();
4325 const listStarts = new Map();
4326 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
4327 while (walker.nextNode()) {
4328 const marker = walker.currentNode;
4329 const value = String(marker.nodeValue ?? "");
4330 const conditionalStart = /^presolve-conditional-start:[^:]+:ti:(.+)$/.exec(value);
4331 if (conditionalStart !== null) {
4332 conditionalStarts.set(conditionalStart[1], marker);
4333 continue;
4334 }
4335 const conditionalEnd = /^presolve-conditional-end:[^:]+:ti:(.+)$/.exec(value);
4336 if (conditionalEnd !== null) {
4337 const start = conditionalStarts.get(conditionalEnd[1]);
4338 if (start !== undefined) {
4339 register(conditionalEnd[1], { kind: "conditional", start, end: marker });
4340 conditionalStarts.delete(conditionalEnd[1]);
4341 }
4342 continue;
4343 }
4344 const listStart = /^presolve-ti-target-start:(.+)$/.exec(value);
4345 if (listStart !== null) {
4346 listStarts.set(listStart[1], marker);
4347 continue;
4348 }
4349 const listEnd = /^presolve-ti-target-end:(.+)$/.exec(value);
4350 if (listEnd !== null) {
4351 const start = listStarts.get(listEnd[1]);
4352 if (start !== undefined) {
4353 register(listEnd[1], { kind: "list", start, end: marker });
4354 listStarts.delete(listEnd[1]);
4355 }
4356 }
4357 }
4358 return { targets, duplicates };
4359 }
4360
4361 function ordinaryEventKey(targetId, eventType) {
4362 return `${targetId}\u001f${eventType}`;
4363 }
4364
4365 function ordinaryTextBindingNode(bindingId) {
4366 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
4367 const start = `presolve-ti-binding-start:${bindingId}`;
4368 const end = `presolve-ti-binding-end:${bindingId}`;
4369 let startMarker = null;
4370 while (walker.nextNode()) {
4371 if (walker.currentNode.data === start) { startMarker = walker.currentNode; continue; }
4372 if (startMarker !== null && walker.currentNode.data === end) {
4373 const text = startMarker.nextSibling;
4374 if (text instanceof Text) return text;
4375 if (text === walker.currentNode && startMarker.parentNode !== null) {
4376 const empty = document.createTextNode("");
4377 startMarker.parentNode.insertBefore(empty, walker.currentNode);
4378 return empty;
4379 }
4380 return null;
4381 }
4382 }
4383 return null;
4384 }
4385
4386 function registerOrdinaryBinding(store, binding, artifactBinding) {
4387 const component = store.components.get(binding.component_instance_id);
4388 const field = fieldNameFromThisMember(binding.expression);
4389 const storageId = artifactBinding.state_storage_ids?.length === 1
4390 ? artifactBinding.state_storage_ids[0]
4391 : null;
4392 const computedId = artifactBinding.computed_ids?.length === 1
4393 ? artifactBinding.computed_ids[0]
4394 : null;
4395 if (component === undefined || field === null || (storageId === null) === (computedId === null)) {
4396 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
4397 }
4398 const target = store.templateTargetsById.get(binding.instance_target_id);
4399 const slot = storageId === null
4400 ? null
4401 : stateSlotForInstanceStorage(store, binding.component_instance_id, storageId);
4402 let update = null;
4403 if (binding.kind === "text") {
4404 const text = ordinaryTextBindingNode(binding.instance_binding_id);
4405 if (text !== null) update = (value) => { text.data = formatBindingValue(value); };
4406 } else if ((binding.kind === "attribute" || binding.kind === "property") && target instanceof Element && typeof binding.attribute_name === "string") {
4407 update = (value) => { updateAttributeBinding(target, binding.attribute_name, value); };
4408 } else if (binding.kind === "conditional" && target?.kind === "conditional") {
4409 const nodes = (component.manifest.template?.nodes ?? []).filter(
4410 (node) => node.kind === "conditional" && node.condition === binding.expression
4411 );
4412 if (nodes.length === 1) {
4413 const node = nodes[0];
4414 const fragment = structuralConditionalHostFragment(store, component, node);
4415 if (fragment === null) {
4416 update = (value) => {
4417 replaceConditionalBranch(
4418 store,
4419 target.start,
4420 target.end,
4421 value === true ? node.when_true_html : node.when_false_html
4422 );
4423 };
4424 } else {
4425 update = (value) => {
4426 const branch = value === true ? "true" : "false";
4427 if (target.structural_branch === branch) return;
4428 replaceStructuralConditionalBranch(store, target, component, node, fragment, value);
4429 };
4430 }
4431 }
4432 } else if (binding.kind === "list" && target?.kind === "list") {
4433 if (slot === null) throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
4434 const nodes = (component.manifest.template?.nodes ?? []).filter(
4435 (node) => node.kind === "list" && node.iterable === binding.expression
4436 );
4437 if (nodes.length === 1) {
4438 const node = nodes[0];
4439 const fragment = structuralKeyedHostFragment(store, component, node);
4440 let instances = initialListInstances(
4441 store,
4442 component,
4443 node,
4444 listItems(store.storageValues.get(slot.slot_id))
4445 );
4446 if (fragment === null) for (const instance of instances.values()) registerListItemEvents(store, component, instance);
4447 update = (value) => {
4448 instances = fragment === null
4449 ? reconcileKeyedList(store, component, node, target.start, target.end, instances, value)
4450 : reconcileStructuralKeyedList(store, component, node, fragment, target.start, target.end, instances, value);
4451 };
4452 }
4453 }
4454 if (update === null) throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
4455 if (slot !== null) {
4456 update(store.storageValues.get(slot.slot_id));
4457 return registerBinding(store, component, field, update, storageId);
4458 } else {
4459 update(computedValueForInstance(store, binding.component_instance_id, computedId));
4460 return registerComputedBinding(store, binding.component_instance_id, computedId, update);
4461 }
4462 }
4463
4464 function initializeOrdinaryInstanceRuntime(store, manifest, componentArtifact) {
4465 if (manifest.schema_version !== SUPPORTED_SCHEMA_VERSION) return;
4466 const activeInstances = new Set((componentArtifact.instances ?? []).map((instance) => instance.instance));
4467 // Caller-owned Slot records selected by a structural host are absent until
4468 // that compiler-issued fragment materializes. Registering them during
4469 // ordinary boot would require anchors that the selected branch/list item
4470 // has not attached yet; structuralHostProjectionRecords owns that staging.
4471 const structuralSlotTargets = structuralSlotProjectionTargetIds(componentArtifact);
4472 const targets = (manifest.ordinary_targets ?? []).filter((target) =>
4473 activeInstances.has(target.component_instance_id) && !structuralSlotTargets.has(target.id)
4474 );
4475 const bindings = (manifest.ordinary_bindings ?? []).filter((binding) =>
4476 activeInstances.has(binding.component_instance_id) && !structuralSlotTargets.has(binding.instance_target_id)
4477 );
4478 const events = (manifest.ordinary_events ?? []).filter((event) =>
4479 activeInstances.has(event.component_instance_id) && !structuralSlotTargets.has(event.instance_target_id)
4480 );
4481 const anchors = collectOrdinaryTargetAnchors();
4482 for (const binding of bindings) {
4483 if (binding.kind !== "text" || anchors.targets.has(binding.instance_target_id)) continue;
4484 const text = ordinaryTextBindingNode(binding.instance_binding_id);
4485 if (text !== null) anchors.targets.set(binding.instance_target_id, text);
4486 }
4487 const artifactTargets = new Map((componentArtifact.ordinary_template_targets ?? []).map((target) => [target.id, target]));
4488 const artifactBindings = new Map((componentArtifact.ordinary_template_bindings ?? []).map((binding) => [binding.id, binding]));
4489 const artifactEvents = new Map((componentArtifact.ordinary_template_events ?? []).map((event) => [ordinaryEventKey(event.target_id, event.event_type), event]));
4490 const preserveStructuralRegistry = store.resumeStructuralRegistryPrepared === true;
4491 store.templateTargetsById = anchors.targets;
4492 if (!preserveStructuralRegistry) {
4493 store.ordinaryBindingsById = new Map();
4494 store.ordinaryEventsByTargetAndType = new Map();
4495 }
4496 for (const target of targets) {
4497 const artifactTarget = artifactTargets.get(target.id);
4498 if (artifactTarget === undefined || artifactTarget.component_instance_id !== target.component_instance_id || anchors.duplicates.has(target.id) || !anchors.targets.has(target.id)) {
4499 throw new PresolveBootError("PSR_INVALID_ORDINARY_TARGET");
4500 }
4501 }
4502 for (const binding of bindings) {
4503 const artifactBinding = artifactBindings.get(binding.instance_binding_id);
4504 if (artifactBinding === undefined || artifactBinding.component_instance_id !== binding.component_instance_id || artifactBinding.target_id !== binding.instance_target_id) {
4505 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
4506 }
4507 if (store.ordinaryBindingsById.has(binding.instance_binding_id)) {
4508 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
4509 }
4510 store.ordinaryBindingsById.set(binding.instance_binding_id, {
4511 ...binding,
4512 execution_context: { component_instance_id: binding.component_instance_id }
4513 });
4514 registerOrdinaryBinding(store, binding, artifactBinding);
4515 }
4516 for (const event of events) {
4517 const key = ordinaryEventKey(event.instance_target_id, event.event_type);
4518 const artifactEvent = artifactEvents.get(key);
4519 if (
4520 artifactEvent === undefined
4521 || artifactEvent.component_instance_id !== event.component_instance_id
4522 || JSON.stringify(artifactEvent.arguments ?? []) !== JSON.stringify(event.arguments ?? [])
4523 || store.ordinaryEventsByTargetAndType.has(key)
4524 ) {
4525 throw new PresolveBootError("PSR_INVALID_ORDINARY_EVENT");
4526 }
4527 store.ordinaryEventsByTargetAndType.set(key, event);
4528 }
4529 }
4530
4531 function installResumeDomBindings(store, manifest, componentArtifact) {
4532 const stateBindingIds = new Set(
4533 (componentArtifact.ordinary_template_bindings ?? [])
4534 .filter((binding) => binding.state_storage_ids?.length === 1)
4535 .map((binding) => binding.id)
4536 );
4537 const bindings = (manifest.ordinary_bindings ?? []).filter((binding) =>
4538 stateBindingIds.has(binding.instance_binding_id)
4539 && (binding.kind === "text" || binding.kind === "attribute" || binding.kind === "property"
4540 || (store.structuralOccurrenceResumeActive === true
4541 && (binding.kind === "conditional" || binding.kind === "list")))
4542 );
4543 const targetIds = new Set(bindings.map((binding) => binding.instance_target_id));
4544 for (const event of manifest.ordinary_events ?? []) targetIds.add(event.instance_target_id);
4545 const targets = (manifest.ordinary_targets ?? []).filter((target) => targetIds.has(target.id));
4546 initializeOrdinaryInstanceRuntime(
4547 store,
4548 { ...manifest, ordinary_targets: targets, ordinary_bindings: bindings },
4549 componentArtifact
4550 );
4551 }
4552
4553 function establishResumeEffects(registry, store, effectArtifact) {
4554 for (const effect of effectArtifact?.effects ?? []) {
4555 if (store.effectSubscriptions.has(effect.effect) || registry.effect_subscriptions.has(effect.effect)) {
4556 throw new ResumeBootError("DuplicateIdentity");
4557 }
4558 const subscription = {
4559 effect_instance_id: effect.effect,
4560 scheduler_order: effect.initial_trigger?.effect_batch_index ?? null,
4561 active_after_restore: true,
4562 run_on_restore: effect.run_on_resume === true
4563 };
4564 store.effectSubscriptions.set(effect.effect, subscription);
4565 registry.effect_subscriptions.set(effect.effect, subscription);
4566 }
4567 executeResumeEffects(store);
4568 }
4569
4570 function ordinaryTargetFromEvent(target) {
4571 let current = target instanceof Element ? target : target?.parentElement;
4572 while (current !== null && current !== undefined) {
4573 const targetId = current.getAttribute("data-presolve-ti");
4574 if (targetId !== null) return targetId;
4575 current = current.parentElement;
4576 }
4577 return null;
4578 }
4579
4580 function dispatchOrdinaryInstanceEvent(store, event) {
4581 const targetId = ordinaryTargetFromEvent(event.target);
4582 if (targetId === null) return;
4583 const record = store.ordinaryEventsByTargetAndType.get(ordinaryEventKey(targetId, event.type));
4584 if (record === undefined) return;
4585 const actionRecord = store.actionsByMethod.get(record.handler_method_id)
4586 ?? (store.opaqueTerminalsByMethod.has(record.handler_method_id)
4587 ? { action_batch_id: record.action_batch_id, actions: [] }
4588 : undefined);
4589 const component = store.components.get(record.component_instance_id);
4590 if (actionRecord === undefined || component === undefined || actionRecord.action_batch_id !== record.action_batch_id) {
4591 throw new PresolveBootError("PSR_INVALID_ORDINARY_EVENT");
4592 }
4593 const context = {
4594 component_instance_id: record.component_instance_id,
4595 trigger_target_id: record.instance_target_id,
4596 declaration_event_id: record.declaration_event_id,
4597 action_batch_id: record.action_batch_id,
4598 method_id: record.handler_method_id,
4599 arguments: Array.isArray(record.arguments) ? record.arguments : []
4600 };
4601 executeActions(store, component, record.action_batch_id, actionRecord.actions, context);
4602 }
4603
4604 function installOrdinaryInstanceEventListeners(store) {
4605 for (const key of store.ordinaryEventsByTargetAndType.keys()) {
4606 const eventType = key.slice(key.lastIndexOf("\u001f") + 1);
4607 if (store.ordinaryEventListenerTypes.has(eventType)) continue;
4608 store.ordinaryEventListenerTypes.add(eventType);
4609 document.addEventListener(eventType, (event) => dispatchOrdinaryInstanceEvent(store, event));
4610 }
4611 }
4612
4613 // Forms are initialized exclusively from the compiler artifact and manifest
4614 // bridge. The DOM contributes only the user event value for a known anchor.
4615 function initializeFormsRuntime(store, formsArtifact, manifest, elementsByNode, diagnostics) {
4616 store.forms = new Map();
4617 store.formInstances = new Map();
4618 store.formBindingsByAnchor = new Map();
4619 store.formBindingsByField = new Map();
4620 store.formHostsByAnchor = new Map();
4621 if (formsArtifact === null) return;
4622
4623 const definitions = new Map(formsArtifact.forms.map((form) => [form.id, form]));
4624 for (const instance of formsArtifact.instances) {
4625 const definition = definitions.get(instance.form);
4626 if (definition === undefined) {
4627 reportDiagnostic(diagnostics, "PSR_UNKNOWN_FORM_INSTANCE", "Forms artifact referenced an unknown Form definition", { instance: instance.id, form: instance.form }, true);
4628 continue;
4629 }
4630 const fields = new Map(definition.fields.map((field) => [field.id, {
4631 value: field.initial_value,
4632 initial: field.initial_value,
4633 dirty: false,
4634 touched: false,
4635 validation: []
4636 }]));
4637 store.forms.set(definition.id, definition);
4638 store.formInstances.set(instance.id, {
4639 definition,
4640 instance,
4641 fields,
4642 aggregate_valid: true,
4643 submission: "Idle"
4644 });
4645 }
4646
4647 for (const bridge of manifest.form_bindings ?? []) {
4648 const element = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
4649 ? store.templateTargetsById.get(bridge.instance_target_id)
4650 : elementsByNode.get(bridge.control_anchor);
4651 const formInstance = store.formInstances.get(bridge.form_instance_id);
4652 if (element === undefined || formInstance === undefined) {
4653 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest bridge did not resolve an exact compiler anchor and instance", { bridge }, true);
4654 continue;
4655 }
4656 const binding = formInstance.definition.bindings.find((item) => item.id === bridge.field_binding_id);
4657 if (binding === undefined || binding.field === undefined || binding.channel !== bridge.channel) {
4658 reportDiagnostic(diagnostics, "PSR_UNKNOWN_FORM_BINDING", "Forms manifest bridge did not match an artifact binding", { bridge }, true);
4659 continue;
4660 }
4661 const record = { bridge, binding, element, formInstance };
4662 store.formBindingsByAnchor.set(manifest.schema_version === SUPPORTED_SCHEMA_VERSION ? bridge.instance_target_id : bridge.control_anchor, record);
4663 const key = `${bridge.form_instance_id}|${binding.field}`;
4664 const bindings = store.formBindingsByField.get(key) ?? [];
4665 bindings.push(record);
4666 store.formBindingsByField.set(key, bindings);
4667 writeFormControl(record, formInstance.fields.get(binding.field)?.value);
4668 }
4669
4670 for (const bridge of manifest.form_hosts ?? []) {
4671 const element = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
4672 ? store.templateTargetsById.get(bridge.instance_target_id)
4673 : elementsByNode.get(bridge.host_anchor);
4674 const formInstance = store.formInstances.get(bridge.form_instance_id);
4675 const host = (formsArtifact.hosts ?? []).find((candidate) => candidate.host_anchor === bridge.host_anchor && candidate.form_instance === bridge.form_instance_id);
4676 if (!(element instanceof HTMLFormElement) || formInstance === undefined || host === undefined || host.event !== "submit") {
4677 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms host bridge did not resolve an exact compiler-owned form anchor", { bridge }, true);
4678 continue;
4679 }
4680 const anchor = manifest.schema_version === SUPPORTED_SCHEMA_VERSION ? bridge.instance_target_id : bridge.host_anchor;
4681 store.formHostsByAnchor.set(anchor, { bridge, host, element, formInstance });
4682 element.addEventListener(host.event, (event) => dispatchFormSubmit(store, event, anchor));
4683 }
4684
4685 document.addEventListener("input", (event) => dispatchFormEvent(store, event, false));
4686 document.addEventListener("change", (event) => dispatchFormEvent(store, event, false));
4687 document.addEventListener("focusout", (event) => dispatchFormEvent(store, event, true));
4688 window.__PRESOLVE_FORMS__ = {
4689 resetForm: (instanceId) => resetForm(store, instanceId),
4690 resetField: (instanceId, fieldId) => resetField(store, instanceId, fieldId)
4691 };
4692 }
4693
4694 function dispatchFormSubmit(store, event, anchor) {
4695 const record = store.formHostsByAnchor.get(anchor);
4696 if (record === undefined || event.type !== record.host.event) return;
4697 if (record.host.prevent_default === true) event.preventDefault();
4698 for (const fieldId of record.formInstance.fields.keys()) validateFormField(record.formInstance, fieldId);
4699 if (!record.formInstance.aggregate_valid) { record.formInstance.submission = "Invalid"; return; }
4700 const action = store.actionsByMethod.get(record.host.submit_action);
4701 const component = store.components.get(record.bridge.component_instance_id) ?? action?.component;
4702 if (action === undefined || action.action_batch_id !== record.host.action_batch || component === undefined) {
4703 reportDiagnostic(store.diagnostics, "PSR_UNRESOLVED_FORM_SUBMIT_ACTION", "Submission host did not resolve its exact compiler action", { host: record.host }, true);
4704 record.formInstance.submission = "Failed";
4705 return;
4706 }
4707 record.formInstance.submission = "Submitting";
4708 // Serialization is deliberately compiler-record driven: field values and
4709 // path shape come from the compiler artifact, never DOM scanning.
4710 record.formInstance.serialized = serializeFormInstance(record.formInstance);
4711 executeActions(store, component, record.host.action_batch, action.actions, {
4712 component_instance_id: record.bridge.component_instance_id,
4713 trigger_target_id: record.bridge.instance_target_id,
4714 declaration_event_id: record.host.submit_action,
4715 action_batch_id: record.host.action_batch
4716 });
4717 record.formInstance.submission = "Completed";
4718 }
4719
4720 function serializeFormInstance(formInstance) {
4721 const definition = formInstance.definition;
4722 const fields = new Map((definition.fields ?? []).map((field) => [field.id, field]));
4723 if (definition.serialization?.format === "Json") {
4724 const result = {};
4725 for (const [fieldId, state] of formInstance.fields.entries()) {
4726 const path = fields.get(fieldId)?.path;
4727 if (!Array.isArray(path) || path.length === 0) continue;
4728 let target = result;
4729 for (const segment of path.slice(0, -1)) target = target[segment] ??= {};
4730 target[path[path.length - 1]] = state.value;
4731 }
4732 return result;
4733 }
4734 return [...formInstance.fields.entries()].map(([field, state]) => ({
4735 key: fields.get(field)?.path?.join(".") ?? field,
4736 value: state.value
4737 }));
4738 }
4739
4740 function dispatchFormEvent(store, event, blur) {
4741 const element = event.target;
4742 if (!(element instanceof HTMLElement)) return;
4743 const anchor = store.templateTargetsById instanceof Map
4744 ? ordinaryTargetFromEvent(element)
4745 : element.getAttribute("data-presolve-node");
4746 const record = anchor === null ? undefined : store.formBindingsByAnchor.get(anchor);
4747 if (record === undefined) return;
4748 if (blur) {
4749 const state = record.formInstance.fields.get(record.binding.field);
4750 state.touched = true;
4751 validateFormField(record.formInstance, record.binding.field);
4752 return;
4753 }
4754 const expected = record.binding.channel === "Checked" || record.binding.channel === "RadioValue" ? "change" : "input";
4755 if (event.type !== expected) return;
4756 const value = readFormControl(record);
4757 if (value === undefined) return;
4758 writeFormField(store, record.formInstance, record.binding.field, value);
4759 }
4760
4761 function readFormControl(record) {
4762 const { element, binding } = record;
4763 if (binding.channel === "Checked") return element.checked === true;
4764 if (binding.channel === "NumericValue") {
4765 if (element.value === "") return binding.normalization === "NullableNumber" ? null : undefined;
4766 const value = Number(element.value);
4767 return Number.isFinite(value) ? value : undefined;
4768 }
4769 if (binding.channel === "SelectedValues") return [...element.selectedOptions].map((option) => option.value);
4770 return element.value;
4771 }
4772
4773 function writeFormControl(record, value) {
4774 const { element, binding } = record;
4775 if (binding.channel === "Checked") element.checked = value === true;
4776 else if (binding.channel === "SelectedValues") {
4777 for (const option of element.options ?? []) option.selected = Array.isArray(value) && value.includes(option.value);
4778 } else element.value = value === null ? "" : String(value ?? "");
4779 }
4780
4781 function writeFormField(store, formInstance, fieldId, value) {
4782 const state = formInstance.fields.get(fieldId);
4783 if (state === undefined) return;
4784 state.value = value;
4785 state.dirty = JSON.stringify(value) !== JSON.stringify(state.initial);
4786 validateFormField(formInstance, fieldId);
4787 for (const dependency of formInstance.definition.validation_dependencies ?? []) {
4788 if (dependency.source_field === fieldId) validateFormField(formInstance, dependency.target_field);
4789 }
4790 for (const record of store.formBindingsByField.get(`${formInstance.instance.id}|${fieldId}`) ?? []) writeFormControl(record, value);
4791 }
4792
4793 function validateFormField(formInstance, fieldId) {
4794 const state = formInstance.fields.get(fieldId);
4795 if (state === undefined) return;
4796 const rules = (formInstance.definition.validation_rules ?? []).filter((rule) => rule.target_field === fieldId);
4797 state.validation = rules.filter((rule) => !validateFormRule(formInstance, state.value, rule)).map((rule) => rule.id);
4798 formInstance.aggregate_valid = [...formInstance.fields.values()].every((field) => field.validation.length === 0);
4799 }
4800
4801 function validateFormRule(formInstance, value, rule) {
4802 const dependency = rule.dependency === undefined ? undefined : formInstance.fields.get(rule.dependency)?.value;
4803 if (rule.kind === "Required") return !(value === null || value === undefined || value === "" || (Array.isArray(value) && value.length === 0));
4804 if (rule.kind === "Email") return value === "" || /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(String(value));
4805 if (rule.kind === "Equals") return value === dependency;
4806 if (rule.kind === "NotEquals") return value !== dependency;
4807 return true;
4808 }
4809
4810 function resetField(store, instanceId, fieldId) {
4811 const formInstance = store.formInstances?.get(instanceId);
4812 const state = formInstance?.fields.get(fieldId);
4813 if (state === undefined) return false;
4814 state.value = state.initial; state.dirty = false; state.touched = false; state.validation = [];
4815 for (const record of store.formBindingsByField.get(`${instanceId}|${fieldId}`) ?? []) writeFormControl(record, state.value);
4816 formInstance.aggregate_valid = true;
4817 return true;
4818 }
4819
4820 function resetForm(store, instanceId) {
4821 const formInstance = store.formInstances?.get(instanceId);
4822 if (formInstance === undefined) return false;
4823 for (const fieldId of formInstance.fields.keys()) resetField(store, instanceId, fieldId);
4824 formInstance.submission = "Idle";
4825 return true;
4826 }
4827
4828 function debugComponents(store) {
4829 return [...store.components.values()].map((component) => ({
4830 name: component.name,
4831 state: component.state
4832 }));
4833 }
4834
4835 function debugComputed(store) {
4836 if (store.instanceQualifiedState) {
4837 return [...store.computedSlotsByInstanceComputed.entries()].map(([key, slot]) => ({
4838 component_instance_id: key.slice(0, key.lastIndexOf("|")),
4839 computed: slot.computed_id,
4840 cache_slot: slot.cache_slot_id,
4841 dirty: store.computedDirtySlots.get(slot.dirty_slot_id) === true,
4842 value: store.computedCaches.get(slot.cache_slot_id)
4843 }));
4844 }
4845 return [...store.computedEvaluations.values()].map((evaluation) => ({
4846 computed: evaluation.computed,
4847 cache_slot: evaluation.cache_slot,
4848 dirty: store.computedDirty.get(evaluation.computed) === true,
4849 value: store.computedCaches.get(evaluation.cache_slot)
4850 }));
4851 }
4852
4853 function refreshComputedDebugState(store) {
4854 if (window.__PRESOLVE__?.store !== store) {
4855 return;
4856 }
4857
4858 window.__PRESOLVE__.computed = debugComputed(store);
4859 window.__PRESOLVE__.computed_update_runs = store.computedUpdateRuns;
4860 }
4861
4862 function initialStateSlotValue(slot) {
4863 if (slot.semantic_type !== "number") return slot.initial_value;
4864 const value = Number(slot.initial_value);
4865 if (!Number.isFinite(value)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
4866 return value;
4867 }
4868
4869 function decodeResumeValue(value, codec) {
4870 switch (codec?.kind) {
4871 case "null_codec":
4872 if (value !== null) throw new ResumeBootError("ValueTypeMismatch");
4873 return null;
4874 case "boolean_codec":
4875 if (typeof value !== "boolean") throw new ResumeBootError("ValueTypeMismatch");
4876 return value;
4877 case "number_codec":
4878 if (typeof value !== "number" || !Number.isFinite(value) || Object.is(value, -0)) {
4879 throw new ResumeBootError("ValueTypeMismatch");
4880 }
4881 return value;
4882 case "string_codec":
4883 if (typeof value !== "string") throw new ResumeBootError("ValueTypeMismatch");
4884 return value;
4885 case "array_codec":
4886 if (!Array.isArray(value)) throw new ResumeBootError("ValueTypeMismatch");
4887 return value.map((entry) => decodeResumeValue(entry, codec.value));
4888 case "object_codec": {
4889 if (value === null || typeof value !== "object" || Array.isArray(value)) {
4890 throw new ResumeBootError("ValueTypeMismatch");
4891 }
4892 const properties = codec.value ?? [];
4893 if (!exactObjectKeys(value, properties.map((property) => property.name))) {
4894 throw new ResumeBootError("ValueTypeMismatch");
4895 }
4896 return Object.fromEntries(properties.map((property) => [
4897 property.name,
4898 decodeResumeValue(value[property.name], property.codec)
4899 ]));
4900 }
4901 case "nullable_codec":
4902 return value === null ? null : decodeResumeValue(value, codec.value);
4903 default:
4904 throw new ResumeBootError("ValueCodecFailure");
4905 }
4906 }
4907
4908 function decodeResourceValue(value, codec) {
4909 try {
4910 return decodeResumeValue(value, codec);
4911 } catch (_) {
4912 throw new PresolveBootError("PSR_RESOURCE_VALUE_CODEC_FAILURE");
4913 }
4914 }
4915
4916 function snapshotValuesBySlot(snapshot) {
4917 const values = new Map();
4918 for (const boundary of snapshot.boundaries) {
4919 for (const record of boundary.values) values.set(record.slotId, record.value);
4920 }
4921 return values;
4922 }
4923
4924 function allocateResumeStateComputedStore(
4925 registry,
4926 manifest,
4927 templateManifest,
4928 computedArtifact,
4929 contextArtifact,
4930 effectArtifact,
4931 componentArtifact,
4932 diagnostics
4933 ) {
4934 const store = createRuntimeStore(
4935 collectElementAnchors(),
4936 diagnostics,
4937 computedArtifact,
4938 contextArtifact,
4939 effectArtifact,
4940 componentArtifact
4941 );
4942 store.componentArtifact = componentArtifact;
4943 store.componentInstances = new Map();
4944 store.slotBindings = new Map();
4945 store.componentRegions = new Map((componentArtifact?.structural_programs ?? []).map(
4946 (program) => [program.region, program]
4947 ));
4948 store.structuralOccurrenceTemplatesByInvocation = structuralOccurrenceTemplateRegistry(
4949 templateManifest,
4950 componentArtifact,
4951 computedArtifact
4952 );
4953 store.structuralSlotProjectionPrograms = structuralSlotProjectionRegistry(templateManifest, componentArtifact);
4954 store.instanceContextBindings = new Map(
4955 (componentArtifact?.instance_context_bindings ?? [])
4956 .map((binding) => [binding.consumer_instance, binding])
4957 );
4958 const definitions = new Map(
4959 (templateManifest.components ?? []).map((component) => [component.component_id, component])
4960 );
4961 for (const boundary of manifest.boundaries) {
4962 registry.boundary_records.set(boundary.boundary_id, {
4963 boundary_id: boundary.boundary_id,
4964 kind: boundary.kind,
4965 status: "allocated"
4966 });
4967 }
4968 for (const instance of componentArtifact.instances ?? []) {
4969 const definition = definitions.get(instance.component);
4970 if (definition === undefined) throw new ResumeBootError("ResumeArtifactMismatch");
4971 for (const slot of instance.state_slots ?? []) {
4972 const key = `${instance.instance}|${slot.storage_id}`;
4973 if (store.stateSlotsByInstanceStorage.has(key)) {
4974 throw new ResumeBootError("DuplicateIdentity");
4975 }
4976 store.stateSlotsByInstanceStorage.set(key, slot);
4977 }
4978 for (const slot of instance.computed_slots ?? []) {
4979 const key = `${instance.instance}|${slot.computed_id}`;
4980 if (store.computedSlotsByInstanceComputed.has(key)) {
4981 throw new ResumeBootError("DuplicateIdentity");
4982 }
4983 store.computedSlotsByInstanceComputed.set(key, slot);
4984 store.computedDirtySlots.set(slot.dirty_slot_id, false);
4985 }
4986 const component = {
4987 instance_id: instance.instance,
4988 name: instance.component,
4989 manifest: definition,
4990 state: {}
4991 };
4992 store.components.set(instance.instance, component);
4993 registerActions(store, component, definition);
4994 }
4995 return store;
4996 }
4997
4998 function writeResumeSlot(store, registry, slotSchema, value) {
4999 registry.slot_values.set(slotSchema.slot_id, value);
5000 const existing = slotSchema.existing_storage_slot_id;
5001 const resourceSlot = store.resourceSlots.get(existing);
5002 if (resourceSlot !== undefined) {
5003 if (slotSchema.restore_phase !== "R3") throw new ResumeBootError("RestoreInstructionFailure");
5004 resourceSlot.resource.restoredValues[resourceSlot.kind] = value;
5005 return;
5006 }
5007 if (slotSchema.restore_phase === "R3") {
5008 store.storageValues.set(existing, value);
5009 return;
5010 }
5011 if (slotSchema.restore_phase === "R4") {
5012 const computedSlot = [...store.computedSlotsByInstanceComputed.values()]
5013 .find((slot) => slot.cache_slot_id === existing || slot.dirty_slot_id === existing);
5014 if (computedSlot === undefined) throw new ResumeBootError("UnknownSlot");
5015 if (computedSlot.cache_slot_id === existing) store.computedCaches.set(existing, value);
5016 else store.computedDirtySlots.set(existing, value);
5017 return;
5018 }
5019 if (slotSchema.restore_phase === "R6") {
5020 store.contextSlots.set(existing, value);
5021 return;
5022 }
5023 throw new ResumeBootError("RestoreInstructionFailure");
5024 }
5025
5026 function executeResumeDecodeAndWrites(store, registry, snapshot, phases) {
5027 const snapshotValues = snapshotValuesBySlot(snapshot);
5028 const decoded = new Map();
5029 for (const program of registry.definitions.restorePrograms.values()) {
5030 for (const record of program.instructions ?? []) {
5031 if (!phases.has(record.phase)) continue;
5032 const instruction = record.instruction;
5033 if (instruction.kind === "decode_value") {
5034 const schema = registry.definitions.slots.get(instruction.slot_id);
5035 if (schema === undefined || schema.restore_phase !== record.phase) {
5036 throw new ResumeBootError("RestoreInstructionFailure");
5037 }
5038 if (!snapshotValues.has(instruction.slot_id)) throw new ResumeBootError("UnknownSlot");
5039 decoded.set(
5040 instruction.slot_id,
5041 decodeResumeValue(snapshotValues.get(instruction.slot_id), instruction.codec)
5042 );
5043 } else if (instruction.kind === "write_slot") {
5044 const schema = registry.definitions.slots.get(instruction.slot_id);
5045 if (schema === undefined || !decoded.has(instruction.slot_id)) {
5046 throw new ResumeBootError("RestoreInstructionFailure");
5047 }
5048 writeResumeSlot(store, registry, schema, decoded.get(instruction.slot_id));
5049 } else {
5050 throw new ResumeBootError("RestoreInstructionFailure");
5051 }
5052 }
5053 }
5054 }
5055
5056 function synchronizeRestoredComponentState(store, computedArtifact, componentArtifact) {
5057 const fieldsByStorage = new Map(
5058 (computedArtifact?.state ?? []).map((state) => [state.storage, state.field])
5059 );
5060 for (const instance of componentArtifact.instances ?? []) {
5061 const component = store.components.get(instance.instance);
5062 if (component === undefined) throw new ResumeBootError("ResumeArtifactMismatch");
5063 for (const slot of instance.state_slots ?? []) {
5064 const field = fieldsByStorage.get(slot.storage_id);
5065 if (field === undefined || !store.storageValues.has(slot.slot_id)) {
5066 throw new ResumeBootError("UnknownSlot");
5067 }
5068 component.state[field] = store.storageValues.get(slot.slot_id);
5069 }
5070 }
5071 }
5072
5073 function executeResumeComputedRecomputation(store, registry) {
5074 for (const schema of registry.definitions.slots.values()) {
5075 if (schema.restore_phase !== "R5") continue;
5076 const computedSlot = [...store.computedSlotsByInstanceComputed.values()]
5077 .find((slot) =>
5078 slot.cache_slot_id === schema.existing_storage_slot_id
5079 || slot.dirty_slot_id === schema.existing_storage_slot_id
5080 );
5081 if (computedSlot === undefined) throw new ResumeBootError("UnknownSlot");
5082 if (computedSlot.dirty_slot_id === schema.existing_storage_slot_id) {
5083 store.computedDirtySlots.set(computedSlot.dirty_slot_id, true);
5084 }
5085 }
5086 const recomputed = new Set();
5087 for (const program of registry.definitions.restorePrograms.values()) {
5088 for (const record of program.instructions ?? []) {
5089 if (record.phase !== "R5") continue;
5090 const instruction = record.instruction;
5091 if (instruction.kind !== "recompute_computed") {
5092 throw new ResumeBootError("RestoreInstructionFailure");
5093 }
5094 const key = `${instruction.component_instance_id}|${instruction.computed_id}`;
5095 const slot = store.computedSlotsByInstanceComputed.get(key);
5096 const evaluation = store.computedEvaluations.get(instruction.computed_id);
5097 if (slot === undefined || evaluation === undefined || recomputed.has(key)) {
5098 throw new ResumeBootError("RestoreInstructionFailure");
5099 }
5100 const prior = store.activeExecutionContext;
5101 store.activeExecutionContext = {
5102 component_instance_id: instruction.component_instance_id
5103 };
5104 try {
5105 const value = executeComputedProgram(store, evaluation);
5106 storeComputedValue(store, evaluation, value);
5107 setComputedDirty(store, instruction.computed_id, false);
5108 const cacheSchema = [...registry.definitions.slots.values()]
5109 .find((schema) => schema.existing_storage_slot_id === slot.cache_slot_id);
5110 const dirtySchema = [...registry.definitions.slots.values()]
5111 .find((schema) => schema.existing_storage_slot_id === slot.dirty_slot_id);
5112 if (cacheSchema === undefined || dirtySchema === undefined) {
5113 throw new ResumeBootError("UnknownSlot");
5114 }
5115 registry.slot_values.set(cacheSchema.slot_id, value);
5116 registry.slot_values.set(dirtySchema.slot_id, false);
5117 recomputed.add(key);
5118 } finally {
5119 store.activeExecutionContext = prior;
5120 }
5121 }
5122 }
5123 return [...recomputed];
5124 }
5125
5126 function executeResumeContextBindings(store, registry, componentArtifact) {
5127 const expected = new Map(
5128 (componentArtifact?.instance_context_bindings ?? [])
5129 .map((binding) => [binding.consumer_instance, binding])
5130 );
5131 for (const program of registry.definitions.restorePrograms.values()) {
5132 for (const record of program.instructions ?? []) {
5133 if (record.phase !== "R7") continue;
5134 const instruction = record.instruction;
5135 if (instruction.kind !== "bind_context_consumer") {
5136 throw new ResumeBootError("RestoreInstructionFailure");
5137 }
5138 const binding = expected.get(instruction.consumer_instance_id);
5139 if (
5140 binding === undefined
5141 || binding.selected_source !== instruction.selected_source
5142 || (binding.provider_source ?? null) !== (instruction.provider_instance_id ?? null)
5143 || binding.runtime_slot !== instruction.value_slot_id
5144 || !store.contextSlots.has(instruction.value_slot_id)
5145 || store.contextConsumerBindings.has(instruction.consumer_instance_id)
5146 ) {
5147 throw new ResumeBootError("ResumeArtifactMismatch");
5148 }
5149 const installed = {
5150 consumer_instance_id: instruction.consumer_instance_id,
5151 selected_source: instruction.selected_source,
5152 provider_instance_id: instruction.provider_instance_id ?? null,
5153 value_slot_id: instruction.value_slot_id
5154 };
5155 store.contextConsumerBindings.set(
5156 instruction.consumer_instance_id,
5157 instruction.value_slot_id
5158 );
5159 registry.context_bindings.set(instruction.consumer_instance_id, installed);
5160 }
5161 }
5162 if (store.contextConsumerBindings.size !== expected.size) {
5163 throw new ResumeBootError("ResumeArtifactMismatch");
5164 }
5165 }
5166
5167 function collectExactResumeAnchors(manifest) {
5168 const elements = new Map();
5169 for (const element of document.querySelectorAll("[data-presolve-r]")) {
5170 const id = element.getAttribute("data-presolve-r");
5171 if (elements.has(id)) throw new ResumeBootError("DuplicateIdentity");
5172 elements.set(id, element);
5173 }
5174 const comments = new Map();
5175 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
5176 for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
5177 const value = node.nodeValue ?? "";
5178 if (!value.startsWith("presolve-r-start:") && !value.startsWith("presolve-r-end:")) continue;
5179 const id = value.slice(value.indexOf(":") + 1);
5180 if (comments.has(id)) throw new ResumeBootError("DuplicateIdentity");
5181 comments.set(id, node);
5182 }
5183 const anchors = new Map();
5184 for (const anchor of manifest.anchors) {
5185 const node = anchor.kind === "structural_start" || anchor.kind === "structural_end"
5186 ? comments.get(anchor.anchor_id)
5187 : elements.get(anchor.anchor_id);
5188 if (anchor.required && node === undefined) throw new ResumeBootError("MissingAnchor");
5189 if (node !== undefined) anchors.set(anchor.anchor_id, node);
5190 }
5191 return anchors;
5192 }
5193
5194 function canonicalResumeValueEqual(left, right) {
5195 return JSON.stringify(left) === JSON.stringify(right);
5196 }
5197
5198 function restoreResumeComponentsSlotsAndStructure(
5199 manifest,
5200 registry,
5201 store,
5202 componentArtifact
5203 ) {
5204 const componentRecords = new Map();
5205 const slotRecords = new Map();
5206 const structuralRecords = new Map();
5207 for (const item of manifest.phase_i_component_resume_records ?? []) {
5208 if (item.record_kind === "component_instance") {
5209 const record = item.component_instance;
5210 if (componentRecords.has(record.instance)) throw new ResumeBootError("DuplicateIdentity");
5211 componentRecords.set(record.instance, record);
5212 } else if (item.record_kind === "slot_binding") {
5213 const record = item.slot_binding;
5214 if (slotRecords.has(record.binding)) throw new ResumeBootError("DuplicateIdentity");
5215 slotRecords.set(record.binding, record);
5216 } else if (item.record_kind === "structural_region") {
5217 const record = item.structural_region;
5218 if (structuralRecords.has(record.region)) throw new ResumeBootError("DuplicateIdentity");
5219 structuralRecords.set(record.region, record);
5220 }
5221 }
5222
5223 const planned = new Map(
5224 (componentArtifact.instances ?? []).map((instance) => [instance.instance, instance])
5225 );
5226 const templateInstances = new Set(
5227 (componentArtifact.structural_programs ?? [])
5228 .flatMap((program) => program.template_instances ?? [])
5229 );
5230 if (componentRecords.size !== planned.size + templateInstances.size) {
5231 throw new ResumeBootError("ResumeArtifactMismatch");
5232 }
5233 for (const record of componentRecords.values()) {
5234 const instance = planned.get(record.instance);
5235 if (instance !== undefined) {
5236 if (
5237 record.component !== instance.component
5238 || (record.parent_instance ?? null) !== (instance.parent ?? null)
5239 || (record.structural_region ?? null) !== (instance.structural_region ?? null)
5240 || record.active_status !== "active"
5241 ) {
5242 throw new ResumeBootError("ResumeArtifactMismatch");
5243 }
5244 } else if (!templateInstances.has(record.instance) || record.active_status !== "inactive") {
5245 throw new ResumeBootError("ResumeArtifactMismatch");
5246 }
5247 const runtimeRecord = { ...record, status: record.active_status };
5248 registry.component_records.set(record.instance, runtimeRecord);
5249 store.componentInstances.set(record.instance, runtimeRecord);
5250 }
5251
5252 const expectedSlots = new Map(
5253 (componentArtifact.slot_binding_programs ?? []).map((binding) => [binding.binding, binding])
5254 );
5255 if (slotRecords.size !== expectedSlots.size) throw new ResumeBootError("ResumeArtifactMismatch");
5256 for (const record of slotRecords.values()) {
5257 const binding = expectedSlots.get(record.binding);
5258 if (
5259 binding === undefined
5260 || record.caller_instance !== binding.caller_instance
5261 || record.callee_instance !== binding.callee_instance
5262 ) {
5263 throw new ResumeBootError("ResumeArtifactMismatch");
5264 }
5265 store.slotBindings.set(record.binding, binding);
5266 }
5267
5268 const expectedRegions = new Map(
5269 (componentArtifact.structural_programs ?? []).map((program) => [program.region, program])
5270 );
5271 if (structuralRecords.size !== expectedRegions.size) {
5272 throw new ResumeBootError("ResumeArtifactMismatch");
5273 }
5274 for (const record of structuralRecords.values()) {
5275 const program = expectedRegions.get(record.region);
5276 if (program === undefined || record.active_status !== "inactive") {
5277 throw new ResumeBootError("ResumeArtifactMismatch");
5278 }
5279 const runtimeRecord = { ...record, program, selection_value: undefined };
5280 registry.structural_records.set(record.region, runtimeRecord);
5281 }
5282
5283 const restoredRegions = new Map();
5284 for (const program of registry.definitions.restorePrograms.values()) {
5285 for (const record of program.instructions ?? []) {
5286 if (record.phase !== "R9") continue;
5287 const instruction = record.instruction;
5288 if (
5289 instruction.kind !== "restore_structural_selection"
5290 || (restoredRegions.has(instruction.region_id)
5291 && restoredRegions.get(instruction.region_id) !== instruction.slot_id)
5292 ) {
5293 throw new ResumeBootError("RestoreInstructionFailure");
5294 }
5295 if (restoredRegions.has(instruction.region_id)) continue;
5296 const runtimeRecord = registry.structural_records.get(instruction.region_id);
5297 const schema = registry.definitions.slots.get(instruction.slot_id);
5298 const value = registry.slot_values.get(instruction.slot_id);
5299 if (runtimeRecord === undefined || schema === undefined || value === undefined) {
5300 throw new ResumeBootError("ResumeArtifactMismatch");
5301 }
5302 const stateSlot = (componentArtifact.instances ?? [])
5303 .flatMap((instance) => instance.state_slots ?? [])
5304 .find((slot) => slot.slot_id === schema.existing_storage_slot_id);
5305 if (stateSlot === undefined || !canonicalResumeValueEqual(value, stateSlot.initial_value)) {
5306 throw new ResumeBootError("StructuralStateMismatch");
5307 }
5308 runtimeRecord.selection_value = value;
5309 restoredRegions.set(instruction.region_id, instruction.slot_id);
5310 }
5311 }
5312 if (restoredRegions.size !== expectedRegions.size) {
5313 throw new ResumeBootError("ResumeArtifactMismatch");
5314 }
5315 store.resumeAnchors = collectExactResumeAnchors(manifest);
5316 }
5317
5318 function restoreResumeStructuralOccurrences(snapshot, registry, store, templateManifest, computedArtifact, componentArtifact) {
5319 if (!(store.structuralOccurrenceTemplatesByInvocation instanceof Map)
5320 || !(store.componentRegions instanceof Map)) {
5321 throw new ResumeBootError("ResumeArtifactMismatch");
5322 }
5323 const templatesByInstance = new Map();
5324 for (const template of store.structuralOccurrenceTemplatesByInvocation.values()) {
5325 const templateInstance = template?.occurrence?.template_instance;
5326 if (typeof templateInstance !== "string" || templatesByInstance.has(templateInstance)) {
5327 throw new ResumeBootError("ResumeArtifactMismatch");
5328 }
5329 templatesByInstance.set(templateInstance, template);
5330 }
5331 const staticInstances = new Set((componentArtifact.instances ?? []).map((instance) => instance.instance));
5332 const snapshotOccurrences = snapshot.structuralOccurrences ?? [];
5333 store.structuralOccurrenceResumeActive = snapshotOccurrences.length > 0;
5334 const snapshotIdentities = new Set(snapshotOccurrences.map((record) => record.occurrenceIdentity));
5335 const domIdentities = new Set();
5336 const addDomIdentity = (identity) => {
5337 if (typeof identity !== "string" || !identity.startsWith(STRUCTURAL_OCCURRENCE_IDENTITY_PREFIX)
5338 || domIdentities.has(identity)) return;
5339 domIdentities.add(identity);
5340 addDomIdentity(decodeStructuralOccurrenceIdentity(identity).parent_scope);
5341 };
5342 for (const target of document.querySelectorAll("[data-presolve-ti]")) {
5343 const value = target.getAttribute("data-presolve-ti") ?? "";
5344 const marker = "/template-target:";
5345 const index = value.indexOf(marker);
5346 if (index !== -1) addDomIdentity(value.slice(0, index));
5347 }
5348 if (domIdentities.size !== snapshotIdentities.size
5349 || [...domIdentities].some((identity) => !snapshotIdentities.has(identity))) {
5350 throw new ResumeBootError("StructuralOccurrenceAnchorMismatch");
5351 }
5352 const restored = new Map();
5353 const byParentLocal = new Map();
5354 store.restoredStructuralOccurrencesByParentLocal = new Map();
5355 const fieldsByComponentStorage = new Map();
5356 for (const state of computedArtifact?.state ?? []) {
5357 fieldsByComponentStorage.set(`${state.component}\u001f${state.storage}`, state.field);
5358 }
5359 store.templateTargetsById = new Map();
5360 store.ordinaryBindingsById = new Map();
5361 store.ordinaryEventsByTargetAndType = new Map();
5362 store.resumeStructuralRegistryPrepared = true;
5363 for (const record of snapshotOccurrences) {
5364 const decoded = decodeStructuralOccurrenceIdentity(record.occurrenceIdentity);
5365 if (decoded.template_instance !== record.templateInstance
5366 || decoded.parent_scope !== record.parentScope
5367 || decoded.region !== record.structuralRegion
5368 || decoded.local_occurrence !== record.localOccurrence) {
5369 throw new ResumeBootError("StructuralOccurrenceIdentityMismatch");
5370 }
5371 const template = templatesByInstance.get(record.templateInstance);
5372 if (template === undefined || template.structural_region !== record.structuralRegion) {
5373 throw new ResumeBootError("StructuralOccurrenceIdentityMismatch");
5374 }
5375 const parentIsStatic = staticInstances.has(record.parentScope);
5376 const parent = restored.get(record.parentScope);
5377 if (!parentIsStatic && parent === undefined) {
5378 throw new ResumeBootError("StructuralOccurrenceParentMismatch");
5379 }
5380 if (parent !== undefined && template.occurrence.parent_template_instance !== parent.template_instance) {
5381 throw new ResumeBootError("StructuralOccurrenceIdentityMismatch");
5382 }
5383 if (parentIsStatic && template.occurrence.parent_template_instance !== record.parentScope) {
5384 throw new ResumeBootError("StructuralOccurrenceIdentityMismatch");
5385 }
5386 const records = deriveStructuralOccurrenceRecords(template, record.occurrenceIdentity);
5387 const expectedState = new Map();
5388 for (const slot of records.state_slots) {
5389 if (slot.serializable !== true || slot.resume_codec === undefined) {
5390 throw new ResumeBootError("StructuralStateUnsupported");
5391 }
5392 expectedState.set(slot.slot_id, slot);
5393 }
5394 if (expectedState.size !== record.state.length) throw new ResumeBootError("StructuralStateMismatch");
5395 const staged = stageStructuralOccurrenceRecords(store, records);
5396 let registration = null;
5397 let effects = null;
5398 try {
5399 for (const state of record.state) {
5400 const slot = expectedState.get(state.slotId);
5401 if (slot === undefined) throw new ResumeBootError("StructuralStateMismatch");
5402 const value = decodeResumeValue(state.value, slot.resume_codec);
5403 store.storageValues.set(slot.slot_id, value);
5404 const field = fieldsByComponentStorage.get(`${records.definition.name}\u001f${slot.storage_id}`);
5405 const component = store.components.get(record.occurrenceIdentity);
5406 if (field === undefined || component === undefined) throw new ResumeBootError("StructuralStateMismatch");
5407 component.state[field] = value;
5408 }
5409 registration = registerStructuralOccurrenceRecords(store, staged);
5410 effects = activateStructuralEffectInstances(store, staged, true);
5411 const transaction = Object.freeze({ ...staged, registration, effects, dispose: () => {
5412 for (const child of [...(transaction.children ?? [])].reverse()) child.dispose();
5413 effects?.dispose();
5414 registration?.rollback();
5415 staged.rollback();
5416 }, children: [] });
5417 restored.set(record.occurrenceIdentity, transaction);
5418 registry.structural_occurrences.set(record.occurrenceIdentity, transaction);
5419 const key = `${record.parentScope}\u001f${record.localOccurrence}`;
5420 const siblings = byParentLocal.get(key) ?? [];
5421 siblings.push(transaction);
5422 byParentLocal.set(key, siblings);
5423 if (parent !== undefined) parent.children.push(transaction);
5424 } catch (error) {
5425 effects?.dispose();
5426 registration?.rollback();
5427 staged.rollback();
5428 throw error;
5429 }
5430 }
5431 for (const [key, transactions] of byParentLocal) {
5432 store.restoredStructuralOccurrencesByParentLocal.set(key, Object.freeze(transactions));
5433 }
5434 const anchors = collectOrdinaryTargetAnchors();
5435 for (const target of templateManifest.ordinary_targets ?? []) {
5436 const artifact = (componentArtifact.ordinary_template_targets ?? []).find((candidate) => candidate.id === target.id);
5437 if (artifact === undefined || artifact.template_entity_id !== target.template_entity_id) continue;
5438 const host = anchors.targets.get(target.id);
5439 if (host?.kind !== "conditional") continue;
5440 const program = [...store.componentRegions.values()].find((candidate) =>
5441 candidate.host_template_entity === artifact.template_entity_id
5442 && candidate.host_component === artifact.component_id
5443 );
5444 const component = store.components.get(target.component_instance_id);
5445 const binding = (templateManifest.ordinary_bindings ?? []).find((candidate) =>
5446 candidate.instance_target_id === target.id && candidate.kind === "conditional"
5447 );
5448 const field = fieldNameFromThisMember(binding?.expression);
5449 if (program === undefined || component === undefined || field === null) {
5450 throw new ResumeBootError("ResumeArtifactMismatch");
5451 }
5452 host.structural_branch = component.state[field] === true ? "true" : "false";
5453 host.structural_occurrences = store.restoredStructuralOccurrencesByParentLocal.get(
5454 `${target.component_instance_id}\u001fconditional:${host.structural_branch}`
5455 ) ?? Object.freeze([]);
5456 }
5457 }
5458
5459 function restoreResumeForms(templateManifest, snapshot, registry, store, formsArtifact) {
5460 const restoreRecords = [...registry.definitions.restorePrograms.values()]
5461 .flatMap((program) => program.instructions ?? [])
5462 .filter((record) => ["R11", "R12", "R13", "R14", "R15"].includes(record.phase));
5463 if (formsArtifact === null) {
5464 if (restoreRecords.length !== 0) throw new ResumeBootError("ResumeArtifactMismatch");
5465 return;
5466 }
5467 const targets = collectOrdinaryTargetAnchors();
5468 const definitions = new Map((formsArtifact.forms ?? []).map((form) => [form.id, form]));
5469 const instances = new Map((formsArtifact.instances ?? []).map((instance) => [instance.id, instance]));
5470 if (definitions.size !== (formsArtifact.forms ?? []).length || instances.size !== (formsArtifact.instances ?? []).length) {
5471 throw new ResumeBootError("DuplicateIdentity");
5472 }
5473 store.forms = new Map();
5474 store.formInstances = new Map();
5475 store.formBindingsByAnchor = new Map();
5476 store.formBindingsByField = new Map();
5477 store.formHostsByAnchor = new Map();
5478 const slotOwners = new Map();
5479 for (const instance of instances.values()) {
5480 const definition = definitions.get(instance.form);
5481 if (definition === undefined || store.formInstances.has(instance.id)) {
5482 throw new ResumeBootError("ResumeArtifactMismatch");
5483 }
5484 const fields = new Map((definition.fields ?? []).map((field) => [field.id, {
5485 value: field.initial_value,
5486 initial: field.initial_value,
5487 dirty: false,
5488 touched: false,
5489 validation: []
5490 }]));
5491 if (fields.size !== (definition.fields ?? []).length) throw new ResumeBootError("DuplicateIdentity");
5492 const runtime = { definition, instance, fields, aggregate_valid: true, submission: "Idle" };
5493 store.forms.set(definition.id, definition);
5494 store.formInstances.set(instance.id, runtime);
5495 registry.form_records.set(instance.id, runtime);
5496 for (const slots of instance.field_slots ?? []) {
5497 if (!fields.has(slots.field)) throw new ResumeBootError("ResumeArtifactMismatch");
5498 for (const [kind, slot] of [["value", slots.value], ["dirty", slots.dirty], ["touched", slots.touched], ["validation", slots.validation]]) {
5499 if (slotOwners.has(slot)) throw new ResumeBootError("DuplicateIdentity");
5500 slotOwners.set(slot, { instance: runtime, field: slots.field, kind });
5501 }
5502 }
5503 if (slotOwners.has(instance.aggregate_validation_slot) || slotOwners.has(instance.submission_slot)) {
5504 throw new ResumeBootError("DuplicateIdentity");
5505 }
5506 slotOwners.set(instance.aggregate_validation_slot, { instance: runtime, field: null, kind: "aggregate" });
5507 slotOwners.set(instance.submission_slot, { instance: runtime, field: null, kind: "submission" });
5508 }
5509
5510 const snapshotValues = snapshotValuesBySlot(snapshot);
5511 const restored = new Set();
5512 for (const record of restoreRecords) {
5513 if (record.phase === "R15") continue;
5514 const instruction = record.instruction;
5515 if (instruction.kind !== "decode_value") continue;
5516 const schema = registry.definitions.slots.get(instruction.slot_id);
5517 if (schema === undefined || schema.restore_phase !== record.phase || !snapshotValues.has(instruction.slot_id)) {
5518 throw new ResumeBootError("RestoreInstructionFailure");
5519 }
5520 const owner = slotOwners.get(schema.existing_storage_slot_id);
5521 if (owner === undefined || restored.has(instruction.slot_id)) throw new ResumeBootError("ResumeArtifactMismatch");
5522 const value = decodeResumeValue(snapshotValues.get(instruction.slot_id), instruction.codec);
5523 const field = owner.field === null ? undefined : owner.instance.fields.get(owner.field);
5524 if (owner.kind === "value" && field !== undefined) field.value = value;
5525 else if (owner.kind === "dirty" && field !== undefined && typeof value === "boolean") field.dirty = value;
5526 else if (owner.kind === "touched" && field !== undefined && typeof value === "boolean") field.touched = value;
5527 else if (owner.kind === "validation" && field !== undefined && Array.isArray(value) && value.every((item) => typeof item === "string")) field.validation = value;
5528 else if (owner.kind === "aggregate" && typeof value === "boolean") owner.instance.aggregate_valid = value;
5529 else if (owner.kind === "submission" && ["Idle", "Completed", "Failed", "Invalid"].includes(value)) owner.instance.submission = value;
5530 else throw new ResumeBootError(owner.kind === "submission" ? "UnstableFormSubmission" : "ValueTypeMismatch");
5531 registry.slot_values.set(instruction.slot_id, value);
5532 restored.add(instruction.slot_id);
5533 }
5534 const expectedSlots = [...registry.definitions.slots.values()]
5535 .filter((schema) => ["R11", "R12", "R13", "R14"].includes(schema.restore_phase));
5536 if (restored.size !== expectedSlots.length) throw new ResumeBootError("RestoreInstructionFailure");
5537
5538 for (const bridge of templateManifest.form_bindings ?? []) {
5539 const target = targets.targets.get(bridge.instance_target_id);
5540 const formInstance = store.formInstances.get(bridge.form_instance_id);
5541 if (!(target instanceof Element) || targets.duplicates.has(bridge.instance_target_id) || formInstance === undefined) {
5542 throw new ResumeBootError("MissingAnchor");
5543 }
5544 const binding = formInstance.definition.bindings.find((item) => item.id === bridge.field_binding_id);
5545 if (binding === undefined || binding.field === undefined || binding.channel !== bridge.channel || store.formBindingsByAnchor.has(bridge.instance_target_id)) {
5546 throw new ResumeBootError("ResumeArtifactMismatch");
5547 }
5548 const runtimeBinding = { bridge, binding, element: target, formInstance };
5549 store.formBindingsByAnchor.set(bridge.instance_target_id, runtimeBinding);
5550 const key = `${bridge.form_instance_id}|${binding.field}`;
5551 const bindings = store.formBindingsByField.get(key) ?? [];
5552 bindings.push(runtimeBinding);
5553 store.formBindingsByField.set(key, bindings);
5554 writeFormControl(runtimeBinding, formInstance.fields.get(binding.field)?.value);
5555 }
5556 const expectedBindings = [...instances.values()].reduce((count, instance) => count + (definitions.get(instance.form)?.bindings?.length ?? 0), 0);
5557 if (store.formBindingsByAnchor.size !== expectedBindings) throw new ResumeBootError("ResumeArtifactMismatch");
5558 window.__PRESOLVE_FORMS__ = {
5559 resetForm: (instanceId) => resetForm(store, instanceId),
5560 resetField: (instanceId, fieldId) => resetField(store, instanceId, fieldId)
5561 };
5562 }
5563
5564 function allocateResumeResources(store, registry, resourcesArtifact) {
5565 if (resourcesArtifact === null) return;
5566 const declarations = new Map((resourcesArtifact.declarations ?? []).map((declaration) => [declaration.id, declaration]));
5567 if (declarations.size !== (resourcesArtifact.declarations ?? []).length) {
5568 throw new ResumeBootError("ResourceArtifactMismatch");
5569 }
5570 const staged = [];
5571 for (const activation of resourcesArtifact.activations ?? []) {
5572 const declaration = declarations.get(activation.declaration);
5573 if (declaration === undefined) {
5574 throw new ResumeBootError("ResourceArtifactMismatch");
5575 }
5576 if (declaration.endpoint?.resume_policy === "reload") {
5577 continue;
5578 }
5579 if (declaration.endpoint?.resume_policy !== "snapshot") {
5580 throw new ResumeBootError("ResourceResumeUnsupported");
5581 }
5582 const slotIds = [activation.state_slot, activation.data_slot, activation.error_slot];
5583 const schemasByExisting = new Map(
5584 [...registry.definitions.slots.values()].map((schema) => [schema.existing_storage_slot_id, schema])
5585 );
5586 const stateSchema = schemasByExisting.get(activation.state_slot);
5587 const dataSchema = schemasByExisting.get(activation.data_slot);
5588 const errorSchema = schemasByExisting.get(activation.error_slot);
5589 if (stateSchema === undefined || dataSchema === undefined || errorSchema === undefined
5590 || stateSchema.restore_phase !== "R3" || dataSchema.restore_phase !== "R3" || errorSchema.restore_phase !== "R3"
5591 || store.resources.has(activation.id)
5592 || store.resourceActivationsByInstanceDeclaration.has(`${activation.component_instance}\u001f${activation.declaration}`)
5593 || slotIds.some((slot) => store.resourceSlots.has(slot))) {
5594 throw new ResumeBootError("ResourceArtifactMismatch");
5595 }
5596 staged.push({ activation, declaration, schemas: [stateSchema, dataSchema, errorSchema] });
5597 }
5598 for (const record of staged) {
5599 const resource = {
5600 activation: record.activation,
5601 declaration: record.declaration,
5602 controller: new AbortController(),
5603 state: "restoring",
5604 generation: null,
5605 data: null,
5606 error: null,
5607 restoredValues: {}
5608 };
5609 store.resources.set(record.activation.id, resource);
5610 store.resourceActivationsByInstanceDeclaration.set(
5611 `${record.activation.component_instance}\u001f${record.activation.declaration}`,
5612 record.activation.id
5613 );
5614 for (const [kind, schema] of [["state", record.schemas[0]], ["data", record.schemas[1]], ["error", record.schemas[2]]]) {
5615 store.resourceSlots.set(schema.existing_storage_slot_id, { resource, kind });
5616 resource.restoredValues[kind] = undefined;
5617 }
5618 }
5619 }
5620
5621 function finalizeRestoredResources(store) {
5622 for (const resource of store.resources.values()) {
5623 const values = resource.restoredValues;
5624 const lifecycle = values.state;
5625 if (lifecycle === null || typeof lifecycle !== "object" || Array.isArray(lifecycle)
5626 || !exactObjectKeys(lifecycle, ["state", "generation"])
5627 || !["ready", "failed"].includes(lifecycle.state)
5628 || !Number.isInteger(lifecycle.generation) || lifecycle.generation < 1
5629 || values.data === undefined || values.error === undefined
5630 || (lifecycle.state === "ready" && values.error !== null)
5631 || (lifecycle.state === "failed" && values.data !== null)) {
5632 throw new ResumeBootError("ResourceSnapshotMismatch");
5633 }
5634 resource.state = lifecycle.state;
5635 resource.generation = lifecycle.generation;
5636 resource.data = values.data;
5637 resource.error = values.error;
5638 delete resource.restoredValues;
5639 }
5640 }
5641
5642 async function restoreResumeRuntimeThroughForms(
5643 manifest,
5644 snapshot,
5645 registry,
5646 templateManifest,
5647 computedArtifact,
5648 contextArtifact,
5649 effectArtifact,
5650 componentArtifact,
5651 formsArtifact,
5652 resourcesArtifact,
5653 diagnostics
5654 ) {
5655 const store = allocateResumeStateComputedStore(
5656 registry,
5657 manifest,
5658 templateManifest,
5659 computedArtifact,
5660 contextArtifact,
5661 effectArtifact,
5662 componentArtifact,
5663 diagnostics
5664 );
5665 allocateResumeResources(store, registry, resourcesArtifact);
5666 executeResumeDecodeAndWrites(store, registry, snapshot, new Set(["R3", "R4"]));
5667 finalizeRestoredResources(store);
5668 synchronizeRestoredComponentState(store, computedArtifact, componentArtifact);
5669 const recomputationRuns = executeResumeComputedRecomputation(store, registry);
5670 executeResumeDecodeAndWrites(store, registry, snapshot, new Set(["R6"]));
5671 executeResumeContextBindings(store, registry, componentArtifact);
5672 restoreResumeComponentsSlotsAndStructure(manifest, registry, store, componentArtifact);
5673 restoreResumeStructuralOccurrences(
5674 snapshot,
5675 registry,
5676 store,
5677 templateManifest,
5678 computedArtifact,
5679 componentArtifact
5680 );
5681 restoreResumeForms(templateManifest, snapshot, registry, store, formsArtifact);
5682 installResumeDomBindings(store, templateManifest, componentArtifact);
5683 await initializeResourcesRuntime(store, resourcesArtifact, diagnostics, "reload");
5684 establishResumeEffects(registry, store, effectArtifact);
5685 installEffectDisposal(store);
5686 installResumeActivationListeners(registry, store);
5687 const state = runtimeState({
5688 manifest: templateManifest,
5689 diagnostics,
5690 store,
5691 components: debugComponents(store),
5692 computed: debugComputed(store),
5693 computed_update_runs: 0,
5694 context_initial_source_runs: store.contextInitialSourceRuns,
5695 context_slots: [...store.contextSlots.entries()],
5696 context_consumer_bindings: [...store.contextConsumerBindings.entries()],
5697 context_failures: store.contextFailures,
5698 context_update_source_runs: store.contextUpdateSourceRuns,
5699 component_initialization_runs: [],
5700 component_instance_tree: [...store.componentInstances.values()],
5701 forms: [...store.formInstances.values()].map((instance) => ({
5702 id: instance.instance.id,
5703 form: instance.definition.id,
5704 aggregate_valid: instance.aggregate_valid,
5705 submission: instance.submission
5706 })),
5707 resources: [...store.resources.entries()].map(([id, resource]) => ({
5708 id,
5709 state: resource.state,
5710 generation: resource.generation
5711 })),
5712 slot_binding_runs: [],
5713 component_failures: []
5714 });
5715 state.resume_recomputation_runs = recomputationRuns;
5716 return state;
5717 }
5718
5719 function runtimeState({
5720 manifest = null,
5721 missingAnchors = [],
5722 store = null,
5723 components = [],
5724 computed = [],
5725 computed_update_runs = 0,
5726 initial_effect_runs = [],
5727 completed_action_effect_runs = [],
5728 structural_effect_runs = [],
5729 context_initial_source_runs = [],
5730 context_slots = [],
5731 context_consumer_bindings = [],
5732 context_failures = [],
5733 context_update_source_runs = [],
5734 component_initialization_runs = [],
5735 component_instance_tree = [],
5736 forms = [],
5737 resources = [],
5738 slot_binding_runs = [],
5739 component_failures = [],
5740 diagnostics
5741 }) {
5742 return {
5743 runtime_version: RUNTIME_VERSION,
5744 supported_schema_version: SUPPORTED_SCHEMA_VERSION,
5745 manifest,
5746 missingAnchors,
5747 diagnostics,
5748 store,
5749 components,
5750 computed,
5751 computed_update_runs,
5752 initial_effect_runs,
5753 completed_action_effect_runs,
5754 structural_effect_runs,
5755 context_initial_source_runs,
5756 context_slots,
5757 context_consumer_bindings,
5758 context_failures,
5759 context_update_source_runs,
5760 component_initialization_runs,
5761 component_instance_tree,
5762 forms,
5763 resources,
5764 slot_binding_runs,
5765 component_failures
5766 };
5767 }
5768
5769 async function initializeResourcesRuntime(store, resourcesArtifact, diagnostics, resumePolicy = null) {
5770 if (resourcesArtifact === null) return;
5771 window.addEventListener("pagehide", () => {
5772 for (const resource of store.resources.values()) resource.controller.abort();
5773 }, { once: true });
5774 const declarations = new Map(resourcesArtifact.declarations.map((declaration) => [declaration.id, declaration]));
5775 const records = [];
5776 for (const activation of resourcesArtifact.activations) {
5777 const declaration = declarations.get(activation.declaration);
5778 if (declaration === undefined) throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
5779 if (resumePolicy !== null && declaration.endpoint.resume_policy !== resumePolicy) continue;
5780 if (!["Client", "Shared"].includes(declaration.execution_boundary)) {
5781 reportDiagnostic(diagnostics, "PSR_RESOURCE_SERVER_UNAVAILABLE", "A server-only Resource cannot activate in the browser runtime", { activation, declaration }, true);
5782 throw new PresolveBootError("PSR_RESOURCE_SERVER_UNAVAILABLE");
5783 }
5784 const controller = new AbortController();
5785 const record = { activation, declaration, controller, state: "pending", generation: 1, data: null, error: null };
5786 const activationKey = `${activation.component_instance}\u001f${activation.declaration}`;
5787 if (store.resources.has(activation.id) || store.resourceActivationsByInstanceDeclaration.has(activationKey)) {
5788 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
5789 }
5790 store.resources.set(activation.id, record);
5791 store.resourceActivationsByInstanceDeclaration.set(activationKey, activation.id);
5792 records.push(record);
5793 }
5794 await Promise.all(records.map(async (record) => {
5795 try {
5796 const module = await import(record.declaration.endpoint.runtime_location);
5797 const endpoint = module[record.declaration.endpoint.export];
5798 if (typeof endpoint !== "function") throw new Error("endpoint-export-missing");
5799 const result = await endpoint({ signal: record.controller.signal, inputs: Object.freeze({}) });
5800 if (record.controller.signal.aborted) {
5801 record.state = "cancelled";
5802 } else {
5803 try {
5804 record.data = decodeResourceValue(result, record.declaration.data_codec);
5805 record.state = "ready";
5806 } catch (_) {
5807 record.state = "failed";
5808 record.data = null;
5809 record.error = null;
5810 reportDiagnostic(diagnostics, "PSR_RESOURCE_VALUE_CODEC_FAILURE", "A Resource endpoint result did not match its compiler-issued data codec", { activation: record.activation.id });
5811 }
5812 }
5813 } catch (error) {
5814 if (record.controller.signal.aborted) {
5815 record.state = "cancelled";
5816 } else {
5817 const endpointError = error instanceof Error ? error.message : error;
5818 try {
5819 record.error = decodeResourceValue(endpointError, record.declaration.error_codec);
5820 record.state = "failed";
5821 reportDiagnostic(diagnostics, "PSR_RESOURCE_ENDPOINT_FAILURE", "A compiler-authorized Resource endpoint failed", { activation: record.activation.id, error: record.error });
5822 } catch (_) {
5823 record.state = "failed";
5824 record.data = null;
5825 record.error = null;
5826 reportDiagnostic(diagnostics, "PSR_RESOURCE_VALUE_CODEC_FAILURE", "A Resource endpoint error did not match its compiler-issued error codec", { activation: record.activation.id });
5827 }
5828 }
5829 }
5830 invalidateResourceComputeds(store, record.activation);
5831 }));
5832 }
5833
5834 async function initializeRuntime(manifest, computedArtifact, contextArtifact, effectArtifact, componentArtifact, formsArtifact, resourcesArtifact, opaqueArtifact, diagnostics) {
5835 const bindingAnchors = collectBindingAnchors();
5836 const conditionalAnchors = collectConditionalAnchors();
5837 const listAnchors = collectListAnchors();
5838 const elementsByNode = collectElementAnchors();
5839 const store = createRuntimeStore(elementsByNode, diagnostics, computedArtifact, contextArtifact, effectArtifact, componentArtifact, opaqueArtifact);
5840 store.componentArtifact = componentArtifact;
5841 store.componentInstances = new Map((componentArtifact?.instances ?? []).map((instance) => [instance.instance, { ...instance, status: "created" }]));
5842 store.slotBindings = new Map((componentArtifact?.slot_binding_programs ?? []).map((binding) => [binding.binding, binding]));
5843 store.instanceContextBindings = new Map((componentArtifact?.instance_context_bindings ?? []).map((binding) => [binding.consumer_instance, binding]));
5844 store.componentRegions = new Map((componentArtifact?.structural_programs ?? []).map((program) => [program.region, program]));
5845 // Inactive compiler-issued occurrence templates. Dynamic materialization
5846 // must consume this table; it must not rediscover component structure from
5847 // DOM nodes or selectors.
5848 store.structuralOccurrences = new Map((componentArtifact?.structural_programs ?? []).map(
5849 (program) => [program.region, program.template_occurrences]
5850 ));
5851 store.structuralOccurrenceTemplatesByInvocation = structuralOccurrenceTemplateRegistry(
5852 manifest,
5853 componentArtifact,
5854 computedArtifact
5855 );
5856 store.structuralSlotProjectionPrograms = structuralSlotProjectionRegistry(manifest, componentArtifact);
5857 store.structuralOccurrencesByInvocation = new Map(
5858 [...store.structuralOccurrenceTemplatesByInvocation].map(([invocation, record]) => [
5859 invocation,
5860 Object.freeze({
5861 ...record.occurrence,
5862 structural_region: record.structural_region
5863 })
5864 ])
5865 );
5866 const missingAnchors = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
5867 ? []
5868 : collectMissingAnchors(
5869 manifest,
5870 bindingAnchors,
5871 conditionalAnchors,
5872 listAnchors,
5873 elementsByNode
5874 );
5875
5876 for (const anchor of missingAnchors) {
5877 reportDiagnostic(
5878 diagnostics,
5879 anchor.code,
5880 anchor.kind === "element"
5881 ? "Manifest element anchor was not found in the rendered DOM"
5882 : anchor.kind === "conditional"
5883 ? "Manifest conditional anchor was not found in the rendered DOM"
5884 : anchor.kind === "list"
5885 ? "Manifest list anchor was not found in the rendered DOM"
5886 : "Manifest binding anchor was not found in the rendered DOM",
5887 anchor
5888 );
5889 }
5890
5891 const resourceInitialization = initializeResourcesRuntime(store, resourcesArtifact, diagnostics);
5892
5893 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
5894 const definitions = new Map((manifest.components ?? []).map((component) => [component.component_id, component]));
5895 for (const instance of componentArtifact.instances ?? []) {
5896 const definition = definitions.get(instance.component);
5897 if (definition === undefined) throw new PresolveBootError("PSR_INVALID_ORDINARY_COMPONENT");
5898 const definitionStates = (computedArtifact?.state ?? []).filter(
5899 (state) => state.component === definition.name
5900 );
5901 if ((instance.state_slots ?? []).length !== definitionStates.length) {
5902 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
5903 }
5904 for (const slot of instance.state_slots ?? []) {
5905 const pair = `${instance.instance}|${slot.storage_id}`;
5906 if (
5907 store.stateSlotsByInstanceStorage.has(pair)
5908 || store.storageValues.has(slot.slot_id)
5909 ) {
5910 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
5911 }
5912 store.stateSlotsByInstanceStorage.set(pair, slot);
5913 store.storageValues.set(slot.slot_id, initialStateSlotValue(slot));
5914 }
5915 for (const state of definitionStates) {
5916 if (!store.stateSlotsByInstanceStorage.has(`${instance.instance}|${state.storage}`)) {
5917 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
5918 }
5919 }
5920 for (const slot of instance.computed_slots ?? []) {
5921 const pair = `${instance.instance}|${slot.computed_id}`;
5922 if (!slot.cache_slot_id.startsWith(`${instance.instance}/computed-cache:`)
5923 || !slot.dirty_slot_id.startsWith(`${instance.instance}/computed-dirty:`)
5924 || store.computedSlotsByInstanceComputed.has(pair)
5925 || store.computedDirtySlots.has(slot.dirty_slot_id)) {
5926 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
5927 }
5928 store.computedSlotsByInstanceComputed.set(pair, slot);
5929 store.computedDirtySlots.set(slot.dirty_slot_id, slot.dirty_initial_value === true);
5930 }
5931 const component = { instance_id: instance.instance, name: instance.component, manifest: definition, state: {} };
5932 for (const state of computedArtifact?.state ?? []) {
5933 if (state.component !== definition.name) continue;
5934 const slot = store.stateSlotsByInstanceStorage.get(`${instance.instance}|${state.storage}`);
5935 if (slot !== undefined) component.state[state.field] = store.storageValues.get(slot.slot_id);
5936 }
5937 store.components.set(instance.instance, component);
5938 registerActions(store, component, definition);
5939 registerLegacyComponentEvents(store, component);
5940 }
5941 initializeOrdinaryInstanceRuntime(store, manifest, componentArtifact);
5942 } else {
5943 for (const manifestComponent of manifest.components ?? []) {
5944 const component = initializeComponentRuntime(
5945 store,
5946 manifestComponent,
5947 bindingAnchors,
5948 conditionalAnchors,
5949 listAnchors
5950 );
5951 registerComponentEvents(store, component);
5952 }
5953 }
5954
5955 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
5956 for (const instance of componentArtifact.instances ?? []) {
5957 executeComputedPlan(store, instance.instance);
5958 }
5959 } else {
5960 executeComputedPlan(store);
5961 }
5962
5963 executeInitialContext(store);
5964
5965 initializeFormsRuntime(store, formsArtifact, manifest, elementsByNode, diagnostics);
5966
5967 await resourceInitialization;
5968
5969 executeInitialEffects(store);
5970 installEffectDisposal(store);
5971
5972 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
5973 installOrdinaryInstanceEventListeners(store);
5974 // Schema-v5 component instances can still host the frozen implicit
5975 // decorator-action shape. Those bindings have no ordinary-event record
5976 // because they intentionally predate canonical action batches.
5977 installDelegatedEventListeners(store);
5978 } else {
5979 installDelegatedEventListeners(store);
5980 }
5981
5982 return runtimeState({
5983 manifest,
5984 missingAnchors,
5985 diagnostics,
5986 store,
5987 components: debugComponents(store),
5988 computed: debugComputed(store),
5989 computed_update_runs: store.computedUpdateRuns,
5990 initial_effect_runs: store.initialEffectRuns,
5991 completed_action_effect_runs: store.completedActionEffectRuns,
5992 structural_effect_runs: store.structuralEffectRuns,
5993 context_initial_source_runs: store.contextInitialSourceRuns,
5994 context_slots: [...store.contextSlots.entries()],
5995 context_consumer_bindings: [...store.contextConsumerBindings.entries()],
5996 context_failures: store.contextFailures,
5997 context_update_source_runs: store.contextUpdateSourceRuns,
5998 component_initialization_runs: (componentArtifact?.initialization_batches ?? []).map((batch) => batch.index),
5999 component_instance_tree: [...store.componentInstances.values()],
6000 forms: [...store.formInstances.values()].map((instance) => ({
6001 id: instance.instance.id,
6002 form: instance.definition.id,
6003 aggregate_valid: instance.aggregate_valid,
6004 submission: instance.submission
6005 })),
6006 resources: [...store.resources.entries()].map(([id, resource]) => ({ id, state: resource.state, generation: resource.generation })),
6007 opaque_terminals: [...store.opaqueActivations],
6008 slot_binding_runs: [...store.slotBindings.keys()],
6009 component_failures: []
6010 });
6011 }
6012
6013 async function boot() {
6014 const diagnostics = [];
6015
6016 try {
6017 const productionArtifact = readProductionRuntimeArtifact();
6018 const productionIndexes = productionOrdinalIndexes(productionArtifact);
6019 const manifest = readManifest(diagnostics);
6020 const computedArtifact = readComputedArtifact(diagnostics);
6021 validateComputedArtifactSchema(computedArtifact, diagnostics);
6022 const contextArtifact = readContextArtifact(diagnostics);
6023 validateContextArtifactSchema(contextArtifact, diagnostics);
6024 const effectArtifact = readEffectArtifact(diagnostics);
6025 validateEffectArtifactSchema(effectArtifact, diagnostics);
6026 const componentArtifact = readComponentArtifact(diagnostics);
6027 validateComponentArtifactSchema(componentArtifact, diagnostics);
6028 validateEffectArtifactInstances(effectArtifact, componentArtifact, diagnostics);
6029 const opaqueArtifact = readOpaqueArtifact(diagnostics);
6030 validateOpaqueArtifact(opaqueArtifact, diagnostics);
6031 validateManifestSchema(manifest, effectArtifact, componentArtifact, opaqueArtifact, diagnostics);
6032 const formsArtifact = readFormsArtifact(diagnostics);
6033 validateFormsArtifact(formsArtifact, manifest, diagnostics);
6034 const resourcesArtifact = readResourcesArtifact(diagnostics);
6035 validateResourcesArtifact(resourcesArtifact, diagnostics);
6036
6037 const result = await bootstrapResume({
6038 diagnostics,
6039 resumeBoot: ({ manifest: resumeManifest, snapshot, registry }) => {
6040 if ((opaqueArtifact?.activations ?? []).length > 0) {
6041 throw new ResumeBootError("OpaqueTerminalColdFallback");
6042 }
6043 return restoreResumeRuntimeThroughForms(
6044 resumeManifest,
6045 snapshot,
6046 registry,
6047 manifest,
6048 computedArtifact,
6049 contextArtifact,
6050 effectArtifact,
6051 componentArtifact,
6052 formsArtifact,
6053 resourcesArtifact,
6054 diagnostics
6055 );
6056 },
6057 coldBoot: () => initializeRuntime(
6058 manifest,
6059 computedArtifact,
6060 contextArtifact,
6061 effectArtifact,
6062 componentArtifact,
6063 formsArtifact,
6064 resourcesArtifact,
6065 opaqueArtifact,
6066 diagnostics
6067 )
6068 });
6069 const state = result.mode === "cold"
6070 ? result.cold
6071 : result.resume;
6072 state.resume = {
6073 mode: result.mode,
6074 failure: result.failure,
6075 contract_version: RESUME_REGISTRY_CONTRACT_VERSION
6076 };
6077 state.disposeEffects = () => disposeEffectInstances(state.store);
6078 state.resume_registry = result.mode === "resume" ? result.registry : null;
6079 state.resume_debug = [...resumeBootstrapState.debug];
6080 state.production = productionIndexes === null ? null : {
6081 artifact_checksum: productionArtifact.integrity?.artifact_checksum ?? null,
6082 table_kinds: [...productionIndexes.keys()]
6083 };
6084 const status = state.diagnostics.some((diagnostic) => diagnostic.fatal)
6085 || state.missingAnchors.length > 0
6086 ? "error"
6087 : "ready";
6088
6089 document.documentElement.dataset.presolveRuntime = status;
6090 window.__PRESOLVE__ = state;
6091
6092 document.dispatchEvent(
6093 new CustomEvent("presolve:ready", {
6094 detail: state
6095 })
6096 );
6097 } catch (error) {
6098 document.documentElement.dataset.presolveRuntime = "error";
6099 if (error instanceof PresolveBootError) {
6100 reportDiagnostic(
6101 diagnostics,
6102 error.code,
6103 "Runtime boot failed at a closed compiler contract boundary",
6104 { code: error.code },
6105 true
6106 );
6107 } else {
6108 reportDiagnostic(
6109 diagnostics,
6110 "PSR_RUNTIME_BOOT_FAILED",
6111 "Runtime boot failed",
6112 { message: error instanceof Error ? error.message : String(error) },
6113 true
6114 );
6115 }
6116
6117 window.__PRESOLVE__ = runtimeState({
6118 diagnostics
6119 });
6120
6121 document.dispatchEvent(
6122 new CustomEvent("presolve:ready", {
6123 detail: window.__PRESOLVE__
6124 })
6125 );
6126 }
6127 }
6128
6129 window.__PRESOLVE_RESUME__ = Object.freeze({
6130 bootstrapResume,
6131 captureSnapshot,
6132 activateByEvent,
6133 activateBoundary,
6134 debugEvidence: () => [...resumeBootstrapState.debug]
6135 });
6136
6137 if (document.readyState === "loading") {
6138 document.addEventListener("DOMContentLoaded", boot, {
6139 once: true
6140 });
6141 } else {
6142 boot();
6143 }
6144})();
6145"#;
6146
6147#[must_use]
6148pub fn generate_runtime_stub() -> String {
6149 RUNTIME_STUB
6150 .replace(
6151 "__EZ_COMPONENT_SCHEMA_VERSION__",
6152 &crate::RUNTIME_COMPONENT_ARTIFACT_SCHEMA_VERSION.to_string(),
6153 )
6154 .replace(
6155 "__EZ_EFFECT_SCHEMA_VERSION__",
6156 &crate::RUNTIME_EFFECT_ARTIFACT_SCHEMA_VERSION.to_string(),
6157 )
6158}
6159
6160#[cfg(test)]
6161mod tests {
6162 use super::generate_runtime_stub;
6163
6164 #[test]
6165 fn emits_runtime_manifest_bootstrap() {
6166 let runtime = generate_runtime_stub();
6167
6168 assert!(runtime.contains("presolve-template-manifest"));
6169 assert!(runtime.contains("presolve-effect-runtime"));
6170 assert!(runtime.contains("presolve-context-runtime"));
6171 assert!(runtime.contains("executeInitialContext(store)"));
6172 assert!(runtime.contains("executeContextUpdates(store, actionBatchId)"));
6173 assert!(runtime.contains("contextSlots: new Map()"));
6174 assert!(runtime.contains("RUNTIME_VERSION = \"0.0.0\""));
6175 assert!(runtime.contains("SUPPORTED_SCHEMA_VERSION = 5"));
6176 assert!(runtime.contains("SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION = 12"));
6177 assert!(runtime.contains("instruction.kind === \"load-resource\""));
6178 assert!(runtime.contains("resourceInvalidationsByDeclaration"));
6179 assert!(runtime.contains("case \"abs\""));
6180 assert!(runtime.contains("instruction.kind === \"select\""));
6181 assert!(runtime.contains("instruction.kind === \"get-index\""));
6182 assert!(runtime.contains("presolve-forms-runtime"));
6183 assert!(runtime.contains("presolve-resources-runtime"));
6184 assert!(runtime.contains("presolve-opaque-runtime"));
6185 assert!(runtime.contains("validateOpaqueArtifact"));
6186 assert!(runtime.contains("executeOpaqueTerminal"));
6187 assert!(runtime.contains("PSR_OPAQUE_TERMINAL_FAILURE"));
6188 assert!(runtime.contains("OpaqueTerminalColdFallback"));
6189 assert!(runtime.contains("validateResourcesArtifact"));
6190 assert!(runtime.contains("initializeResourcesRuntime"));
6191 assert!(runtime.contains("AbortController"));
6192 assert!(runtime.contains("pagehide"));
6193 assert!(runtime.contains("initializeFormsRuntime"));
6194 assert!(runtime.contains("dispatchFormSubmit"));
6195 assert!(runtime.contains("form_hosts"));
6196 assert!(runtime.contains("structuralOccurrences = new Map"));
6197 assert!(runtime.contains("structuralOccurrencesByInvocation"));
6198 assert!(runtime.contains("function structuralOccurrenceIdentity"));
6199 assert!(runtime.contains("function decodeStructuralOccurrenceIdentity"));
6200 assert!(runtime.contains("function instantiateStructuralTemplateSlots"));
6201 assert!(runtime.contains("function rewriteStructuralTemplateIdentity"));
6202 assert!(runtime.contains("function deriveStructuralOccurrenceRecords"));
6203 assert!(runtime.contains("function stageStructuralOccurrenceRecords"));
6204 assert!(runtime.contains("function renderStructuralOccurrenceTemplate"));
6205 assert!(runtime.contains("function attachStructuralOccurrenceFragment"));
6206 assert!(runtime.contains("function registerStructuralOccurrenceRecords"));
6207 assert!(runtime.contains("function materializeStructuralOccurrence"));
6208 assert!(runtime.contains("function structuralConditionalHostFragment"));
6209 assert!(runtime.contains("function structuralKeyedHostFragment"));
6210 assert!(runtime.contains("function renderStructuralKeyedListItem"));
6211 assert!(runtime.contains("function reconcileStructuralKeyedList"));
6212 assert!(runtime.contains("function compilerFragmentInvocationAnchors"));
6213 assert!(runtime.contains("function replaceStructuralConditionalBranch"));
6214 assert!(runtime.contains("active.indexOf(updateBinding)"));
6215 assert!(runtime.contains("function structuralOccurrenceTemplateRegistry"));
6216 assert!(runtime.contains("structuralOccurrenceTemplatesByInvocation"));
6217 assert!(runtime.contains("structuralStateSlots = new Set()"));
6218 assert!(runtime.contains("structuralComputedCacheSlots = new Set()"));
6219 assert!(runtime.contains("Conditional host fragments were attached to a keyed-list host"));
6220 assert!(runtime.contains("occurrence.ordinary_template_targets"));
6221 assert!(runtime.contains("occurrence.ordinary_template_bindings"));
6222 assert!(runtime.contains("occurrence.ordinary_template_events"));
6223 assert!(!runtime.contains("FormData(formElement)"));
6224 assert!(runtime.contains("PSR_MISSING_MANIFEST"));
6225 assert!(runtime.contains("PSR_INVALID_MANIFEST_JSON"));
6226 assert!(runtime.contains("PSR_UNSUPPORTED_SCHEMA"));
6227 assert!(runtime.contains("data-presolve-node"));
6228 assert!(runtime.contains("ordinaryEventsByTargetAndType"));
6229 assert!(runtime.contains("ordinaryEventListenerTypes: new Set()"));
6230 assert!(runtime.contains("component_instance_id: record.component_instance_id"));
6231 assert!(runtime.contains("computedSlotsByInstanceComputed: new Map()"));
6232 assert!(runtime.contains("computedDirtySlots: new Map()"));
6233 assert!(runtime.contains("function computedSlotForExecution"));
6234 assert!(runtime.contains("/computed-cache:"));
6235 assert!(runtime.contains("/computed-dirty:"));
6236 assert!(runtime.contains("LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION = 2"));
6237 assert!(runtime.contains("RESUME_REGISTRY_CONTRACT_VERSION = 1"));
6238 assert!(runtime.contains("function validateResumeManifest"));
6239 assert!(runtime.contains("function validateResumeSnapshot"));
6240 assert!(runtime.contains("async function bootstrapResume"));
6241 assert!(runtime.contains("function captureSnapshot"));
6242 assert!(runtime.contains("async function activateByEvent"));
6243 assert!(runtime.contains("async function activateBoundary"));
6244 assert!(runtime.contains("function decodeResumeValue"));
6245 assert!(runtime.contains("async function restoreResumeRuntimeThroughForms"));
6246 assert!(runtime.contains("function allocateResumeResources"));
6247 assert!(runtime.contains("function finalizeRestoredResources"));
6248 assert!(runtime.contains("ResourceResumeUnsupported"));
6249 assert!(runtime.contains("executeResumeComputedRecomputation"));
6250 assert!(runtime.contains("function executeResumeContextBindings"));
6251 assert!(runtime.contains("function restoreResumeComponentsSlotsAndStructure"));
6252 assert!(runtime.contains("function restoreResumeForms"));
6253 assert!(runtime.contains("function installResumeDomBindings"));
6254 assert!(runtime.contains("function establishResumeEffects"));
6255 assert!(runtime.contains("function collectExactResumeAnchors"));
6256 assert!(runtime.contains("window.__PRESOLVE_RESUME__ = Object.freeze"));
6257 assert!(runtime.contains("throw new ResumeBootError(\"DoubleBootstrap\")"));
6258 assert!(runtime.contains("presolve-binding:"));
6259 assert!(runtime.contains("reportDiagnostic"));
6260 assert!(runtime.contains("validateManifestSchema"));
6261 assert!(runtime.contains("validateEffectArtifactSchema"));
6262 assert!(runtime.contains("createRuntimeStore"));
6263 assert!(runtime.contains("readField"));
6264 assert!(runtime.contains("writeField"));
6265 assert!(runtime.contains("notifyField"));
6266 assert!(runtime.contains("actionDelta"));
6267 assert!(runtime.contains("isBooleanAttribute"));
6268 assert!(runtime.contains("isPropertyAttribute"));
6269 assert!(runtime.contains("updateAttributeBinding"));
6270 assert!(runtime.contains("function legacyActionBinding"));
6271 assert!(runtime.contains("function legacyEventBinding"));
6272 assert!(runtime.contains("function registerLegacyComponentEvents"));
6273 assert!(runtime.contains(
6274 "Legacy template action binding did not match its compiler action implementation"
6275 ));
6276 assert!(runtime.contains("actionsByMethod.set(action.method_id, action.action_batch_id)"));
6277 assert!(runtime.contains("const actionRecord = store.actionsByMethod.get(event.method_id)"));
6278 assert!(runtime.contains("actionRecord.action_batch_id !== event.action_batch_id"));
6279 assert!(runtime.contains("executeActions"));
6280 assert!(runtime.contains("executeCompletedActionEffects"));
6281 assert!(runtime.contains("activeActionBatch"));
6282 assert!(runtime.contains("executeInitialEffects"));
6283 assert!(runtime.contains("function executeResumeEffects"));
6284 assert!(runtime.contains("function validateEffectArtifactInstances"));
6285 assert!(runtime.contains("effectArtifact.structural_templates"));
6286 assert!(runtime.contains("function disposeEffectInstances"));
6287 assert!(runtime.contains("effect.run_on_resume !== true"));
6288 assert!(runtime.contains("dispatchEffectCapability"));
6289 assert!(!runtime.contains("const arguments ="));
6290 assert!(runtime.contains("formatBindingValue"));
6291 assert!(runtime.contains("value === null ? \"\" : String(value)"));
6292 assert!(runtime.contains("listItemMemberPath"));
6293 assert!(runtime.contains("populateListItemMemberBindings"));
6294 assert!(runtime.contains("updateListItemTextBindings"));
6295 assert!(runtime.contains("updateListItemAttributes"));
6296 assert!(runtime.contains("registerListItemEvents"));
6297 assert!(runtime.contains("unregisterListItemEvents"));
6298 assert!(runtime.contains("presolve-list-binding-end:"));
6299 assert!(runtime.contains("renderListItemElement"));
6300 assert!(runtime.contains("component.state[field] = node.initial_value"));
6301 assert!(!runtime.contains("component.state[field] = Number(node.initial_value)"));
6302 assert!(runtime.contains("installDelegatedEventListeners"));
6303 assert!(runtime.contains("document.addEventListener(eventType"));
6304 assert!(!runtime.contains("element.addEventListener(\"click\""));
6305 assert!(runtime.contains("action.operation !== \"toggle\""));
6306 assert!(runtime.contains("action.operation === \"assign\""));
6307 assert!(runtime.contains("action.operation === \"toggle\""));
6308 assert!(runtime.contains("PSR_MISSING_ELEMENT_ANCHOR"));
6309 assert!(runtime.contains("PSR_MISSING_BINDING_ANCHOR"));
6310 assert!(runtime.contains("PSR_UNRESOLVED_EVENT"));
6311 assert!(runtime.contains("PSR_UNRESOLVED_ACTION"));
6312 assert!(runtime.contains("PSR_INVALID_STATE_OPERATION"));
6313 assert!(runtime.contains("current + delta"));
6314 assert!(runtime.contains("dataset.presolveRuntime"));
6315 assert!(runtime.contains("presolve:ready"));
6316 assert!(runtime.contains("runtime_version"));
6317 assert!(runtime.contains("diagnostics"));
6318 assert!(runtime.contains("window.__PRESOLVE__"));
6319 }
6320}