Package ordinary PresentationMount contracts and typed React hosting
This commit is contained in:
@@ -37,8 +37,8 @@ export function validatePresentations(workspace: WorkspaceRevision): CapabilityV
|
|||||||
?.modules?.find((entry) => entry.id === presentation.componentId);
|
?.modules?.find((entry) => entry.id === presentation.componentId);
|
||||||
if (module) {
|
if (module) {
|
||||||
const contract = moduleContract(workspace.interfaceImports, module);
|
const contract = moduleContract(workspace.interfaceImports, module);
|
||||||
if (contract?.contract !== "org.quixos.react.subject-only/1")
|
if (!contract || !["org.quixos.react.subject-only/1", "org.quixos.react.component/2"].includes(contract.contract))
|
||||||
fail(`${path}.componentId`, "Standalone presentation requires a subject-only module contract");
|
fail(`${path}.componentId`, "Standalone presentation requires a supported React presentation contract");
|
||||||
if (contract?.subject?.kind !== "interface" || contract.subject.interfaceRevisionId !== presentation.subject)
|
if (contract?.subject?.kind !== "interface" || contract.subject.interfaceRevisionId !== presentation.subject)
|
||||||
fail(`${path}.subject`, "Component requires its exact declared subject interface");
|
fail(`${path}.subject`, "Component requires its exact declared subject interface");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -211,6 +211,19 @@ export function generateComponentClientBindings(plan: ExecutionPlan, packageRevi
|
|||||||
const subjects = Object.fromEntries(components.map((component) => [component.displayName, find(component.subject)]));
|
const subjects = Object.fromEntries(components.map((component) => [component.displayName, find(component.subject)]));
|
||||||
const props = (iface: string, member: string) =>
|
const props = (iface: string, member: string) =>
|
||||||
`ExtraModuleProps<${JSON.stringify(JSON.stringify([iface, member]))}>`;
|
`ExtraModuleProps<${JSON.stringify(JSON.stringify([iface, member]))}>`;
|
||||||
|
// Preserve closed type arguments for the artifact consumer. Interpretation of
|
||||||
|
// a particular module contract belongs to that consumer, not this compiler.
|
||||||
|
const moduleArguments = (iface: InterfaceRevision) => `[${(iface.application?.arguments ?? []).map(argument => {
|
||||||
|
if (argument.kind === "value") return tsType(argument.type);
|
||||||
|
const target = argument.target;
|
||||||
|
const reference = target.kind === "atom" ? `ManagedReference<"atom", ${JSON.stringify(target.atomId)}>` : `ManagedReference<"interface", ${JSON.stringify(target.interfaceRevisionId)}>`;
|
||||||
|
const fields = target.kind === "interface" ? find(target.interfaceRevisionId).members.flatMap(member => {
|
||||||
|
if ((member.kind !== "value" && member.kind !== "relationship") || !member.queryRead) return [];
|
||||||
|
const getter = member.operations.find(operation => operation.displayName === (member.kind === "value" ? "get" : "resolve"));
|
||||||
|
return getter ? [`readonly ${JSON.stringify(member.displayName)}: ${tsType(getter.outputType)}`] : [];
|
||||||
|
}) : [];
|
||||||
|
return `{readonly reference: ${reference}; readonly values: {${fields.join(";")}}}`;
|
||||||
|
}).join(",")}]`;
|
||||||
const interfaces = generateInterfaceClientBindings(imports, "@quixos/camino-react", props);
|
const interfaces = generateInterfaceClientBindings(imports, "@quixos/camino-react", props);
|
||||||
const subjectBindings = generateInterfaceClientBindings(subjects, "@quixos/camino-react", props)
|
const subjectBindings = generateInterfaceClientBindings(subjects, "@quixos/camino-react", props)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
@@ -248,7 +261,7 @@ export function generateComponentClientBindings(plan: ExecutionPlan, packageRevi
|
|||||||
.map((component) => {
|
.map((component) => {
|
||||||
const module = modules.find((module) => module.id === component.id)!;
|
const module = modules.find((module) => module.id === component.id)!;
|
||||||
const key = JSON.stringify([module.interfaceRevisionId, module.memberId]);
|
const key = JSON.stringify([module.interfaceRevisionId, module.memberId]);
|
||||||
return `${JSON.stringify(component.displayName)}: ComponentType<ComponentProps<${JSON.stringify(component.subject)}> & ExtraModuleProps<${JSON.stringify(key)}>>;`;
|
return `${JSON.stringify(component.displayName)}: ComponentType<ComponentProps<${JSON.stringify(component.subject)}, ${JSON.stringify(module.contract)}, ${moduleArguments(find(module.interfaceRevisionId))}> & ExtraModuleProps<${JSON.stringify(key)}>>;`;
|
||||||
})
|
})
|
||||||
.join("\n") +
|
.join("\n") +
|
||||||
"\n}\n"
|
"\n}\n"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const meta = () => selection("_camino", { selection: [selection("ref")] });
|
|||||||
export const inspectionPackage = "org.quixos.inspection/1";
|
export const inspectionPackage = "org.quixos.inspection/1";
|
||||||
export interface InspectionTemplate {
|
export interface InspectionTemplate {
|
||||||
interface: string;
|
interface: string;
|
||||||
members: { id: string; name: string; key: string; flag: string; lane: "local" | "server"; many: boolean }[];
|
members: { id: string; name: string; key: string; flag: string; lane: "local" | "server"; many: boolean; relationship: boolean }[];
|
||||||
inputs?: PublicQueryContract;
|
inputs?: PublicQueryContract;
|
||||||
view?: PublicQueryContract;
|
view?: PublicQueryContract;
|
||||||
server?: PublicQueryContract;
|
server?: PublicQueryContract;
|
||||||
@@ -81,7 +81,7 @@ export function compileInspectionTemplates(plan: ExecutionPlan) {
|
|||||||
// Only native edges promise the relationship traversal used by the interpreter.
|
// Only native edges promise the relationship traversal used by the interpreter.
|
||||||
if (member.kind === "relationship" && providers.some((op) => op!.binding.kind !== "edge")) continue;
|
if (member.kind === "relationship" && providers.some((op) => op!.binding.kind !== "edge")) continue;
|
||||||
if (!complete && (lane === "local" || !portable))
|
if (!complete && (lane === "local" || !portable))
|
||||||
template.members.push({ id: member.id, name: member.displayName, key, flag, lane, many });
|
template.members.push({ id: member.id, name: member.displayName, key, flag, lane, many, relationship: member.kind === "relationship" });
|
||||||
inputFields[flag] = scalar("bool");
|
inputFields[flag] = scalar("bool");
|
||||||
const field = selection(member.displayName, {
|
const field = selection(member.displayName, {
|
||||||
key,
|
key,
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ function componentDeclarations(workspace: WorkspaceRevision, pkg: WorkspaceRevis
|
|||||||
...(pkg.modules ?? []).flatMap((module) => {
|
...(pkg.modules ?? []).flatMap((module) => {
|
||||||
const contract = moduleContract(workspace.interfaceImports, module)!;
|
const contract = moduleContract(workspace.interfaceImports, module)!;
|
||||||
if (
|
if (
|
||||||
!["org.quixos.react.component/1", "org.quixos.react.subject-only/1"].includes(contract.contract) ||
|
!["org.quixos.react.component/1", "org.quixos.react.component/2", "org.quixos.react.subject-only/1"].includes(contract.contract) ||
|
||||||
contract.subject?.kind !== "interface"
|
contract.subject?.kind !== "interface"
|
||||||
)
|
)
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user