Migrate TODO implementations to generated candidate bindings
This commit is contained in:
+25
-7
@@ -8,6 +8,20 @@ import { assertReferenceFree, referenceToWire } from "./references.js";
|
||||
declare const watchBrand: unique symbol;
|
||||
export type QxWatchHandle = string & { readonly [watchBrand]: true };
|
||||
export type MessageBinding<T> = { encode(value: T): Value; decode(value: Value): T };
|
||||
export type QxLiveValue = ReturnType<typeof liveValue>;
|
||||
const reactPropsDescriptor = "org.quixos.web-studio.ReactProps";
|
||||
// Explicit temporary props exception, matching orch's RPC contract. Field shapes
|
||||
// remain unchecked pending generics; ordinary messages and state do not gain it.
|
||||
export const opaqueReactPropsBinding: MessageBinding<Record<string, unknown>> = {
|
||||
encode(value) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("React props must be an object");
|
||||
return jsToProtoValue(value);
|
||||
},
|
||||
decode(value) {
|
||||
if (value.kind.case !== "objectValue") throw new Error("React props must be an object");
|
||||
return protoValueToJs(value) as Record<string, unknown>;
|
||||
},
|
||||
};
|
||||
export type BindingValue<B> = B extends MessageBinding<infer T> ? T : never;
|
||||
export type QxHandler<C, O> = (context: C) => O | Promise<O>;
|
||||
export type QxDerived<C, O> = { kind: "derived"; get: QxHandler<C, O> };
|
||||
@@ -52,9 +66,9 @@ export const decodeQxValue = (type: QxValueType, value: Value | undefined, messa
|
||||
return value.kind.value.values.map((entry) => decodeQxValue(type.value, entry, messages));
|
||||
}
|
||||
if (type.kind === "message") {
|
||||
assertReferenceFree(protoValueToJs(value));
|
||||
if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(protoValueToJs(value));
|
||||
const decoded = requireMessage(messages, type.descriptorId).decode(value);
|
||||
assertReferenceFree(decoded);
|
||||
if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(decoded);
|
||||
return decoded;
|
||||
}
|
||||
if (type.kind === "object-ref") {
|
||||
@@ -94,10 +108,10 @@ export const encodeQxValue = (type: QxValueType, value: any, messages: QxMessage
|
||||
if (type.kind === "optional") return value === null ? jsToProtoValue(null) : encodeQxValue(type.value, value, messages);
|
||||
if (type.kind === "list") return jsToProtoValue(value.map((entry: unknown) => liveValue(encodeQxValue(type.value, entry, messages))));
|
||||
if (type.kind === "object-ref") { referenceToWire(value); return jsToProtoValue(value); }
|
||||
assertReferenceFree(value);
|
||||
if (type.kind !== "message" || type.descriptorId !== reactPropsDescriptor) assertReferenceFree(value);
|
||||
if (type.kind === "message") {
|
||||
const encoded = requireMessage(messages, type.descriptorId).encode(value);
|
||||
assertReferenceFree(protoValueToJs(encoded));
|
||||
if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(protoValueToJs(encoded));
|
||||
return encoded;
|
||||
}
|
||||
return jsToProtoValue(value);
|
||||
@@ -128,7 +142,7 @@ export const bindQxHandler = <C, O>(
|
||||
case "state": {
|
||||
const state = raw.state(port.id);
|
||||
return [name, {
|
||||
...(port.primitives.includes("read") ? { get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages) } : {}),
|
||||
...(port.primitives.includes("read") ? { get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages), live: () => state.live() } : {}),
|
||||
...(port.primitives.includes("write") ? { set: async (value: unknown) => state.set(liveValue(encodeQxValue(port.valueType, value, messages))) } : {}),
|
||||
}];
|
||||
}
|
||||
@@ -140,10 +154,14 @@ export const bindQxHandler = <C, O>(
|
||||
}
|
||||
case "interface": {
|
||||
const target = raw.interface(port.id);
|
||||
return [name, Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name,
|
||||
return [name, {objectId: target.objectId,
|
||||
live: Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name,
|
||||
(input: unknown) => target.live(operation.id, inputFields(operation.inputType, input, messages)),
|
||||
])),
|
||||
...Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name,
|
||||
async (input: unknown) => decodeQxValue(operation.outputType,
|
||||
(await target.live(operation.id, inputFields(operation.inputType, input, messages))).$quixosValue, messages),
|
||||
]))];
|
||||
]))}];
|
||||
}
|
||||
case "constructor": return [name, { construct: (input: unknown) => raw.constructor(port.id).construct(inputFields(port.inputType, input, messages)) }];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user