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