Complete prepared presentation build and runtime integration
This commit is contained in:
@@ -46,7 +46,7 @@ export type CapabilityRepositoryResolver = (
|
||||
/** An explicitly selected build frontend supplies ordinary query sources. The
|
||||
* assembler never chooses a frontend by inspecting an interface/contract ID.
|
||||
* Generated sources go through the same query and imported-fragment checks. */
|
||||
export type PackageQueryGenerator = (input: {
|
||||
export type PackageQueryGenerator = ((input: {
|
||||
readonly package: Readonly<PackageRevision>;
|
||||
readonly interfaces: readonly InterfaceRevision[];
|
||||
readonly readSource: (name: string) => Promise<string>;
|
||||
@@ -54,7 +54,12 @@ export type PackageQueryGenerator = (input: {
|
||||
readonly queries: readonly QueryDeclaration[];
|
||||
readonly documents: ReadonlyMap<string, string>;
|
||||
readonly compositions?: readonly QueryComposition[];
|
||||
}>;
|
||||
readonly artifacts?: Readonly<Record<string, unknown>>;
|
||||
}>) & {
|
||||
/** Optional explicit frontend stage after workspace binding resolution.
|
||||
* Its output still passes normal workspace/query validation. */
|
||||
completeWorkspace?: (workspace: WorkspaceRevision) => Promise<void>;
|
||||
};
|
||||
|
||||
export type ResolvedCapabilityResource = {
|
||||
key: string;
|
||||
@@ -297,6 +302,7 @@ const createResourceGraphResolver = (
|
||||
readSource: (name) => readRepositorySource(snapshot.directory, name),
|
||||
});
|
||||
if (generated) {
|
||||
if (generated.artifacts) compiled.resource.revision.buildArtifacts = structuredClone(generated.artifacts);
|
||||
const queries = [...(compiled.resource.revision.queries ?? [])];
|
||||
for (const query of generated.queries) {
|
||||
if (queries.some((entry) => entry.id === query.id || entry.displayName === query.displayName))
|
||||
@@ -534,6 +540,7 @@ export const compileWorkspaceRepository = async (options: {
|
||||
: {}),
|
||||
...(options.sourceRootCommit ? { sourceRootCommit: options.sourceRootCommit } : {}),
|
||||
};
|
||||
await options.generateQueries?.completeWorkspace?.(workspace);
|
||||
const checked = compileWorkspaceRevision(workspace);
|
||||
if (!checked.ok) {
|
||||
throw new Error(
|
||||
|
||||
@@ -59,12 +59,26 @@ export async function scaffoldComponent(
|
||||
const configBefore = await fs.readFile(path.join(root, configFile), "utf8");
|
||||
const config = JSON.parse(configBefore);
|
||||
config.queryFrontends = [...new Set([...(config.queryFrontends ?? []), "react"])];
|
||||
const preparationFile = prefix + "quixos.react.json";
|
||||
let preparationBefore: string | undefined;
|
||||
try {
|
||||
preparationBefore = await fs.readFile(path.join(root, preparationFile), "utf8");
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
||||
}
|
||||
const preparation = preparationBefore ? JSON.parse(preparationBefore) : { modules: {} };
|
||||
if (Object.hasOwn(preparation.modules, spec.name)) throw Error("Duplicate presentation preparation");
|
||||
preparation.modules[spec.name] = { inputs: [] };
|
||||
const preparationText = JSON.stringify(preparation, null, 2) + "\n";
|
||||
return {
|
||||
kind: "package",
|
||||
source: spec.source,
|
||||
resourceRoot: spec.directory,
|
||||
validation: "syntax",
|
||||
files: [
|
||||
preparationBefore === undefined
|
||||
? { file: preparationFile, create: preparationText }
|
||||
: { file: preparationFile, expected: preparationBefore, replace: preparationText },
|
||||
{ file: configFile, expected: configBefore, replace: JSON.stringify(config, null, 2) + "\n" },
|
||||
{
|
||||
file: prefix + "package.qx",
|
||||
@@ -79,7 +93,7 @@ export async function scaffoldComponent(
|
||||
{ file: prefix + filename, expected: before, replace: after },
|
||||
{
|
||||
file: prefix + `src/components/${spec.name}.tsx`,
|
||||
create: `import type { Modules } from "../gen/client.js";\n\n// Add explicit query admission and named interface operations from the generated client.\nexport const ${spec.name}: Modules[${JSON.stringify(spec.name)}] = ({subject}) => <p>{subject.object}</p>;\n`,
|
||||
create: `import type { Modules } from "../gen/client.js";\n\n// Declare initial query inputs in quixos.react.json; use interfaces for actions.\nexport const ${spec.name}: Modules[${JSON.stringify(spec.name)}] = ({subject}) => <p>{subject.object}</p>;\n`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -9,5 +9,10 @@ export async function loadQueryGenerator(filename?: string): Promise<PackageQuer
|
||||
const module = await import(pathToFileURL(path.resolve(filename)).href);
|
||||
if (typeof module.generatePackageQueries !== "function")
|
||||
throw new Error("Query generator must export generatePackageQueries");
|
||||
return module.generatePackageQueries;
|
||||
const generator: PackageQueryGenerator = module.generatePackageQueries;
|
||||
if (module.completeWorkspace !== undefined) {
|
||||
if (typeof module.completeWorkspace !== "function") throw new Error("completeWorkspace must be a function");
|
||||
generator.completeWorkspace = module.completeWorkspace;
|
||||
}
|
||||
return generator;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user