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:
+23
-17
@@ -8,29 +8,35 @@ import {
|
||||
jsToProtoValue,
|
||||
liveValue,
|
||||
protoValueToJs,
|
||||
opaqueReactPropsBinding,
|
||||
} from "../dist/index.js";
|
||||
const scalar = (name) => ({ kind: "scalar", name });
|
||||
const unit = { kind: "builtin", name: "unit" };
|
||||
test("opaque React props retain managed references while ordinary messages remain closed", () => {
|
||||
test("free function handlers never acquire a receiver or object session", async () => {
|
||||
const handler = bindQxHandler(
|
||||
{ receiver: "none", inputType: unit, outputType: unit, ports: {} },
|
||||
async (context) => {
|
||||
assert.equal("objectId" in context, false);
|
||||
assert.equal("openSession" in context, false);
|
||||
},
|
||||
{},
|
||||
);
|
||||
await handler({
|
||||
inputProto: {},
|
||||
get objectId() {
|
||||
throw new Error("no receiver exists");
|
||||
},
|
||||
openSession() {
|
||||
assert.fail("no object session");
|
||||
},
|
||||
});
|
||||
});
|
||||
test("React props no longer have an opaque-message reference escape hatch", () => {
|
||||
const descriptorId = "org.quixos.web-studio.ReactProps";
|
||||
const reference = referenceFromWire("obj:task");
|
||||
const type = { kind: "message", descriptorId };
|
||||
const messages = { [descriptorId]: opaqueReactPropsBinding };
|
||||
const decoded = decodeQxValue(type, encodeQxValue(type, { subject: reference, title: "Task" }, messages), messages);
|
||||
assert.ok(decoded.subject.equals(reference));
|
||||
assert.equal(decoded.title, "Task");
|
||||
assert.throws(() => opaqueReactPropsBinding.encode(null), /object/);
|
||||
assert.throws(() => opaqueReactPropsBinding.encode([]), /object/);
|
||||
assert.throws(
|
||||
() =>
|
||||
encodeQxValue(
|
||||
{ kind: "message", descriptorId: "Ordinary" },
|
||||
{ subject: reference },
|
||||
{ Ordinary: opaqueReactPropsBinding },
|
||||
),
|
||||
/Managed references/,
|
||||
);
|
||||
const messages = { [descriptorId]: { encode: jsToProtoValue, decode: protoValueToJs } };
|
||||
assert.throws(() => encodeQxValue(type, { subject: reference }, messages), /Managed references/);
|
||||
assert.throws(() => decodeQxValue(type, jsToProtoValue({ subject: reference }), messages), /Managed references/);
|
||||
});
|
||||
test("declared record inputs preserve opaque references without opening message codecs", async () => {
|
||||
const target = referenceFromWire("obj:board");
|
||||
|
||||
Reference in New Issue
Block a user