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