Add checked React field bindings and Web Studio factories

Replace opaque props with checked generic presentation contracts and lazy typed
interface references. Generate readonly/writable field APIs and component checks.

Preserve CRDT editing through explicit resolved getter/setter contracts, binding-
fenced delta RPCs, native watches and replica-aware field adapters. Custom setters
retain semantic writes; storage snapshots never grant write authority. Cover
concurrent edits, lost acknowledgements, readonly contracts and authorization.

Add receiver-free static factory dispatch, state-field binding shorthand, and
conformance-based creation. Migrate TODO, editable scaffolds and authoring guides.
Verify language/codegen, SDK, RPC, browser lifecycle, local scaffolds, production
browser bundling and CRDT persistence with temporary PostgreSQL.
This commit is contained in:
Timothy J. Aveni
2026-09-16 16:49:09 -07:00
parent 52803dda05
commit 59735c5e38
22 changed files with 3034 additions and 2300 deletions
+46 -1
View File
@@ -552,6 +552,7 @@ const lowerOperationMember = (state: LoweringState, context: OperationMemberCont
inputType,
outputType,
mode: "call",
...(context.STATIC() ? { scope: "class" as const } : {}),
},
],
};
@@ -702,7 +703,15 @@ const lowerInterfaceTemplate = (
inputType,
outputType,
operations: [
signature("call", capabilityId.operation(stringValue(operation.stringLiteral(1))), inputType, outputType),
{
...signature(
"call",
capabilityId.operation(stringValue(operation.stringLiteral(1))),
inputType,
outputType,
),
...(operation.STATIC() ? { scope: "class" as const } : {}),
},
],
};
}
@@ -1430,6 +1439,42 @@ const lowerConformance = (
const operationBindings = context
.conformanceItem()
.flatMap<WorkspaceRevision["conformances"][number]["operationBindings"][number]>((item) => {
const field = item.stateFieldBindingDecl();
if (field) {
const memberName = identifier(field.identifier(0));
const member = interfaceSymbol.definition?.members.find((entry) => entry.displayName === memberName);
const attachment = requireSymbol(
state,
state.attachments,
identifier(field.identifier(1)),
field,
"attachment",
);
if (!member || member.kind !== "value" || attachment?.attachment.kind !== "state") {
loweringIssue(
state,
field,
"invalid-state-field-binding",
"Field shorthand requires a value member and a state slot",
);
return [];
}
const primitives = {
get: "read",
set: "write",
"watch-start": "watch-start",
"watch-stop": "watch-stop",
} as const;
const slotId = attachment.attachment.id;
return member.operations.map((operation) => ({
operationId: operation.id,
binding: {
kind: "state" as const,
slotId,
primitive: primitives[operation.displayName as keyof typeof primitives],
},
}));
}
const bindingContext = item.operationBindingDecl();
if (!bindingContext) {
return [];