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 = 1;
22 const SUPPORTED_CONTEXT_ARTIFACT_SCHEMA_VERSION = 2;
23 const SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION = __EZ_COMPONENT_SCHEMA_VERSION__;
24 const LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION = 2;
25 const SUPPORTED_FORMS_ARTIFACT_SCHEMA_VERSION = 2;
26 const SUPPORTED_RESOURCES_ARTIFACT_SCHEMA_VERSION = 1;
27 const SUPPORTED_OPAQUE_ARTIFACT_SCHEMA_VERSION = 1;
28 const SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION = 6;
29 const SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION = 1;
30 const SUPPORTED_RESUME_RUNTIME_PROTOCOL_VERSION = 1;
31 const RESUME_REGISTRY_CONTRACT_VERSION = 1;
32
33 class PresolveBootError extends Error {
34 constructor(code) {
35 super(code);
36 this.name = "PresolveBootError";
37 this.code = code;
38 }
39 }
40
41 function createDiagnostic(code, message, detail, fatal = false) {
42 return {
43 code,
44 message,
45 detail,
46 fatal
47 };
48 }
49
50 function reportDiagnostic(diagnostics, code, message, detail, fatal = false) {
51 const diagnostic = createDiagnostic(code, message, detail, fatal);
52 diagnostics.push(diagnostic);
53 console.error(`[Presolve] ${code}`, diagnostic);
54 return diagnostic;
55 }
56
57 class ResumeBootError extends Error {
58 constructor(failure, detail = {}) {
59 super(failure);
60 this.name = "ResumeBootError";
61 this.failure = failure;
62 this.detail = detail;
63 }
64 }
65
66 const resumeBootstrapState = {
67 phase: "idle",
68 manifest: null,
69 registry: null,
70 result: null,
71 debug: []
72 };
73
74 function exactObjectKeys(value, expected) {
75 if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
76 const actual = Object.keys(value).sort();
77 const canonical = [...expected].sort();
78 return actual.length === canonical.length
79 && actual.every((key, index) => key === canonical[index]);
80 }
81
82 function readResumeManifest(diagnostics) {
83 const element = document.getElementById(RESUME_MANIFEST_ELEMENT_ID);
84 if (!(element instanceof HTMLScriptElement)) {
85 reportDiagnostic(diagnostics, "PSR_RESUME_MANIFEST_MISSING", "Resume manifest v6 is missing", { artifactElementId: RESUME_MANIFEST_ELEMENT_ID });
86 throw new ResumeBootError("ManifestVersionMismatch");
87 }
88 try {
89 return JSON.parse(element.textContent ?? "");
90 } catch (error) {
91 reportDiagnostic(diagnostics, "PSR_RESUME_MANIFEST_PARSE", "Resume manifest v6 could not be parsed", { message: error instanceof Error ? error.message : String(error) });
92 throw new ResumeBootError("ManifestVersionMismatch");
93 }
94 }
95
96 function readProductionRuntimeArtifact() {
97 const element = document.getElementById(PRODUCTION_RUNTIME_ELEMENT_ID);
98 if (element === null) return null;
99 if (!(element instanceof HTMLScriptElement)) throw new ResumeBootError("ProductionArtifactMismatch");
100 let artifact;
101 try { artifact = JSON.parse(element.textContent ?? ""); } catch (_) { throw new ResumeBootError("ProductionArtifactMismatch"); }
102 if (artifact === null || typeof artifact !== "object"
103 || artifact.schemaVersion !== 1 || artifact.runtimeProtocolVersion !== 1
104 || !Array.isArray(artifact.tables?.tables) || !Array.isArray(artifact.chunks)) {
105 throw new ResumeBootError("ProductionArtifactMismatch");
106 }
107 return artifact;
108 }
109
110 function productionOrdinalIndexes(artifact) {
111 if (artifact === null) return null;
112 const indexes = new Map();
113 for (const table of artifact.tables.tables) {
114 if (typeof table.table_kind !== "string" || !Array.isArray(table.mappings)) {
115 throw new ResumeBootError("ProductionArtifactMismatch");
116 }
117 const values = new Map();
118 for (const mapping of table.mappings) {
119 if (typeof mapping.canonical_id !== "string" || !Number.isInteger(mapping.ordinal)
120 || mapping.ordinal < 0 || values.has(mapping.canonical_id)) {
121 throw new ResumeBootError("ProductionArtifactMismatch");
122 }
123 values.set(mapping.canonical_id, mapping.ordinal);
124 }
125 indexes.set(table.table_kind, values);
126 }
127 for (const required of ["anchors", "events", "activations", "activation_roots"]) {
128 if (!indexes.has(required)) throw new ResumeBootError("ProductionArtifactMismatch");
129 }
130 return indexes;
131 }
132
133 function readOptionalResumeSnapshot(diagnostics, explicitSnapshot) {
134 if (explicitSnapshot !== undefined) return explicitSnapshot;
135 if (window.__PRESOLVE_RESUME_SNAPSHOT__ !== undefined) {
136 return window.__PRESOLVE_RESUME_SNAPSHOT__;
137 }
138 const element = document.getElementById(RESUME_SNAPSHOT_ELEMENT_ID);
139 if (element === null) return null;
140 if (!(element instanceof HTMLScriptElement)) {
141 reportDiagnostic(diagnostics, "PSR_RESUME_SNAPSHOT_PARSE", "Resume snapshot was not stored in a JSON script element", { artifactElementId: RESUME_SNAPSHOT_ELEMENT_ID });
142 throw new ResumeBootError("SnapshotParseFailure");
143 }
144 try {
145 return JSON.parse(element.textContent ?? "");
146 } catch (error) {
147 reportDiagnostic(diagnostics, "PSR_RESUME_SNAPSHOT_PARSE", "Resume snapshot v1 could not be parsed", { message: error instanceof Error ? error.message : String(error) });
148 throw new ResumeBootError("SnapshotParseFailure");
149 }
150 }
151
152 function uniqueRecordIndex(records, key, failure = "DuplicateIdentity") {
153 if (!Array.isArray(records)) throw new ResumeBootError("ResumeArtifactMismatch");
154 const index = new Map();
155 for (const record of records) {
156 const id = record?.[key];
157 if (typeof id !== "string" || index.has(id)) throw new ResumeBootError(failure, { id });
158 index.set(id, record);
159 }
160 return index;
161 }
162
163 function validateResumeManifest(manifest) {
164 const topLevelKeys = [
165 "schema_version", "build_id", "snapshot_schema_version", "runtime_protocol_version",
166 "application_root_boundary_id", "boundaries", "slot_schemas", "capture_programs",
167 "restore_programs", "chunks", "activations", "anchors", "events",
168 "phase_i_component_resume_records", "phase_i_form_resume_records"
169 ];
170 if (!exactObjectKeys(manifest, topLevelKeys)) throw new ResumeBootError("ResumeArtifactMismatch");
171 if (manifest.schema_version !== SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION) {
172 throw new ResumeBootError("ManifestVersionMismatch");
173 }
174 if (manifest.snapshot_schema_version !== SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION) {
175 throw new ResumeBootError("SnapshotSchemaMismatch");
176 }
177 if (manifest.runtime_protocol_version !== SUPPORTED_RESUME_RUNTIME_PROTOCOL_VERSION) {
178 throw new ResumeBootError("RuntimeProtocolMismatch");
179 }
180 if (typeof manifest.build_id !== "string" || !/^resume-build:[0-9a-f]{64}$/.test(manifest.build_id)) {
181 throw new ResumeBootError("ResumeArtifactMismatch");
182 }
183 const boundaries = uniqueRecordIndex(manifest.boundaries, "boundary_id");
184 const slots = uniqueRecordIndex(manifest.slot_schemas, "slot_id");
185 const capturePrograms = uniqueRecordIndex(manifest.capture_programs, "program_id");
186 const restorePrograms = uniqueRecordIndex(manifest.restore_programs, "program_id");
187 const chunks = uniqueRecordIndex(manifest.chunks, "chunk_id");
188 const activations = uniqueRecordIndex(manifest.activations, "activation_id");
189 const anchors = uniqueRecordIndex(manifest.anchors, "anchor_id");
190 const events = uniqueRecordIndex(manifest.events, "resume_event_id");
191 if (!boundaries.has(manifest.application_root_boundary_id)) {
192 throw new ResumeBootError("UnknownBoundary");
193 }
194 for (const boundary of boundaries.values()) {
195 if (!capturePrograms.has(boundary.capture_program_id)
196 || !restorePrograms.has(boundary.restore_program_id)
197 || (boundary.parent_boundary_id !== undefined && !boundaries.has(boundary.parent_boundary_id))
198 || !boundary.child_boundary_ids.every((id) => boundaries.has(id))
199 || !boundary.anchor_ids.every((id) => anchors.has(id))
200 || !boundary.event_ids.every((id) => events.has(id))) {
201 throw new ResumeBootError("ResumeArtifactMismatch", { boundary: boundary.boundary_id });
202 }
203 }
204 for (const slot of slots.values()) {
205 if (!boundaries.has(slot.owner_boundary_id)) throw new ResumeBootError("UnknownBoundary");
206 }
207 for (const activation of activations.values()) {
208 if (!boundaries.has(activation.boundary_id)
209 || !chunks.has(activation.chunk_id)
210 || !activation.prerequisite_boundary_ids.every((id) => boundaries.has(id))
211 || (activation.event_id !== undefined && !events.has(activation.event_id))) {
212 throw new ResumeBootError("ResumeArtifactMismatch", { activation: activation.activation_id });
213 }
214 }
215 for (const anchor of anchors.values()) {
216 if (!boundaries.has(anchor.boundary_id)) throw new ResumeBootError("UnknownBoundary");
217 }
218 for (const event of events.values()) {
219 if (!anchors.has(event.exact_target_anchor_id)
220 || !boundaries.has(event.owner_boundary_id)
221 || !chunks.has(event.chunk_id)) {
222 throw new ResumeBootError("ResumeArtifactMismatch", { event: event.resume_event_id });
223 }
224 }
225 return { boundaries, slots, capturePrograms, restorePrograms, chunks, activations, anchors, events };
226 }
227
228 function validateResumeSnapshot(snapshot, manifest, definitions) {
229 if (!exactObjectKeys(snapshot, ["schemaVersion", "buildId", "snapshotId", "manifestVersion", "capturedAt", "boundaries"])) {
230 throw new ResumeBootError("SnapshotSchemaMismatch");
231 }
232 if (snapshot.schemaVersion !== SUPPORTED_RESUME_SNAPSHOT_SCHEMA_VERSION
233 || snapshot.manifestVersion !== SUPPORTED_RESUME_MANIFEST_SCHEMA_VERSION
234 || snapshot.capturedAt !== null
235 || typeof snapshot.snapshotId !== "string") {
236 throw new ResumeBootError("SnapshotSchemaMismatch");
237 }
238 if (snapshot.buildId !== manifest.build_id) throw new ResumeBootError("BuildIdMismatch");
239 const seenBoundaries = new Set();
240 const seenSlots = new Set();
241 if (!Array.isArray(snapshot.boundaries)) throw new ResumeBootError("SnapshotSchemaMismatch");
242 for (const boundary of snapshot.boundaries) {
243 if (!exactObjectKeys(boundary, ["boundaryId", "schemaId", "values"])) {
244 throw new ResumeBootError("SnapshotSchemaMismatch");
245 }
246 const definition = definitions.boundaries.get(boundary.boundaryId);
247 if (definition === undefined) throw new ResumeBootError("UnknownBoundary");
248 if (seenBoundaries.has(boundary.boundaryId)) throw new ResumeBootError("DuplicateIdentity");
249 if (boundary.schemaId !== definition.schema_id || !Array.isArray(boundary.values)) {
250 throw new ResumeBootError("ResumeArtifactMismatch");
251 }
252 seenBoundaries.add(boundary.boundaryId);
253 for (const value of boundary.values) {
254 if (!exactObjectKeys(value, ["valueRecordId", "slotId", "value"])) {
255 throw new ResumeBootError("SnapshotSchemaMismatch");
256 }
257 const slot = definitions.slots.get(value.slotId);
258 if (slot === undefined) throw new ResumeBootError("UnknownSlot");
259 if (slot.owner_boundary_id !== boundary.boundaryId || seenSlots.has(value.slotId)) {
260 throw new ResumeBootError("DuplicateIdentity");
261 }
262 seenSlots.add(value.slotId);
263 }
264 }
265 return { seenBoundaries, seenSlots };
266 }
267
268 function allocateResumeRegistry(manifest, definitions) {
269 return {
270 contract_version: RESUME_REGISTRY_CONTRACT_VERSION,
271 build_id: manifest.build_id,
272 definitions,
273 boundary_records: new Map(),
274 slot_values: new Map(),
275 context_bindings: new Map(),
276 component_records: new Map(),
277 form_records: new Map(),
278 structural_records: new Map(),
279 effect_subscriptions: new Map(),
280 activation_states: new Map(),
281 debug: []
282 };
283 }
284
285 function resumeDebugEvidence(result) {
286 return {
287 contract_version: RESUME_REGISTRY_CONTRACT_VERSION,
288 mode: result.mode,
289 failure: result.failure ?? null,
290 build_id: resumeBootstrapState.manifest?.build_id ?? null,
291 boundary_ids: resumeBootstrapState.registry === null
292 ? []
293 : [...resumeBootstrapState.registry.definitions.boundaries.keys()],
294 slot_ids: resumeBootstrapState.registry === null
295 ? []
296 : [...resumeBootstrapState.registry.definitions.slots.keys()]
297 };
298 }
299
300 async function bootstrapResume(input = {}) {
301 if (resumeBootstrapState.phase !== "idle") {
302 throw new ResumeBootError("DoubleBootstrap");
303 }
304 resumeBootstrapState.phase = "booting";
305 const diagnostics = input.diagnostics ?? [];
306 let snapshot = null;
307 let coldAttempted = false;
308 try {
309 const manifest = input.resumeManifest ?? readResumeManifest(diagnostics);
310 const definitions = validateResumeManifest(manifest);
311 snapshot = readOptionalResumeSnapshot(diagnostics, input.snapshot);
312 resumeBootstrapState.manifest = manifest;
313 if (snapshot === null) {
314 coldAttempted = true;
315 const cold = await input.coldBoot?.("NoSnapshot");
316 const result = { mode: "cold", failure: null, cold };
317 resumeBootstrapState.phase = "ready";
318 resumeBootstrapState.result = result;
319 resumeBootstrapState.debug = [resumeDebugEvidence(result)];
320 return result;
321 }
322 validateResumeSnapshot(snapshot, manifest, definitions);
323 resumeBootstrapState.registry = allocateResumeRegistry(manifest, definitions);
324 const resume = await input.resumeBoot?.({
325 manifest,
326 snapshot,
327 registry: resumeBootstrapState.registry
328 });
329 const result = {
330 mode: "resume",
331 failure: null,
332 registry: resumeBootstrapState.registry,
333 resume
334 };
335 resumeBootstrapState.phase = "ready";
336 resumeBootstrapState.result = result;
337 resumeBootstrapState.debug = [resumeDebugEvidence(result)];
338 return result;
339 } catch (error) {
340 if (coldAttempted) {
341 resumeBootstrapState.phase = "failed";
342 throw error;
343 }
344 const failure = error instanceof ResumeBootError ? error.failure : "RestoreInstructionFailure";
345 resumeBootstrapState.registry = null;
346 resumeBootstrapState.debug = [{ contract_version: RESUME_REGISTRY_CONTRACT_VERSION, mode: "cold", failure }];
347 try {
348 coldAttempted = true;
349 const cold = await input.coldBoot?.(failure);
350 const result = { mode: "cold", failure, cold };
351 resumeBootstrapState.phase = "ready";
352 resumeBootstrapState.result = result;
353 return result;
354 } catch (coldError) {
355 resumeBootstrapState.phase = "failed";
356 throw coldError;
357 }
358 }
359 }
360
361 function captureSnapshot() {
362 if (resumeBootstrapState.phase !== "ready" || resumeBootstrapState.manifest === null) {
363 return { ok: false, failure: "NotQuiescent" };
364 }
365 return { ok: false, failure: "NotQuiescent" };
366 }
367
368 async function activateBoundary(boundaryId) {
369 const registry = resumeBootstrapState.registry;
370 if (registry === null || !registry.definitions.boundaries.has(boundaryId)) {
371 throw new ResumeBootError("UnknownBoundary", { boundaryId });
372 }
373 const activation = [...registry.definitions.activations.values()]
374 .find((record) => record.boundary_id === boundaryId);
375 if (activation === undefined) throw new ResumeBootError("ActivationFailure", { boundaryId });
376 const prior = registry.activation_states.get(activation.activation_id);
377 if (prior?.status === "active") return prior.result;
378 if (prior?.status === "failed") throw new ResumeBootError("ActivationFailure", { boundaryId });
379 if (prior?.promise !== undefined) return prior.promise;
380 const chunk = registry.definitions.chunks.get(activation.chunk_id);
381 if (chunk === undefined || typeof chunk.module_path !== "string") {
382 throw new ResumeBootError("ActivationFailure", { boundaryId });
383 }
384 const promise = import(new URL(chunk.module_path, document.baseURI).href)
385 .then(() => {
386 const result = { boundary_id: boundaryId, activation_id: activation.activation_id, chunk_id: activation.chunk_id, status: "active" };
387 registry.activation_states.set(activation.activation_id, { status: "active", result });
388 return result;
389 })
390 .catch(() => {
391 registry.activation_states.set(activation.activation_id, { status: "failed" });
392 throw new ResumeBootError("ActivationFailure", { boundaryId });
393 });
394 registry.activation_states.set(activation.activation_id, { status: "loading", promise });
395 return promise;
396 }
397
398 async function activateByEvent(resumeEventId) {
399 const registry = resumeBootstrapState.registry;
400 if (registry === null || !registry.definitions.events.has(resumeEventId)) {
401 throw new ResumeBootError("ActivationFailure", { resumeEventId });
402 }
403 const activation = [...registry.definitions.activations.values()]
404 .find((record) => record.event_id === resumeEventId);
405 if (activation === undefined) throw new ResumeBootError("ActivationFailure", { resumeEventId });
406 return activateBoundary(activation.boundary_id);
407 }
408
409 function resumeEventMarker(event) {
410 let current = event.target instanceof Element ? event.target : event.target?.parentElement;
411 while (current !== null && current !== undefined) {
412 const marker = current.getAttribute("data-presolve-e");
413 if (marker !== null) return marker;
414 current = current.parentElement;
415 }
416 return null;
417 }
418
419 function installResumeActivationListeners(registry, store) {
420 const events = new Map([...registry.definitions.events.values()].map((event) => [event.resume_event_id, event]));
421 const eventTypes = new Set([...events.values()].map((event) => event.event_type));
422 for (const eventType of eventTypes) {
423 document.addEventListener(eventType, async (event) => {
424 const marker = resumeEventMarker(event);
425 if (marker === null) return;
426 const record = events.get(marker);
427 if (record === undefined || record.event_type !== event.type) return;
428 try {
429 await activateByEvent(marker);
430 dispatchOrdinaryInstanceEvent(store, event);
431 } catch (error) {
432 registry.debug.push({ event_id: marker, failure: error instanceof ResumeBootError ? error.failure : "ActivationFailure" });
433 }
434 });
435 }
436 }
437
438 function readManifest(diagnostics) {
439 const element = document.getElementById(MANIFEST_ELEMENT_ID);
440
441 if (!(element instanceof HTMLScriptElement)) {
442 reportDiagnostic(
443 diagnostics,
444 "PSR_MISSING_MANIFEST",
445 `Missing template manifest script #${MANIFEST_ELEMENT_ID}`,
446 { manifestElementId: MANIFEST_ELEMENT_ID },
447 true
448 );
449 throw new PresolveBootError("PSR_MISSING_MANIFEST");
450 }
451
452 try {
453 return JSON.parse(element.textContent ?? "");
454 } catch (error) {
455 reportDiagnostic(
456 diagnostics,
457 "PSR_INVALID_MANIFEST_JSON",
458 "Template manifest JSON could not be parsed",
459 { message: error instanceof Error ? error.message : String(error) },
460 true
461 );
462 throw new PresolveBootError("PSR_INVALID_MANIFEST_JSON");
463 }
464 }
465
466 function effectArtifactHasActionPlans(effectArtifact) {
467 return (effectArtifact?.effects ?? []).some((effect) =>
468 Array.isArray(effect.action_batch_triggers) && effect.action_batch_triggers.length > 0
469 );
470 }
471
472 function validateManifestActionBindings(manifest, opaqueArtifact, diagnostics) {
473 const opaqueMethods = new Set((opaqueArtifact?.activations ?? []).map((activation) => activation.method));
474 for (const component of manifest.components ?? []) {
475 const actionsByMethod = new Map();
476
477 for (const action of component.actions ?? []) {
478 if (
479 typeof action.method_id !== "string"
480 || typeof action.action_batch_id !== "string"
481 || (manifest.schema_version === SUPPORTED_SCHEMA_VERSION && typeof action.storage_id !== "string")
482 ) {
483 reportDiagnostic(
484 diagnostics,
485 "PSR_INVALID_ACTION_BINDING",
486 "Schema-v2 template action was missing compiler action identities",
487 { component: component.name, action },
488 true
489 );
490 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
491 }
492 actionsByMethod.set(action.method_id, action.action_batch_id);
493 }
494
495 for (const event of component.template?.events ?? []) {
496 if (event.kind !== "action") {
497 reportDiagnostic(
498 diagnostics,
499 "PSR_INVALID_ACTION_BINDING",
500 "Schema-v2 template event was not an explicit action binding",
501 { component: component.name, event },
502 true
503 );
504 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
505 }
506 if (typeof event.method_id !== "string" || typeof event.action_batch_id !== "string") {
507 reportDiagnostic(
508 diagnostics,
509 "PSR_INVALID_ACTION_BINDING",
510 "Schema-v2 template action binding was missing an action batch identity",
511 { component: component.name, event },
512 true
513 );
514 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
515 }
516 if (actionsByMethod.get(event.method_id) !== event.action_batch_id
517 && !opaqueMethods.has(event.method_id)) {
518 reportDiagnostic(
519 diagnostics,
520 "PSR_INVALID_ACTION_BINDING",
521 "Template action binding did not match its compiler action implementation",
522 { component: component.name, event },
523 true
524 );
525 throw new PresolveBootError("PSR_INVALID_ACTION_BINDING");
526 }
527 }
528 }
529 }
530
531 function validateManifestSchema(manifest, effectArtifact, componentArtifact, opaqueArtifact, diagnostics) {
532 if (
533 manifest?.schema_version !== SUPPORTED_SCHEMA_VERSION &&
534 manifest?.schema_version !== FORMS_MANIFEST_SCHEMA_VERSION &&
535 manifest?.schema_version !== ACTION_MANIFEST_SCHEMA_VERSION &&
536 manifest?.schema_version !== LEGACY_MANIFEST_SCHEMA_VERSION
537 ) {
538 reportDiagnostic(
539 diagnostics,
540 "PSR_UNSUPPORTED_SCHEMA",
541 `Unsupported template manifest schema version ${String(manifest?.schema_version)}`,
542 {
543 schema_version: manifest?.schema_version,
544 supported_schema_version: SUPPORTED_SCHEMA_VERSION
545 },
546 true
547 );
548 throw new PresolveBootError("PSR_UNSUPPORTED_SCHEMA");
549 }
550
551 const isOrdinaryInstancePair = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
552 && componentArtifact?.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION;
553 const isLegacyColdPair = manifest.schema_version === FORMS_MANIFEST_SCHEMA_VERSION
554 && componentArtifact?.schema_version === LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION;
555 if (!isOrdinaryInstancePair && !isLegacyColdPair) {
556 reportDiagnostic(
557 diagnostics,
558 "PSR_UNSUPPORTED_SCHEMA",
559 "Template manifest and component artifact are not an exact runtime contract pair",
560 { manifest_schema_version: manifest.schema_version, component_schema_version: componentArtifact?.schema_version },
561 true
562 );
563 throw new PresolveBootError("PSR_UNSUPPORTED_SCHEMA");
564 }
565
566 if (
567 manifest.schema_version === LEGACY_MANIFEST_SCHEMA_VERSION &&
568 effectArtifactHasActionPlans(effectArtifact)
569 ) {
570 reportDiagnostic(
571 diagnostics,
572 "PSR_LEGACY_MANIFEST_EFFECT_ACTIONS",
573 "A legacy template manifest cannot activate compiler-generated effect action batches",
574 { schema_version: manifest.schema_version },
575 true
576 );
577 throw new PresolveBootError("PSR_LEGACY_MANIFEST_EFFECT_ACTIONS");
578 }
579
580 if (manifest.schema_version >= ACTION_MANIFEST_SCHEMA_VERSION) {
581 validateManifestActionBindings(manifest, opaqueArtifact, diagnostics);
582 }
583 }
584
585 function readFormsArtifact(diagnostics) {
586 const element = document.getElementById(FORMS_ARTIFACT_ELEMENT_ID);
587 if (element === null) return null;
588 if (!(element instanceof HTMLScriptElement)) {
589 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Forms runtime metadata was not stored in a script element", { artifactElementId: FORMS_ARTIFACT_ELEMENT_ID }, true);
590 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
591 }
592 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
593 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Forms runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
594 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
595 }
596 }
597
598 function readResourcesArtifact(diagnostics) {
599 const element = document.getElementById(RESOURCES_ARTIFACT_ELEMENT_ID);
600 if (element === null) return null;
601 if (!(element instanceof HTMLScriptElement)) {
602 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource runtime metadata was not stored in a script element", { artifactElementId: RESOURCES_ARTIFACT_ELEMENT_ID }, true);
603 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
604 }
605 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
606 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
607 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
608 }
609 }
610
611 function readOpaqueArtifact(diagnostics) {
612 const element = document.getElementById(OPAQUE_ARTIFACT_ELEMENT_ID);
613 if (element === null) return null;
614 if (!(element instanceof HTMLScriptElement)) {
615 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal metadata was not stored in a script element", { artifactElementId: OPAQUE_ARTIFACT_ELEMENT_ID }, true);
616 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
617 }
618 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
619 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
620 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
621 }
622 }
623
624 function validateOpaqueArtifact(opaqueArtifact, diagnostics) {
625 if (opaqueArtifact === null) return;
626 if (opaqueArtifact.schema_version !== SUPPORTED_OPAQUE_ARTIFACT_SCHEMA_VERSION
627 || !Array.isArray(opaqueArtifact.activations)) {
628 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_OPAQUE_ARTIFACT_SCHEMA", "Opaque terminal metadata did not match the compiler artifact contract", { schema_version: opaqueArtifact.schema_version }, true);
629 throw new PresolveBootError("PSR_UNSUPPORTED_OPAQUE_ARTIFACT_SCHEMA");
630 }
631 const ids = new Set();
632 const methods = new Set();
633 for (const activation of opaqueArtifact.activations) {
634 if (typeof activation?.id !== "string" || ids.has(activation.id)
635 || typeof activation?.method !== "string" || methods.has(activation.method)
636 || typeof activation?.owner_component !== "string"
637 || typeof activation?.package !== "string" || typeof activation?.version !== "string"
638 || typeof activation?.integrity !== "string" || typeof activation?.export !== "string"
639 || typeof activation?.runtime_module !== "string" || typeof activation?.runtime_location !== "string"
640 || activation?.type_signature !== "() -> void"
641 || activation?.execution_boundary !== "client" || activation?.resume_policy !== "cold_fallback") {
642 reportDiagnostic(diagnostics, "PSR_INVALID_OPAQUE_ARTIFACT", "Opaque terminal activation did not retain one exact callable client boundary", { activation }, true);
643 throw new PresolveBootError("PSR_INVALID_OPAQUE_ARTIFACT");
644 }
645 ids.add(activation.id);
646 methods.add(activation.method);
647 }
648 }
649
650 function validateResourcesArtifact(resourcesArtifact, diagnostics) {
651 if (resourcesArtifact === null) return;
652 if (resourcesArtifact.schema_version !== SUPPORTED_RESOURCES_ARTIFACT_SCHEMA_VERSION
653 || !Array.isArray(resourcesArtifact.declarations)
654 || !Array.isArray(resourcesArtifact.activations)) {
655 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_RESOURCES_ARTIFACT_SCHEMA", "Resource runtime metadata did not match the compiler artifact contract", { schema_version: resourcesArtifact.schema_version }, true);
656 throw new PresolveBootError("PSR_UNSUPPORTED_RESOURCES_ARTIFACT_SCHEMA");
657 }
658 const declarations = new Set();
659 for (const declaration of resourcesArtifact.declarations) {
660 const endpoint = declaration?.endpoint;
661 if (typeof declaration?.id !== "string" || declarations.has(declaration.id)
662 || typeof endpoint?.package !== "string" || typeof endpoint?.version !== "string"
663 || typeof endpoint?.integrity !== "string" || typeof endpoint?.export !== "string"
664 || typeof endpoint?.runtime_module !== "string" || typeof endpoint?.runtime_location !== "string") {
665 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource declaration did not retain one exact executable endpoint", { declaration }, true);
666 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
667 }
668 declarations.add(declaration.id);
669 }
670 const activations = new Set();
671 for (const activation of resourcesArtifact.activations) {
672 const generationRequired = ["pending", "ready", "failed", "cancelled"].includes(activation?.state);
673 if (typeof activation?.id !== "string" || activations.has(activation.id)
674 || !declarations.has(activation?.declaration)
675 || generationRequired !== Number.isInteger(activation?.generation)) {
676 reportDiagnostic(diagnostics, "PSR_INVALID_RESOURCES_ARTIFACT", "Resource activation did not retain canonical lifecycle linkage", { activation }, true);
677 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
678 }
679 activations.add(activation.id);
680 }
681 }
682
683 function validateFormsArtifact(formsArtifact, manifest, diagnostics) {
684 if (formsArtifact === null) {
685 if (manifest.schema_version >= 3) {
686 reportDiagnostic(diagnostics, "PSR_MISSING_FORMS_ARTIFACT", "A schema-v3 template manifest requires Forms runtime metadata", {}, true);
687 throw new PresolveBootError("PSR_MISSING_FORMS_ARTIFACT");
688 }
689 return;
690 }
691 if (formsArtifact.schema_version !== SUPPORTED_FORMS_ARTIFACT_SCHEMA_VERSION || !Array.isArray(formsArtifact.forms) || !Array.isArray(formsArtifact.instances) || !Array.isArray(formsArtifact.hosts)) {
692 reportDiagnostic(diagnostics, "PSR_UNSUPPORTED_FORMS_ARTIFACT_SCHEMA", "Forms runtime metadata did not match the compiler artifact contract", { schema_version: formsArtifact.schema_version }, true);
693 throw new PresolveBootError("PSR_UNSUPPORTED_FORMS_ARTIFACT_SCHEMA");
694 }
695 for (const form of formsArtifact.forms) {
696 const paths = new Set();
697 for (const field of form?.fields ?? []) {
698 if (!Array.isArray(field?.path) || field.path.length === 0 || field.path.length > 16
699 || !field.path.every((segment) => typeof segment === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(segment))) {
700 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Form Field path was not compiler-canonical", { field }, true);
701 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
702 }
703 const path = field.path.join(".");
704 if ([...paths].some((other) => path === other || path.startsWith(`${other}.`) || other.startsWith(`${path}.`))) {
705 reportDiagnostic(diagnostics, "PSR_INVALID_FORMS_ARTIFACT", "Form Field paths conflicted", { form: form.id, path }, true);
706 throw new PresolveBootError("PSR_INVALID_FORMS_ARTIFACT");
707 }
708 paths.add(path);
709 }
710 }
711 const hasForms = formsArtifact.forms.length > 0 || formsArtifact.instances.length > 0;
712 if (hasForms && manifest.schema_version < 3) {
713 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms runtime metadata requires a schema-v3 template manifest", { schema_version: manifest.schema_version }, true);
714 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
715 }
716 const instances = new Set(formsArtifact.instances.map((instance) => instance.id));
717 for (const binding of manifest.form_bindings ?? []) {
718 if (!instances.has(binding.form_instance_id)) {
719 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest bridge referenced an unknown Form instance", { binding }, true);
720 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
721 }
722 }
723 const artifactHosts = new Map(formsArtifact.hosts.map((host) => [`${host.host_anchor}|${host.form_instance}`, host]));
724 for (const host of manifest.form_hosts ?? []) {
725 const artifact = artifactHosts.get(`${host.host_anchor}|${host.form_instance_id}`);
726 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) {
727 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest host bridge did not match an exact compiler host record", { host }, true);
728 throw new PresolveBootError("PSR_FORMS_MANIFEST_MISMATCH");
729 }
730 }
731 }
732
733 function readComputedArtifact(diagnostics) {
734 const element = document.getElementById(COMPUTED_ARTIFACT_ELEMENT_ID);
735
736 if (element === null) {
737 return null;
738 }
739
740 if (!(element instanceof HTMLScriptElement)) {
741 reportDiagnostic(
742 diagnostics,
743 "PSR_INVALID_COMPUTED_ARTIFACT",
744 "Computed runtime metadata was not stored in a script element",
745 { artifactElementId: COMPUTED_ARTIFACT_ELEMENT_ID },
746 true
747 );
748 throw new PresolveBootError("PSR_INVALID_COMPUTED_ARTIFACT");
749 }
750
751 try {
752 return JSON.parse(element.textContent ?? "");
753 } catch (error) {
754 reportDiagnostic(
755 diagnostics,
756 "PSR_INVALID_COMPUTED_ARTIFACT",
757 "Computed runtime metadata JSON could not be parsed",
758 { message: error instanceof Error ? error.message : String(error) },
759 true
760 );
761 throw new PresolveBootError("PSR_INVALID_COMPUTED_ARTIFACT");
762 }
763 }
764
765 function validateComputedArtifactSchema(artifact, diagnostics) {
766 if (artifact === null) {
767 return;
768 }
769
770 if (artifact.schema_version !== SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION) {
771 reportDiagnostic(
772 diagnostics,
773 "PSR_UNSUPPORTED_COMPUTED_ARTIFACT_SCHEMA",
774 `Unsupported computed runtime metadata schema version ${String(artifact.schema_version)}`,
775 {
776 schema_version: artifact.schema_version,
777 supported_schema_version: SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION
778 },
779 true
780 );
781 throw new PresolveBootError("PSR_UNSUPPORTED_COMPUTED_ARTIFACT_SCHEMA");
782 }
783 }
784
785 function readEffectArtifact(diagnostics) {
786 const element = document.getElementById(EFFECT_ARTIFACT_ELEMENT_ID);
787
788 if (element === null) {
789 return null;
790 }
791
792 if (!(element instanceof HTMLScriptElement)) {
793 reportDiagnostic(
794 diagnostics,
795 "PSR_INVALID_EFFECT_ARTIFACT",
796 "Effect runtime metadata was not stored in a script element",
797 { artifactElementId: EFFECT_ARTIFACT_ELEMENT_ID },
798 true
799 );
800 throw new PresolveBootError("PSR_INVALID_EFFECT_ARTIFACT");
801 }
802
803 try {
804 return JSON.parse(element.textContent ?? "");
805 } catch (error) {
806 reportDiagnostic(
807 diagnostics,
808 "PSR_INVALID_EFFECT_ARTIFACT",
809 "Effect runtime metadata JSON could not be parsed",
810 { message: error instanceof Error ? error.message : String(error) },
811 true
812 );
813 throw new PresolveBootError("PSR_INVALID_EFFECT_ARTIFACT");
814 }
815 }
816
817 function validateEffectArtifactSchema(artifact, diagnostics) {
818 if (artifact === null) {
819 return;
820 }
821
822 if (artifact.schema_version !== SUPPORTED_EFFECT_ARTIFACT_SCHEMA_VERSION) {
823 reportDiagnostic(
824 diagnostics,
825 "PSR_UNSUPPORTED_EFFECT_ARTIFACT_SCHEMA",
826 `Unsupported effect runtime metadata schema version ${String(artifact.schema_version)}`,
827 {
828 schema_version: artifact.schema_version,
829 supported_schema_version: SUPPORTED_EFFECT_ARTIFACT_SCHEMA_VERSION
830 },
831 true
832 );
833 throw new PresolveBootError("PSR_UNSUPPORTED_EFFECT_ARTIFACT_SCHEMA");
834 }
835 }
836
837 function readComponentArtifact(diagnostics) {
838 const element = document.getElementById(COMPONENT_ARTIFACT_ELEMENT_ID);
839 if (element === null) return null;
840 if (!(element instanceof HTMLScriptElement)) {
841 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata was not stored in a script element", { artifactElementId: COMPONENT_ARTIFACT_ELEMENT_ID }, true);
842 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
843 }
844 try { return JSON.parse(element.textContent ?? ""); } catch (error) {
845 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
846 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
847 }
848 }
849
850 function canonicalStateSlotId(componentInstanceId, storageId) {
851 const encoded = [...new TextEncoder().encode(storageId)].map((byte) => {
852 const unreserved = (byte >= 65 && byte <= 90)
853 || (byte >= 97 && byte <= 122)
854 || (byte >= 48 && byte <= 57)
855 || byte === 45 || byte === 46 || byte === 95 || byte === 126;
856 return unreserved ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
857 }).join("");
858 return `${componentInstanceId}/state-slot:${encoded}`;
859 }
860
861 function validateComponentArtifactSchema(artifact, diagnostics) {
862 if (artifact === null) return;
863 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)) {
864 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata did not match the compiler artifact contract", { schema_version: artifact.schema_version }, true);
865 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
866 }
867 const instances = new Set(artifact.instances.map((instance) => instance.instance));
868 const structuralTemplates = new Set(
869 (artifact.structural_programs ?? [])
870 .flatMap((program) => program.template_instances ?? [])
871 );
872 const stateSlots = new Set();
873 const statePairs = new Set();
874 for (const instance of artifact.instances) if (
875 instance.parent !== null
876 && instance.parent !== undefined
877 && !instances.has(instance.parent)
878 && !structuralTemplates.has(instance.parent)
879 ) {
880 reportDiagnostic(diagnostics, "PSR_INVALID_COMPONENT_ARTIFACT", "Component runtime metadata referenced an unknown parent instance", { instance: instance.instance, parent: instance.parent }, true);
881 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
882 }
883 if (artifact.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION) {
884 for (const instance of artifact.instances) {
885 if (!Array.isArray(instance.state_slots)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
886 for (const slot of instance.state_slots) {
887 const pair = `${instance.instance}|${slot.storage_id}`;
888 if (
889 typeof slot.slot_id !== "string"
890 || slot.slot_id !== canonicalStateSlotId(instance.instance, slot.storage_id)
891 || typeof slot.state_id !== "string"
892 || typeof slot.storage_id !== "string"
893 || slot.storage_id !== `storage:${slot.state_id}`
894 || stateSlots.has(slot.slot_id)
895 || statePairs.has(pair)
896 ) {
897 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
898 }
899 stateSlots.add(slot.slot_id);
900 statePairs.add(pair);
901 }
902 }
903 }
904 }
905
906 function readContextArtifact(diagnostics) {
907 const element = document.getElementById(CONTEXT_ARTIFACT_ELEMENT_ID);
908 if (!(element instanceof HTMLScriptElement)) {
909 reportDiagnostic(diagnostics, "PSR_INVALID_CONTEXT_ARTIFACT", "Context runtime metadata was not stored in a script element", { artifactElementId: CONTEXT_ARTIFACT_ELEMENT_ID }, true);
910 throw new PresolveBootError("PSR_INVALID_CONTEXT_ARTIFACT");
911 }
912 try { return JSON.parse(element.textContent ?? ""); }
913 catch (error) {
914 reportDiagnostic(diagnostics, "PSR_INVALID_CONTEXT_ARTIFACT", "Context runtime metadata JSON could not be parsed", { message: error instanceof Error ? error.message : String(error) }, true);
915 throw new PresolveBootError("PSR_INVALID_CONTEXT_ARTIFACT");
916 }
917 }
918
919 function validateContextArtifactSchema(artifact, diagnostics) {
920 if (artifact.schema_version !== SUPPORTED_CONTEXT_ARTIFACT_SCHEMA_VERSION) {
921 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);
922 throw new PresolveBootError("PSR_UNSUPPORTED_CONTEXT_ARTIFACT_SCHEMA");
923 }
924 }
925
926 function fieldNameFromThisMember(expression) {
927 const match = /^this\.([A-Za-z_$][\w$]*)$/.exec(String(expression ?? ""));
928 return match === null ? null : match[1];
929 }
930
931 function collectBindingAnchors() {
932 const anchors = new Map();
933 const walker = document.createTreeWalker(
934 document.body,
935 NodeFilter.SHOW_COMMENT
936 );
937
938 while (walker.nextNode()) {
939 const value = (walker.currentNode.nodeValue ?? "").trim();
940 const match = /^presolve-binding:([^:]+):(.*)$/.exec(value);
941
942 if (match !== null) {
943 anchors.set(match[1], {
944 id: match[1],
945 expression: match[2],
946 marker: walker.currentNode
947 });
948 }
949 }
950
951 return anchors;
952 }
953
954 function collectConditionalAnchors() {
955 const starts = new Map();
956 const ends = new Map();
957 const walker = document.createTreeWalker(
958 document.body,
959 NodeFilter.SHOW_COMMENT
960 );
961
962 while (walker.nextNode()) {
963 const value = (walker.currentNode.nodeValue ?? "").trim();
964 const startMatch = /^presolve-conditional-start:([^:]+):(.*)$/.exec(value);
965
966 if (startMatch !== null) {
967 starts.set(startMatch[1], {
968 id: startMatch[1],
969 condition: startMatch[2],
970 marker: walker.currentNode
971 });
972 continue;
973 }
974
975 const endMatch = /^presolve-conditional-end:([^:]+)$/.exec(value);
976
977 if (endMatch !== null) {
978 ends.set(endMatch[1], {
979 id: endMatch[1],
980 marker: walker.currentNode
981 });
982 }
983 }
984
985 return {
986 starts,
987 ends
988 };
989 }
990
991 function collectListAnchors() {
992 const starts = new Map();
993 const ends = new Map();
994 const walker = document.createTreeWalker(
995 document.body,
996 NodeFilter.SHOW_COMMENT
997 );
998
999 while (walker.nextNode()) {
1000 const value = (walker.currentNode.nodeValue ?? "").trim();
1001 const startMatch = /^presolve-list-start:([^:]+):(.*)$/.exec(value);
1002
1003 if (startMatch !== null) {
1004 starts.set(startMatch[1], {
1005 id: startMatch[1],
1006 iterable: startMatch[2],
1007 marker: walker.currentNode
1008 });
1009 continue;
1010 }
1011
1012 const endMatch = /^presolve-list-end:([^:]+)$/.exec(value);
1013
1014 if (endMatch !== null) {
1015 ends.set(endMatch[1], {
1016 id: endMatch[1],
1017 marker: walker.currentNode
1018 });
1019 }
1020 }
1021
1022 return {
1023 starts,
1024 ends
1025 };
1026 }
1027
1028 function collectElementAnchors() {
1029 const elementsByNode = new Map();
1030
1031 for (const element of document.querySelectorAll("[data-presolve-node]")) {
1032 elementsByNode.set(element.dataset.presolveNode, element);
1033 }
1034
1035 return elementsByNode;
1036 }
1037
1038 function collectMissingAnchors(
1039 manifest,
1040 bindingAnchors,
1041 conditionalAnchors,
1042 listAnchors,
1043 elementsByNode
1044 ) {
1045 const missing = [];
1046
1047 for (const component of manifest.components ?? []) {
1048 for (const node of component.template?.nodes ?? []) {
1049 if (node.kind === "element") {
1050 if (!elementsByNode.has(node.id)) {
1051 missing.push({
1052 component: component.name,
1053 id: node.id,
1054 kind: node.kind,
1055 code: "PSR_MISSING_ELEMENT_ANCHOR"
1056 });
1057 }
1058 }
1059
1060 if (
1061 node.kind === "binding" &&
1062 node.target !== "attribute" &&
1063 !bindingAnchors.has(node.id)
1064 ) {
1065 missing.push({
1066 component: component.name,
1067 id: node.id,
1068 kind: node.kind,
1069 code: "PSR_MISSING_BINDING_ANCHOR"
1070 });
1071 }
1072
1073 if (node.kind === "conditional") {
1074 if (!conditionalAnchors.starts.has(node.start)) {
1075 missing.push({
1076 component: component.name,
1077 id: node.start,
1078 kind: node.kind,
1079 code: "PSR_MISSING_CONDITIONAL_ANCHOR"
1080 });
1081 }
1082
1083 if (!conditionalAnchors.ends.has(node.end)) {
1084 missing.push({
1085 component: component.name,
1086 id: node.end,
1087 kind: node.kind,
1088 code: "PSR_MISSING_CONDITIONAL_ANCHOR"
1089 });
1090 }
1091 }
1092
1093 if (node.kind === "list") {
1094 if (!listAnchors.starts.has(node.start)) {
1095 missing.push({
1096 component: component.name,
1097 id: node.start,
1098 kind: node.kind,
1099 code: "PSR_MISSING_LIST_ANCHOR"
1100 });
1101 }
1102
1103 if (!listAnchors.ends.has(node.end)) {
1104 missing.push({
1105 component: component.name,
1106 id: node.end,
1107 kind: node.kind,
1108 code: "PSR_MISSING_LIST_ANCHOR"
1109 });
1110 }
1111 }
1112 }
1113 }
1114
1115 return missing;
1116 }
1117
1118 function buildActionsByMethod(component) {
1119 const actionsByMethod = new Map();
1120
1121 for (const action of component.actions ?? []) {
1122 const record = actionsByMethod.get(action.method_id) ?? {
1123 action_batch_id: action.action_batch_id,
1124 actions: []
1125 };
1126 record.actions.push(action);
1127 actionsByMethod.set(action.method_id, record);
1128 }
1129
1130 return actionsByMethod;
1131 }
1132
1133 function componentFieldKey(componentName, field) {
1134 return `${componentName}:${field}`;
1135 }
1136
1137 function formatBindingValue(value) {
1138 return value === null ? "" : String(value);
1139 }
1140
1141 function isBooleanAttribute(attribute) {
1142 return new Set([
1143 "allowfullscreen",
1144 "async",
1145 "autofocus",
1146 "autoplay",
1147 "checked",
1148 "controls",
1149 "default",
1150 "defer",
1151 "disabled",
1152 "formnovalidate",
1153 "hidden",
1154 "inert",
1155 "loop",
1156 "multiple",
1157 "muted",
1158 "nomodule",
1159 "novalidate",
1160 "open",
1161 "readonly",
1162 "required",
1163 "reversed",
1164 "selected"
1165 ]).has(String(attribute).toLowerCase());
1166 }
1167
1168 function isPropertyAttribute(attribute) {
1169 return new Set([
1170 "checked",
1171 "disabled",
1172 "selected",
1173 "value"
1174 ]).has(String(attribute).toLowerCase());
1175 }
1176
1177 function updateAttributeBinding(element, attribute, value) {
1178 const normalizedAttribute = String(attribute);
1179
1180 if (isBooleanAttribute(normalizedAttribute)) {
1181 const enabled = Boolean(value);
1182 element.toggleAttribute(normalizedAttribute, enabled);
1183
1184 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1185 element[normalizedAttribute] = enabled;
1186 }
1187
1188 return;
1189 }
1190
1191 if (value === null || value === undefined) {
1192 element.removeAttribute(normalizedAttribute);
1193
1194 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1195 element[normalizedAttribute] = "";
1196 }
1197
1198 return;
1199 }
1200
1201 const text = formatBindingValue(value);
1202 element.setAttribute(normalizedAttribute, text);
1203
1204 if (isPropertyAttribute(normalizedAttribute) && normalizedAttribute in element) {
1205 element[normalizedAttribute] = text;
1206 }
1207 }
1208
1209 function replaceConditionalBranch(store, startMarker, endMarker, html) {
1210 if (
1211 startMarker.parentNode === null ||
1212 endMarker.parentNode === null ||
1213 startMarker.parentNode !== endMarker.parentNode
1214 ) {
1215 reportDiagnostic(
1216 store.diagnostics,
1217 "PSR_MISSING_CONDITIONAL_ANCHOR",
1218 "Conditional anchor range was not contiguous in one parent",
1219 {}
1220 );
1221 return;
1222 }
1223
1224 let current = startMarker.nextSibling;
1225
1226 while (current !== null && current !== endMarker) {
1227 const next = current.nextSibling;
1228 current.remove();
1229 current = next;
1230 }
1231
1232 const template = document.createElement("template");
1233 template.innerHTML = String(html ?? "");
1234 endMarker.parentNode.insertBefore(template.content, endMarker);
1235 store.elementsByNode = collectElementAnchors();
1236 }
1237
1238 function listItems(value) {
1239 return Array.isArray(value) ? value : [];
1240 }
1241
1242 function normalizeListKey(value) {
1243 return String(value).replaceAll("--", "—");
1244 }
1245
1246 function listItemMemberPath(node, expression) {
1247 const prefix = `${String(node.item_variable ?? "")}.`;
1248 const value = String(expression ?? "");
1249
1250 if (!value.startsWith(prefix)) {
1251 return null;
1252 }
1253
1254 const path = value.slice(prefix.length).split(".");
1255 return path.length === 0 || path.some((member) => member === "") ? null : path;
1256 }
1257
1258 function listItemMemberValue(node, item, expression) {
1259 const path = listItemMemberPath(node, expression);
1260
1261 if (path === null) {
1262 return undefined;
1263 }
1264
1265 let value = item;
1266
1267 for (const member of path) {
1268 if (
1269 value === null ||
1270 typeof value !== "object" ||
1271 Array.isArray(value) ||
1272 !Object.prototype.hasOwnProperty.call(value, member)
1273 ) {
1274 return undefined;
1275 }
1276
1277 value = value[member];
1278 }
1279
1280 return value;
1281 }
1282
1283 function listItemBindingValue(node, item, index, expression) {
1284 if (expression === node.item_variable) {
1285 return item;
1286 }
1287
1288 if (expression === node.index_variable) {
1289 return index;
1290 }
1291
1292 if (listItemMemberPath(node, expression) !== null) {
1293 return listItemMemberValue(node, item, expression);
1294 }
1295
1296 return undefined;
1297 }
1298
1299 function isListKeyPrimitive(value) {
1300 return value === null || (typeof value !== "object" && typeof value !== "undefined");
1301 }
1302
1303 function listItemKey(node, item, index) {
1304 if (node.key_expression === node.item_variable) {
1305 return isListKeyPrimitive(item) ? normalizeListKey(item) : String(index);
1306 }
1307
1308 if (listItemMemberPath(node, node.key_expression) !== null) {
1309 const value = listItemMemberValue(node, item, node.key_expression);
1310 return isListKeyPrimitive(value) ? normalizeListKey(value) : String(index);
1311 }
1312
1313 if (node.index_variable === node.key_expression) {
1314 return String(index);
1315 }
1316
1317 return String(index);
1318 }
1319
1320 function escapeHtmlText(value) {
1321 return String(value)
1322 .replaceAll("&", "&")
1323 .replaceAll("<", "<")
1324 .replaceAll(">", ">");
1325 }
1326
1327 function escapeHtmlAttribute(value) {
1328 return escapeHtmlText(value).replaceAll('"', """);
1329 }
1330
1331 function renderListItemHtml(node, item, index, key) {
1332 return String(node.item_template_html ?? "")
1333 .replaceAll("__ez_list_key__", escapeHtmlAttribute(key))
1334 .replaceAll("__ez_list_item__", escapeHtmlText(formatBindingValue(item)))
1335 .replaceAll("__ez_list_index__", String(index));
1336 }
1337
1338 function populateListItemMemberBindings(node, item, fragment) {
1339 const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_COMMENT);
1340 const markers = [];
1341
1342 while (walker.nextNode()) {
1343 markers.push(walker.currentNode);
1344 }
1345
1346 const memberPrefix = `:${String(node.item_variable ?? "")}.`;
1347
1348 for (const marker of markers) {
1349 const comment = String(marker.nodeValue ?? "");
1350 const expressionStart = comment.lastIndexOf(memberPrefix);
1351
1352 if (expressionStart < 0) {
1353 continue;
1354 }
1355
1356 const expression = comment.slice(expressionStart + 1);
1357 const value = listItemMemberValue(node, item, expression);
1358 marker.after(document.createTextNode(value === undefined ? "" : formatBindingValue(value)));
1359 }
1360 }
1361
1362 function listItemBindingExpression(node, comment) {
1363 const value = String(comment ?? "");
1364 const memberPrefix = `:${String(node.item_variable ?? "")}.`;
1365 const memberStart = value.lastIndexOf(memberPrefix);
1366
1367 if (memberStart >= 0) {
1368 return value.slice(memberStart + 1);
1369 }
1370
1371 const itemSuffix = `:${String(node.item_variable ?? "")}`;
1372 if (value.endsWith(itemSuffix)) {
1373 return String(node.item_variable ?? "");
1374 }
1375
1376 const indexSuffix = `:${String(node.index_variable ?? "")}`;
1377 if (node.index_variable !== null && node.index_variable !== undefined && value.endsWith(indexSuffix)) {
1378 return String(node.index_variable);
1379 }
1380
1381 return null;
1382 }
1383
1384 function updateListItemTextBindings(node, instance) {
1385 const walker = document.createTreeWalker(instance.element, NodeFilter.SHOW_COMMENT);
1386
1387 while (walker.nextNode()) {
1388 const marker = walker.currentNode;
1389 const expression = listItemBindingExpression(node, marker.nodeValue);
1390
1391 if (expression === null) {
1392 continue;
1393 }
1394
1395 const textNode = marker.nextSibling;
1396 const value = listItemBindingValue(node, instance.item, instance.index, expression);
1397 const text = value === undefined ? "" : formatBindingValue(value);
1398
1399 if (textNode instanceof Text) {
1400 textNode.textContent = text;
1401 } else if (
1402 textNode instanceof Comment &&
1403 String(textNode.nodeValue ?? "").startsWith("presolve-list-binding-end:")
1404 ) {
1405 textNode.before(document.createTextNode(text));
1406 }
1407 }
1408 }
1409
1410 function listItemElements(root) {
1411 return [root, ...root.querySelectorAll("[data-presolve-list-bindings]")];
1412 }
1413
1414 function updateListItemAttributes(node, instance) {
1415 for (const element of listItemElements(instance.element)) {
1416 const bindings = String(element.dataset.presolveListBindings ?? "");
1417
1418 for (const binding of bindings.split(";")) {
1419 const separator = binding.indexOf("=");
1420
1421 if (separator < 1) {
1422 continue;
1423 }
1424
1425 const attribute = binding.slice(0, separator);
1426 const expression = binding.slice(separator + 1);
1427 const value = listItemBindingValue(node, instance.item, instance.index, expression);
1428 updateAttributeBinding(element, attribute, value);
1429 }
1430 }
1431 }
1432
1433 function listItemEventElements(root) {
1434 return [root, ...root.querySelectorAll("[data-presolve-on-click]")];
1435 }
1436
1437 function registerListItemEvents(store, component, instance) {
1438 for (const element of listItemEventElements(instance.element)) {
1439 const node = element.dataset.presolveNode;
1440 const handler = element.dataset.presolveOnClick;
1441
1442 if (node !== undefined && handler !== undefined) {
1443 registerEvent(store, component, { node, event: "click", handler });
1444 }
1445 }
1446 }
1447
1448 function unregisterListItemEvents(store, instance) {
1449 const eventsByNode = store.eventsByType.get("click");
1450
1451 if (eventsByNode === undefined) {
1452 return;
1453 }
1454
1455 for (const element of listItemEventElements(instance.element)) {
1456 const node = element.dataset.presolveNode;
1457
1458 if (node !== undefined) {
1459 eventsByNode.delete(node);
1460 }
1461 }
1462 }
1463
1464 function renderListItemElement(node, item, index, key) {
1465 const template = document.createElement("template");
1466 template.innerHTML = renderListItemHtml(node, item, index, key);
1467 populateListItemMemberBindings(node, item, template.content);
1468 return template.content.firstElementChild;
1469 }
1470
1471 function initialListInstances(store, node, items) {
1472 const instances = new Map();
1473
1474 for (const [index, item] of items.entries()) {
1475 const key = listItemKey(node, item, index);
1476 const element = store.elementsByNode.get(`${node.item_root}:${key}`);
1477
1478 if (element !== undefined) {
1479 instances.set(key, { element, item, index, key });
1480 }
1481 }
1482
1483 return instances;
1484 }
1485
1486 function reconcileKeyedList(store, component, node, startMarker, endMarker, instances, value) {
1487 if (
1488 startMarker.parentNode === null ||
1489 endMarker.parentNode === null ||
1490 startMarker.parentNode !== endMarker.parentNode
1491 ) {
1492 reportDiagnostic(
1493 store.diagnostics,
1494 "PSR_MISSING_LIST_ANCHOR",
1495 "List anchor range was not contiguous in one parent",
1496 {}
1497 );
1498 return instances;
1499 }
1500
1501 const parent = startMarker.parentNode;
1502 const nextInstances = new Map();
1503 const ordered = [];
1504
1505 for (const [index, item] of listItems(value).entries()) {
1506 const key = listItemKey(node, item, index);
1507
1508 if (nextInstances.has(key)) {
1509 reportDiagnostic(
1510 store.diagnostics,
1511 "PSR_DUPLICATE_LIST_KEY",
1512 "List update produced a duplicate key",
1513 { id: node.id, key }
1514 );
1515 continue;
1516 }
1517
1518 let instance = instances.get(key);
1519
1520 if (instance === undefined) {
1521 const element = renderListItemElement(node, item, index, key);
1522
1523 if (element === null) {
1524 reportDiagnostic(
1525 store.diagnostics,
1526 "PSR_INVALID_LIST_TEMPLATE",
1527 "List item template did not produce a root element",
1528 node
1529 );
1530 continue;
1531 }
1532
1533 parent.insertBefore(element, endMarker);
1534 instance = { element, item, index, key };
1535 registerListItemEvents(store, component, instance);
1536 }
1537
1538 instance.item = item;
1539 instance.index = index;
1540 updateListItemTextBindings(node, instance);
1541 updateListItemAttributes(node, instance);
1542 nextInstances.set(key, instance);
1543 ordered.push(instance);
1544 }
1545
1546 for (const [key, instance] of instances) {
1547 if (!nextInstances.has(key)) {
1548 unregisterListItemEvents(store, instance);
1549 instance.element.remove();
1550 }
1551 }
1552
1553 let cursor = startMarker.nextSibling;
1554
1555 for (const instance of ordered) {
1556 if (instance.element !== cursor) {
1557 parent.insertBefore(instance.element, cursor);
1558 }
1559 cursor = instance.element.nextSibling;
1560 }
1561
1562 store.elementsByNode = collectElementAnchors();
1563 return nextInstances;
1564 }
1565
1566 function createRuntimeStore(elementsByNode, diagnostics, computedArtifact, contextArtifact, effectArtifact, componentArtifact, opaqueArtifact) {
1567 const computedEvaluations = new Map();
1568 const storageValues = new Map();
1569 const storageByComponentField = new Map();
1570 const instanceQualifiedState = componentArtifact?.schema_version === SUPPORTED_COMPONENT_ARTIFACT_SCHEMA_VERSION;
1571 const invalidationsByStorage = new Map();
1572 const resourceInvalidationsByDeclaration = new Map();
1573 const computedDirty = new Map();
1574
1575 if (!instanceQualifiedState) {
1576 for (const state of computedArtifact?.state ?? []) {
1577 storageValues.set(state.storage, state.initial_value);
1578 storageByComponentField.set(
1579 componentFieldKey(state.component, state.field),
1580 state.storage
1581 );
1582 }
1583 }
1584
1585 for (const invalidation of computedArtifact?.invalidations ?? []) {
1586 invalidationsByStorage.set(invalidation.storage, invalidation.dependents ?? []);
1587 }
1588
1589 for (const invalidation of computedArtifact?.resource_invalidations ?? []) {
1590 resourceInvalidationsByDeclaration.set(invalidation.declaration, invalidation.dependents ?? []);
1591 }
1592
1593 for (const evaluation of computedArtifact?.evaluations ?? []) {
1594 computedEvaluations.set(evaluation.computed, evaluation);
1595 computedDirty.set(evaluation.computed, evaluation.dirty_flag?.initial_value === true);
1596 }
1597
1598 return {
1599 components: new Map(),
1600 bindingsByField: new Map(),
1601 bindingsByStateSlot: new Map(),
1602 bindingsByInstanceComputed: new Map(),
1603 actionsByMethod: new Map(),
1604 opaqueTerminalsByMethod: new Map((opaqueArtifact?.activations ?? []).map((activation) => [activation.method, activation])),
1605 opaqueActivations: [],
1606 eventsByType: new Map(),
1607 elementsByNode,
1608 diagnostics,
1609 computedArtifact,
1610 contextArtifact,
1611 effectArtifact,
1612 contextSlots: new Map(),
1613 contextConsumerBindings: new Map(),
1614 contextInitialSourceRuns: [],
1615 contextUpdateSourceRuns: [],
1616 contextFailures: [],
1617 computedEvaluations,
1618 computedDirty,
1619 computedValues: new Map(),
1620 computedCaches: new Map(),
1621 computedSlotsByInstanceComputed: new Map(),
1622 computedDirtySlots: new Map(),
1623 storageValues,
1624 storageByComponentField,
1625 stateSlotsByInstanceStorage: new Map(),
1626 instanceQualifiedState,
1627 invalidationsByStorage,
1628 resourceInvalidationsByDeclaration,
1629 computedUpdateRuns: 0,
1630 initialEffectRuns: [],
1631 completedActionEffectRuns: [],
1632 effectSubscriptions: new Map(),
1633 resources: new Map(),
1634 resourceActivationsByInstanceDeclaration: new Map(),
1635 activeActionBatch: null
1636 };
1637 }
1638
1639 function computedSlotForExecution(store, computed) {
1640 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
1641 if (componentInstanceId === undefined) return undefined;
1642 const slot = store.computedSlotsByInstanceComputed.get(`${componentInstanceId}|${computed}`);
1643 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1644 return slot;
1645 }
1646
1647 function stateSlotForInstanceStorage(store, componentInstanceId, storage) {
1648 const slot = store.stateSlotsByInstanceStorage.get(`${componentInstanceId}|${storage}`);
1649 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1650 return slot;
1651 }
1652
1653 function stateValueForStorage(store, storage) {
1654 if (!store.instanceQualifiedState) return store.storageValues.get(storage);
1655 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
1656 if (componentInstanceId === undefined) throw new PresolveBootError("PSR_MISSING_EXECUTION_CONTEXT");
1657 return store.storageValues.get(
1658 stateSlotForInstanceStorage(store, componentInstanceId, storage).slot_id
1659 );
1660 }
1661
1662 function isComputedDirty(store, computed) {
1663 const slot = computedSlotForExecution(store, computed);
1664 return slot === undefined
1665 ? store.computedDirty.get(computed) === true
1666 : store.computedDirtySlots.get(slot.dirty_slot_id) === true;
1667 }
1668
1669 function setComputedDirty(store, computed, value) {
1670 const slot = computedSlotForExecution(store, computed);
1671 if (slot === undefined) store.computedDirty.set(computed, value);
1672 else store.computedDirtySlots.set(slot.dirty_slot_id, value);
1673 }
1674
1675 function computedValue(store, computed) {
1676 const slot = computedSlotForExecution(store, computed);
1677 return slot === undefined
1678 ? store.computedValues.get(computed)
1679 : store.computedCaches.get(slot.cache_slot_id);
1680 }
1681
1682 function computedValueForInstance(store, componentInstanceId, computed) {
1683 const slot = store.computedSlotsByInstanceComputed.get(`${componentInstanceId}|${computed}`);
1684 if (slot === undefined) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
1685 return store.computedCaches.get(slot.cache_slot_id);
1686 }
1687
1688 function notifyComputed(store, computed, value) {
1689 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
1690 if (componentInstanceId === undefined) return;
1691 for (const updateBinding of store.bindingsByInstanceComputed.get(`${componentInstanceId}|${computed}`) ?? []) {
1692 updateBinding(value);
1693 }
1694 }
1695
1696 function registerComputedBinding(store, componentInstanceId, computed, updateBinding) {
1697 const key = `${componentInstanceId}|${computed}`;
1698 const bindings = store.bindingsByInstanceComputed.get(key) ?? [];
1699 bindings.push(updateBinding);
1700 store.bindingsByInstanceComputed.set(key, bindings);
1701 }
1702
1703 function storeComputedValue(store, evaluation, value) {
1704 const slot = computedSlotForExecution(store, evaluation.computed);
1705 if (slot === undefined) {
1706 store.computedValues.set(evaluation.computed, value);
1707 store.computedCaches.set(evaluation.cache_slot, value);
1708 } else {
1709 store.computedCaches.set(slot.cache_slot_id, value);
1710 }
1711 notifyComputed(store, evaluation.computed, value);
1712 }
1713
1714 function computedOperandValue(store, values, operand) {
1715 if (operand?.kind === "value") {
1716 return values.get(operand.value);
1717 }
1718
1719 if (operand?.kind === "constant") {
1720 return operand.value;
1721 }
1722
1723 if (operand?.kind === "storage") {
1724 return stateValueForStorage(store, operand.storage);
1725 }
1726
1727 return undefined;
1728 }
1729
1730 function computedBinary(operation, left, right) {
1731 switch (operation) {
1732 case "add": return left + right;
1733 case "subtract": return left - right;
1734 case "multiply": return left * right;
1735 case "divide": return left / right;
1736 case "remainder": return left % right;
1737 case "equal": return left === right;
1738 case "not-equal": return left !== right;
1739 case "less-than": return left < right;
1740 case "less-than-or-equal": return left <= right;
1741 case "greater-than": return left > right;
1742 case "greater-than-or-equal": return left >= right;
1743 case "and": return left && right;
1744 case "or": return left || right;
1745 case "nullish-coalesce": return left ?? right;
1746 case "min": return typeof left === "number" && typeof right === "number" ? Math.min(left, right) : undefined;
1747 case "max": return typeof left === "number" && typeof right === "number" ? Math.max(left, right) : undefined;
1748 default: return undefined;
1749 }
1750 }
1751
1752 function computedUnary(operation, value) {
1753 switch (operation) {
1754 case "not": return !value;
1755 case "identity": return +value;
1756 case "negate": return -value;
1757 case "abs": return typeof value === "number" ? Math.abs(value) : undefined;
1758 case "floor": return typeof value === "number" ? Math.floor(value) : undefined;
1759 case "ceil": return typeof value === "number" ? Math.ceil(value) : undefined;
1760 case "round": return typeof value === "number" ? Math.round(value) : undefined;
1761 default: return undefined;
1762 }
1763 }
1764
1765 function executePureProgramInstruction(store, values, instruction, subject) {
1766 if (instruction.kind === "constant") {
1767 values.set(instruction.result, instruction.value);
1768 return true;
1769 }
1770
1771 if (instruction.kind === "load-state") {
1772 values.set(instruction.result, stateValueForStorage(store, instruction.storage));
1773 return true;
1774 }
1775
1776 if (instruction.kind === "load-computed") {
1777 if (isComputedDirty(store, instruction.computed)) {
1778 reportDiagnostic(
1779 store.diagnostics,
1780 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
1781 "Compiler program depended on a value not yet evaluated by the compiler plan",
1782 { subject, dependency: instruction.computed }
1783 );
1784 values.set(instruction.result, undefined);
1785 } else {
1786 values.set(instruction.result, computedValue(store, instruction.computed));
1787 }
1788 return true;
1789 }
1790
1791 if (instruction.kind === "load-resource") {
1792 const componentInstanceId = store.activeExecutionContext?.component_instance_id;
1793 const activationId = typeof componentInstanceId === "string"
1794 ? store.resourceActivationsByInstanceDeclaration.get(`${componentInstanceId}\u001f${instruction.declaration}`)
1795 : undefined;
1796 const resource = activationId === undefined ? undefined : store.resources.get(activationId);
1797 if (resource === undefined) {
1798 reportDiagnostic(
1799 store.diagnostics,
1800 "PSR_RESOURCE_ACTIVATION_MISSING",
1801 "Computed Resource projection lacked exactly one compiler-selected activation",
1802 { subject, component_instance_id: componentInstanceId, declaration: instruction.declaration },
1803 true
1804 );
1805 throw new PresolveBootError("PSR_RESOURCE_ACTIVATION_MISSING");
1806 }
1807 values.set(instruction.result, { data: resource.data, error: resource.error, state: resource.state });
1808 return true;
1809 }
1810
1811 if (instruction.kind === "get-member") {
1812 const object = computedOperandValue(store, values, instruction.object);
1813 const value = object !== null && typeof object === "object"
1814 && Object.prototype.hasOwnProperty.call(object, instruction.property)
1815 ? object[instruction.property]
1816 : undefined;
1817 values.set(instruction.result, value);
1818 return true;
1819 }
1820
1821 if (instruction.kind === "get-index") {
1822 const object = computedOperandValue(store, values, instruction.object);
1823 const index = computedOperandValue(store, values, instruction.index);
1824 const key = typeof index === "string" || (typeof index === "number" && Number.isInteger(index) && index >= 0)
1825 ? String(index)
1826 : null;
1827 const value = key !== null && object !== null && typeof object === "object"
1828 && Object.prototype.hasOwnProperty.call(object, key)
1829 ? object[key]
1830 : undefined;
1831 values.set(instruction.result, value);
1832 return true;
1833 }
1834
1835 if (instruction.kind === "select") {
1836 const condition = computedOperandValue(store, values, instruction.condition);
1837 const value = condition === true
1838 ? computedOperandValue(store, values, instruction.when_true)
1839 : computedOperandValue(store, values, instruction.when_false);
1840 values.set(instruction.result, value);
1841 return true;
1842 }
1843
1844 if (instruction.kind === "template") {
1845 const quasis = instruction.quasis ?? [];
1846 const expressions = instruction.expressions ?? [];
1847 if (quasis.length !== expressions.length + 1) {
1848 reportDiagnostic(
1849 store.diagnostics,
1850 "PSR_INVALID_TEMPLATE_PROGRAM",
1851 "Compiler artifact contained an invalid template interpolation program",
1852 { subject }
1853 );
1854 values.set(instruction.result, undefined);
1855 } else {
1856 let value = quasis[0];
1857 for (let index = 0; index < expressions.length; index += 1) {
1858 value += String(values.get(expressions[index]));
1859 value += quasis[index + 1];
1860 }
1861 values.set(instruction.result, value);
1862 }
1863 return true;
1864 }
1865
1866 if (instruction.kind === "binary") {
1867 values.set(
1868 instruction.result,
1869 computedBinary(
1870 instruction.operation,
1871 computedOperandValue(store, values, instruction.left),
1872 computedOperandValue(store, values, instruction.right)
1873 )
1874 );
1875 return true;
1876 }
1877
1878 if (instruction.kind === "unary") {
1879 values.set(
1880 instruction.result,
1881 computedUnary(
1882 instruction.operation,
1883 computedOperandValue(store, values, instruction.operand)
1884 )
1885 );
1886 return true;
1887 }
1888
1889 if (instruction.kind === "pure-package-call") {
1890 if (instruction.operation === "identity" && instruction.arguments?.length === 1) {
1891 values.set(instruction.result, values.get(instruction.arguments[0]));
1892 } else {
1893 reportDiagnostic(
1894 store.diagnostics,
1895 "PSR_INVALID_PURE_PACKAGE_OPERATION",
1896 "Compiler artifact contained an unsupported pure package operation",
1897 { subject, package: instruction.package, export: instruction.export, operation: instruction.operation }
1898 );
1899 values.set(instruction.result, undefined);
1900 }
1901 return true;
1902 }
1903
1904 return false;
1905 }
1906
1907 function executeComputedProgram(store, evaluation) {
1908 const values = new Map();
1909
1910 for (const instruction of evaluation.program?.instructions ?? []) {
1911 executePureProgramInstruction(store, values, instruction, evaluation.computed);
1912 }
1913
1914 return values.get(evaluation.program?.result);
1915 }
1916
1917 function initialEffectBatches(effectArtifact) {
1918 const batches = new Map();
1919
1920 for (const effect of effectArtifact?.effects ?? []) {
1921 const trigger = effect.initial_trigger;
1922
1923 if (trigger === null || trigger === undefined) {
1924 continue;
1925 }
1926
1927 const effects = batches.get(trigger.effect_batch_index) ?? [];
1928 effects.push(effect);
1929 batches.set(trigger.effect_batch_index, effects);
1930 }
1931
1932 return [...batches.entries()].sort(([left], [right]) => left - right);
1933 }
1934
1935 function dispatchEffectCapability(store, effect, instruction, values, evidence) {
1936 const runtimeLowering = instruction.runtime_lowering;
1937 const capabilityArguments = (instruction.arguments ?? []).map((operand) =>
1938 computedOperandValue(store, values, operand)
1939 );
1940 const value = computedOperandValue(store, values, instruction.value);
1941
1942 switch (runtimeLowering) {
1943 case "builtin.browser.document.title.assign":
1944 document.title = value;
1945 break;
1946 case "builtin.browser.console.log":
1947 console.log(...capabilityArguments);
1948 break;
1949 case "builtin.browser.console.info":
1950 console.info(...capabilityArguments);
1951 break;
1952 case "builtin.browser.console.warn":
1953 console.warn(...capabilityArguments);
1954 break;
1955 case "builtin.browser.console.error":
1956 console.error(...capabilityArguments);
1957 break;
1958 case "builtin.browser.local_storage.set_item":
1959 localStorage.setItem(...capabilityArguments);
1960 break;
1961 case "builtin.browser.local_storage.remove_item":
1962 localStorage.removeItem(...capabilityArguments);
1963 break;
1964 case "builtin.browser.session_storage.set_item":
1965 sessionStorage.setItem(...capabilityArguments);
1966 break;
1967 case "builtin.browser.session_storage.remove_item":
1968 sessionStorage.removeItem(...capabilityArguments);
1969 break;
1970 default:
1971 reportDiagnostic(
1972 store.diagnostics,
1973 "PSR_UNSUPPORTED_EFFECT_CAPABILITY",
1974 "Effect program referenced an unsupported compiler runtime lowering",
1975 { effect: effect.effect, runtime_lowering: runtimeLowering }
1976 );
1977 return;
1978 }
1979
1980 evidence.capability_operations.push({
1981 operation: instruction.operation,
1982 runtime_lowering: runtimeLowering
1983 });
1984 }
1985
1986 function executeEffectProgram(store, effect, evidence) {
1987 const values = new Map();
1988
1989 for (const instruction of effect.program?.instructions ?? []) {
1990 if (executePureProgramInstruction(store, values, instruction, effect.effect)) {
1991 continue;
1992 }
1993
1994 if (instruction.kind === "capability-call" || instruction.kind === "capability-assign") {
1995 dispatchEffectCapability(store, effect, instruction, values, evidence);
1996 continue;
1997 }
1998
1999 reportDiagnostic(
2000 store.diagnostics,
2001 "PSR_UNSUPPORTED_EFFECT_INSTRUCTION",
2002 "Effect program contained an unsupported compiler instruction",
2003 { effect: effect.effect, kind: instruction.kind }
2004 );
2005 }
2006 }
2007
2008 function executeInitialEffects(store) {
2009 for (const [effectBatchIndex, effects] of initialEffectBatches(store.effectArtifact)) {
2010 for (const effect of effects) {
2011 const evidence = {
2012 effect: effect.effect,
2013 effect_batch_index: effectBatchIndex,
2014 capability_operations: []
2015 };
2016 executeEffectProgram(store, effect, evidence);
2017 store.initialEffectRuns.push(evidence);
2018 }
2019 }
2020 }
2021
2022 function actionEffectBatches(effectArtifact, actionBatchId) {
2023 const batches = new Map();
2024
2025 for (const effect of effectArtifact?.effects ?? []) {
2026 const trigger = (effect.action_batch_triggers ?? []).find(
2027 (candidate) => candidate.action_batch === actionBatchId
2028 );
2029
2030 if (trigger === undefined) {
2031 continue;
2032 }
2033
2034 const effects = batches.get(trigger.effect_batch_index) ?? [];
2035 effects.push(effect);
2036 batches.set(trigger.effect_batch_index, effects);
2037 }
2038
2039 return [...batches.entries()].sort(([left], [right]) => left - right);
2040 }
2041
2042 function executeCompletedActionEffects(store, actionBatchId) {
2043 for (const [effectBatchIndex, effects] of actionEffectBatches(
2044 store.effectArtifact,
2045 actionBatchId
2046 )) {
2047 for (const effect of effects) {
2048 const evidence = {
2049 action_batch_id: actionBatchId,
2050 effect: effect.effect,
2051 effect_batch_index: effectBatchIndex,
2052 capability_operations: []
2053 };
2054 executeEffectProgram(store, effect, evidence);
2055 store.completedActionEffectRuns.push(evidence);
2056 }
2057 }
2058 }
2059
2060 function executeComputedPlan(store, componentInstanceId = null) {
2061 if (store.computedArtifact === null) {
2062 return;
2063 }
2064
2065 const priorExecutionContext = store.activeExecutionContext;
2066 if (componentInstanceId !== null) {
2067 store.activeExecutionContext = { component_instance_id: componentInstanceId };
2068 }
2069
2070 try {
2071 for (const computed of store.computedArtifact.evaluation_order ?? []) {
2072 const evaluation = store.computedEvaluations.get(computed);
2073
2074 if (evaluation === undefined) {
2075 reportDiagnostic(
2076 store.diagnostics,
2077 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
2078 "Compiler plan referenced a missing computed evaluation",
2079 { computed }
2080 );
2081 continue;
2082 }
2083
2084 if (!isComputedDirty(store, computed)) continue;
2085
2086 storeComputedValue(store, evaluation, executeComputedProgram(store, evaluation));
2087 setComputedDirty(store, computed, false);
2088 }
2089 } finally {
2090 store.activeExecutionContext = priorExecutionContext;
2091 }
2092 }
2093
2094 function executeInitialContext(store) {
2095 const sources = new Map((store.contextArtifact?.sources ?? []).map((source) => [source.source, source]));
2096 for (const batch of store.contextArtifact?.initial_batches ?? []) {
2097 for (const sourceId of batch.sources ?? []) {
2098 const source = sources.get(sourceId);
2099 if (source === undefined) { continue; }
2100 const unavailable = (source.required_computed ?? []).some((computed) => store.computedDirty.get(computed) === true);
2101 if (unavailable) {
2102 store.contextFailures.push({ source: source.source, failure: "unavailable-computed-prerequisite" });
2103 continue;
2104 }
2105 const values = new Map();
2106 let initialized = false;
2107 for (const instruction of source.program?.instructions ?? []) {
2108 if (executePureProgramInstruction(store, values, instruction, source.source)) { continue; }
2109 if (instruction.kind === "initialize_context_slot") {
2110 store.contextSlots.set(instruction.slot, computedOperandValue(store, values, instruction.value));
2111 initialized = true;
2112 continue;
2113 }
2114 store.contextFailures.push({ source: source.source, failure: `unsupported-instruction:${String(instruction.kind)}` });
2115 break;
2116 }
2117 if (initialized) { store.contextInitialSourceRuns.push(source.source); }
2118 }
2119 }
2120 for (const consumer of store.contextArtifact?.consumers ?? []) {
2121 store.contextConsumerBindings.set(consumer.consumer, consumer.slot);
2122 if (!store.contextSlots.has(consumer.slot)) {
2123 store.contextFailures.push({ consumer: consumer.consumer, failure: "source-slot-unavailable" });
2124 }
2125 }
2126 }
2127
2128 function executeContextUpdates(store, actionBatchId) {
2129 const update = (store.contextArtifact?.action_updates ?? []).find(
2130 (candidate) => candidate.action_batch === actionBatchId
2131 );
2132 if (update === undefined || update.invalidated_sources.length === 0) { return; }
2133 const sources = new Map((store.contextArtifact?.sources ?? []).map((source) => [source.source, source]));
2134 for (const sourceId of update.invalidated_sources) {
2135 const source = sources.get(sourceId);
2136 if (source === undefined) { continue; }
2137 const values = new Map();
2138 let initialized = false;
2139 for (const instruction of source.program?.instructions ?? []) {
2140 if (executePureProgramInstruction(store, values, instruction, source.source)) { continue; }
2141 if (instruction.kind === "initialize_context_slot") {
2142 store.contextSlots.set(instruction.slot, computedOperandValue(store, values, instruction.value));
2143 initialized = true;
2144 continue;
2145 }
2146 store.contextFailures.push({ action_batch: actionBatchId, source: source.source, failure: `unsupported-update-instruction:${String(instruction.kind)}` });
2147 break;
2148 }
2149 if (initialized) { store.contextUpdateSourceRuns.push({ action_batch: actionBatchId, source: source.source }); }
2150 }
2151 }
2152
2153 function executeComputedUpdateBatches(store) {
2154 if (store.computedArtifact === null) {
2155 return;
2156 }
2157
2158 let executed = false;
2159
2160 for (const batch of store.computedArtifact.update_batches ?? []) {
2161 for (const computed of batch) {
2162 if (!isComputedDirty(store, computed)) {
2163 continue;
2164 }
2165
2166 const evaluation = store.computedEvaluations.get(computed);
2167
2168 if (evaluation === undefined) {
2169 reportDiagnostic(
2170 store.diagnostics,
2171 "PSR_UNPLANNED_COMPUTED_DEPENDENCY",
2172 "Compiler update batch referenced a missing computed evaluation",
2173 { computed }
2174 );
2175 continue;
2176 }
2177
2178 storeComputedValue(store, evaluation, executeComputedProgram(store, evaluation));
2179 setComputedDirty(store, computed, false);
2180 executed = true;
2181 }
2182 }
2183
2184 if (executed) {
2185 store.computedUpdateRuns += 1;
2186 }
2187 }
2188
2189 function invalidateResourceComputeds(store, activation) {
2190 const priorExecutionContext = store.activeExecutionContext;
2191 store.activeExecutionContext = { component_instance_id: activation.component_instance };
2192 try {
2193 for (const computed of store.resourceInvalidationsByDeclaration.get(activation.declaration) ?? []) {
2194 setComputedDirty(store, computed, true);
2195 }
2196 executeComputedUpdateBatches(store);
2197 } finally {
2198 store.activeExecutionContext = priorExecutionContext;
2199 }
2200 }
2201
2202 function readField(store, component, field, storageId = null) {
2203 if (store.instanceQualifiedState) {
2204 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
2205 throw new PresolveBootError("PSR_INVALID_STATE_OPERATION");
2206 }
2207 return store.storageValues.get(
2208 stateSlotForInstanceStorage(store, component.instance_id, storageId).slot_id
2209 );
2210 }
2211 if (!(field in component.state)) {
2212 reportDiagnostic(
2213 store.diagnostics,
2214 "PSR_INVALID_STATE_OPERATION",
2215 "Action referenced a missing state field",
2216 { component: component.name, field }
2217 );
2218 return undefined;
2219 }
2220
2221 return component.state[field];
2222 }
2223
2224 function writeField(store, component, field, value, storageId = null) {
2225 if (store.instanceQualifiedState) {
2226 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
2227 throw new PresolveBootError("PSR_INVALID_STATE_OPERATION");
2228 }
2229 const slot = stateSlotForInstanceStorage(store, component.instance_id, storageId);
2230 store.storageValues.set(slot.slot_id, value);
2231 component.state[field] = value;
2232 for (const computed of store.invalidationsByStorage.get(storageId) ?? []) {
2233 setComputedDirty(store, computed, true);
2234 }
2235 notifyField(store, component, field, slot.slot_id);
2236 return;
2237 }
2238 if (!(field in component.state)) {
2239 reportDiagnostic(
2240 store.diagnostics,
2241 "PSR_INVALID_STATE_OPERATION",
2242 "Action referenced a missing state field",
2243 { component: component.name, field }
2244 );
2245 return;
2246 }
2247
2248 component.state[field] = value;
2249 const storage = store.storageByComponentField.get(
2250 componentFieldKey(component.name, field)
2251 );
2252
2253 if (storage !== undefined) {
2254 store.storageValues.set(storage, value);
2255 for (const computed of store.invalidationsByStorage.get(storage) ?? []) {
2256 setComputedDirty(store, computed, true);
2257 }
2258 }
2259 notifyField(store, component, field, null);
2260 }
2261
2262 function notifyField(store, component, field, stateSlotId) {
2263 const bindings = stateSlotId === null
2264 ? store.bindingsByField.get(componentFieldKey(component.name, field))
2265 : store.bindingsByStateSlot.get(stateSlotId);
2266
2267 if (bindings === undefined) {
2268 reportDiagnostic(
2269 store.diagnostics,
2270 "PSR_MISSING_BINDING_ANCHOR",
2271 "State field has no registered binding anchor",
2272 { component: component.name, field }
2273 );
2274 return;
2275 }
2276
2277 for (const updateBinding of bindings) {
2278 updateBinding(stateSlotId === null ? component.state[field] : store.storageValues.get(stateSlotId));
2279 }
2280 }
2281
2282 function registerBinding(store, component, field, updateBinding, storageId = null) {
2283 if (store.instanceQualifiedState) {
2284 if (typeof component.instance_id !== "string" || typeof storageId !== "string") {
2285 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
2286 }
2287 const slot = stateSlotForInstanceStorage(store, component.instance_id, storageId);
2288 const bindings = store.bindingsByStateSlot.get(slot.slot_id) ?? [];
2289 bindings.push(updateBinding);
2290 store.bindingsByStateSlot.set(slot.slot_id, bindings);
2291 return;
2292 }
2293 const key = componentFieldKey(component.name, field);
2294 const bindings = store.bindingsByField.get(key) ?? [];
2295 bindings.push(updateBinding);
2296 store.bindingsByField.set(key, bindings);
2297 }
2298
2299 function registerActions(store, component, manifestComponent) {
2300 const actionsByMethod = buildActionsByMethod(manifestComponent);
2301
2302 for (const [methodId, actionRecord] of actionsByMethod) {
2303 store.actionsByMethod.set(methodId, actionRecord);
2304 }
2305 }
2306
2307 function registerEvent(store, component, event) {
2308 if (event.event !== "click") {
2309 reportDiagnostic(
2310 store.diagnostics,
2311 "PSR_UNRESOLVED_EVENT",
2312 "Unsupported event type in template manifest",
2313 event
2314 );
2315 return;
2316 }
2317
2318 const actionRecord = store.actionsByMethod.get(event.method_id)
2319 ?? (store.opaqueTerminalsByMethod.has(event.method_id)
2320 ? { action_batch_id: event.action_batch_id, actions: [] }
2321 : undefined);
2322
2323 if (
2324 actionRecord === undefined ||
2325 actionRecord.action_batch_id !== event.action_batch_id
2326 ) {
2327 reportDiagnostic(
2328 store.diagnostics,
2329 "PSR_UNRESOLVED_ACTION",
2330 "Event handler did not resolve to a compiler action",
2331 event
2332 );
2333 return;
2334 }
2335
2336 const eventsByNode = store.eventsByType.get(event.event) ?? new Map();
2337
2338 if (eventsByNode.has(event.node)) {
2339 reportDiagnostic(
2340 store.diagnostics,
2341 "PSR_UNRESOLVED_EVENT",
2342 "Duplicate event registration for template node",
2343 event
2344 );
2345 return;
2346 }
2347
2348 eventsByNode.set(event.node, {
2349 component,
2350 method_id: event.method_id,
2351 action_batch_id: event.action_batch_id,
2352 actions: actionRecord.actions,
2353 arguments: Array.isArray(event.arguments) ? event.arguments : []
2354 });
2355 store.eventsByType.set(event.event, eventsByNode);
2356 }
2357
2358 function initializeComponentRuntime(
2359 store,
2360 manifestComponent,
2361 bindingAnchors,
2362 conditionalAnchors,
2363 listAnchors
2364 ) {
2365 const component = {
2366 name: manifestComponent.name,
2367 manifest: manifestComponent,
2368 state: {}
2369 };
2370
2371 store.components.set(component.name, component);
2372 registerActions(store, component, manifestComponent);
2373
2374 for (const node of manifestComponent.template?.nodes ?? []) {
2375 if (node.kind === "list") {
2376 const field = fieldNameFromThisMember(node.iterable);
2377
2378 if (field === null) {
2379 continue;
2380 }
2381
2382 if (component.state[field] === undefined) {
2383 component.state[field] = node.initial_value;
2384 }
2385
2386 const start = listAnchors.starts.get(node.start);
2387 const end = listAnchors.ends.get(node.end);
2388
2389 if (start === undefined || end === undefined) {
2390 continue;
2391 }
2392
2393 let instances = initialListInstances(
2394 store,
2395 node,
2396 listItems(component.state[field])
2397 );
2398 for (const instance of instances.values()) {
2399 registerListItemEvents(store, component, instance);
2400 }
2401 registerBinding(store, component, field, (value) => {
2402 instances = reconcileKeyedList(
2403 store,
2404 component,
2405 node,
2406 start.marker,
2407 end.marker,
2408 instances,
2409 value
2410 );
2411 });
2412 continue;
2413 }
2414
2415 if (node.kind === "conditional") {
2416 const field = fieldNameFromThisMember(node.condition);
2417
2418 if (field === null) {
2419 continue;
2420 }
2421
2422 if (component.state[field] === undefined) {
2423 component.state[field] = node.initial_value;
2424 }
2425
2426 const start = conditionalAnchors.starts.get(node.start);
2427 const end = conditionalAnchors.ends.get(node.end);
2428
2429 if (start === undefined || end === undefined) {
2430 continue;
2431 }
2432
2433 registerBinding(store, component, field, (value) => {
2434 replaceConditionalBranch(
2435 store,
2436 start.marker,
2437 end.marker,
2438 value === true ? node.when_true_html : node.when_false_html
2439 );
2440 });
2441 continue;
2442 }
2443
2444 if (node.kind !== "binding") {
2445 continue;
2446 }
2447
2448 const field = fieldNameFromThisMember(node.expression);
2449
2450 if (field === null) {
2451 continue;
2452 }
2453
2454 if (component.state[field] === undefined) {
2455 component.state[field] = node.initial_value;
2456 }
2457
2458 if (node.target === "attribute") {
2459 const element = store.elementsByNode.get(node.element);
2460
2461 if (element === undefined) {
2462 continue;
2463 }
2464
2465 updateAttributeBinding(element, node.attribute, component.state[field]);
2466 registerBinding(store, component, field, (value) => {
2467 updateAttributeBinding(element, node.attribute, value);
2468 });
2469 continue;
2470 }
2471
2472 const anchor = bindingAnchors.get(node.id);
2473
2474 if (anchor === undefined) {
2475 continue;
2476 }
2477
2478 const textNode = anchor.marker.nextSibling;
2479
2480 if (!(textNode instanceof Text)) {
2481 reportDiagnostic(
2482 store.diagnostics,
2483 "PSR_MISSING_BINDING_ANCHOR",
2484 "Binding anchor was not followed by a text node",
2485 node
2486 );
2487 continue;
2488 }
2489
2490 registerBinding(store, component, field, (value) => {
2491 textNode.textContent = formatBindingValue(value);
2492 });
2493 }
2494
2495 return component;
2496 }
2497
2498 function actionDelta(store, action) {
2499 if (action.operation === "increment") {
2500 return 1;
2501 }
2502
2503 if (action.operation === "decrement") {
2504 return -1;
2505 }
2506
2507 if (action.operation === "add_assign" || action.operation === "subtract_assign") {
2508 const operand = Number(action.operand);
2509
2510 if (Number.isNaN(operand)) {
2511 reportDiagnostic(
2512 store.diagnostics,
2513 "PSR_INVALID_STATE_OPERATION",
2514 "Numeric state operation had a non-numeric operand",
2515 action
2516 );
2517 return null;
2518 }
2519
2520 return action.operation === "add_assign" ? operand : -operand;
2521 }
2522
2523 return null;
2524 }
2525
2526 function executeAction(store, component, action, executionContext = null) {
2527 if (
2528 action.operation !== "increment" &&
2529 action.operation !== "decrement" &&
2530 action.operation !== "add_assign" &&
2531 action.operation !== "subtract_assign" &&
2532 action.operation !== "assign" &&
2533 action.operation !== "assign_parameter" &&
2534 action.operation !== "toggle"
2535 ) {
2536 reportDiagnostic(
2537 store.diagnostics,
2538 "PSR_INVALID_STATE_OPERATION",
2539 "Action used an unsupported state operation",
2540 action
2541 );
2542 return;
2543 }
2544
2545 if (action.operation === "toggle") {
2546 const current = readField(store, component, action.field, action.storage_id);
2547
2548 if (typeof current !== "boolean") {
2549 reportDiagnostic(
2550 store.diagnostics,
2551 "PSR_INVALID_STATE_OPERATION",
2552 "Toggle action requires a boolean state field",
2553 action
2554 );
2555 return;
2556 }
2557
2558 writeField(store, component, action.field, !current, action.storage_id);
2559 return;
2560 }
2561
2562 if (action.operation === "assign") {
2563 writeField(store, component, action.field, action.operand, action.storage_id);
2564 return;
2565 }
2566
2567 if (action.operation === "assign_parameter") {
2568 const parameterIndex = Number(action.operand);
2569 const value = executionContext?.arguments?.[parameterIndex];
2570 if (value === undefined || !Number.isInteger(parameterIndex)) {
2571 reportDiagnostic(store.diagnostics, "PSR_INVALID_STATE_OPERATION", "Action parameter assignment had no compiler-projected argument", action);
2572 return;
2573 }
2574 writeField(store, component, action.field, value, action.storage_id);
2575 return;
2576 }
2577
2578 const current = Number(readField(store, component, action.field, action.storage_id));
2579
2580 if (Number.isNaN(current)) {
2581 reportDiagnostic(
2582 store.diagnostics,
2583 "PSR_INVALID_STATE_OPERATION",
2584 "Numeric state operation requires a numeric state field",
2585 action
2586 );
2587 return;
2588 }
2589
2590 const delta = actionDelta(store, action);
2591
2592 if (delta === null) {
2593 return;
2594 }
2595
2596 writeField(store, component, action.field, current + delta, action.storage_id);
2597 }
2598
2599 function executeActions(store, component, actionBatchId, actions, executionContext = null) {
2600 store.activeActionBatch = actionBatchId;
2601 store.activeExecutionContext = executionContext;
2602
2603 try {
2604 for (const action of actions) {
2605 executeAction(store, component, action, executionContext);
2606 }
2607
2608 executeComputedUpdateBatches(store);
2609 executeContextUpdates(store, actionBatchId);
2610 executeCompletedActionEffects(store, actionBatchId);
2611 const methodId = executionContext?.method_id;
2612 if (typeof methodId === "string") executeOpaqueTerminal(store, methodId);
2613 } finally {
2614 store.activeActionBatch = null;
2615 store.activeExecutionContext = null;
2616 refreshComputedDebugState(store);
2617 }
2618 }
2619
2620 function executeOpaqueTerminal(store, methodId) {
2621 const terminal = store.opaqueTerminalsByMethod.get(methodId);
2622 if (terminal === undefined) return;
2623 const evidence = { activation: terminal.id, method: methodId, status: "loading" };
2624 store.opaqueActivations.push(evidence);
2625 import(new URL(terminal.runtime_location, document.baseURI).href)
2626 .then((module) => {
2627 const callable = module?.[terminal.export];
2628 if (typeof callable !== "function") throw new Error("declared export is not callable");
2629 return callable();
2630 })
2631 .then(() => { evidence.status = "complete"; })
2632 .catch((error) => {
2633 evidence.status = "failed";
2634 reportDiagnostic(store.diagnostics, "PSR_OPAQUE_TERMINAL_FAILURE", "A compiler-authorized opaque terminal failed", { activation: terminal.id, message: error instanceof Error ? error.message : String(error) });
2635 });
2636 }
2637
2638 function registerComponentEvents(store, component) {
2639 for (const event of component.manifest.template?.events ?? []) {
2640 registerEvent(store, component, event);
2641 }
2642 }
2643
2644 function delegatedEventRecord(store, eventType, target) {
2645 const eventsByNode = store.eventsByType.get(eventType);
2646
2647 if (eventsByNode === undefined) {
2648 return null;
2649 }
2650
2651 let current = target instanceof Element ? target : target?.parentElement;
2652
2653 while (current !== null && current !== undefined) {
2654 const nodeId = current.dataset?.presolveNode;
2655
2656 if (nodeId !== undefined) {
2657 const record = eventsByNode.get(nodeId);
2658
2659 if (record !== undefined) {
2660 return record;
2661 }
2662 }
2663
2664 current = current.parentElement;
2665 }
2666
2667 return null;
2668 }
2669
2670 function dispatchDelegatedEvent(store, event) {
2671 const record = delegatedEventRecord(store, event.type, event.target);
2672
2673 if (record === null) {
2674 return;
2675 }
2676
2677 executeActions(store, record.component, record.action_batch_id, record.actions, { arguments: record.arguments, method_id: record.method_id });
2678 }
2679
2680 function installDelegatedEventListeners(store) {
2681 for (const eventType of store.eventsByType.keys()) {
2682 document.addEventListener(eventType, (event) => {
2683 dispatchDelegatedEvent(store, event);
2684 });
2685 }
2686 }
2687
2688 function collectOrdinaryTargetAnchors() {
2689 const targets = new Map();
2690 const duplicates = new Set();
2691 const register = (id, target) => {
2692 if (targets.has(id)) duplicates.add(id);
2693 targets.set(id, target);
2694 };
2695 for (const element of document.querySelectorAll("[data-presolve-ti]")) {
2696 const id = element.getAttribute("data-presolve-ti");
2697 if (id === null) continue;
2698 register(id, element);
2699 }
2700 const conditionalStarts = new Map();
2701 const listStarts = new Map();
2702 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
2703 while (walker.nextNode()) {
2704 const marker = walker.currentNode;
2705 const value = String(marker.nodeValue ?? "");
2706 const conditionalStart = /^presolve-conditional-start:[^:]+:ti:(.+)$/.exec(value);
2707 if (conditionalStart !== null) {
2708 conditionalStarts.set(conditionalStart[1], marker);
2709 continue;
2710 }
2711 const conditionalEnd = /^presolve-conditional-end:[^:]+:ti:(.+)$/.exec(value);
2712 if (conditionalEnd !== null) {
2713 const start = conditionalStarts.get(conditionalEnd[1]);
2714 if (start !== undefined) {
2715 register(conditionalEnd[1], { kind: "conditional", start, end: marker });
2716 conditionalStarts.delete(conditionalEnd[1]);
2717 }
2718 continue;
2719 }
2720 const listStart = /^presolve-ti-target-start:(.+)$/.exec(value);
2721 if (listStart !== null) {
2722 listStarts.set(listStart[1], marker);
2723 continue;
2724 }
2725 const listEnd = /^presolve-ti-target-end:(.+)$/.exec(value);
2726 if (listEnd !== null) {
2727 const start = listStarts.get(listEnd[1]);
2728 if (start !== undefined) {
2729 register(listEnd[1], { kind: "list", start, end: marker });
2730 listStarts.delete(listEnd[1]);
2731 }
2732 }
2733 }
2734 return { targets, duplicates };
2735 }
2736
2737 function ordinaryEventKey(targetId, eventType) {
2738 return `${targetId}\u001f${eventType}`;
2739 }
2740
2741 function ordinaryTextBindingNode(bindingId) {
2742 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
2743 const start = `presolve-ti-binding-start:${bindingId}`;
2744 const end = `presolve-ti-binding-end:${bindingId}`;
2745 let startMarker = null;
2746 while (walker.nextNode()) {
2747 if (walker.currentNode.data === start) { startMarker = walker.currentNode; continue; }
2748 if (startMarker !== null && walker.currentNode.data === end) {
2749 const text = startMarker.nextSibling;
2750 if (text instanceof Text) return text;
2751 if (text === walker.currentNode && startMarker.parentNode !== null) {
2752 const empty = document.createTextNode("");
2753 startMarker.parentNode.insertBefore(empty, walker.currentNode);
2754 return empty;
2755 }
2756 return null;
2757 }
2758 }
2759 return null;
2760 }
2761
2762 function registerOrdinaryBinding(store, binding, artifactBinding) {
2763 const component = store.components.get(binding.component_instance_id);
2764 const field = fieldNameFromThisMember(binding.expression);
2765 const storageId = artifactBinding.state_storage_ids?.length === 1
2766 ? artifactBinding.state_storage_ids[0]
2767 : null;
2768 const computedId = artifactBinding.computed_ids?.length === 1
2769 ? artifactBinding.computed_ids[0]
2770 : null;
2771 if (component === undefined || field === null || (storageId === null) === (computedId === null)) {
2772 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
2773 }
2774 const target = store.templateTargetsById.get(binding.instance_target_id);
2775 const slot = storageId === null
2776 ? null
2777 : stateSlotForInstanceStorage(store, binding.component_instance_id, storageId);
2778 let update = null;
2779 if (binding.kind === "text") {
2780 const text = ordinaryTextBindingNode(binding.instance_binding_id);
2781 if (text !== null) update = (value) => { text.data = formatBindingValue(value); };
2782 } else if ((binding.kind === "attribute" || binding.kind === "property") && target instanceof Element && typeof binding.attribute_name === "string") {
2783 update = (value) => { updateAttributeBinding(target, binding.attribute_name, value); };
2784 } else if (binding.kind === "conditional" && target?.kind === "conditional") {
2785 const nodes = (component.manifest.template?.nodes ?? []).filter(
2786 (node) => node.kind === "conditional" && node.condition === binding.expression
2787 );
2788 if (nodes.length === 1) {
2789 const node = nodes[0];
2790 update = (value) => {
2791 replaceConditionalBranch(
2792 store,
2793 target.start,
2794 target.end,
2795 value === true ? node.when_true_html : node.when_false_html
2796 );
2797 };
2798 }
2799 } else if (binding.kind === "list" && target?.kind === "list") {
2800 if (slot === null) throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
2801 const nodes = (component.manifest.template?.nodes ?? []).filter(
2802 (node) => node.kind === "list" && node.iterable === binding.expression
2803 );
2804 if (nodes.length === 1) {
2805 const node = nodes[0];
2806 let instances = initialListInstances(
2807 store,
2808 node,
2809 listItems(store.storageValues.get(slot.slot_id))
2810 );
2811 for (const instance of instances.values()) {
2812 registerListItemEvents(store, component, instance);
2813 }
2814 update = (value) => {
2815 instances = reconcileKeyedList(
2816 store,
2817 component,
2818 node,
2819 target.start,
2820 target.end,
2821 instances,
2822 value
2823 );
2824 };
2825 }
2826 }
2827 if (update === null) throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
2828 if (slot !== null) {
2829 update(store.storageValues.get(slot.slot_id));
2830 registerBinding(store, component, field, update, storageId);
2831 } else {
2832 update(computedValueForInstance(store, binding.component_instance_id, computedId));
2833 registerComputedBinding(store, binding.component_instance_id, computedId, update);
2834 }
2835 }
2836
2837 function initializeOrdinaryInstanceRuntime(store, manifest, componentArtifact) {
2838 if (manifest.schema_version !== SUPPORTED_SCHEMA_VERSION) return;
2839 const anchors = collectOrdinaryTargetAnchors();
2840 for (const binding of manifest.ordinary_bindings ?? []) {
2841 if (binding.kind !== "text" || anchors.targets.has(binding.instance_target_id)) continue;
2842 const text = ordinaryTextBindingNode(binding.instance_binding_id);
2843 if (text !== null) anchors.targets.set(binding.instance_target_id, text);
2844 }
2845 const artifactTargets = new Map((componentArtifact.ordinary_template_targets ?? []).map((target) => [target.id, target]));
2846 const artifactBindings = new Map((componentArtifact.ordinary_template_bindings ?? []).map((binding) => [binding.id, binding]));
2847 const artifactEvents = new Map((componentArtifact.ordinary_template_events ?? []).map((event) => [ordinaryEventKey(event.target_id, event.event_type), event]));
2848 store.templateTargetsById = anchors.targets;
2849 store.ordinaryBindingsById = new Map();
2850 store.ordinaryEventsByTargetAndType = new Map();
2851 for (const target of manifest.ordinary_targets ?? []) {
2852 const artifactTarget = artifactTargets.get(target.id);
2853 if (artifactTarget === undefined || artifactTarget.component_instance_id !== target.component_instance_id || anchors.duplicates.has(target.id) || !anchors.targets.has(target.id)) {
2854 throw new PresolveBootError("PSR_INVALID_ORDINARY_TARGET");
2855 }
2856 }
2857 for (const binding of manifest.ordinary_bindings ?? []) {
2858 const artifactBinding = artifactBindings.get(binding.instance_binding_id);
2859 if (artifactBinding === undefined || artifactBinding.component_instance_id !== binding.component_instance_id || artifactBinding.target_id !== binding.instance_target_id) {
2860 throw new PresolveBootError("PSR_INVALID_ORDINARY_BINDING");
2861 }
2862 store.ordinaryBindingsById.set(binding.instance_binding_id, {
2863 ...binding,
2864 execution_context: { component_instance_id: binding.component_instance_id }
2865 });
2866 registerOrdinaryBinding(store, binding, artifactBinding);
2867 }
2868 for (const event of manifest.ordinary_events ?? []) {
2869 const key = ordinaryEventKey(event.instance_target_id, event.event_type);
2870 const artifactEvent = artifactEvents.get(key);
2871 if (
2872 artifactEvent === undefined
2873 || artifactEvent.component_instance_id !== event.component_instance_id
2874 || JSON.stringify(artifactEvent.arguments ?? []) !== JSON.stringify(event.arguments ?? [])
2875 || store.ordinaryEventsByTargetAndType.has(key)
2876 ) {
2877 throw new PresolveBootError("PSR_INVALID_ORDINARY_EVENT");
2878 }
2879 store.ordinaryEventsByTargetAndType.set(key, event);
2880 }
2881 }
2882
2883 function installResumeDomBindings(store, manifest, componentArtifact) {
2884 const stateBindingIds = new Set(
2885 (componentArtifact.ordinary_template_bindings ?? [])
2886 .filter((binding) => binding.state_storage_ids?.length === 1)
2887 .map((binding) => binding.id)
2888 );
2889 const bindings = (manifest.ordinary_bindings ?? []).filter((binding) =>
2890 stateBindingIds.has(binding.instance_binding_id)
2891 && (binding.kind === "text" || binding.kind === "attribute" || binding.kind === "property")
2892 );
2893 const targetIds = new Set(bindings.map((binding) => binding.instance_target_id));
2894 for (const event of manifest.ordinary_events ?? []) targetIds.add(event.instance_target_id);
2895 const targets = (manifest.ordinary_targets ?? []).filter((target) => targetIds.has(target.id));
2896 initializeOrdinaryInstanceRuntime(
2897 store,
2898 { ...manifest, ordinary_targets: targets, ordinary_bindings: bindings },
2899 componentArtifact
2900 );
2901 }
2902
2903 function establishResumeEffects(registry, store, effectArtifact) {
2904 for (const effect of effectArtifact?.effects ?? []) {
2905 if (store.effectSubscriptions.has(effect.effect) || registry.effect_subscriptions.has(effect.effect)) {
2906 throw new ResumeBootError("DuplicateIdentity");
2907 }
2908 const subscription = {
2909 effect_instance_id: effect.effect,
2910 scheduler_order: effect.initial_trigger?.effect_batch_index ?? null,
2911 active_after_restore: true,
2912 run_on_restore: false
2913 };
2914 store.effectSubscriptions.set(effect.effect, subscription);
2915 registry.effect_subscriptions.set(effect.effect, subscription);
2916 }
2917 }
2918
2919 function ordinaryTargetFromEvent(target) {
2920 let current = target instanceof Element ? target : target?.parentElement;
2921 while (current !== null && current !== undefined) {
2922 const targetId = current.getAttribute("data-presolve-ti");
2923 if (targetId !== null) return targetId;
2924 current = current.parentElement;
2925 }
2926 return null;
2927 }
2928
2929 function dispatchOrdinaryInstanceEvent(store, event) {
2930 const targetId = ordinaryTargetFromEvent(event.target);
2931 if (targetId === null) return;
2932 const record = store.ordinaryEventsByTargetAndType.get(ordinaryEventKey(targetId, event.type));
2933 if (record === undefined) return;
2934 const actionRecord = store.actionsByMethod.get(record.handler_method_id)
2935 ?? (store.opaqueTerminalsByMethod.has(record.handler_method_id)
2936 ? { action_batch_id: record.action_batch_id, actions: [] }
2937 : undefined);
2938 const component = store.components.get(record.component_instance_id);
2939 if (actionRecord === undefined || component === undefined || actionRecord.action_batch_id !== record.action_batch_id) {
2940 throw new PresolveBootError("PSR_INVALID_ORDINARY_EVENT");
2941 }
2942 const context = {
2943 component_instance_id: record.component_instance_id,
2944 trigger_target_id: record.instance_target_id,
2945 declaration_event_id: record.declaration_event_id,
2946 action_batch_id: record.action_batch_id,
2947 method_id: record.handler_method_id,
2948 arguments: Array.isArray(record.arguments) ? record.arguments : []
2949 };
2950 executeActions(store, component, record.action_batch_id, actionRecord.actions, context);
2951 }
2952
2953 function installOrdinaryInstanceEventListeners(store) {
2954 for (const key of store.ordinaryEventsByTargetAndType.keys()) {
2955 const eventType = key.slice(key.lastIndexOf("\u001f") + 1);
2956 document.addEventListener(eventType, (event) => dispatchOrdinaryInstanceEvent(store, event));
2957 }
2958 }
2959
2960 // Forms are initialized exclusively from the compiler artifact and manifest
2961 // bridge. The DOM contributes only the user event value for a known anchor.
2962 function initializeFormsRuntime(store, formsArtifact, manifest, elementsByNode, diagnostics) {
2963 store.forms = new Map();
2964 store.formInstances = new Map();
2965 store.formBindingsByAnchor = new Map();
2966 store.formBindingsByField = new Map();
2967 store.formHostsByAnchor = new Map();
2968 if (formsArtifact === null) return;
2969
2970 const definitions = new Map(formsArtifact.forms.map((form) => [form.id, form]));
2971 for (const instance of formsArtifact.instances) {
2972 const definition = definitions.get(instance.form);
2973 if (definition === undefined) {
2974 reportDiagnostic(diagnostics, "PSR_UNKNOWN_FORM_INSTANCE", "Forms artifact referenced an unknown Form definition", { instance: instance.id, form: instance.form }, true);
2975 continue;
2976 }
2977 const fields = new Map(definition.fields.map((field) => [field.id, {
2978 value: field.initial_value,
2979 initial: field.initial_value,
2980 dirty: false,
2981 touched: false,
2982 validation: []
2983 }]));
2984 store.forms.set(definition.id, definition);
2985 store.formInstances.set(instance.id, {
2986 definition,
2987 instance,
2988 fields,
2989 aggregate_valid: true,
2990 submission: "Idle"
2991 });
2992 }
2993
2994 for (const bridge of manifest.form_bindings ?? []) {
2995 const element = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
2996 ? store.templateTargetsById.get(bridge.instance_target_id)
2997 : elementsByNode.get(bridge.control_anchor);
2998 const formInstance = store.formInstances.get(bridge.form_instance_id);
2999 if (element === undefined || formInstance === undefined) {
3000 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms manifest bridge did not resolve an exact compiler anchor and instance", { bridge }, true);
3001 continue;
3002 }
3003 const binding = formInstance.definition.bindings.find((item) => item.id === bridge.field_binding_id);
3004 if (binding === undefined || binding.field === undefined || binding.channel !== bridge.channel) {
3005 reportDiagnostic(diagnostics, "PSR_UNKNOWN_FORM_BINDING", "Forms manifest bridge did not match an artifact binding", { bridge }, true);
3006 continue;
3007 }
3008 const record = { bridge, binding, element, formInstance };
3009 store.formBindingsByAnchor.set(manifest.schema_version === SUPPORTED_SCHEMA_VERSION ? bridge.instance_target_id : bridge.control_anchor, record);
3010 const key = `${bridge.form_instance_id}|${binding.field}`;
3011 const bindings = store.formBindingsByField.get(key) ?? [];
3012 bindings.push(record);
3013 store.formBindingsByField.set(key, bindings);
3014 writeFormControl(record, formInstance.fields.get(binding.field)?.value);
3015 }
3016
3017 for (const bridge of manifest.form_hosts ?? []) {
3018 const element = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
3019 ? store.templateTargetsById.get(bridge.instance_target_id)
3020 : elementsByNode.get(bridge.host_anchor);
3021 const formInstance = store.formInstances.get(bridge.form_instance_id);
3022 const host = (formsArtifact.hosts ?? []).find((candidate) => candidate.host_anchor === bridge.host_anchor && candidate.form_instance === bridge.form_instance_id);
3023 if (!(element instanceof HTMLFormElement) || formInstance === undefined || host === undefined || host.event !== "submit") {
3024 reportDiagnostic(diagnostics, "PSR_FORMS_MANIFEST_MISMATCH", "Forms host bridge did not resolve an exact compiler-owned form anchor", { bridge }, true);
3025 continue;
3026 }
3027 const anchor = manifest.schema_version === SUPPORTED_SCHEMA_VERSION ? bridge.instance_target_id : bridge.host_anchor;
3028 store.formHostsByAnchor.set(anchor, { bridge, host, element, formInstance });
3029 element.addEventListener(host.event, (event) => dispatchFormSubmit(store, event, anchor));
3030 }
3031
3032 document.addEventListener("input", (event) => dispatchFormEvent(store, event, false));
3033 document.addEventListener("change", (event) => dispatchFormEvent(store, event, false));
3034 document.addEventListener("focusout", (event) => dispatchFormEvent(store, event, true));
3035 window.__PRESOLVE_FORMS__ = {
3036 resetForm: (instanceId) => resetForm(store, instanceId),
3037 resetField: (instanceId, fieldId) => resetField(store, instanceId, fieldId)
3038 };
3039 }
3040
3041 function dispatchFormSubmit(store, event, anchor) {
3042 const record = store.formHostsByAnchor.get(anchor);
3043 if (record === undefined || event.type !== record.host.event) return;
3044 if (record.host.prevent_default === true) event.preventDefault();
3045 for (const fieldId of record.formInstance.fields.keys()) validateFormField(record.formInstance, fieldId);
3046 if (!record.formInstance.aggregate_valid) { record.formInstance.submission = "Invalid"; return; }
3047 const action = store.actionsByMethod.get(record.host.submit_action);
3048 const component = store.components.get(record.bridge.component_instance_id) ?? action?.component;
3049 if (action === undefined || action.action_batch_id !== record.host.action_batch || component === undefined) {
3050 reportDiagnostic(store.diagnostics, "PSR_UNRESOLVED_FORM_SUBMIT_ACTION", "Submission host did not resolve its exact compiler action", { host: record.host }, true);
3051 record.formInstance.submission = "Failed";
3052 return;
3053 }
3054 record.formInstance.submission = "Submitting";
3055 // Serialization is deliberately compiler-record driven: field values and
3056 // path shape come from the compiler artifact, never DOM scanning.
3057 record.formInstance.serialized = serializeFormInstance(record.formInstance);
3058 executeActions(store, component, record.host.action_batch, action.actions, {
3059 component_instance_id: record.bridge.component_instance_id,
3060 trigger_target_id: record.bridge.instance_target_id,
3061 declaration_event_id: record.host.submit_action,
3062 action_batch_id: record.host.action_batch
3063 });
3064 record.formInstance.submission = "Completed";
3065 }
3066
3067 function serializeFormInstance(formInstance) {
3068 const definition = formInstance.definition;
3069 const fields = new Map((definition.fields ?? []).map((field) => [field.id, field]));
3070 if (definition.serialization?.format === "Json") {
3071 const result = {};
3072 for (const [fieldId, state] of formInstance.fields.entries()) {
3073 const path = fields.get(fieldId)?.path;
3074 if (!Array.isArray(path) || path.length === 0) continue;
3075 let target = result;
3076 for (const segment of path.slice(0, -1)) target = target[segment] ??= {};
3077 target[path[path.length - 1]] = state.value;
3078 }
3079 return result;
3080 }
3081 return [...formInstance.fields.entries()].map(([field, state]) => ({
3082 key: fields.get(field)?.path?.join(".") ?? field,
3083 value: state.value
3084 }));
3085 }
3086
3087 function dispatchFormEvent(store, event, blur) {
3088 const element = event.target;
3089 if (!(element instanceof HTMLElement)) return;
3090 const anchor = store.templateTargetsById instanceof Map
3091 ? ordinaryTargetFromEvent(element)
3092 : element.getAttribute("data-presolve-node");
3093 const record = anchor === null ? undefined : store.formBindingsByAnchor.get(anchor);
3094 if (record === undefined) return;
3095 if (blur) {
3096 const state = record.formInstance.fields.get(record.binding.field);
3097 state.touched = true;
3098 validateFormField(record.formInstance, record.binding.field);
3099 return;
3100 }
3101 const expected = record.binding.channel === "Checked" || record.binding.channel === "RadioValue" ? "change" : "input";
3102 if (event.type !== expected) return;
3103 const value = readFormControl(record);
3104 if (value === undefined) return;
3105 writeFormField(store, record.formInstance, record.binding.field, value);
3106 }
3107
3108 function readFormControl(record) {
3109 const { element, binding } = record;
3110 if (binding.channel === "Checked") return element.checked === true;
3111 if (binding.channel === "NumericValue") {
3112 if (element.value === "") return binding.normalization === "NullableNumber" ? null : undefined;
3113 const value = Number(element.value);
3114 return Number.isFinite(value) ? value : undefined;
3115 }
3116 if (binding.channel === "SelectedValues") return [...element.selectedOptions].map((option) => option.value);
3117 return element.value;
3118 }
3119
3120 function writeFormControl(record, value) {
3121 const { element, binding } = record;
3122 if (binding.channel === "Checked") element.checked = value === true;
3123 else if (binding.channel === "SelectedValues") {
3124 for (const option of element.options ?? []) option.selected = Array.isArray(value) && value.includes(option.value);
3125 } else element.value = value === null ? "" : String(value ?? "");
3126 }
3127
3128 function writeFormField(store, formInstance, fieldId, value) {
3129 const state = formInstance.fields.get(fieldId);
3130 if (state === undefined) return;
3131 state.value = value;
3132 state.dirty = JSON.stringify(value) !== JSON.stringify(state.initial);
3133 validateFormField(formInstance, fieldId);
3134 for (const dependency of formInstance.definition.validation_dependencies ?? []) {
3135 if (dependency.source_field === fieldId) validateFormField(formInstance, dependency.target_field);
3136 }
3137 for (const record of store.formBindingsByField.get(`${formInstance.instance.id}|${fieldId}`) ?? []) writeFormControl(record, value);
3138 }
3139
3140 function validateFormField(formInstance, fieldId) {
3141 const state = formInstance.fields.get(fieldId);
3142 if (state === undefined) return;
3143 const rules = (formInstance.definition.validation_rules ?? []).filter((rule) => rule.target_field === fieldId);
3144 state.validation = rules.filter((rule) => !validateFormRule(formInstance, state.value, rule)).map((rule) => rule.id);
3145 formInstance.aggregate_valid = [...formInstance.fields.values()].every((field) => field.validation.length === 0);
3146 }
3147
3148 function validateFormRule(formInstance, value, rule) {
3149 const dependency = rule.dependency === undefined ? undefined : formInstance.fields.get(rule.dependency)?.value;
3150 if (rule.kind === "Required") return !(value === null || value === undefined || value === "" || (Array.isArray(value) && value.length === 0));
3151 if (rule.kind === "Email") return value === "" || /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(String(value));
3152 if (rule.kind === "Equals") return value === dependency;
3153 if (rule.kind === "NotEquals") return value !== dependency;
3154 return true;
3155 }
3156
3157 function resetField(store, instanceId, fieldId) {
3158 const formInstance = store.formInstances?.get(instanceId);
3159 const state = formInstance?.fields.get(fieldId);
3160 if (state === undefined) return false;
3161 state.value = state.initial; state.dirty = false; state.touched = false; state.validation = [];
3162 for (const record of store.formBindingsByField.get(`${instanceId}|${fieldId}`) ?? []) writeFormControl(record, state.value);
3163 formInstance.aggregate_valid = true;
3164 return true;
3165 }
3166
3167 function resetForm(store, instanceId) {
3168 const formInstance = store.formInstances?.get(instanceId);
3169 if (formInstance === undefined) return false;
3170 for (const fieldId of formInstance.fields.keys()) resetField(store, instanceId, fieldId);
3171 formInstance.submission = "Idle";
3172 return true;
3173 }
3174
3175 function debugComponents(store) {
3176 return [...store.components.values()].map((component) => ({
3177 name: component.name,
3178 state: component.state
3179 }));
3180 }
3181
3182 function debugComputed(store) {
3183 if (store.instanceQualifiedState) {
3184 return [...store.computedSlotsByInstanceComputed.entries()].map(([key, slot]) => ({
3185 component_instance_id: key.slice(0, key.lastIndexOf("|")),
3186 computed: slot.computed_id,
3187 cache_slot: slot.cache_slot_id,
3188 dirty: store.computedDirtySlots.get(slot.dirty_slot_id) === true,
3189 value: store.computedCaches.get(slot.cache_slot_id)
3190 }));
3191 }
3192 return [...store.computedEvaluations.values()].map((evaluation) => ({
3193 computed: evaluation.computed,
3194 cache_slot: evaluation.cache_slot,
3195 dirty: store.computedDirty.get(evaluation.computed) === true,
3196 value: store.computedCaches.get(evaluation.cache_slot)
3197 }));
3198 }
3199
3200 function refreshComputedDebugState(store) {
3201 if (window.__PRESOLVE__?.store !== store) {
3202 return;
3203 }
3204
3205 window.__PRESOLVE__.computed = debugComputed(store);
3206 window.__PRESOLVE__.computed_update_runs = store.computedUpdateRuns;
3207 }
3208
3209 function initialStateSlotValue(slot) {
3210 if (slot.semantic_type !== "number") return slot.initial_value;
3211 const value = Number(slot.initial_value);
3212 if (!Number.isFinite(value)) throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3213 return value;
3214 }
3215
3216 function decodeResumeValue(value, codec) {
3217 switch (codec?.kind) {
3218 case "null_codec":
3219 if (value !== null) throw new ResumeBootError("ValueTypeMismatch");
3220 return null;
3221 case "boolean_codec":
3222 if (typeof value !== "boolean") throw new ResumeBootError("ValueTypeMismatch");
3223 return value;
3224 case "number_codec":
3225 if (typeof value !== "number" || !Number.isFinite(value) || Object.is(value, -0)) {
3226 throw new ResumeBootError("ValueTypeMismatch");
3227 }
3228 return value;
3229 case "string_codec":
3230 if (typeof value !== "string") throw new ResumeBootError("ValueTypeMismatch");
3231 return value;
3232 case "array_codec":
3233 if (!Array.isArray(value)) throw new ResumeBootError("ValueTypeMismatch");
3234 return value.map((entry) => decodeResumeValue(entry, codec.value));
3235 case "object_codec": {
3236 if (value === null || typeof value !== "object" || Array.isArray(value)) {
3237 throw new ResumeBootError("ValueTypeMismatch");
3238 }
3239 const properties = codec.value ?? [];
3240 if (!exactObjectKeys(value, properties.map((property) => property.name))) {
3241 throw new ResumeBootError("ValueTypeMismatch");
3242 }
3243 return Object.fromEntries(properties.map((property) => [
3244 property.name,
3245 decodeResumeValue(value[property.name], property.codec)
3246 ]));
3247 }
3248 case "nullable_codec":
3249 return value === null ? null : decodeResumeValue(value, codec.value);
3250 default:
3251 throw new ResumeBootError("ValueCodecFailure");
3252 }
3253 }
3254
3255 function snapshotValuesBySlot(snapshot) {
3256 const values = new Map();
3257 for (const boundary of snapshot.boundaries) {
3258 for (const record of boundary.values) values.set(record.slotId, record.value);
3259 }
3260 return values;
3261 }
3262
3263 function allocateResumeStateComputedStore(
3264 registry,
3265 manifest,
3266 templateManifest,
3267 computedArtifact,
3268 contextArtifact,
3269 effectArtifact,
3270 componentArtifact,
3271 diagnostics
3272 ) {
3273 const store = createRuntimeStore(
3274 collectElementAnchors(),
3275 diagnostics,
3276 computedArtifact,
3277 contextArtifact,
3278 effectArtifact,
3279 componentArtifact
3280 );
3281 store.componentArtifact = componentArtifact;
3282 store.componentInstances = new Map();
3283 store.slotBindings = new Map();
3284 store.componentRegions = new Map();
3285 store.instanceContextBindings = new Map(
3286 (componentArtifact?.instance_context_bindings ?? [])
3287 .map((binding) => [binding.consumer_instance, binding])
3288 );
3289 const definitions = new Map(
3290 (templateManifest.components ?? []).map((component) => [component.component_id, component])
3291 );
3292 for (const boundary of manifest.boundaries) {
3293 registry.boundary_records.set(boundary.boundary_id, {
3294 boundary_id: boundary.boundary_id,
3295 kind: boundary.kind,
3296 status: "allocated"
3297 });
3298 }
3299 for (const instance of componentArtifact.instances ?? []) {
3300 const definition = definitions.get(instance.component);
3301 if (definition === undefined) throw new ResumeBootError("ResumeArtifactMismatch");
3302 for (const slot of instance.state_slots ?? []) {
3303 const key = `${instance.instance}|${slot.storage_id}`;
3304 if (store.stateSlotsByInstanceStorage.has(key)) {
3305 throw new ResumeBootError("DuplicateIdentity");
3306 }
3307 store.stateSlotsByInstanceStorage.set(key, slot);
3308 }
3309 for (const slot of instance.computed_slots ?? []) {
3310 const key = `${instance.instance}|${slot.computed_id}`;
3311 if (store.computedSlotsByInstanceComputed.has(key)) {
3312 throw new ResumeBootError("DuplicateIdentity");
3313 }
3314 store.computedSlotsByInstanceComputed.set(key, slot);
3315 store.computedDirtySlots.set(slot.dirty_slot_id, false);
3316 }
3317 const component = {
3318 instance_id: instance.instance,
3319 name: instance.component,
3320 manifest: definition,
3321 state: {}
3322 };
3323 store.components.set(instance.instance, component);
3324 registerActions(store, component, definition);
3325 }
3326 return store;
3327 }
3328
3329 function writeResumeSlot(store, registry, slotSchema, value) {
3330 registry.slot_values.set(slotSchema.slot_id, value);
3331 const existing = slotSchema.existing_storage_slot_id;
3332 if (slotSchema.restore_phase === "R3") {
3333 store.storageValues.set(existing, value);
3334 return;
3335 }
3336 if (slotSchema.restore_phase === "R4") {
3337 const computedSlot = [...store.computedSlotsByInstanceComputed.values()]
3338 .find((slot) => slot.cache_slot_id === existing || slot.dirty_slot_id === existing);
3339 if (computedSlot === undefined) throw new ResumeBootError("UnknownSlot");
3340 if (computedSlot.cache_slot_id === existing) store.computedCaches.set(existing, value);
3341 else store.computedDirtySlots.set(existing, value);
3342 return;
3343 }
3344 if (slotSchema.restore_phase === "R6") {
3345 store.contextSlots.set(existing, value);
3346 return;
3347 }
3348 throw new ResumeBootError("RestoreInstructionFailure");
3349 }
3350
3351 function executeResumeDecodeAndWrites(store, registry, snapshot, phases) {
3352 const snapshotValues = snapshotValuesBySlot(snapshot);
3353 const decoded = new Map();
3354 for (const program of registry.definitions.restorePrograms.values()) {
3355 for (const record of program.instructions ?? []) {
3356 if (!phases.has(record.phase)) continue;
3357 const instruction = record.instruction;
3358 if (instruction.kind === "decode_value") {
3359 const schema = registry.definitions.slots.get(instruction.slot_id);
3360 if (schema === undefined || schema.restore_phase !== record.phase) {
3361 throw new ResumeBootError("RestoreInstructionFailure");
3362 }
3363 if (!snapshotValues.has(instruction.slot_id)) throw new ResumeBootError("UnknownSlot");
3364 decoded.set(
3365 instruction.slot_id,
3366 decodeResumeValue(snapshotValues.get(instruction.slot_id), instruction.codec)
3367 );
3368 } else if (instruction.kind === "write_slot") {
3369 const schema = registry.definitions.slots.get(instruction.slot_id);
3370 if (schema === undefined || !decoded.has(instruction.slot_id)) {
3371 throw new ResumeBootError("RestoreInstructionFailure");
3372 }
3373 writeResumeSlot(store, registry, schema, decoded.get(instruction.slot_id));
3374 } else {
3375 throw new ResumeBootError("RestoreInstructionFailure");
3376 }
3377 }
3378 }
3379 }
3380
3381 function synchronizeRestoredComponentState(store, computedArtifact, componentArtifact) {
3382 const fieldsByStorage = new Map(
3383 (computedArtifact?.state ?? []).map((state) => [state.storage, state.field])
3384 );
3385 for (const instance of componentArtifact.instances ?? []) {
3386 const component = store.components.get(instance.instance);
3387 if (component === undefined) throw new ResumeBootError("ResumeArtifactMismatch");
3388 for (const slot of instance.state_slots ?? []) {
3389 const field = fieldsByStorage.get(slot.storage_id);
3390 if (field === undefined || !store.storageValues.has(slot.slot_id)) {
3391 throw new ResumeBootError("UnknownSlot");
3392 }
3393 component.state[field] = store.storageValues.get(slot.slot_id);
3394 }
3395 }
3396 }
3397
3398 function executeResumeComputedRecomputation(store, registry) {
3399 for (const schema of registry.definitions.slots.values()) {
3400 if (schema.restore_phase !== "R5") continue;
3401 const computedSlot = [...store.computedSlotsByInstanceComputed.values()]
3402 .find((slot) =>
3403 slot.cache_slot_id === schema.existing_storage_slot_id
3404 || slot.dirty_slot_id === schema.existing_storage_slot_id
3405 );
3406 if (computedSlot === undefined) throw new ResumeBootError("UnknownSlot");
3407 if (computedSlot.dirty_slot_id === schema.existing_storage_slot_id) {
3408 store.computedDirtySlots.set(computedSlot.dirty_slot_id, true);
3409 }
3410 }
3411 const recomputed = new Set();
3412 for (const program of registry.definitions.restorePrograms.values()) {
3413 for (const record of program.instructions ?? []) {
3414 if (record.phase !== "R5") continue;
3415 const instruction = record.instruction;
3416 if (instruction.kind !== "recompute_computed") {
3417 throw new ResumeBootError("RestoreInstructionFailure");
3418 }
3419 const key = `${instruction.component_instance_id}|${instruction.computed_id}`;
3420 const slot = store.computedSlotsByInstanceComputed.get(key);
3421 const evaluation = store.computedEvaluations.get(instruction.computed_id);
3422 if (slot === undefined || evaluation === undefined || recomputed.has(key)) {
3423 throw new ResumeBootError("RestoreInstructionFailure");
3424 }
3425 const prior = store.activeExecutionContext;
3426 store.activeExecutionContext = {
3427 component_instance_id: instruction.component_instance_id
3428 };
3429 try {
3430 const value = executeComputedProgram(store, evaluation);
3431 storeComputedValue(store, evaluation, value);
3432 setComputedDirty(store, instruction.computed_id, false);
3433 const cacheSchema = [...registry.definitions.slots.values()]
3434 .find((schema) => schema.existing_storage_slot_id === slot.cache_slot_id);
3435 const dirtySchema = [...registry.definitions.slots.values()]
3436 .find((schema) => schema.existing_storage_slot_id === slot.dirty_slot_id);
3437 if (cacheSchema === undefined || dirtySchema === undefined) {
3438 throw new ResumeBootError("UnknownSlot");
3439 }
3440 registry.slot_values.set(cacheSchema.slot_id, value);
3441 registry.slot_values.set(dirtySchema.slot_id, false);
3442 recomputed.add(key);
3443 } finally {
3444 store.activeExecutionContext = prior;
3445 }
3446 }
3447 }
3448 return [...recomputed];
3449 }
3450
3451 function executeResumeContextBindings(store, registry, componentArtifact) {
3452 const expected = new Map(
3453 (componentArtifact?.instance_context_bindings ?? [])
3454 .map((binding) => [binding.consumer_instance, binding])
3455 );
3456 for (const program of registry.definitions.restorePrograms.values()) {
3457 for (const record of program.instructions ?? []) {
3458 if (record.phase !== "R7") continue;
3459 const instruction = record.instruction;
3460 if (instruction.kind !== "bind_context_consumer") {
3461 throw new ResumeBootError("RestoreInstructionFailure");
3462 }
3463 const binding = expected.get(instruction.consumer_instance_id);
3464 if (
3465 binding === undefined
3466 || binding.selected_source !== instruction.selected_source
3467 || (binding.provider_source ?? null) !== (instruction.provider_instance_id ?? null)
3468 || binding.runtime_slot !== instruction.value_slot_id
3469 || !store.contextSlots.has(instruction.value_slot_id)
3470 || store.contextConsumerBindings.has(instruction.consumer_instance_id)
3471 ) {
3472 throw new ResumeBootError("ResumeArtifactMismatch");
3473 }
3474 const installed = {
3475 consumer_instance_id: instruction.consumer_instance_id,
3476 selected_source: instruction.selected_source,
3477 provider_instance_id: instruction.provider_instance_id ?? null,
3478 value_slot_id: instruction.value_slot_id
3479 };
3480 store.contextConsumerBindings.set(
3481 instruction.consumer_instance_id,
3482 instruction.value_slot_id
3483 );
3484 registry.context_bindings.set(instruction.consumer_instance_id, installed);
3485 }
3486 }
3487 if (store.contextConsumerBindings.size !== expected.size) {
3488 throw new ResumeBootError("ResumeArtifactMismatch");
3489 }
3490 }
3491
3492 function collectExactResumeAnchors(manifest) {
3493 const elements = new Map();
3494 for (const element of document.querySelectorAll("[data-presolve-r]")) {
3495 const id = element.getAttribute("data-presolve-r");
3496 if (elements.has(id)) throw new ResumeBootError("DuplicateIdentity");
3497 elements.set(id, element);
3498 }
3499 const comments = new Map();
3500 const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
3501 for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
3502 const value = node.nodeValue ?? "";
3503 if (!value.startsWith("presolve-r-start:") && !value.startsWith("presolve-r-end:")) continue;
3504 const id = value.slice(value.indexOf(":") + 1);
3505 if (comments.has(id)) throw new ResumeBootError("DuplicateIdentity");
3506 comments.set(id, node);
3507 }
3508 const anchors = new Map();
3509 for (const anchor of manifest.anchors) {
3510 const node = anchor.kind === "structural_start" || anchor.kind === "structural_end"
3511 ? comments.get(anchor.anchor_id)
3512 : elements.get(anchor.anchor_id);
3513 if (anchor.required && node === undefined) throw new ResumeBootError("MissingAnchor");
3514 if (node !== undefined) anchors.set(anchor.anchor_id, node);
3515 }
3516 return anchors;
3517 }
3518
3519 function canonicalResumeValueEqual(left, right) {
3520 return JSON.stringify(left) === JSON.stringify(right);
3521 }
3522
3523 function restoreResumeComponentsSlotsAndStructure(
3524 manifest,
3525 registry,
3526 store,
3527 componentArtifact
3528 ) {
3529 const componentRecords = new Map();
3530 const slotRecords = new Map();
3531 const structuralRecords = new Map();
3532 for (const item of manifest.phase_i_component_resume_records ?? []) {
3533 if (item.record_kind === "component_instance") {
3534 const record = item.component_instance;
3535 if (componentRecords.has(record.instance)) throw new ResumeBootError("DuplicateIdentity");
3536 componentRecords.set(record.instance, record);
3537 } else if (item.record_kind === "slot_binding") {
3538 const record = item.slot_binding;
3539 if (slotRecords.has(record.binding)) throw new ResumeBootError("DuplicateIdentity");
3540 slotRecords.set(record.binding, record);
3541 } else if (item.record_kind === "structural_region") {
3542 const record = item.structural_region;
3543 if (structuralRecords.has(record.region)) throw new ResumeBootError("DuplicateIdentity");
3544 structuralRecords.set(record.region, record);
3545 }
3546 }
3547
3548 const planned = new Map(
3549 (componentArtifact.instances ?? []).map((instance) => [instance.instance, instance])
3550 );
3551 const templateInstances = new Set(
3552 (componentArtifact.structural_programs ?? [])
3553 .flatMap((program) => program.template_instances ?? [])
3554 );
3555 if (componentRecords.size !== planned.size + templateInstances.size) {
3556 throw new ResumeBootError("ResumeArtifactMismatch");
3557 }
3558 for (const record of componentRecords.values()) {
3559 const instance = planned.get(record.instance);
3560 if (instance !== undefined) {
3561 if (
3562 record.component !== instance.component
3563 || (record.parent_instance ?? null) !== (instance.parent ?? null)
3564 || (record.structural_region ?? null) !== (instance.structural_region ?? null)
3565 || record.active_status !== "active"
3566 ) {
3567 throw new ResumeBootError("ResumeArtifactMismatch");
3568 }
3569 } else if (!templateInstances.has(record.instance) || record.active_status !== "inactive") {
3570 throw new ResumeBootError("ResumeArtifactMismatch");
3571 }
3572 const runtimeRecord = { ...record, status: record.active_status };
3573 registry.component_records.set(record.instance, runtimeRecord);
3574 store.componentInstances.set(record.instance, runtimeRecord);
3575 }
3576
3577 const expectedSlots = new Map(
3578 (componentArtifact.slot_binding_programs ?? []).map((binding) => [binding.binding, binding])
3579 );
3580 if (slotRecords.size !== expectedSlots.size) throw new ResumeBootError("ResumeArtifactMismatch");
3581 for (const record of slotRecords.values()) {
3582 const binding = expectedSlots.get(record.binding);
3583 if (
3584 binding === undefined
3585 || record.caller_instance !== binding.caller_instance
3586 || record.callee_instance !== binding.callee_instance
3587 ) {
3588 throw new ResumeBootError("ResumeArtifactMismatch");
3589 }
3590 store.slotBindings.set(record.binding, binding);
3591 }
3592
3593 const expectedRegions = new Map(
3594 (componentArtifact.structural_programs ?? []).map((program) => [program.region, program])
3595 );
3596 if (structuralRecords.size !== expectedRegions.size) {
3597 throw new ResumeBootError("ResumeArtifactMismatch");
3598 }
3599 for (const record of structuralRecords.values()) {
3600 const program = expectedRegions.get(record.region);
3601 if (program === undefined || record.active_status !== "inactive") {
3602 throw new ResumeBootError("ResumeArtifactMismatch");
3603 }
3604 const runtimeRecord = { ...record, program, selection_value: undefined };
3605 registry.structural_records.set(record.region, runtimeRecord);
3606 store.componentRegions.set(record.region, runtimeRecord);
3607 }
3608
3609 const restoredRegions = new Set();
3610 for (const program of registry.definitions.restorePrograms.values()) {
3611 for (const record of program.instructions ?? []) {
3612 if (record.phase !== "R9") continue;
3613 const instruction = record.instruction;
3614 if (
3615 instruction.kind !== "restore_structural_selection"
3616 || restoredRegions.has(instruction.region_id)
3617 ) {
3618 throw new ResumeBootError("RestoreInstructionFailure");
3619 }
3620 const runtimeRecord = registry.structural_records.get(instruction.region_id);
3621 const schema = registry.definitions.slots.get(instruction.slot_id);
3622 const value = registry.slot_values.get(instruction.slot_id);
3623 if (runtimeRecord === undefined || schema === undefined || value === undefined) {
3624 throw new ResumeBootError("ResumeArtifactMismatch");
3625 }
3626 const stateSlot = (componentArtifact.instances ?? [])
3627 .flatMap((instance) => instance.state_slots ?? [])
3628 .find((slot) => slot.slot_id === schema.existing_storage_slot_id);
3629 if (stateSlot === undefined || !canonicalResumeValueEqual(value, stateSlot.initial_value)) {
3630 throw new ResumeBootError("StructuralStateMismatch");
3631 }
3632 runtimeRecord.selection_value = value;
3633 restoredRegions.add(instruction.region_id);
3634 }
3635 }
3636 if (restoredRegions.size !== expectedRegions.size) {
3637 throw new ResumeBootError("ResumeArtifactMismatch");
3638 }
3639 store.resumeAnchors = collectExactResumeAnchors(manifest);
3640 }
3641
3642 function restoreResumeForms(templateManifest, snapshot, registry, store, formsArtifact) {
3643 const restoreRecords = [...registry.definitions.restorePrograms.values()]
3644 .flatMap((program) => program.instructions ?? [])
3645 .filter((record) => ["R11", "R12", "R13", "R14", "R15"].includes(record.phase));
3646 if (formsArtifact === null) {
3647 if (restoreRecords.length !== 0) throw new ResumeBootError("ResumeArtifactMismatch");
3648 return;
3649 }
3650 const targets = collectOrdinaryTargetAnchors();
3651 const definitions = new Map((formsArtifact.forms ?? []).map((form) => [form.id, form]));
3652 const instances = new Map((formsArtifact.instances ?? []).map((instance) => [instance.id, instance]));
3653 if (definitions.size !== (formsArtifact.forms ?? []).length || instances.size !== (formsArtifact.instances ?? []).length) {
3654 throw new ResumeBootError("DuplicateIdentity");
3655 }
3656 store.forms = new Map();
3657 store.formInstances = new Map();
3658 store.formBindingsByAnchor = new Map();
3659 store.formBindingsByField = new Map();
3660 store.formHostsByAnchor = new Map();
3661 const slotOwners = new Map();
3662 for (const instance of instances.values()) {
3663 const definition = definitions.get(instance.form);
3664 if (definition === undefined || store.formInstances.has(instance.id)) {
3665 throw new ResumeBootError("ResumeArtifactMismatch");
3666 }
3667 const fields = new Map((definition.fields ?? []).map((field) => [field.id, {
3668 value: field.initial_value,
3669 initial: field.initial_value,
3670 dirty: false,
3671 touched: false,
3672 validation: []
3673 }]));
3674 if (fields.size !== (definition.fields ?? []).length) throw new ResumeBootError("DuplicateIdentity");
3675 const runtime = { definition, instance, fields, aggregate_valid: true, submission: "Idle" };
3676 store.forms.set(definition.id, definition);
3677 store.formInstances.set(instance.id, runtime);
3678 registry.form_records.set(instance.id, runtime);
3679 for (const slots of instance.field_slots ?? []) {
3680 if (!fields.has(slots.field)) throw new ResumeBootError("ResumeArtifactMismatch");
3681 for (const [kind, slot] of [["value", slots.value], ["dirty", slots.dirty], ["touched", slots.touched], ["validation", slots.validation]]) {
3682 if (slotOwners.has(slot)) throw new ResumeBootError("DuplicateIdentity");
3683 slotOwners.set(slot, { instance: runtime, field: slots.field, kind });
3684 }
3685 }
3686 if (slotOwners.has(instance.aggregate_validation_slot) || slotOwners.has(instance.submission_slot)) {
3687 throw new ResumeBootError("DuplicateIdentity");
3688 }
3689 slotOwners.set(instance.aggregate_validation_slot, { instance: runtime, field: null, kind: "aggregate" });
3690 slotOwners.set(instance.submission_slot, { instance: runtime, field: null, kind: "submission" });
3691 }
3692
3693 const snapshotValues = snapshotValuesBySlot(snapshot);
3694 const restored = new Set();
3695 for (const record of restoreRecords) {
3696 if (record.phase === "R15") continue;
3697 const instruction = record.instruction;
3698 if (instruction.kind !== "decode_value") continue;
3699 const schema = registry.definitions.slots.get(instruction.slot_id);
3700 if (schema === undefined || schema.restore_phase !== record.phase || !snapshotValues.has(instruction.slot_id)) {
3701 throw new ResumeBootError("RestoreInstructionFailure");
3702 }
3703 const owner = slotOwners.get(schema.existing_storage_slot_id);
3704 if (owner === undefined || restored.has(instruction.slot_id)) throw new ResumeBootError("ResumeArtifactMismatch");
3705 const value = decodeResumeValue(snapshotValues.get(instruction.slot_id), instruction.codec);
3706 const field = owner.field === null ? undefined : owner.instance.fields.get(owner.field);
3707 if (owner.kind === "value" && field !== undefined) field.value = value;
3708 else if (owner.kind === "dirty" && field !== undefined && typeof value === "boolean") field.dirty = value;
3709 else if (owner.kind === "touched" && field !== undefined && typeof value === "boolean") field.touched = value;
3710 else if (owner.kind === "validation" && field !== undefined && Array.isArray(value) && value.every((item) => typeof item === "string")) field.validation = value;
3711 else if (owner.kind === "aggregate" && typeof value === "boolean") owner.instance.aggregate_valid = value;
3712 else if (owner.kind === "submission" && ["Idle", "Completed", "Failed", "Invalid"].includes(value)) owner.instance.submission = value;
3713 else throw new ResumeBootError(owner.kind === "submission" ? "UnstableFormSubmission" : "ValueTypeMismatch");
3714 registry.slot_values.set(instruction.slot_id, value);
3715 restored.add(instruction.slot_id);
3716 }
3717 const expectedSlots = [...registry.definitions.slots.values()]
3718 .filter((schema) => ["R11", "R12", "R13", "R14"].includes(schema.restore_phase));
3719 if (restored.size !== expectedSlots.length) throw new ResumeBootError("RestoreInstructionFailure");
3720
3721 for (const bridge of templateManifest.form_bindings ?? []) {
3722 const target = targets.targets.get(bridge.instance_target_id);
3723 const formInstance = store.formInstances.get(bridge.form_instance_id);
3724 if (!(target instanceof Element) || targets.duplicates.has(bridge.instance_target_id) || formInstance === undefined) {
3725 throw new ResumeBootError("MissingAnchor");
3726 }
3727 const binding = formInstance.definition.bindings.find((item) => item.id === bridge.field_binding_id);
3728 if (binding === undefined || binding.field === undefined || binding.channel !== bridge.channel || store.formBindingsByAnchor.has(bridge.instance_target_id)) {
3729 throw new ResumeBootError("ResumeArtifactMismatch");
3730 }
3731 const runtimeBinding = { bridge, binding, element: target, formInstance };
3732 store.formBindingsByAnchor.set(bridge.instance_target_id, runtimeBinding);
3733 const key = `${bridge.form_instance_id}|${binding.field}`;
3734 const bindings = store.formBindingsByField.get(key) ?? [];
3735 bindings.push(runtimeBinding);
3736 store.formBindingsByField.set(key, bindings);
3737 writeFormControl(runtimeBinding, formInstance.fields.get(binding.field)?.value);
3738 }
3739 const expectedBindings = [...instances.values()].reduce((count, instance) => count + (definitions.get(instance.form)?.bindings?.length ?? 0), 0);
3740 if (store.formBindingsByAnchor.size !== expectedBindings) throw new ResumeBootError("ResumeArtifactMismatch");
3741 window.__PRESOLVE_FORMS__ = {
3742 resetForm: (instanceId) => resetForm(store, instanceId),
3743 resetField: (instanceId, fieldId) => resetField(store, instanceId, fieldId)
3744 };
3745 }
3746
3747 function restoreResumeRuntimeThroughForms(
3748 manifest,
3749 snapshot,
3750 registry,
3751 templateManifest,
3752 computedArtifact,
3753 contextArtifact,
3754 effectArtifact,
3755 componentArtifact,
3756 formsArtifact,
3757 diagnostics
3758 ) {
3759 if ((computedArtifact?.evaluations ?? []).some((evaluation) =>
3760 (evaluation.program?.instructions ?? []).some((instruction) => instruction.kind === "load-resource")
3761 )) {
3762 throw new ResumeBootError("ResourceComputedReadUnsupported");
3763 }
3764 const store = allocateResumeStateComputedStore(
3765 registry,
3766 manifest,
3767 templateManifest,
3768 computedArtifact,
3769 contextArtifact,
3770 effectArtifact,
3771 componentArtifact,
3772 diagnostics
3773 );
3774 executeResumeDecodeAndWrites(store, registry, snapshot, new Set(["R3", "R4"]));
3775 synchronizeRestoredComponentState(store, computedArtifact, componentArtifact);
3776 const recomputationRuns = executeResumeComputedRecomputation(store, registry);
3777 executeResumeDecodeAndWrites(store, registry, snapshot, new Set(["R6"]));
3778 executeResumeContextBindings(store, registry, componentArtifact);
3779 restoreResumeComponentsSlotsAndStructure(manifest, registry, store, componentArtifact);
3780 restoreResumeForms(templateManifest, snapshot, registry, store, formsArtifact);
3781 installResumeDomBindings(store, templateManifest, componentArtifact);
3782 establishResumeEffects(registry, store, effectArtifact);
3783 installResumeActivationListeners(registry, store);
3784 const state = runtimeState({
3785 manifest: templateManifest,
3786 diagnostics,
3787 store,
3788 components: debugComponents(store),
3789 computed: debugComputed(store),
3790 computed_update_runs: 0,
3791 context_initial_source_runs: store.contextInitialSourceRuns,
3792 context_slots: [...store.contextSlots.entries()],
3793 context_consumer_bindings: [...store.contextConsumerBindings.entries()],
3794 context_failures: store.contextFailures,
3795 context_update_source_runs: store.contextUpdateSourceRuns,
3796 component_initialization_runs: [],
3797 component_instance_tree: [...store.componentInstances.values()],
3798 forms: [...store.formInstances.values()].map((instance) => ({
3799 id: instance.instance.id,
3800 form: instance.definition.id,
3801 aggregate_valid: instance.aggregate_valid,
3802 submission: instance.submission
3803 })),
3804 slot_binding_runs: [],
3805 component_failures: []
3806 });
3807 state.resume_recomputation_runs = recomputationRuns;
3808 return state;
3809 }
3810
3811 function runtimeState({
3812 manifest = null,
3813 missingAnchors = [],
3814 store = null,
3815 components = [],
3816 computed = [],
3817 computed_update_runs = 0,
3818 initial_effect_runs = [],
3819 completed_action_effect_runs = [],
3820 context_initial_source_runs = [],
3821 context_slots = [],
3822 context_consumer_bindings = [],
3823 context_failures = [],
3824 context_update_source_runs = [],
3825 component_initialization_runs = [],
3826 component_instance_tree = [],
3827 forms = [],
3828 slot_binding_runs = [],
3829 component_failures = [],
3830 diagnostics
3831 }) {
3832 return {
3833 runtime_version: RUNTIME_VERSION,
3834 supported_schema_version: SUPPORTED_SCHEMA_VERSION,
3835 manifest,
3836 missingAnchors,
3837 diagnostics,
3838 store,
3839 components,
3840 computed,
3841 computed_update_runs,
3842 initial_effect_runs,
3843 completed_action_effect_runs,
3844 context_initial_source_runs,
3845 context_slots,
3846 context_consumer_bindings,
3847 context_failures,
3848 context_update_source_runs,
3849 component_initialization_runs,
3850 component_instance_tree,
3851 forms,
3852 slot_binding_runs,
3853 component_failures
3854 };
3855 }
3856
3857 async function initializeResourcesRuntime(store, resourcesArtifact, diagnostics) {
3858 if (resourcesArtifact === null) return;
3859 window.addEventListener("pagehide", () => {
3860 for (const resource of store.resources.values()) resource.controller.abort();
3861 }, { once: true });
3862 const declarations = new Map(resourcesArtifact.declarations.map((declaration) => [declaration.id, declaration]));
3863 const records = [];
3864 for (const activation of resourcesArtifact.activations) {
3865 const declaration = declarations.get(activation.declaration);
3866 if (declaration === undefined) throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
3867 if (!["Client", "Shared"].includes(declaration.execution_boundary)) {
3868 reportDiagnostic(diagnostics, "PSR_RESOURCE_SERVER_UNAVAILABLE", "A server-only Resource cannot activate in the browser runtime", { activation, declaration }, true);
3869 throw new PresolveBootError("PSR_RESOURCE_SERVER_UNAVAILABLE");
3870 }
3871 const controller = new AbortController();
3872 const record = { activation, declaration, controller, state: "pending", generation: 1, data: null, error: null };
3873 const activationKey = `${activation.component_instance}\u001f${activation.declaration}`;
3874 if (store.resources.has(activation.id) || store.resourceActivationsByInstanceDeclaration.has(activationKey)) {
3875 throw new PresolveBootError("PSR_INVALID_RESOURCES_ARTIFACT");
3876 }
3877 store.resources.set(activation.id, record);
3878 store.resourceActivationsByInstanceDeclaration.set(activationKey, activation.id);
3879 records.push(record);
3880 }
3881 await Promise.all(records.map(async (record) => {
3882 try {
3883 const module = await import(record.declaration.endpoint.runtime_location);
3884 const endpoint = module[record.declaration.endpoint.export];
3885 if (typeof endpoint !== "function") throw new Error("endpoint-export-missing");
3886 const result = await endpoint({ signal: record.controller.signal, inputs: Object.freeze({}) });
3887 if (record.controller.signal.aborted) {
3888 record.state = "cancelled";
3889 } else {
3890 JSON.stringify(result);
3891 record.state = "ready";
3892 record.data = result;
3893 }
3894 } catch (error) {
3895 if (record.controller.signal.aborted) {
3896 record.state = "cancelled";
3897 } else {
3898 record.state = "failed";
3899 record.error = error instanceof Error ? error.message : String(error);
3900 reportDiagnostic(diagnostics, "PSR_RESOURCE_ENDPOINT_FAILURE", "A compiler-authorized Resource endpoint failed", { activation: record.activation.id, error: record.error });
3901 }
3902 }
3903 invalidateResourceComputeds(store, record.activation);
3904 }));
3905 }
3906
3907 async function initializeRuntime(manifest, computedArtifact, contextArtifact, effectArtifact, componentArtifact, formsArtifact, resourcesArtifact, opaqueArtifact, diagnostics) {
3908 const bindingAnchors = collectBindingAnchors();
3909 const conditionalAnchors = collectConditionalAnchors();
3910 const listAnchors = collectListAnchors();
3911 const elementsByNode = collectElementAnchors();
3912 const store = createRuntimeStore(elementsByNode, diagnostics, computedArtifact, contextArtifact, effectArtifact, componentArtifact, opaqueArtifact);
3913 store.componentArtifact = componentArtifact;
3914 store.componentInstances = new Map((componentArtifact?.instances ?? []).map((instance) => [instance.instance, { ...instance, status: "created" }]));
3915 store.slotBindings = new Map((componentArtifact?.slot_binding_programs ?? []).map((binding) => [binding.binding, binding]));
3916 store.instanceContextBindings = new Map((componentArtifact?.instance_context_bindings ?? []).map((binding) => [binding.consumer_instance, binding]));
3917 store.componentRegions = new Map((componentArtifact?.structural_programs ?? []).map((program) => [program.region, program]));
3918 const missingAnchors = manifest.schema_version === SUPPORTED_SCHEMA_VERSION
3919 ? []
3920 : collectMissingAnchors(
3921 manifest,
3922 bindingAnchors,
3923 conditionalAnchors,
3924 listAnchors,
3925 elementsByNode
3926 );
3927
3928 for (const anchor of missingAnchors) {
3929 reportDiagnostic(
3930 diagnostics,
3931 anchor.code,
3932 anchor.kind === "element"
3933 ? "Manifest element anchor was not found in the rendered DOM"
3934 : anchor.kind === "conditional"
3935 ? "Manifest conditional anchor was not found in the rendered DOM"
3936 : anchor.kind === "list"
3937 ? "Manifest list anchor was not found in the rendered DOM"
3938 : "Manifest binding anchor was not found in the rendered DOM",
3939 anchor
3940 );
3941 }
3942
3943 const resourceInitialization = initializeResourcesRuntime(store, resourcesArtifact, diagnostics);
3944
3945 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
3946 const definitions = new Map((manifest.components ?? []).map((component) => [component.component_id, component]));
3947 for (const instance of componentArtifact.instances ?? []) {
3948 const definition = definitions.get(instance.component);
3949 if (definition === undefined) throw new PresolveBootError("PSR_INVALID_ORDINARY_COMPONENT");
3950 const definitionStates = (computedArtifact?.state ?? []).filter(
3951 (state) => state.component === definition.name
3952 );
3953 if ((instance.state_slots ?? []).length !== definitionStates.length) {
3954 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3955 }
3956 for (const slot of instance.state_slots ?? []) {
3957 const pair = `${instance.instance}|${slot.storage_id}`;
3958 if (
3959 store.stateSlotsByInstanceStorage.has(pair)
3960 || store.storageValues.has(slot.slot_id)
3961 ) {
3962 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3963 }
3964 store.stateSlotsByInstanceStorage.set(pair, slot);
3965 store.storageValues.set(slot.slot_id, initialStateSlotValue(slot));
3966 }
3967 for (const state of definitionStates) {
3968 if (!store.stateSlotsByInstanceStorage.has(`${instance.instance}|${state.storage}`)) {
3969 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3970 }
3971 }
3972 for (const slot of instance.computed_slots ?? []) {
3973 const pair = `${instance.instance}|${slot.computed_id}`;
3974 if (!slot.cache_slot_id.startsWith(`${instance.instance}/computed-cache:`)
3975 || !slot.dirty_slot_id.startsWith(`${instance.instance}/computed-dirty:`)
3976 || store.computedSlotsByInstanceComputed.has(pair)
3977 || store.computedDirtySlots.has(slot.dirty_slot_id)) {
3978 throw new PresolveBootError("PSR_INVALID_COMPONENT_ARTIFACT");
3979 }
3980 store.computedSlotsByInstanceComputed.set(pair, slot);
3981 store.computedDirtySlots.set(slot.dirty_slot_id, slot.dirty_initial_value === true);
3982 }
3983 const component = { instance_id: instance.instance, name: instance.component, manifest: definition, state: {} };
3984 for (const state of computedArtifact?.state ?? []) {
3985 if (state.component !== definition.name) continue;
3986 const slot = store.stateSlotsByInstanceStorage.get(`${instance.instance}|${state.storage}`);
3987 if (slot !== undefined) component.state[state.field] = store.storageValues.get(slot.slot_id);
3988 }
3989 store.components.set(instance.instance, component);
3990 registerActions(store, component, definition);
3991 }
3992 initializeOrdinaryInstanceRuntime(store, manifest, componentArtifact);
3993 } else {
3994 for (const manifestComponent of manifest.components ?? []) {
3995 const component = initializeComponentRuntime(
3996 store,
3997 manifestComponent,
3998 bindingAnchors,
3999 conditionalAnchors,
4000 listAnchors
4001 );
4002 registerComponentEvents(store, component);
4003 }
4004 }
4005
4006 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
4007 for (const instance of componentArtifact.instances ?? []) {
4008 executeComputedPlan(store, instance.instance);
4009 }
4010 } else {
4011 executeComputedPlan(store);
4012 }
4013
4014 executeInitialContext(store);
4015
4016 initializeFormsRuntime(store, formsArtifact, manifest, elementsByNode, diagnostics);
4017
4018 await resourceInitialization;
4019
4020 executeInitialEffects(store);
4021
4022 if (manifest.schema_version === SUPPORTED_SCHEMA_VERSION) {
4023 installOrdinaryInstanceEventListeners(store);
4024 } else {
4025 installDelegatedEventListeners(store);
4026 }
4027
4028 return runtimeState({
4029 manifest,
4030 missingAnchors,
4031 diagnostics,
4032 store,
4033 components: debugComponents(store),
4034 computed: debugComputed(store),
4035 computed_update_runs: store.computedUpdateRuns,
4036 initial_effect_runs: store.initialEffectRuns,
4037 completed_action_effect_runs: store.completedActionEffectRuns,
4038 context_initial_source_runs: store.contextInitialSourceRuns,
4039 context_slots: [...store.contextSlots.entries()],
4040 context_consumer_bindings: [...store.contextConsumerBindings.entries()],
4041 context_failures: store.contextFailures,
4042 context_update_source_runs: store.contextUpdateSourceRuns,
4043 component_initialization_runs: (componentArtifact?.initialization_batches ?? []).map((batch) => batch.index),
4044 component_instance_tree: [...store.componentInstances.values()],
4045 forms: [...store.formInstances.values()].map((instance) => ({
4046 id: instance.instance.id,
4047 form: instance.definition.id,
4048 aggregate_valid: instance.aggregate_valid,
4049 submission: instance.submission
4050 })),
4051 resources: [...store.resources.entries()].map(([id, resource]) => ({ id, state: resource.state, generation: resource.generation })),
4052 opaque_terminals: [...store.opaqueActivations],
4053 slot_binding_runs: [...store.slotBindings.keys()],
4054 component_failures: []
4055 });
4056 }
4057
4058 async function boot() {
4059 const diagnostics = [];
4060
4061 try {
4062 const productionArtifact = readProductionRuntimeArtifact();
4063 const productionIndexes = productionOrdinalIndexes(productionArtifact);
4064 const manifest = readManifest(diagnostics);
4065 const computedArtifact = readComputedArtifact(diagnostics);
4066 validateComputedArtifactSchema(computedArtifact, diagnostics);
4067 const contextArtifact = readContextArtifact(diagnostics);
4068 validateContextArtifactSchema(contextArtifact, diagnostics);
4069 const effectArtifact = readEffectArtifact(diagnostics);
4070 validateEffectArtifactSchema(effectArtifact, diagnostics);
4071 const componentArtifact = readComponentArtifact(diagnostics);
4072 validateComponentArtifactSchema(componentArtifact, diagnostics);
4073 const opaqueArtifact = readOpaqueArtifact(diagnostics);
4074 validateOpaqueArtifact(opaqueArtifact, diagnostics);
4075 validateManifestSchema(manifest, effectArtifact, componentArtifact, opaqueArtifact, diagnostics);
4076 const formsArtifact = readFormsArtifact(diagnostics);
4077 validateFormsArtifact(formsArtifact, manifest, diagnostics);
4078 const resourcesArtifact = readResourcesArtifact(diagnostics);
4079 validateResourcesArtifact(resourcesArtifact, diagnostics);
4080
4081 const result = await bootstrapResume({
4082 diagnostics,
4083 resumeBoot: ({ manifest: resumeManifest, snapshot, registry }) => {
4084 if ((opaqueArtifact?.activations ?? []).length > 0) {
4085 throw new ResumeBootError("OpaqueTerminalColdFallback");
4086 }
4087 return restoreResumeRuntimeThroughForms(
4088 resumeManifest,
4089 snapshot,
4090 registry,
4091 manifest,
4092 computedArtifact,
4093 contextArtifact,
4094 effectArtifact,
4095 componentArtifact,
4096 formsArtifact,
4097 diagnostics
4098 );
4099 },
4100 coldBoot: () => initializeRuntime(
4101 manifest,
4102 computedArtifact,
4103 contextArtifact,
4104 effectArtifact,
4105 componentArtifact,
4106 formsArtifact,
4107 resourcesArtifact,
4108 opaqueArtifact,
4109 diagnostics
4110 )
4111 });
4112 const state = result.mode === "cold"
4113 ? result.cold
4114 : result.resume;
4115 state.resume = {
4116 mode: result.mode,
4117 failure: result.failure,
4118 contract_version: RESUME_REGISTRY_CONTRACT_VERSION
4119 };
4120 state.resume_registry = result.mode === "resume" ? result.registry : null;
4121 state.resume_debug = [...resumeBootstrapState.debug];
4122 state.production = productionIndexes === null ? null : {
4123 artifact_checksum: productionArtifact.integrity?.artifact_checksum ?? null,
4124 table_kinds: [...productionIndexes.keys()]
4125 };
4126 const status = state.diagnostics.some((diagnostic) => diagnostic.fatal)
4127 || state.missingAnchors.length > 0
4128 ? "error"
4129 : "ready";
4130
4131 document.documentElement.dataset.presolveRuntime = status;
4132 window.__PRESOLVE__ = state;
4133
4134 document.dispatchEvent(
4135 new CustomEvent("presolve:ready", {
4136 detail: state
4137 })
4138 );
4139 } catch (error) {
4140 document.documentElement.dataset.presolveRuntime = "error";
4141 if (error instanceof PresolveBootError) {
4142 reportDiagnostic(
4143 diagnostics,
4144 error.code,
4145 "Runtime boot failed at a closed compiler contract boundary",
4146 { code: error.code },
4147 true
4148 );
4149 } else {
4150 reportDiagnostic(
4151 diagnostics,
4152 "PSR_RUNTIME_BOOT_FAILED",
4153 "Runtime boot failed",
4154 { message: error instanceof Error ? error.message : String(error) },
4155 true
4156 );
4157 }
4158
4159 window.__PRESOLVE__ = runtimeState({
4160 diagnostics
4161 });
4162
4163 document.dispatchEvent(
4164 new CustomEvent("presolve:ready", {
4165 detail: window.__PRESOLVE__
4166 })
4167 );
4168 }
4169 }
4170
4171 window.__PRESOLVE_RESUME__ = Object.freeze({
4172 bootstrapResume,
4173 captureSnapshot,
4174 activateByEvent,
4175 activateBoundary,
4176 debugEvidence: () => [...resumeBootstrapState.debug]
4177 });
4178
4179 if (document.readyState === "loading") {
4180 document.addEventListener("DOMContentLoaded", boot, {
4181 once: true
4182 });
4183 } else {
4184 boot();
4185 }
4186})();
4187"#;
4188
4189#[must_use]
4190pub fn generate_runtime_stub() -> String {
4191 RUNTIME_STUB.replace(
4192 "__EZ_COMPONENT_SCHEMA_VERSION__",
4193 &crate::RUNTIME_COMPONENT_ARTIFACT_SCHEMA_VERSION.to_string(),
4194 )
4195}
4196
4197#[cfg(test)]
4198mod tests {
4199 use super::generate_runtime_stub;
4200
4201 #[test]
4202 fn emits_runtime_manifest_bootstrap() {
4203 let runtime = generate_runtime_stub();
4204
4205 assert!(runtime.contains("presolve-template-manifest"));
4206 assert!(runtime.contains("presolve-effect-runtime"));
4207 assert!(runtime.contains("presolve-context-runtime"));
4208 assert!(runtime.contains("executeInitialContext(store)"));
4209 assert!(runtime.contains("executeContextUpdates(store, actionBatchId)"));
4210 assert!(runtime.contains("contextSlots: new Map()"));
4211 assert!(runtime.contains("RUNTIME_VERSION = \"0.0.0\""));
4212 assert!(runtime.contains("SUPPORTED_SCHEMA_VERSION = 5"));
4213 assert!(runtime.contains("SUPPORTED_COMPUTED_ARTIFACT_SCHEMA_VERSION = 12"));
4214 assert!(runtime.contains("instruction.kind === \"load-resource\""));
4215 assert!(runtime.contains("resourceInvalidationsByDeclaration"));
4216 assert!(runtime.contains("case \"abs\""));
4217 assert!(runtime.contains("instruction.kind === \"select\""));
4218 assert!(runtime.contains("instruction.kind === \"get-index\""));
4219 assert!(runtime.contains("presolve-forms-runtime"));
4220 assert!(runtime.contains("presolve-resources-runtime"));
4221 assert!(runtime.contains("presolve-opaque-runtime"));
4222 assert!(runtime.contains("validateOpaqueArtifact"));
4223 assert!(runtime.contains("executeOpaqueTerminal"));
4224 assert!(runtime.contains("PSR_OPAQUE_TERMINAL_FAILURE"));
4225 assert!(runtime.contains("OpaqueTerminalColdFallback"));
4226 assert!(runtime.contains("validateResourcesArtifact"));
4227 assert!(runtime.contains("initializeResourcesRuntime"));
4228 assert!(runtime.contains("AbortController"));
4229 assert!(runtime.contains("pagehide"));
4230 assert!(runtime.contains("initializeFormsRuntime"));
4231 assert!(runtime.contains("dispatchFormSubmit"));
4232 assert!(runtime.contains("form_hosts"));
4233 assert!(!runtime.contains("FormData(formElement)"));
4234 assert!(runtime.contains("PSR_MISSING_MANIFEST"));
4235 assert!(runtime.contains("PSR_INVALID_MANIFEST_JSON"));
4236 assert!(runtime.contains("PSR_UNSUPPORTED_SCHEMA"));
4237 assert!(runtime.contains("data-presolve-node"));
4238 assert!(runtime.contains("ordinaryEventsByTargetAndType"));
4239 assert!(runtime.contains("component_instance_id: record.component_instance_id"));
4240 assert!(runtime.contains("computedSlotsByInstanceComputed: new Map()"));
4241 assert!(runtime.contains("computedDirtySlots: new Map()"));
4242 assert!(runtime.contains("function computedSlotForExecution"));
4243 assert!(runtime.contains("/computed-cache:"));
4244 assert!(runtime.contains("/computed-dirty:"));
4245 assert!(runtime.contains("LEGACY_COMPONENT_ARTIFACT_SCHEMA_VERSION = 2"));
4246 assert!(runtime.contains("RESUME_REGISTRY_CONTRACT_VERSION = 1"));
4247 assert!(runtime.contains("function validateResumeManifest"));
4248 assert!(runtime.contains("function validateResumeSnapshot"));
4249 assert!(runtime.contains("async function bootstrapResume"));
4250 assert!(runtime.contains("function captureSnapshot"));
4251 assert!(runtime.contains("async function activateByEvent"));
4252 assert!(runtime.contains("async function activateBoundary"));
4253 assert!(runtime.contains("function decodeResumeValue"));
4254 assert!(runtime.contains("function restoreResumeRuntimeThroughForms"));
4255 assert!(runtime.contains("ResourceComputedReadUnsupported"));
4256 assert!(runtime.contains("executeResumeComputedRecomputation"));
4257 assert!(runtime.contains("function executeResumeContextBindings"));
4258 assert!(runtime.contains("function restoreResumeComponentsSlotsAndStructure"));
4259 assert!(runtime.contains("function restoreResumeForms"));
4260 assert!(runtime.contains("function installResumeDomBindings"));
4261 assert!(runtime.contains("function establishResumeEffects"));
4262 assert!(runtime.contains("function collectExactResumeAnchors"));
4263 assert!(runtime.contains("window.__PRESOLVE_RESUME__ = Object.freeze"));
4264 assert!(runtime.contains("throw new ResumeBootError(\"DoubleBootstrap\")"));
4265 assert!(runtime.contains("presolve-binding:"));
4266 assert!(runtime.contains("reportDiagnostic"));
4267 assert!(runtime.contains("validateManifestSchema"));
4268 assert!(runtime.contains("validateEffectArtifactSchema"));
4269 assert!(runtime.contains("createRuntimeStore"));
4270 assert!(runtime.contains("readField"));
4271 assert!(runtime.contains("writeField"));
4272 assert!(runtime.contains("notifyField"));
4273 assert!(runtime.contains("actionDelta"));
4274 assert!(runtime.contains("isBooleanAttribute"));
4275 assert!(runtime.contains("isPropertyAttribute"));
4276 assert!(runtime.contains("updateAttributeBinding"));
4277 assert!(runtime.contains("actionsByMethod.set(action.method_id, action.action_batch_id)"));
4278 assert!(runtime.contains("const actionRecord = store.actionsByMethod.get(event.method_id)"));
4279 assert!(runtime.contains("actionRecord.action_batch_id !== event.action_batch_id"));
4280 assert!(runtime.contains("executeActions"));
4281 assert!(runtime.contains("executeCompletedActionEffects"));
4282 assert!(runtime.contains("activeActionBatch"));
4283 assert!(runtime.contains("executeInitialEffects"));
4284 assert!(runtime.contains("dispatchEffectCapability"));
4285 assert!(!runtime.contains("const arguments ="));
4286 assert!(runtime.contains("formatBindingValue"));
4287 assert!(runtime.contains("value === null ? \"\" : String(value)"));
4288 assert!(runtime.contains("listItemMemberPath"));
4289 assert!(runtime.contains("populateListItemMemberBindings"));
4290 assert!(runtime.contains("updateListItemTextBindings"));
4291 assert!(runtime.contains("updateListItemAttributes"));
4292 assert!(runtime.contains("registerListItemEvents"));
4293 assert!(runtime.contains("unregisterListItemEvents"));
4294 assert!(runtime.contains("presolve-list-binding-end:"));
4295 assert!(runtime.contains("renderListItemElement"));
4296 assert!(runtime.contains("component.state[field] = node.initial_value"));
4297 assert!(!runtime.contains("component.state[field] = Number(node.initial_value)"));
4298 assert!(runtime.contains("installDelegatedEventListeners"));
4299 assert!(runtime.contains("document.addEventListener(eventType"));
4300 assert!(!runtime.contains("element.addEventListener(\"click\""));
4301 assert!(runtime.contains("action.operation !== \"toggle\""));
4302 assert!(runtime.contains("action.operation === \"assign\""));
4303 assert!(runtime.contains("action.operation === \"toggle\""));
4304 assert!(runtime.contains("PSR_MISSING_ELEMENT_ANCHOR"));
4305 assert!(runtime.contains("PSR_MISSING_BINDING_ANCHOR"));
4306 assert!(runtime.contains("PSR_UNRESOLVED_EVENT"));
4307 assert!(runtime.contains("PSR_UNRESOLVED_ACTION"));
4308 assert!(runtime.contains("PSR_INVALID_STATE_OPERATION"));
4309 assert!(runtime.contains("current + delta"));
4310 assert!(runtime.contains("dataset.presolveRuntime"));
4311 assert!(runtime.contains("presolve:ready"));
4312 assert!(runtime.contains("runtime_version"));
4313 assert!(runtime.contains("diagnostics"));
4314 assert!(runtime.contains("window.__PRESOLVE__"));
4315 }
4316}