Fix supervised credentials and declare typed RPC records for canvas placement
This commit is contained in:
+19
-2
@@ -20,6 +20,7 @@ export type QxValueType =
|
||||
| { kind: "builtin"; name: "unit" | "watch-handle" }
|
||||
| { kind: "scalar"; name: string }
|
||||
| { kind: "message"; descriptorId: string }
|
||||
| { kind: "record"; fields: Record<string, QxValueType> }
|
||||
| { kind: "object-ref"; expectation: unknown }
|
||||
| { kind: "optional" | "list"; value: QxValueType };
|
||||
export type QxOperationSpec = { id: string; inputType: QxValueType; outputType: QxValueType };
|
||||
@@ -37,7 +38,14 @@ export type QxMessages = Record<string, MessageBinding<any>>;
|
||||
// Conversion belongs at the binding boundary. It does not add orchestrator validation.
|
||||
export const decodeQxValue = (type: QxValueType, value: Value | undefined, messages: QxMessages): any => {
|
||||
if (type.kind === "builtin" && type.name === "unit") return null;
|
||||
if (type.kind === "optional" && !value) return null;
|
||||
if (!value) throw new Error("Missing QX wire value");
|
||||
if (type.kind === "record") {
|
||||
if (value.kind.case !== "objectValue") throw new Error("Expected QX record");
|
||||
const fields = value.kind.value.fields;
|
||||
if (Object.keys(fields).some((name) => !Object.hasOwn(type.fields, name))) throw new Error("Unexpected QX record field");
|
||||
return Object.fromEntries(Object.entries(type.fields).map(([name, field]) => [name, decodeQxValue(field, fields[name], messages)]));
|
||||
}
|
||||
if (type.kind === "optional") return value.kind.case === "nullValue" ? null : decodeQxValue(type.value, value, messages);
|
||||
if (type.kind === "list") {
|
||||
if (value.kind.case !== "listValue") throw new Error("Expected QX list");
|
||||
@@ -74,6 +82,15 @@ const requireMessage = (messages: QxMessages, id: string) => {
|
||||
};
|
||||
export const encodeQxValue = (type: QxValueType, value: any, messages: QxMessages): Value => {
|
||||
if (type.kind === "builtin" && type.name === "unit") return jsToProtoValue(null);
|
||||
if (type.kind === "record") {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected QX record");
|
||||
if (Object.keys(value).some((name) => !Object.hasOwn(type.fields, name))) throw new Error("Unexpected QX record field");
|
||||
const fields = Object.fromEntries(Object.entries(type.fields).map(([name, field]) => {
|
||||
if (!Object.hasOwn(value, name) && field.kind !== "optional") throw new Error(`Missing QX record field ${name}`);
|
||||
return [name, encodeQxValue(field, value[name] ?? (field.kind === "optional" ? null : value[name]), messages)];
|
||||
}));
|
||||
return create(ValueSchema, {kind: {case: "objectValue", value: create(ObjectValueSchema, {fields})}});
|
||||
}
|
||||
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); }
|
||||
@@ -87,14 +104,14 @@ export const encodeQxValue = (type: QxValueType, value: any, messages: QxMessage
|
||||
};
|
||||
|
||||
const inputValue = (context: RuntimeContext, type: QxValueType) => {
|
||||
if (type.kind === "message") return create(ValueSchema, { kind: { case: "objectValue",
|
||||
if (type.kind === "message" || type.kind === "record") return create(ValueSchema, { kind: { case: "objectValue",
|
||||
value: create(ObjectValueSchema, { fields: context.inputProto }) } });
|
||||
return context.inputProto.value;
|
||||
};
|
||||
const inputFields = (type: QxValueType, value: unknown, messages: QxMessages): Record<string, unknown> => {
|
||||
if (type.kind === "builtin" && type.name === "unit") return {};
|
||||
const encoded = encodeQxValue(type, value, messages);
|
||||
if (type.kind === "message") {
|
||||
if (type.kind === "message" || type.kind === "record") {
|
||||
if (encoded.kind.case !== "objectValue") throw new Error("Message inputs must encode an object value");
|
||||
return Object.fromEntries(Object.entries(encoded.kind.value.fields).map(([key, entry]) => [key, liveValue(entry)]));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user