Implement main-thread Camino sessions and synchronous CRDT editing
This commit is contained in:
@@ -11,6 +11,8 @@ export const generateClientContracts = (
|
||||
throw new Error("Host contracts require closed interface applications, not generic definitions");
|
||||
const type = (value: ValueType): string => {
|
||||
switch (value.kind) {
|
||||
case "variant":
|
||||
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||
case "builtin":
|
||||
return value.name === "unit" ? "undefined" : "string";
|
||||
case "scalar":
|
||||
|
||||
@@ -49,6 +49,8 @@ export const genericImplementationType = (
|
||||
const value = (type: ValueTypeExpression, scope: Scope, depth = 0): string => {
|
||||
if (depth > 128) throw new Error("Generic type expansion exceeds depth limit");
|
||||
switch (type.kind) {
|
||||
case "variant":
|
||||
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||
case "parameter": {
|
||||
const bound = scope.arguments.get(type.parameterId);
|
||||
if (bound) return bound;
|
||||
|
||||
@@ -112,6 +112,8 @@ export const generateTypeScriptBindings = (
|
||||
const messages = new Map<string, string>();
|
||||
const type = (value: ValueType): string => {
|
||||
switch (value.kind) {
|
||||
case "variant":
|
||||
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||
case "builtin":
|
||||
return value.name === "unit" ? "null" : "QxWatchHandle";
|
||||
case "scalar":
|
||||
@@ -340,6 +342,7 @@ export const generateTypeScriptBindings = (
|
||||
// A used port still fails above if its own required codec is missing.
|
||||
const hasCodec = (value: ValueType): boolean => {
|
||||
if (value.kind === "message") return !!options.messages?.[value.descriptorId];
|
||||
if (value.kind === "variant") return false;
|
||||
if (value.kind === "record") return Object.values(value.fields).every(hasCodec);
|
||||
if (value.kind === "list" || value.kind === "optional") return hasCodec(value.value);
|
||||
return true;
|
||||
|
||||
@@ -29,6 +29,17 @@ const expectation = (e: ObjectExpectation) => ({
|
||||
/** The protocol IR and replica wire schema intentionally share structure, not reference identity. */
|
||||
export function portableValueSchema(type: ValueType): unknown {
|
||||
switch (type.kind) {
|
||||
case "variant":
|
||||
return {
|
||||
kind: "variant",
|
||||
id: type.id,
|
||||
cases: Object.fromEntries(
|
||||
Object.entries(type.cases).map(([label, entry]) => [
|
||||
label,
|
||||
{ id: entry.id, value: portableValueSchema(entry.value) },
|
||||
]),
|
||||
),
|
||||
};
|
||||
case "change":
|
||||
return {
|
||||
kind: "record",
|
||||
@@ -65,6 +76,10 @@ export function portableValueSchema(type: ValueType): unknown {
|
||||
export function tsType(type: ValueType): string {
|
||||
portableValueSchema(type);
|
||||
switch (type.kind) {
|
||||
case "variant":
|
||||
return `(${Object.entries(type.cases)
|
||||
.map(([label, entry]) => `{ readonly kind: ${q(label)}; readonly value: ${tsType(entry.value)} }`)
|
||||
.join(" | ")})`;
|
||||
case "change":
|
||||
return `PreparedChange<${tsType(type.value)}>`;
|
||||
case "scalar":
|
||||
|
||||
@@ -15,6 +15,8 @@ export function generateReactBindings(
|
||||
const declarations: string[] = [];
|
||||
const type = (value: ValueType): string => {
|
||||
switch (value.kind) {
|
||||
case "variant":
|
||||
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||
case "builtin":
|
||||
if (value.name !== "unit") throw new Error(`Unsupported React props builtin ${value.name}`);
|
||||
return "null";
|
||||
@@ -85,6 +87,7 @@ export function generateReactBindings(
|
||||
const browserValue = (value: ValueType, seen: Set<string>): boolean => {
|
||||
if (value.kind === "message") return false;
|
||||
if (value.kind === "builtin") return value.name === "unit";
|
||||
if (value.kind === "variant") return false;
|
||||
if (value.kind === "record") return Object.values(value.fields).every((field) => browserValue(field, seen));
|
||||
if (value.kind === "list" || value.kind === "optional") return browserValue(value.value, seen);
|
||||
if (value.kind === "object-ref" && value.expectation.kind === "interface") {
|
||||
|
||||
Reference in New Issue
Block a user