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:
@@ -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 [];
|
||||
|
||||
Reference in New Issue
Block a user