Correct Reader producer completion and prepare only the active source
Route durable completion to the initiating PresentationMount, guarded against stale navigation and unrelated fan-out. Publish Gutenberg passages directly, preserve unfinished manual drafts, and open completed documents in the main reader pane. Prepare the selected provider and explicitly opened inspector only. Consolidate Studio factory contracts so optional execution placement is determined by implementations. Support typed stateful presentation props and local-module role selection in the React build layer. Update integration, browser, portal, and authoring documentation.
This commit is contained in:
@@ -165,6 +165,7 @@ let
|
||||
interface = "${compile dependency}/candidate.json";
|
||||
generator = protocol;
|
||||
typescript = runtimePackages.camino-replica-core.dependencies;
|
||||
reactSdk = runtimePackages.camino-react;
|
||||
dependencies = buildDependencies dependency;
|
||||
})
|
||||
]
|
||||
|
||||
@@ -101,13 +101,28 @@ export function presentationDeclarations(
|
||||
if (!child.name || names.has(child.name) || !child.slot || !child.root)
|
||||
throw Error(`${name}: child requires a unique name, slot and root`);
|
||||
names.add(child.name);
|
||||
// A local module identifies a closed role, including its generic arguments.
|
||||
// This avoids inventing an import alias for a generic application solely
|
||||
// to select that role in the React preparation manifest.
|
||||
let role: ReactChildDeclaration["role"];
|
||||
if (child.role) {
|
||||
const authored = object(child.role, `${name}.${child.name} role`);
|
||||
if (typeof authored.module === "string") {
|
||||
const target = pkg.modules?.find((entry) => entry.displayName === authored.module);
|
||||
if (!target || Object.keys(authored).length !== 1)
|
||||
throw Error(`${name}.${child.name}: expected a local module role`);
|
||||
role = { interface: target.interfaceRevisionId, member: target.memberId };
|
||||
} else {
|
||||
role = { ...child.role, interface: imported(child.role.interface) };
|
||||
}
|
||||
}
|
||||
if (child.each && child.with) throw Error(`${name}.${child.name}: cannot combine each and with`);
|
||||
if (child.each && (!Number.isSafeInteger(child.each.limit) || child.each.limit < 1))
|
||||
throw Error(`${name}.${child.name}: finite positive collection limit required`);
|
||||
return {
|
||||
...child,
|
||||
subject: imported(child.subject),
|
||||
...(child.role ? { role: { ...child.role, interface: imported(child.role.interface) } } : {}),
|
||||
...(role ? { role } : {}),
|
||||
};
|
||||
});
|
||||
artifact.modules.push({ module: module.id, composition, children: parsed });
|
||||
|
||||
@@ -121,6 +121,19 @@ async function fixture(replacement = "First", change?: (artifact: ReactPreparati
|
||||
return { workspace: workspace.workspace, pkg: workspace.workspace.packageImports[0]!, authored, interfaces };
|
||||
}
|
||||
|
||||
test("a local module names its checked role without a synthetic import alias", async () => {
|
||||
const { pkg, authored, interfaces } = await fixture();
|
||||
const source = structuredClone(authored) as any;
|
||||
source.modules.LibraryCard.children[0].role = { module: "First" };
|
||||
const declared = presentationDeclarations(JSON.stringify(source), pkg, [...interfaces.values()]);
|
||||
assert.deepEqual(declared.artifact.modules[0]!.children[0]!.role, { interface: "row-role@1", member: "row-render" });
|
||||
source.modules.LibraryCard.children[0].role = { module: "Missing" };
|
||||
assert.throws(
|
||||
() => presentationDeclarations(JSON.stringify(source), pkg, [...interfaces.values()]),
|
||||
/local module role/,
|
||||
);
|
||||
});
|
||||
|
||||
test("workspace expands distinct providers' private inputs without parent field declarations", async () => {
|
||||
const { pkg } = await fixture();
|
||||
const parent = pkg.checkedQueries!.find((query) => query.declaration.id === "react:input:library-card:data")!;
|
||||
|
||||
Reference in New Issue
Block a user