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