Add graph-aware React controls with stable asynchronous selection

Add typed command/live bindings, Choice and Toggle, CRDT text wrappers, and explicit browser-local controls. Migrate Reader and current example inputs. Enforce editable-control authoring checks in the React artifact backend.

Command controls display query observations, permit one local execution at a time, and delay pending feedback for 200 ms. Include keyboard, focus, shadow-root, top-layer popup, rejection/no-op, native-field eligibility, and IME coverage.

Verified SDK builds and tests, synchronous-editing browser qualification, Reader/Tasks qualification, presentation-mount qualification, and the review-dashboard browser test. Monorepo formatting and lint pass.

The full composition qualification stops at the unchanged combined-to-review workspace fixture transition: STORAGE_MODEL_CHANGED from changed/removed stored conformance owners. Its dashboard browser test passes when run directly.
This commit is contained in:
Timothy J. Aveni
2026-09-30 00:20:00 -07:00
parent 8e55a11c4a
commit 988e10263a
+10
View File
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import { createHash } from "node:crypto";
import { execFileSync } from "node:child_process";
import { pathToFileURL } from "node:url";
const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
export async function buildComponents({ config, compiler, plan, pkg, output, recordFile, generated }) {
const settings = config.components;
@@ -158,6 +159,9 @@ export async function buildComponents({ config, compiler, plan, pkg, output, rec
`\nexport {};\ndeclare module ${JSON.stringify("./" + bindingPath.replace(/\.ts$/, ".js"))} { interface ModuleProps {${propsMembers.join("\n")}} }\n`,
);
const components = [];
const { checkControls } = await import(pathToFileURL(path.join(settings.sdk, "lint-controls.mjs")));
const { default: ts } = await import(pathToFileURL(path.resolve(path.dirname(config.tsc), "../lib/typescript.js")));
const checkedControls = new Set();
for (const [id, entry] of entries) {
const declaration = declarations.find((component) => component.id === id),
source = compiler.artifactPath(entry.entry);
@@ -225,6 +229,12 @@ export async function buildComponents({ config, compiler, plan, pkg, output, rec
);
const meta = JSON.parse(await fs.readFile(metadata, "utf8")),
outputs = Object.entries(meta.outputs);
for (const file of Object.keys(meta.inputs)) {
if (!/\.[cm]?[jt]sx?$/.test(file) || file.includes("node_modules/") || checkedControls.has(file)) continue;
checkedControls.add(file);
const errors = checkControls(ts, await fs.readFile(path.resolve(work, file), "utf8"), file);
if (errors.length) throw Error(errors.join("\n"));
}
const main = outputs.find(([file]) => file.endsWith("/module.mjs"))?.[1];
if (!main?.exports.includes(entry.export)) throw Error("Component bundle lacks declared export");
if (