Implement workspace evolution, migrations, and runtime continuity

Enable evolution by default for source-backed workspaces. Add stable
conformance ownership, semantic-major review, candidate typechecking,
and durable fenced cutover with explicit migrations and forward recovery.

Independently supervise package runtimes so unchanged resource owners keep
their processes and connections across cutover. Add scoped invocation
authority, resource sessions, and typed callback rebinding.

Wire opaque object references through generated bindings and RPCs. Add
canonical relationship sets, keyed maps, and ordered lists with scoped
transactional mutations, revision checks, and inverse consistency. Support
planned cascade deletion, protection, tombstones, and lifecycle foundations.

Add journaled structural edits, package/function/migration scaffolding,
managed repository creation, and resumable bottom-up dependency pin
publication. Document lifetime boundaries, revision pinning, prototype
compatibility policy, commands, and deferred work.

Validate with 210 tests, user-systemd process/connection continuity,
generated-package TypeScript checks, and Nix host/protocol checks.
TTL handoff, physical reclamation, general multi-step migrations, and
root-systemd migration isolation acceptance remain deferred.
This commit is contained in:
Timothy J. Aveni
2026-09-10 18:27:41 -07:00
parent 1e25f391e7
commit 483bc68a94
38 changed files with 3790 additions and 1463 deletions
+7 -4
View File
@@ -64,6 +64,8 @@ export const generateTypeScriptBindings = (
if (primitive === "connect" || primitive === "disconnect") return [primitive, `(target: ${ref(requirement.target)}) => Promise<void>`];
throw new Error(`Edge primitive ${primitive} is not supported by the TypeScript runtime binding yet`);
});
if (requirement.primitives.includes("resolve")) methods.push(["collection", `() => Promise<RelationshipCollection<${ref(requirement.target)}>>`]);
if (["resolve", "connect", "disconnect"].every((primitive) => requirement.primitives.includes(primitive as "resolve"))) methods.push(["replace", `(entries: RelationshipEntry<${ref(requirement.target)}>[], expectedRevision: bigint) => Promise<RelationshipCollection<${ref(requirement.target)}>>`]);
return { type: object(methods), spec: { kind: "edge", id: entry.id, primitives: requirement.primitives } };
}
case "interface": {
@@ -97,9 +99,10 @@ export const generateTypeScriptBindings = (
entry.kind === "operation" && entry.receiverRequirement.kind === "exact-atom" ?
ref({ kind: "atom", atomId: entry.receiverRequirement.atomId }) :
entry.kind === "operation" && entry.receiverRequirement.kind === "all-interfaces" ?
`QxObjectRef<${entry.receiverRequirement.interfaceRevisionIds.map((id) => q(`interface:${id}`)).join(" | ") || "never"}>` : "string";
contexts.push([entry.displayName, object([["objectId", receiver], ["input", type(entry.inputType)],
["ports", object(ports.map((port) => [port.name, port.type]))]])]);
`QxObjectRef<${entry.receiverRequirement.interfaceRevisionIds.map((id) => q(`interface:${id}`)).join(" | ") || "never"}>` : "QxObjectRef<string>";
const contextShape = object([["objectId", receiver], ["input", type(entry.inputType)],
["ports", object(ports.map((port) => [port.name, port.type]))]]);
contexts.push([entry.displayName, `${contextShape} & QxContextLifecycle<${contextShape} & {signal?: AbortSignal}>`]);
const event = entry.kind === "operation" ? entry.eventType : undefined;
const contextType = `Contexts[${q(entry.displayName)}]`;
const outputType = type(event ?? entry.outputType);
@@ -115,7 +118,7 @@ export const generateTypeScriptBindings = (
return `import { ${binding.export} as ${alias} } from ${q(binding.module)};`;
});
const signatures = `${object(contexts)} ${object(handlers)}`;
const typeImports = ["BindingValue", "QxObjectRef", "QxWatchHandle", "QxHandler", "QxDerived"].filter((name) => new RegExp(`\\b${name}\\b`).test(signatures));
const typeImports = ["BindingValue", "QxObjectRef", "QxWatchHandle", "QxHandler", "QxDerived", "QxContextLifecycle", "RelationshipCollection", "RelationshipEntry"].filter((name) => new RegExp(`\\b${name}\\b`).test(signatures));
return `// Generated by quixos-codegen-ts. Do not edit. Binding ABI version 1.\n` +
`import { ${exports.length ? "bindQxHandler, " : ""}${[...typeImports, "QxHandlerSpec", "QxMessages"].map((name) => `type ${name}`).join(", ")} } from ${q(options.runtimeModule ?? "@quixos/camino-package-runtime")};\n` +
imports.join("\n") + `\nexport const packageRevisionId = ${q(pkg.revisionId)};\n` +
+18 -1
View File
@@ -1,9 +1,11 @@
import { readFile } from "node:fs/promises";
import { readFile, realpath } from "node:fs/promises";
import path from "node:path";
import { loadQxSources, resolveQxSources } from "./source-loader.js";
import {
capabilityId,
compileWorkspaceRevision,
validateMigrationCatalog,
contentDigest,
type AtomDefinition,
type CompiledWorkspaceRevision,
type InterfaceRevision,
@@ -228,6 +230,21 @@ const createResourceGraphResolver = (
);
}
assertImportsMatchLock(manifestPath, compiled.resource.imports, lockResult.lock.resources);
if (compiled.resource.kind === "package") {
let catalogText: string | undefined;
try { catalogText = await readFile(path.join(snapshot.directory, "quixos.migrations.json"), "utf8"); }
catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
if (catalogText !== undefined) {
const catalog = validateMigrationCatalog(JSON.parse(catalogText), new Set(compiled.resource.revision.exports.map((entry) => entry.id)));
const root = await realpath(snapshot.directory);
for (const migration of catalog.migrations) {
const implementation = await realpath(path.join(root, migration.implementation.file));
if (!implementation.startsWith(`${root}${path.sep}`)) throw new Error("Migration implementation escapes its package");
if (contentDigest(await readFile(implementation, "utf8")) !== migration.implementation.digest) throw new Error(`Migration implementation digest mismatch: ${migration.id}`);
}
compiled.resource.revision.migrationCatalog = catalog;
}
}
return {
key,
kind: locked.kind,
+179
View File
@@ -0,0 +1,179 @@
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { execFile as execFileCallback } from "node:child_process";
import { promisify } from "node:util";
import { createHash } from "node:crypto";
import { compileWorkspaceRepository, compileCapabilityResourceRepository } from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import { contentDigest, planEvolution, type EvolutionReview, type WorkspaceRevision } from "../capability-model/index.js";
import { bindingSchema, generateTypeScriptBindings, type BindingSchema, type TypeScriptBindingOptions } from "../bindings/index.js";
const execFile = promisify(execFileCallback);
const bytesDigest = (value: Uint8Array) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
export const localResourceSnapshots = async (root: string, filename?: string): Promise<{resources: {kind: string; repository: string; commit: string; directory: string}[]}> => {
if (filename) {
const document = JSON.parse(await fs.readFile(filename, "utf8"));
return {resources: document.resources.map((entry: {directory: string}) => ({...entry, directory: path.resolve(path.dirname(filename), entry.directory)}))};
}
let directory = await fs.realpath(root);
for (;;) {
let graphText: string | undefined;
try {graphText = await fs.readFile(path.join(directory, ".quixos/resource-graph.json"), "utf8");}
catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;}
if (graphText !== undefined) {
const graph = JSON.parse(graphText);
const resources = [];
for (const entry of graph.resources) {
const location = await fs.realpath(path.resolve(directory, entry.directory));
if (!location.startsWith(`${directory}/resources/`)) throw new Error("Workbench resource escapes managed directory");
resources.push({kind: entry.kind, ...entry.source, directory: location});
}
return {resources};
}
const parent = path.dirname(directory);
if (parent === directory) return {resources: []};
directory = parent;
}
};
/** Copy actual authoring files without snapshotting jj or creating a Git commit. */
export const snapshotRepository = async (source: string, destination: string) => {
const root = await fs.realpath(source);
const files = async () => (await execFile("git", ["-C", root, "ls-files", "--cached", "--others", "--exclude-standard", "-z"], { maxBuffer: 16 * 1024 * 1024 })).stdout.split("\0").filter(Boolean).sort();
const names = [...new Set(await files())];
if (names.length > 50_000) throw new Error("Candidate source exceeds 50000 files");
const contents: {name: string; digest: string; mode: number}[] = [];
let bytes = 0;
await fs.mkdir(destination, { recursive: true, mode: 0o700 });
for (const name of names) {
if (path.isAbsolute(name) || name.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".jj")) throw new Error("Invalid candidate source path");
const file = path.join(root, name);
let metadata;
try { metadata = await fs.lstat(file); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") continue; throw error; }
if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(file)).startsWith(`${root}${path.sep}`)) throw new Error(`Candidate source must be a regular file: ${name}`);
const data = await fs.readFile(file);
bytes += data.length;
if (bytes > 128 * 1024 * 1024) throw new Error("Candidate source exceeds 128 MiB");
contents.push({ name, digest: bytesDigest(data), mode: metadata.mode & 0o777 });
await fs.mkdir(path.dirname(path.join(destination, name)), { recursive: true });
await fs.writeFile(path.join(destination, name), data, { flag: "wx", mode: metadata.mode & 0o777 });
}
if (JSON.stringify([...new Set(await files())]) !== JSON.stringify(names)) throw new Error("Source files changed during candidate snapshot");
for (const entry of contents) if (bytesDigest(await fs.readFile(path.join(root, entry.name))) !== entry.digest) throw new Error(`Source changed during candidate snapshot: ${entry.name}`);
return { source: root, directory: destination, treeDigest: contentDigest(contents), files: contents };
};
export const checkResourceCandidate = async (options: {root: string; output: string; kind: "package" | "interface"; source: {repository: string; commit: string}; snapshotMap?: string; publishedOnly?: boolean}) => {
await fs.mkdir(options.output, {mode: 0o700});
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-resource-check-"));
const blockers: string[] = [];
let diagnostics = "";
let treeDigest: string | undefined;
try {
const root = await snapshotRepository(options.root, path.join(temporary, "root"));
treeDigest = root.treeDigest;
const map = options.publishedOnly ? {resources: []} : await localResourceSnapshots(options.root, options.snapshotMap);
const resources = [];
for (const [index, entry] of map.resources.entries()) {
const snapshot = await snapshotRepository(entry.directory, path.join(temporary, `dependency-${index}`));
resources.push({...entry, directory: snapshot.directory});
}
const snapshotMap = path.join(temporary, "snapshots.json");
await fs.writeFile(snapshotMap, JSON.stringify({resources}));
const resolveResource = await createGitCapabilityResolver({checkoutRoot: path.join(temporary, "resolved"), snapshotMap});
const compiled = await compileCapabilityResourceRepository({rootDirectory: root.directory, kind: options.kind, source: {resolver: "git", ...options.source}, resolveResource});
if (compiled.resource.kind === "package") {
const configuration = JSON.parse(await fs.readFile(path.join(root.directory, "quixos.check.json"), "utf8"));
const output = configuration.bindingOutput as string;
if (configuration.backend !== "typescript" || !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.ts$/.test(output)) throw new Error("Unsupported candidate checker configuration");
const modules = path.join(root.source, "node_modules");
await fs.access(path.join(modules, ".bin/tsc"));
await fs.symlink(modules, path.join(root.directory, "node_modules"), "dir");
const destination = path.join(root.directory, output);
await fs.mkdir(path.dirname(destination), {recursive: true});
await fs.writeFile(destination, generateTypeScriptBindings(bindingSchema(compiled), compiled.resource.revision.revisionId, configuration.options));
diagnostics = (await execFile(path.join(modules, ".bin/tsc"), ["--noEmit", "--pretty", "false"], {cwd: root.directory, maxBuffer: 16 * 1024 * 1024})).stdout;
}
await fs.writeFile(path.join(options.output, "candidate.json"), JSON.stringify(compiled.resource, null, 2));
} catch (error) {
blockers.push(error instanceof Error ? error.message : String(error));
diagnostics += (error as {stdout?: string; stderr?: string}).stdout ?? "";
diagnostics += (error as {stderr?: string}).stderr ?? "";
} finally {await fs.rm(temporary, {recursive: true, force: true});}
const result = {candidateOnly: true, activationEvidence: false, treeDigest, blockers, diagnostics};
await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2));
return result;
};
export const checkWorkspaceCandidate = async (options: { root: string; output: string; snapshotMap?: string; baseline?: string; reviews?: string }) => {
// A new output directory is the whole artifact boundary; never overwrite a prior check.
await fs.mkdir(options.output, { mode: 0o700 });
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "quixos-candidate-"));
const blockers: string[] = [];
const checks: unknown[] = [];
const snapshots = [];
try {
const root = await snapshotRepository(options.root, path.join(temporary, "root"));
snapshots.push(root);
const map = await localResourceSnapshots(options.root, options.snapshotMap);
const resources = [];
for (const [index, entry] of map.resources.entries()) {
const source = entry.directory;
const snapshot = await snapshotRepository(source, path.join(temporary, `resource-${index}`));
snapshots.push(snapshot);
resources.push({ ...entry, directory: snapshot.directory });
}
const mapFile = path.join(temporary, "snapshots.json");
await fs.writeFile(mapFile, JSON.stringify({ resources }));
const resolveResource = await createGitCapabilityResolver({ checkoutRoot: path.join(temporary, "resolved"), snapshotMap: mapFile });
const compiled = await compileWorkspaceRepository({ rootDirectory: root.directory, resolveResource });
const baseline = options.baseline ? JSON.parse(await fs.readFile(options.baseline, "utf8")) as WorkspaceRevision : null;
const reviews = options.reviews ? JSON.parse(await fs.readFile(options.reviews, "utf8")) as EvolutionReview[] : [];
const evolution = planEvolution(baseline, compiled.workspace, { reviews });
blockers.push(...evolution.blockers);
const schema: BindingSchema = { format: "quixos-bindings", version: 1, interfaces: compiled.workspace.interfaceImports, packages: compiled.workspace.packageImports };
for (const resource of compiled.resources.filter((entry) => entry.kind === "package")) {
if (resource.resource.kind !== "package") continue;
const revision = resource.resource.revision;
let config: { backend: string; bindingOutput: string; options?: TypeScriptBindingOptions };
try { config = JSON.parse(await fs.readFile(path.join(resource.directory, "quixos.check.json"), "utf8")); }
catch { blockers.push(`No candidate checker configured for ${revision.revisionId} (quixos.check.json)`); continue; }
if (config.backend !== "typescript" || !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.ts$/.test(config.bindingOutput)
|| config.bindingOutput.split("/").includes("..")) { blockers.push(`Unsupported checker or binding path for ${revision.revisionId}`); continue; }
const sourceSnapshot = snapshots.find((entry) => entry.directory === resource.directory);
const dependencyRoot = sourceSnapshot?.source ?? resource.directory;
const modules = path.join(dependencyRoot, "node_modules");
try { await fs.access(path.join(modules, ".bin", "tsc")); }
catch { blockers.push(`Missing installed TypeScript checker/dependencies for ${revision.revisionId}; install its locked development dependencies first`); continue; }
if (sourceSnapshot) await fs.symlink(modules, path.join(resource.directory, "node_modules"), "dir");
const generated = generateTypeScriptBindings(schema, revision.revisionId, config.options);
const destination = path.join(resource.directory, config.bindingOutput);
await fs.mkdir(path.dirname(destination), { recursive: true });
await fs.writeFile(destination, generated);
let success = false, diagnostics = "";
try { diagnostics = (await execFile(path.join(modules, ".bin", "tsc"), ["--noEmit", "--pretty", "false", "--listFiles"], { cwd: resource.directory, maxBuffer: 16 * 1024 * 1024 })).stdout; success = true; }
catch (error) { const result = error as Error & {stdout?: string; stderr?: string}; diagnostics = `${result.stdout ?? ""}\n${result.stderr ?? result.message}`; }
const typeInputs = [];
for (const line of diagnostics.split(/\r?\n/)) if (path.isAbsolute(line) && /\.[cm]?tsx?$/.test(line)) {
try { typeInputs.push({file: line, digest: bytesDigest(await fs.readFile(line))}); } catch { success = false; }
}
const checker = await fs.realpath(path.join(modules, ".bin", "tsc"));
const check = { packageRevisionId: revision.revisionId, success, bindingSchemaDigest: contentDigest(schema), generatedDigest: contentDigest(generated),
checkerDigest: contentDigest({ executable: bytesDigest(await fs.readFile(checker)), typeInputs }), diagnostics };
checks.push(check);
if (!success) blockers.push(`Typecheck failed for ${revision.revisionId}`);
}
const result = { schemaVersion: 1, candidateOnly: true, activationEvidence: false,
sourceDigest: contentDigest(snapshots.map(({source, treeDigest}) => ({source, treeDigest}))),
snapshots: snapshots.map(({source, treeDigest}) => ({source, treeDigest})), evolution, checks, blockers,
note: "Local source-tree checks do not certify old Git revisions. Publication must repin the DAG and recheck final immutable artifacts." };
await fs.writeFile(path.join(options.output, "candidate.json"), JSON.stringify(compiled.workspace, null, 2));
await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2));
return result;
} catch (error) {
const result = { schemaVersion: 1, candidateOnly: true, activationEvidence: false, checks, blockers: [...blockers, error instanceof Error ? error.message : String(error)] };
await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2));
return result;
} finally { await fs.rm(temporary, { recursive: true, force: true }); }
};
File diff suppressed because one or more lines are too long
@@ -35,73 +35,78 @@ SOURCE=34
REPOSITORY=35
COMMIT=36
REVISION=37
ID=38
DOC=39
MODE=40
EMITS=41
RECEIVER=42
REQUIRES=43
ANY=44
GET=45
SET=46
WATCH=47
START=48
STOP=49
READ=50
WRITE=51
RESOLVE=52
CONNECT=53
DISCONNECT=54
CALL=55
WATCH_START=56
WATCH_STOP=57
SUBSCRIBE=58
UNSUBSCRIBE=59
OPTIMISTIC_REGISTER=60
CRDT=61
OPTIONAL_ONE=62
EXACTLY_ONE=63
MANY_UNIQUE=64
MANY=65
ORDERED=66
UNIT=67
WATCH_HANDLE=68
MESSAGE=69
ATOM_REF=70
INTERFACE_REF=71
OPTIONAL=72
LIST=73
BOOL=74
BYTES=75
DOUBLE=76
INT32=77
INT64=78
STRING=79
UINT32=80
UINT64=81
TRUE=82
FALSE=83
NULL=84
ARROW=85
COLON=86
SEMI=87
COMMA=88
DOT=89
LBRACE=90
RBRACE=91
LBRACK=92
RBRACK=93
LPAREN=94
RPAREN=95
LT=96
GT=97
INTEGER=98
JSON_NUMBER=99
IDENTIFIER=100
STRING_LITERAL=101
LINE_COMMENT=102
BLOCK_COMMENT=103
WS=104
SEMANTIC_MAJOR=38
ON_DELETE=39
RETAIN_OTHER=40
KEYED=41
PUBLIC_TRAVERSAL=42
ID=43
DOC=44
MODE=45
EMITS=46
RECEIVER=47
REQUIRES=48
ANY=49
GET=50
SET=51
WATCH=52
START=53
STOP=54
READ=55
WRITE=56
RESOLVE=57
CONNECT=58
DISCONNECT=59
CALL=60
WATCH_START=61
WATCH_STOP=62
SUBSCRIBE=63
UNSUBSCRIBE=64
OPTIMISTIC_REGISTER=65
CRDT=66
OPTIONAL_ONE=67
EXACTLY_ONE=68
MANY_UNIQUE=69
MANY=70
ORDERED=71
UNIT=72
WATCH_HANDLE=73
MESSAGE=74
ATOM_REF=75
INTERFACE_REF=76
OPTIONAL=77
LIST=78
BOOL=79
BYTES=80
DOUBLE=81
INT32=82
INT64=83
STRING=84
UINT32=85
UINT64=86
TRUE=87
FALSE=88
NULL=89
ARROW=90
COLON=91
SEMI=92
COMMA=93
DOT=94
LBRACE=95
RBRACE=96
LBRACK=97
RBRACK=98
LPAREN=99
RPAREN=100
LT=101
GT=102
INTEGER=103
JSON_NUMBER=104
IDENTIFIER=105
STRING_LITERAL=106
LINE_COMMENT=107
BLOCK_COMMENT=108
WS=109
'workspace'=1
'fragment'=2
'import'=3
@@ -139,63 +144,68 @@ WS=104
'repository'=35
'commit'=36
'revision'=37
'id'=38
'doc'=39
'mode'=40
'emits'=41
'receiver'=42
'requires'=43
'any'=44
'get'=45
'set'=46
'watch'=47
'start'=48
'stop'=49
'read'=50
'write'=51
'resolve'=52
'connect'=53
'disconnect'=54
'call'=55
'watch-start'=56
'watch-stop'=57
'subscribe'=58
'unsubscribe'=59
'optimistic-register'=60
'crdt'=61
'optional-one'=62
'exactly-one'=63
'many-unique'=64
'many'=65
'ordered'=66
'unit'=67
'watch-handle'=68
'message'=69
'atom-ref'=70
'interface-ref'=71
'optional'=72
'list'=73
'bool'=74
'bytes'=75
'double'=76
'int32'=77
'int64'=78
'string'=79
'uint32'=80
'uint64'=81
'true'=82
'false'=83
'null'=84
'->'=85
':'=86
';'=87
','=88
'.'=89
'{'=90
'}'=91
'['=92
']'=93
'('=94
')'=95
'<'=96
'>'=97
'semantic-major'=38
'on-delete'=39
'retain-other'=40
'keyed'=41
'public-traversal'=42
'id'=43
'doc'=44
'mode'=45
'emits'=46
'receiver'=47
'requires'=48
'any'=49
'get'=50
'set'=51
'watch'=52
'start'=53
'stop'=54
'read'=55
'write'=56
'resolve'=57
'connect'=58
'disconnect'=59
'call'=60
'watch-start'=61
'watch-stop'=62
'subscribe'=63
'unsubscribe'=64
'optimistic-register'=65
'crdt'=66
'optional-one'=67
'exactly-one'=68
'many-unique'=69
'many'=70
'ordered'=71
'unit'=72
'watch-handle'=73
'message'=74
'atom-ref'=75
'interface-ref'=76
'optional'=77
'list'=78
'bool'=79
'bytes'=80
'double'=81
'int32'=82
'int64'=83
'string'=84
'uint32'=85
'uint64'=86
'true'=87
'false'=88
'null'=89
'->'=90
':'=91
';'=92
','=93
'.'=94
'{'=95
'}'=96
'['=97
']'=98
'('=99
')'=100
'<'=101
'>'=102
File diff suppressed because one or more lines are too long
@@ -35,73 +35,78 @@ SOURCE=34
REPOSITORY=35
COMMIT=36
REVISION=37
ID=38
DOC=39
MODE=40
EMITS=41
RECEIVER=42
REQUIRES=43
ANY=44
GET=45
SET=46
WATCH=47
START=48
STOP=49
READ=50
WRITE=51
RESOLVE=52
CONNECT=53
DISCONNECT=54
CALL=55
WATCH_START=56
WATCH_STOP=57
SUBSCRIBE=58
UNSUBSCRIBE=59
OPTIMISTIC_REGISTER=60
CRDT=61
OPTIONAL_ONE=62
EXACTLY_ONE=63
MANY_UNIQUE=64
MANY=65
ORDERED=66
UNIT=67
WATCH_HANDLE=68
MESSAGE=69
ATOM_REF=70
INTERFACE_REF=71
OPTIONAL=72
LIST=73
BOOL=74
BYTES=75
DOUBLE=76
INT32=77
INT64=78
STRING=79
UINT32=80
UINT64=81
TRUE=82
FALSE=83
NULL=84
ARROW=85
COLON=86
SEMI=87
COMMA=88
DOT=89
LBRACE=90
RBRACE=91
LBRACK=92
RBRACK=93
LPAREN=94
RPAREN=95
LT=96
GT=97
INTEGER=98
JSON_NUMBER=99
IDENTIFIER=100
STRING_LITERAL=101
LINE_COMMENT=102
BLOCK_COMMENT=103
WS=104
SEMANTIC_MAJOR=38
ON_DELETE=39
RETAIN_OTHER=40
KEYED=41
PUBLIC_TRAVERSAL=42
ID=43
DOC=44
MODE=45
EMITS=46
RECEIVER=47
REQUIRES=48
ANY=49
GET=50
SET=51
WATCH=52
START=53
STOP=54
READ=55
WRITE=56
RESOLVE=57
CONNECT=58
DISCONNECT=59
CALL=60
WATCH_START=61
WATCH_STOP=62
SUBSCRIBE=63
UNSUBSCRIBE=64
OPTIMISTIC_REGISTER=65
CRDT=66
OPTIONAL_ONE=67
EXACTLY_ONE=68
MANY_UNIQUE=69
MANY=70
ORDERED=71
UNIT=72
WATCH_HANDLE=73
MESSAGE=74
ATOM_REF=75
INTERFACE_REF=76
OPTIONAL=77
LIST=78
BOOL=79
BYTES=80
DOUBLE=81
INT32=82
INT64=83
STRING=84
UINT32=85
UINT64=86
TRUE=87
FALSE=88
NULL=89
ARROW=90
COLON=91
SEMI=92
COMMA=93
DOT=94
LBRACE=95
RBRACE=96
LBRACK=97
RBRACK=98
LPAREN=99
RPAREN=100
LT=101
GT=102
INTEGER=103
JSON_NUMBER=104
IDENTIFIER=105
STRING_LITERAL=106
LINE_COMMENT=107
BLOCK_COMMENT=108
WS=109
'workspace'=1
'fragment'=2
'import'=3
@@ -139,63 +144,68 @@ WS=104
'repository'=35
'commit'=36
'revision'=37
'id'=38
'doc'=39
'mode'=40
'emits'=41
'receiver'=42
'requires'=43
'any'=44
'get'=45
'set'=46
'watch'=47
'start'=48
'stop'=49
'read'=50
'write'=51
'resolve'=52
'connect'=53
'disconnect'=54
'call'=55
'watch-start'=56
'watch-stop'=57
'subscribe'=58
'unsubscribe'=59
'optimistic-register'=60
'crdt'=61
'optional-one'=62
'exactly-one'=63
'many-unique'=64
'many'=65
'ordered'=66
'unit'=67
'watch-handle'=68
'message'=69
'atom-ref'=70
'interface-ref'=71
'optional'=72
'list'=73
'bool'=74
'bytes'=75
'double'=76
'int32'=77
'int64'=78
'string'=79
'uint32'=80
'uint64'=81
'true'=82
'false'=83
'null'=84
'->'=85
':'=86
';'=87
','=88
'.'=89
'{'=90
'}'=91
'['=92
']'=93
'('=94
')'=95
'<'=96
'>'=97
'semantic-major'=38
'on-delete'=39
'retain-other'=40
'keyed'=41
'public-traversal'=42
'id'=43
'doc'=44
'mode'=45
'emits'=46
'receiver'=47
'requires'=48
'any'=49
'get'=50
'set'=51
'watch'=52
'start'=53
'stop'=54
'read'=55
'write'=56
'resolve'=57
'connect'=58
'disconnect'=59
'call'=60
'watch-start'=61
'watch-stop'=62
'subscribe'=63
'unsubscribe'=64
'optimistic-register'=65
'crdt'=66
'optional-one'=67
'exactly-one'=68
'many-unique'=69
'many'=70
'ordered'=71
'unit'=72
'watch-handle'=73
'message'=74
'atom-ref'=75
'interface-ref'=76
'optional'=77
'list'=78
'bool'=79
'bytes'=80
'double'=81
'int32'=82
'int64'=83
'string'=84
'uint32'=85
'uint64'=86
'true'=87
'false'=88
'null'=89
'->'=90
':'=91
';'=92
','=93
'.'=94
'{'=95
'}'=96
'['=97
']'=98
'('=99
')'=100
'<'=101
'>'=102
@@ -41,73 +41,78 @@ export class QuixosCapabilityLexer extends antlr.Lexer {
public static readonly REPOSITORY = 35;
public static readonly COMMIT = 36;
public static readonly REVISION = 37;
public static readonly ID = 38;
public static readonly DOC = 39;
public static readonly MODE = 40;
public static readonly EMITS = 41;
public static readonly RECEIVER = 42;
public static readonly REQUIRES = 43;
public static readonly ANY = 44;
public static readonly GET = 45;
public static readonly SET = 46;
public static readonly WATCH = 47;
public static readonly START = 48;
public static readonly STOP = 49;
public static readonly READ = 50;
public static readonly WRITE = 51;
public static readonly RESOLVE = 52;
public static readonly CONNECT = 53;
public static readonly DISCONNECT = 54;
public static readonly CALL = 55;
public static readonly WATCH_START = 56;
public static readonly WATCH_STOP = 57;
public static readonly SUBSCRIBE = 58;
public static readonly UNSUBSCRIBE = 59;
public static readonly OPTIMISTIC_REGISTER = 60;
public static readonly CRDT = 61;
public static readonly OPTIONAL_ONE = 62;
public static readonly EXACTLY_ONE = 63;
public static readonly MANY_UNIQUE = 64;
public static readonly MANY = 65;
public static readonly ORDERED = 66;
public static readonly UNIT = 67;
public static readonly WATCH_HANDLE = 68;
public static readonly MESSAGE = 69;
public static readonly ATOM_REF = 70;
public static readonly INTERFACE_REF = 71;
public static readonly OPTIONAL = 72;
public static readonly LIST = 73;
public static readonly BOOL = 74;
public static readonly BYTES = 75;
public static readonly DOUBLE = 76;
public static readonly INT32 = 77;
public static readonly INT64 = 78;
public static readonly STRING = 79;
public static readonly UINT32 = 80;
public static readonly UINT64 = 81;
public static readonly TRUE = 82;
public static readonly FALSE = 83;
public static readonly NULL = 84;
public static readonly ARROW = 85;
public static readonly COLON = 86;
public static readonly SEMI = 87;
public static readonly COMMA = 88;
public static readonly DOT = 89;
public static readonly LBRACE = 90;
public static readonly RBRACE = 91;
public static readonly LBRACK = 92;
public static readonly RBRACK = 93;
public static readonly LPAREN = 94;
public static readonly RPAREN = 95;
public static readonly LT = 96;
public static readonly GT = 97;
public static readonly INTEGER = 98;
public static readonly JSON_NUMBER = 99;
public static readonly IDENTIFIER = 100;
public static readonly STRING_LITERAL = 101;
public static readonly LINE_COMMENT = 102;
public static readonly BLOCK_COMMENT = 103;
public static readonly WS = 104;
public static readonly SEMANTIC_MAJOR = 38;
public static readonly ON_DELETE = 39;
public static readonly RETAIN_OTHER = 40;
public static readonly KEYED = 41;
public static readonly PUBLIC_TRAVERSAL = 42;
public static readonly ID = 43;
public static readonly DOC = 44;
public static readonly MODE = 45;
public static readonly EMITS = 46;
public static readonly RECEIVER = 47;
public static readonly REQUIRES = 48;
public static readonly ANY = 49;
public static readonly GET = 50;
public static readonly SET = 51;
public static readonly WATCH = 52;
public static readonly START = 53;
public static readonly STOP = 54;
public static readonly READ = 55;
public static readonly WRITE = 56;
public static readonly RESOLVE = 57;
public static readonly CONNECT = 58;
public static readonly DISCONNECT = 59;
public static readonly CALL = 60;
public static readonly WATCH_START = 61;
public static readonly WATCH_STOP = 62;
public static readonly SUBSCRIBE = 63;
public static readonly UNSUBSCRIBE = 64;
public static readonly OPTIMISTIC_REGISTER = 65;
public static readonly CRDT = 66;
public static readonly OPTIONAL_ONE = 67;
public static readonly EXACTLY_ONE = 68;
public static readonly MANY_UNIQUE = 69;
public static readonly MANY = 70;
public static readonly ORDERED = 71;
public static readonly UNIT = 72;
public static readonly WATCH_HANDLE = 73;
public static readonly MESSAGE = 74;
public static readonly ATOM_REF = 75;
public static readonly INTERFACE_REF = 76;
public static readonly OPTIONAL = 77;
public static readonly LIST = 78;
public static readonly BOOL = 79;
public static readonly BYTES = 80;
public static readonly DOUBLE = 81;
public static readonly INT32 = 82;
public static readonly INT64 = 83;
public static readonly STRING = 84;
public static readonly UINT32 = 85;
public static readonly UINT64 = 86;
public static readonly TRUE = 87;
public static readonly FALSE = 88;
public static readonly NULL = 89;
public static readonly ARROW = 90;
public static readonly COLON = 91;
public static readonly SEMI = 92;
public static readonly COMMA = 93;
public static readonly DOT = 94;
public static readonly LBRACE = 95;
public static readonly RBRACE = 96;
public static readonly LBRACK = 97;
public static readonly RBRACK = 98;
public static readonly LPAREN = 99;
public static readonly RPAREN = 100;
public static readonly LT = 101;
public static readonly GT = 102;
public static readonly INTEGER = 103;
public static readonly JSON_NUMBER = 104;
public static readonly IDENTIFIER = 105;
public static readonly STRING_LITERAL = 106;
public static readonly LINE_COMMENT = 107;
public static readonly BLOCK_COMMENT = 108;
public static readonly WS = 109;
public static readonly channelNames = [
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
@@ -120,17 +125,18 @@ export class QuixosCapabilityLexer extends antlr.Lexer {
"'conform'", "'as'", "'bind'", "'to'", "'private'", "'shared'",
"'state'", "'edge'", "'projection'", "'with'", "'using'", "'via'",
"'materialize'", "'if'", "'absent'", "'on'", "'policy'", "'default'",
"'source'", "'repository'", "'commit'", "'revision'", "'id'", "'doc'",
"'mode'", "'emits'", "'receiver'", "'requires'", "'any'", "'get'",
"'set'", "'watch'", "'start'", "'stop'", "'read'", "'write'", "'resolve'",
"'connect'", "'disconnect'", "'call'", "'watch-start'", "'watch-stop'",
"'subscribe'", "'unsubscribe'", "'optimistic-register'", "'crdt'",
"'optional-one'", "'exactly-one'", "'many-unique'", "'many'", "'ordered'",
"'unit'", "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'",
"'optional'", "'list'", "'bool'", "'bytes'", "'double'", "'int32'",
"'int64'", "'string'", "'uint32'", "'uint64'", "'true'", "'false'",
"'null'", "'->'", "':'", "';'", "','", "'.'", "'{'", "'}'", "'['",
"']'", "'('", "')'", "'<'", "'>'"
"'source'", "'repository'", "'commit'", "'revision'", "'semantic-major'",
"'on-delete'", "'retain-other'", "'keyed'", "'public-traversal'",
"'id'", "'doc'", "'mode'", "'emits'", "'receiver'", "'requires'",
"'any'", "'get'", "'set'", "'watch'", "'start'", "'stop'", "'read'",
"'write'", "'resolve'", "'connect'", "'disconnect'", "'call'", "'watch-start'",
"'watch-stop'", "'subscribe'", "'unsubscribe'", "'optimistic-register'",
"'crdt'", "'optional-one'", "'exactly-one'", "'many-unique'", "'many'",
"'ordered'", "'unit'", "'watch-handle'", "'message'", "'atom-ref'",
"'interface-ref'", "'optional'", "'list'", "'bool'", "'bytes'",
"'double'", "'int32'", "'int64'", "'string'", "'uint32'", "'uint64'",
"'true'", "'false'", "'null'", "'->'", "':'", "';'", "','", "'.'",
"'{'", "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'"
];
public static readonly symbolicNames = [
@@ -139,17 +145,19 @@ export class QuixosCapabilityLexer extends antlr.Lexer {
"CONSTRUCTOR", "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO",
"PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING",
"VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT",
"SOURCE", "REPOSITORY", "COMMIT", "REVISION", "ID", "DOC", "MODE",
"EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START",
"STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL",
"WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER",
"CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED",
"UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF",
"OPTIONAL", "LIST", "BOOL", "BYTES", "DOUBLE", "INT32", "INT64",
"STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", "ARROW",
"COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", "RBRACK",
"LPAREN", "RPAREN", "LT", "GT", "INTEGER", "JSON_NUMBER", "IDENTIFIER",
"STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT", "WS"
"SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR",
"ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID",
"DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET",
"WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT",
"DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE",
"UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE",
"MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE",
"ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "BOOL", "BYTES",
"DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", "TRUE",
"FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE",
"RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "INTEGER",
"JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT",
"WS"
];
public static readonly modeNames = [
@@ -162,18 +170,19 @@ export class QuixosCapabilityLexer extends antlr.Lexer {
"CONSTRUCTOR", "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO",
"PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING",
"VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT",
"SOURCE", "REPOSITORY", "COMMIT", "REVISION", "ID", "DOC", "MODE",
"EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START",
"STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL",
"WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER",
"CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED",
"UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF",
"OPTIONAL", "LIST", "BOOL", "BYTES", "DOUBLE", "INT32", "INT64",
"STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", "ARROW",
"COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", "RBRACK",
"LPAREN", "RPAREN", "LT", "GT", "INTEGER", "JSON_NUMBER", "IDENTIFIER",
"STRING_LITERAL", "ESC", "HEX", "LINE_COMMENT", "BLOCK_COMMENT",
"WS",
"SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR",
"ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID",
"DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET",
"WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT",
"DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE",
"UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE",
"MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE",
"ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "BOOL", "BYTES",
"DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", "TRUE",
"FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE",
"RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "INTEGER",
"JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "ESC", "HEX", "LINE_COMMENT",
"BLOCK_COMMENT", "WS",
];
@@ -195,354 +204,381 @@ export class QuixosCapabilityLexer extends antlr.Lexer {
public get modeNames(): string[] { return QuixosCapabilityLexer.modeNames; }
public static readonly _serializedATN: number[] = [
4,0,104,976,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,
26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,
32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,
39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,
45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,
52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,
58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,
65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,
71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,
78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,
84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,2,
91,7,91,2,92,7,92,2,93,7,93,2,94,7,94,2,95,7,95,2,96,7,96,2,97,7,
97,2,98,7,98,2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103,
7,103,2,104,7,104,2,105,7,105,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,
2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,
5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,
6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,
8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,
1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,
1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,
1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,
1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,
1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,19,1,19,
1,19,1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,1,20,1,20,
1,21,1,21,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,23,1,23,
1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,
1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,27,1,27,
1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,
1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,31,
1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,33,
1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,
1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,
1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,38,1,38,1,38,
1,38,1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,41,
1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,42,
1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,1,45,
1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,
1,47,1,47,1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,50,
1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,
1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,
1,53,1,53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,55,1,55,
1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,56,1,56,1,56,
4,0,109,1047,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,
5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,
2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,
7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,
2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,
7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,
2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,
7,45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,
2,52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,
7,58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,
2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,
7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,
2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,
7,84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,
2,91,7,91,2,92,7,92,2,93,7,93,2,94,7,94,2,95,7,95,2,96,7,96,2,97,
7,97,2,98,7,98,2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103,
7,103,2,104,7,104,2,105,7,105,2,106,7,106,2,107,7,107,2,108,7,108,
2,109,7,109,2,110,7,110,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,
1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,5,1,5,
1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,6,
1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,
1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,
1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,
1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,
1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,
1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,19,1,19,1,19,
1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,21,
1,21,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,
1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,24,
1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,27,1,27,1,27,
1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,29,
1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,31,1,31,
1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,33,1,33,
1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,
1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,
1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,37,
1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,
1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,
1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1,41,
1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,
1,41,1,41,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,
1,44,1,45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,
1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,48,
1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,51,1,51,
1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,53,1,53,1,53,
1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,55,
1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,
1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1,58,
1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,
1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,60,1,60,
1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,
1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1,58,
1,58,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,60,1,60,
1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,
1,61,1,61,1,61,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,
1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,
1,63,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,65,1,65,1,65,
1,65,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,
1,67,1,67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,
1,68,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,70,1,70,1,70,
1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1,71,
1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,73,
1,73,1,73,1,73,1,73,1,74,1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,
1,75,1,75,1,75,1,75,1,76,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,
1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,79,1,79,1,79,
1,79,1,79,1,79,1,79,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,81,1,81,
1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,
1,83,1,84,1,84,1,84,1,85,1,85,1,86,1,86,1,87,1,87,1,88,1,88,1,89,
1,89,1,90,1,90,1,91,1,91,1,92,1,92,1,93,1,93,1,94,1,94,1,95,1,95,
1,96,1,96,1,97,3,97,877,8,97,1,97,4,97,880,8,97,11,97,12,97,881,
1,98,3,98,885,8,98,1,98,1,98,1,98,5,98,890,8,98,10,98,12,98,893,
9,98,3,98,895,8,98,1,98,1,98,4,98,899,8,98,11,98,12,98,900,3,98,
903,8,98,1,98,1,98,3,98,907,8,98,1,98,4,98,910,8,98,11,98,12,98,
911,3,98,914,8,98,1,99,1,99,5,99,918,8,99,10,99,12,99,921,9,99,1,
100,1,100,1,100,5,100,926,8,100,10,100,12,100,929,9,100,1,100,1,
100,1,101,1,101,1,101,1,101,1,101,1,101,1,101,1,101,3,101,941,8,
101,1,102,1,102,1,103,1,103,1,103,1,103,5,103,949,8,103,10,103,12,
103,952,9,103,1,103,1,103,1,104,1,104,1,104,1,104,5,104,960,8,104,
10,104,12,104,963,9,104,1,104,1,104,1,104,1,104,1,104,1,105,4,105,
971,8,105,11,105,12,105,972,1,105,1,105,1,961,0,106,1,1,3,2,5,3,
7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,
31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,
53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,
75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,
97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56,113,57,115,
58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67,
135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,76,153,
77,155,78,157,79,159,80,161,81,163,82,165,83,167,84,169,85,171,86,
173,87,175,88,177,89,179,90,181,91,183,92,185,93,187,94,189,95,191,
96,193,97,195,98,197,99,199,100,201,101,203,0,205,0,207,102,209,
103,211,104,1,0,11,1,0,48,57,1,0,49,57,2,0,69,69,101,101,2,0,43,
43,45,45,3,0,65,90,95,95,97,122,4,0,48,57,65,90,95,95,97,122,4,0,
10,10,13,13,34,34,92,92,8,0,34,34,47,47,92,92,98,98,102,102,110,
110,114,114,116,116,3,0,48,57,65,70,97,102,2,0,10,10,13,13,3,0,9,
10,13,13,32,32,990,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,
0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,
0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,
0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,
0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,
0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,
0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,
0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,
0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,
0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,
0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,
0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,
117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,
0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,
1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,
0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,
0,0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,
163,1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,
0,0,0,173,1,0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,
1,0,0,0,0,183,1,0,0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,0,0,0,
0,191,1,0,0,0,0,193,1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,199,1,
0,0,0,0,201,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0,0,211,1,0,0,0,1,
213,1,0,0,0,3,223,1,0,0,0,5,232,1,0,0,0,7,239,1,0,0,0,9,248,1,0,
0,0,11,253,1,0,0,0,13,263,1,0,0,0,15,274,1,0,0,0,17,282,1,0,0,0,
19,288,1,0,0,0,21,297,1,0,0,0,23,307,1,0,0,0,25,316,1,0,0,0,27,328,
1,0,0,0,29,339,1,0,0,0,31,345,1,0,0,0,33,353,1,0,0,0,35,356,1,0,
0,0,37,361,1,0,0,0,39,364,1,0,0,0,41,372,1,0,0,0,43,379,1,0,0,0,
45,385,1,0,0,0,47,390,1,0,0,0,49,401,1,0,0,0,51,406,1,0,0,0,53,412,
1,0,0,0,55,416,1,0,0,0,57,428,1,0,0,0,59,431,1,0,0,0,61,438,1,0,
0,0,63,441,1,0,0,0,65,448,1,0,0,0,67,456,1,0,0,0,69,463,1,0,0,0,
71,474,1,0,0,0,73,481,1,0,0,0,75,490,1,0,0,0,77,493,1,0,0,0,79,497,
1,0,0,0,81,502,1,0,0,0,83,508,1,0,0,0,85,517,1,0,0,0,87,526,1,0,
0,0,89,530,1,0,0,0,91,534,1,0,0,0,93,538,1,0,0,0,95,544,1,0,0,0,
97,550,1,0,0,0,99,555,1,0,0,0,101,560,1,0,0,0,103,566,1,0,0,0,105,
574,1,0,0,0,107,582,1,0,0,0,109,593,1,0,0,0,111,598,1,0,0,0,113,
610,1,0,0,0,115,621,1,0,0,0,117,631,1,0,0,0,119,643,1,0,0,0,121,
663,1,0,0,0,123,668,1,0,0,0,125,681,1,0,0,0,127,693,1,0,0,0,129,
705,1,0,0,0,131,710,1,0,0,0,133,718,1,0,0,0,135,723,1,0,0,0,137,
736,1,0,0,0,139,744,1,0,0,0,141,753,1,0,0,0,143,767,1,0,0,0,145,
776,1,0,0,0,147,781,1,0,0,0,149,786,1,0,0,0,151,792,1,0,0,0,153,
799,1,0,0,0,155,805,1,0,0,0,157,811,1,0,0,0,159,818,1,0,0,0,161,
825,1,0,0,0,163,832,1,0,0,0,165,837,1,0,0,0,167,843,1,0,0,0,169,
848,1,0,0,0,171,851,1,0,0,0,173,853,1,0,0,0,175,855,1,0,0,0,177,
857,1,0,0,0,179,859,1,0,0,0,181,861,1,0,0,0,183,863,1,0,0,0,185,
865,1,0,0,0,187,867,1,0,0,0,189,869,1,0,0,0,191,871,1,0,0,0,193,
873,1,0,0,0,195,876,1,0,0,0,197,884,1,0,0,0,199,915,1,0,0,0,201,
922,1,0,0,0,203,932,1,0,0,0,205,942,1,0,0,0,207,944,1,0,0,0,209,
955,1,0,0,0,211,970,1,0,0,0,213,214,5,119,0,0,214,215,5,111,0,0,
215,216,5,114,0,0,216,217,5,107,0,0,217,218,5,115,0,0,218,219,5,
112,0,0,219,220,5,97,0,0,220,221,5,99,0,0,221,222,5,101,0,0,222,
2,1,0,0,0,223,224,5,102,0,0,224,225,5,114,0,0,225,226,5,97,0,0,226,
227,5,103,0,0,227,228,5,109,0,0,228,229,5,101,0,0,229,230,5,110,
0,0,230,231,5,116,0,0,231,4,1,0,0,0,232,233,5,105,0,0,233,234,5,
109,0,0,234,235,5,112,0,0,235,236,5,111,0,0,236,237,5,114,0,0,237,
238,5,116,0,0,238,6,1,0,0,0,239,240,5,101,0,0,240,241,5,120,0,0,
241,242,5,116,0,0,242,243,5,101,0,0,243,244,5,114,0,0,244,245,5,
110,0,0,245,246,5,97,0,0,246,247,5,108,0,0,247,8,1,0,0,0,248,249,
5,97,0,0,249,250,5,116,0,0,250,251,5,111,0,0,251,252,5,109,0,0,252,
10,1,0,0,0,253,254,5,105,0,0,254,255,5,110,0,0,255,256,5,116,0,0,
256,257,5,101,0,0,257,258,5,114,0,0,258,259,5,102,0,0,259,260,5,
97,0,0,260,261,5,99,0,0,261,262,5,101,0,0,262,12,1,0,0,0,263,264,
5,105,0,0,264,265,5,110,0,0,265,266,5,116,0,0,266,267,5,101,0,0,
267,268,5,114,0,0,268,269,5,102,0,0,269,270,5,97,0,0,270,271,5,99,
0,0,271,272,5,101,0,0,272,273,5,115,0,0,273,14,1,0,0,0,274,275,5,
112,0,0,275,276,5,97,0,0,276,277,5,99,0,0,277,278,5,107,0,0,278,
279,5,97,0,0,279,280,5,103,0,0,280,281,5,101,0,0,281,16,1,0,0,0,
282,283,5,118,0,0,283,284,5,97,0,0,284,285,5,108,0,0,285,286,5,117,
0,0,286,287,5,101,0,0,287,18,1,0,0,0,288,289,5,114,0,0,289,290,5,
101,0,0,290,291,5,108,0,0,291,292,5,97,0,0,292,293,5,116,0,0,293,
294,5,105,0,0,294,295,5,111,0,0,295,296,5,110,0,0,296,20,1,0,0,0,
297,298,5,111,0,0,298,299,5,112,0,0,299,300,5,101,0,0,300,301,5,
114,0,0,301,302,5,97,0,0,302,303,5,116,0,0,303,304,5,105,0,0,304,
305,5,111,0,0,305,306,5,110,0,0,306,22,1,0,0,0,307,308,5,102,0,0,
308,309,5,117,0,0,309,310,5,110,0,0,310,311,5,99,0,0,311,312,5,116,
0,0,312,313,5,105,0,0,313,314,5,111,0,0,314,315,5,110,0,0,315,24,
1,0,0,0,316,317,5,99,0,0,317,318,5,111,0,0,318,319,5,110,0,0,319,
320,5,115,0,0,320,321,5,116,0,0,321,322,5,114,0,0,322,323,5,117,
0,0,323,324,5,99,0,0,324,325,5,116,0,0,325,326,5,111,0,0,326,327,
5,114,0,0,327,26,1,0,0,0,328,329,5,99,0,0,329,330,5,111,0,0,330,
331,5,110,0,0,331,332,5,115,0,0,332,333,5,116,0,0,333,334,5,114,
0,0,334,335,5,117,0,0,335,336,5,99,0,0,336,337,5,116,0,0,337,338,
5,115,0,0,338,28,1,0,0,0,339,340,5,105,0,0,340,341,5,110,0,0,341,
342,5,112,0,0,342,343,5,117,0,0,343,344,5,116,0,0,344,30,1,0,0,0,
345,346,5,99,0,0,346,347,5,111,0,0,347,348,5,110,0,0,348,349,5,102,
0,0,349,350,5,111,0,0,350,351,5,114,0,0,351,352,5,109,0,0,352,32,
1,0,0,0,353,354,5,97,0,0,354,355,5,115,0,0,355,34,1,0,0,0,356,357,
5,98,0,0,357,358,5,105,0,0,358,359,5,110,0,0,359,360,5,100,0,0,360,
36,1,0,0,0,361,362,5,116,0,0,362,363,5,111,0,0,363,38,1,0,0,0,364,
365,5,112,0,0,365,366,5,114,0,0,366,367,5,105,0,0,367,368,5,118,
0,0,368,369,5,97,0,0,369,370,5,116,0,0,370,371,5,101,0,0,371,40,
1,0,0,0,372,373,5,115,0,0,373,374,5,104,0,0,374,375,5,97,0,0,375,
376,5,114,0,0,376,377,5,101,0,0,377,378,5,100,0,0,378,42,1,0,0,0,
379,380,5,115,0,0,380,381,5,116,0,0,381,382,5,97,0,0,382,383,5,116,
0,0,383,384,5,101,0,0,384,44,1,0,0,0,385,386,5,101,0,0,386,387,5,
100,0,0,387,388,5,103,0,0,388,389,5,101,0,0,389,46,1,0,0,0,390,391,
5,112,0,0,391,392,5,114,0,0,392,393,5,111,0,0,393,394,5,106,0,0,
394,395,5,101,0,0,395,396,5,99,0,0,396,397,5,116,0,0,397,398,5,105,
0,0,398,399,5,111,0,0,399,400,5,110,0,0,400,48,1,0,0,0,401,402,5,
119,0,0,402,403,5,105,0,0,403,404,5,116,0,0,404,405,5,104,0,0,405,
50,1,0,0,0,406,407,5,117,0,0,407,408,5,115,0,0,408,409,5,105,0,0,
409,410,5,110,0,0,410,411,5,103,0,0,411,52,1,0,0,0,412,413,5,118,
0,0,413,414,5,105,0,0,414,415,5,97,0,0,415,54,1,0,0,0,416,417,5,
109,0,0,417,418,5,97,0,0,418,419,5,116,0,0,419,420,5,101,0,0,420,
421,5,114,0,0,421,422,5,105,0,0,422,423,5,97,0,0,423,424,5,108,0,
0,424,425,5,105,0,0,425,426,5,122,0,0,426,427,5,101,0,0,427,56,1,
0,0,0,428,429,5,105,0,0,429,430,5,102,0,0,430,58,1,0,0,0,431,432,
5,97,0,0,432,433,5,98,0,0,433,434,5,115,0,0,434,435,5,101,0,0,435,
436,5,110,0,0,436,437,5,116,0,0,437,60,1,0,0,0,438,439,5,111,0,0,
439,440,5,110,0,0,440,62,1,0,0,0,441,442,5,112,0,0,442,443,5,111,
0,0,443,444,5,108,0,0,444,445,5,105,0,0,445,446,5,99,0,0,446,447,
5,121,0,0,447,64,1,0,0,0,448,449,5,100,0,0,449,450,5,101,0,0,450,
451,5,102,0,0,451,452,5,97,0,0,452,453,5,117,0,0,453,454,5,108,0,
0,454,455,5,116,0,0,455,66,1,0,0,0,456,457,5,115,0,0,457,458,5,111,
0,0,458,459,5,117,0,0,459,460,5,114,0,0,460,461,5,99,0,0,461,462,
5,101,0,0,462,68,1,0,0,0,463,464,5,114,0,0,464,465,5,101,0,0,465,
466,5,112,0,0,466,467,5,111,0,0,467,468,5,115,0,0,468,469,5,105,
0,0,469,470,5,116,0,0,470,471,5,111,0,0,471,472,5,114,0,0,472,473,
5,121,0,0,473,70,1,0,0,0,474,475,5,99,0,0,475,476,5,111,0,0,476,
477,5,109,0,0,477,478,5,109,0,0,478,479,5,105,0,0,479,480,5,116,
0,0,480,72,1,0,0,0,481,482,5,114,0,0,482,483,5,101,0,0,483,484,5,
118,0,0,484,485,5,105,0,0,485,486,5,115,0,0,486,487,5,105,0,0,487,
488,5,111,0,0,488,489,5,110,0,0,489,74,1,0,0,0,490,491,5,105,0,0,
491,492,5,100,0,0,492,76,1,0,0,0,493,494,5,100,0,0,494,495,5,111,
0,0,495,496,5,99,0,0,496,78,1,0,0,0,497,498,5,109,0,0,498,499,5,
111,0,0,499,500,5,100,0,0,500,501,5,101,0,0,501,80,1,0,0,0,502,503,
5,101,0,0,503,504,5,109,0,0,504,505,5,105,0,0,505,506,5,116,0,0,
506,507,5,115,0,0,507,82,1,0,0,0,508,509,5,114,0,0,509,510,5,101,
0,0,510,511,5,99,0,0,511,512,5,101,0,0,512,513,5,105,0,0,513,514,
5,118,0,0,514,515,5,101,0,0,515,516,5,114,0,0,516,84,1,0,0,0,517,
518,5,114,0,0,518,519,5,101,0,0,519,520,5,113,0,0,520,521,5,117,
0,0,521,522,5,105,0,0,522,523,5,114,0,0,523,524,5,101,0,0,524,525,
5,115,0,0,525,86,1,0,0,0,526,527,5,97,0,0,527,528,5,110,0,0,528,
529,5,121,0,0,529,88,1,0,0,0,530,531,5,103,0,0,531,532,5,101,0,0,
532,533,5,116,0,0,533,90,1,0,0,0,534,535,5,115,0,0,535,536,5,101,
0,0,536,537,5,116,0,0,537,92,1,0,0,0,538,539,5,119,0,0,539,540,5,
97,0,0,540,541,5,116,0,0,541,542,5,99,0,0,542,543,5,104,0,0,543,
94,1,0,0,0,544,545,5,115,0,0,545,546,5,116,0,0,546,547,5,97,0,0,
547,548,5,114,0,0,548,549,5,116,0,0,549,96,1,0,0,0,550,551,5,115,
0,0,551,552,5,116,0,0,552,553,5,111,0,0,553,554,5,112,0,0,554,98,
1,0,0,0,555,556,5,114,0,0,556,557,5,101,0,0,557,558,5,97,0,0,558,
559,5,100,0,0,559,100,1,0,0,0,560,561,5,119,0,0,561,562,5,114,0,
0,562,563,5,105,0,0,563,564,5,116,0,0,564,565,5,101,0,0,565,102,
1,0,0,0,566,567,5,114,0,0,567,568,5,101,0,0,568,569,5,115,0,0,569,
570,5,111,0,0,570,571,5,108,0,0,571,572,5,118,0,0,572,573,5,101,
0,0,573,104,1,0,0,0,574,575,5,99,0,0,575,576,5,111,0,0,576,577,5,
110,0,0,577,578,5,110,0,0,578,579,5,101,0,0,579,580,5,99,0,0,580,
581,5,116,0,0,581,106,1,0,0,0,582,583,5,100,0,0,583,584,5,105,0,
0,584,585,5,115,0,0,585,586,5,99,0,0,586,587,5,111,0,0,587,588,5,
110,0,0,588,589,5,110,0,0,589,590,5,101,0,0,590,591,5,99,0,0,591,
592,5,116,0,0,592,108,1,0,0,0,593,594,5,99,0,0,594,595,5,97,0,0,
595,596,5,108,0,0,596,597,5,108,0,0,597,110,1,0,0,0,598,599,5,119,
0,0,599,600,5,97,0,0,600,601,5,116,0,0,601,602,5,99,0,0,602,603,
5,104,0,0,603,604,5,45,0,0,604,605,5,115,0,0,605,606,5,116,0,0,606,
607,5,97,0,0,607,608,5,114,0,0,608,609,5,116,0,0,609,112,1,0,0,0,
610,611,5,119,0,0,611,612,5,97,0,0,612,613,5,116,0,0,613,614,5,99,
0,0,614,615,5,104,0,0,615,616,5,45,0,0,616,617,5,115,0,0,617,618,
5,116,0,0,618,619,5,111,0,0,619,620,5,112,0,0,620,114,1,0,0,0,621,
622,5,115,0,0,622,623,5,117,0,0,623,624,5,98,0,0,624,625,5,115,0,
0,625,626,5,99,0,0,626,627,5,114,0,0,627,628,5,105,0,0,628,629,5,
98,0,0,629,630,5,101,0,0,630,116,1,0,0,0,631,632,5,117,0,0,632,633,
5,110,0,0,633,634,5,115,0,0,634,635,5,117,0,0,635,636,5,98,0,0,636,
637,5,115,0,0,637,638,5,99,0,0,638,639,5,114,0,0,639,640,5,105,0,
0,640,641,5,98,0,0,641,642,5,101,0,0,642,118,1,0,0,0,643,644,5,111,
0,0,644,645,5,112,0,0,645,646,5,116,0,0,646,647,5,105,0,0,647,648,
5,109,0,0,648,649,5,105,0,0,649,650,5,115,0,0,650,651,5,116,0,0,
651,652,5,105,0,0,652,653,5,99,0,0,653,654,5,45,0,0,654,655,5,114,
0,0,655,656,5,101,0,0,656,657,5,103,0,0,657,658,5,105,0,0,658,659,
5,115,0,0,659,660,5,116,0,0,660,661,5,101,0,0,661,662,5,114,0,0,
662,120,1,0,0,0,663,664,5,99,0,0,664,665,5,114,0,0,665,666,5,100,
0,0,666,667,5,116,0,0,667,122,1,0,0,0,668,669,5,111,0,0,669,670,
5,112,0,0,670,671,5,116,0,0,671,672,5,105,0,0,672,673,5,111,0,0,
673,674,5,110,0,0,674,675,5,97,0,0,675,676,5,108,0,0,676,677,5,45,
0,0,677,678,5,111,0,0,678,679,5,110,0,0,679,680,5,101,0,0,680,124,
1,0,0,0,681,682,5,101,0,0,682,683,5,120,0,0,683,684,5,97,0,0,684,
685,5,99,0,0,685,686,5,116,0,0,686,687,5,108,0,0,687,688,5,121,0,
0,688,689,5,45,0,0,689,690,5,111,0,0,690,691,5,110,0,0,691,692,5,
101,0,0,692,126,1,0,0,0,693,694,5,109,0,0,694,695,5,97,0,0,695,696,
5,110,0,0,696,697,5,121,0,0,697,698,5,45,0,0,698,699,5,117,0,0,699,
700,5,110,0,0,700,701,5,105,0,0,701,702,5,113,0,0,702,703,5,117,
0,0,703,704,5,101,0,0,704,128,1,0,0,0,705,706,5,109,0,0,706,707,
5,97,0,0,707,708,5,110,0,0,708,709,5,121,0,0,709,130,1,0,0,0,710,
711,5,111,0,0,711,712,5,114,0,0,712,713,5,100,0,0,713,714,5,101,
0,0,714,715,5,114,0,0,715,716,5,101,0,0,716,717,5,100,0,0,717,132,
1,0,0,0,718,719,5,117,0,0,719,720,5,110,0,0,720,721,5,105,0,0,721,
722,5,116,0,0,722,134,1,0,0,0,723,724,5,119,0,0,724,725,5,97,0,0,
725,726,5,116,0,0,726,727,5,99,0,0,727,728,5,104,0,0,728,729,5,45,
0,0,729,730,5,104,0,0,730,731,5,97,0,0,731,732,5,110,0,0,732,733,
5,100,0,0,733,734,5,108,0,0,734,735,5,101,0,0,735,136,1,0,0,0,736,
737,5,109,0,0,737,738,5,101,0,0,738,739,5,115,0,0,739,740,5,115,
0,0,740,741,5,97,0,0,741,742,5,103,0,0,742,743,5,101,0,0,743,138,
1,0,0,0,744,745,5,97,0,0,745,746,5,116,0,0,746,747,5,111,0,0,747,
748,5,109,0,0,748,749,5,45,0,0,749,750,5,114,0,0,750,751,5,101,0,
0,751,752,5,102,0,0,752,140,1,0,0,0,753,754,5,105,0,0,754,755,5,
110,0,0,755,756,5,116,0,0,756,757,5,101,0,0,757,758,5,114,0,0,758,
759,5,102,0,0,759,760,5,97,0,0,760,761,5,99,0,0,761,762,5,101,0,
0,762,763,5,45,0,0,763,764,5,114,0,0,764,765,5,101,0,0,765,766,5,
102,0,0,766,142,1,0,0,0,767,768,5,111,0,0,768,769,5,112,0,0,769,
770,5,116,0,0,770,771,5,105,0,0,771,772,5,111,0,0,772,773,5,110,
0,0,773,774,5,97,0,0,774,775,5,108,0,0,775,144,1,0,0,0,776,777,5,
108,0,0,777,778,5,105,0,0,778,779,5,115,0,0,779,780,5,116,0,0,780,
146,1,0,0,0,781,782,5,98,0,0,782,783,5,111,0,0,783,784,5,111,0,0,
784,785,5,108,0,0,785,148,1,0,0,0,786,787,5,98,0,0,787,788,5,121,
0,0,788,789,5,116,0,0,789,790,5,101,0,0,790,791,5,115,0,0,791,150,
1,0,0,0,792,793,5,100,0,0,793,794,5,111,0,0,794,795,5,117,0,0,795,
796,5,98,0,0,796,797,5,108,0,0,797,798,5,101,0,0,798,152,1,0,0,0,
799,800,5,105,0,0,800,801,5,110,0,0,801,802,5,116,0,0,802,803,5,
51,0,0,803,804,5,50,0,0,804,154,1,0,0,0,805,806,5,105,0,0,806,807,
5,110,0,0,807,808,5,116,0,0,808,809,5,54,0,0,809,810,5,52,0,0,810,
156,1,0,0,0,811,812,5,115,0,0,812,813,5,116,0,0,813,814,5,114,0,
0,814,815,5,105,0,0,815,816,5,110,0,0,816,817,5,103,0,0,817,158,
1,0,0,0,818,819,5,117,0,0,819,820,5,105,0,0,820,821,5,110,0,0,821,
822,5,116,0,0,822,823,5,51,0,0,823,824,5,50,0,0,824,160,1,0,0,0,
825,826,5,117,0,0,826,827,5,105,0,0,827,828,5,110,0,0,828,829,5,
116,0,0,829,830,5,54,0,0,830,831,5,52,0,0,831,162,1,0,0,0,832,833,
5,116,0,0,833,834,5,114,0,0,834,835,5,117,0,0,835,836,5,101,0,0,
836,164,1,0,0,0,837,838,5,102,0,0,838,839,5,97,0,0,839,840,5,108,
0,0,840,841,5,115,0,0,841,842,5,101,0,0,842,166,1,0,0,0,843,844,
5,110,0,0,844,845,5,117,0,0,845,846,5,108,0,0,846,847,5,108,0,0,
847,168,1,0,0,0,848,849,5,45,0,0,849,850,5,62,0,0,850,170,1,0,0,
0,851,852,5,58,0,0,852,172,1,0,0,0,853,854,5,59,0,0,854,174,1,0,
0,0,855,856,5,44,0,0,856,176,1,0,0,0,857,858,5,46,0,0,858,178,1,
0,0,0,859,860,5,123,0,0,860,180,1,0,0,0,861,862,5,125,0,0,862,182,
1,0,0,0,863,864,5,91,0,0,864,184,1,0,0,0,865,866,5,93,0,0,866,186,
1,0,0,0,867,868,5,40,0,0,868,188,1,0,0,0,869,870,5,41,0,0,870,190,
1,0,0,0,871,872,5,60,0,0,872,192,1,0,0,0,873,874,5,62,0,0,874,194,
1,0,0,0,875,877,5,45,0,0,876,875,1,0,0,0,876,877,1,0,0,0,877,879,
1,0,0,0,878,880,7,0,0,0,879,878,1,0,0,0,880,881,1,0,0,0,881,879,
1,0,0,0,881,882,1,0,0,0,882,196,1,0,0,0,883,885,5,45,0,0,884,883,
1,0,0,0,884,885,1,0,0,0,885,894,1,0,0,0,886,895,5,48,0,0,887,891,
7,1,0,0,888,890,7,0,0,0,889,888,1,0,0,0,890,893,1,0,0,0,891,889,
1,0,0,0,891,892,1,0,0,0,892,895,1,0,0,0,893,891,1,0,0,0,894,886,
1,0,0,0,894,887,1,0,0,0,895,902,1,0,0,0,896,898,5,46,0,0,897,899,
7,0,0,0,898,897,1,0,0,0,899,900,1,0,0,0,900,898,1,0,0,0,900,901,
1,0,0,0,901,903,1,0,0,0,902,896,1,0,0,0,902,903,1,0,0,0,903,913,
1,0,0,0,904,906,7,2,0,0,905,907,7,3,0,0,906,905,1,0,0,0,906,907,
1,0,0,0,907,909,1,0,0,0,908,910,7,0,0,0,909,908,1,0,0,0,910,911,
1,0,0,0,911,909,1,0,0,0,911,912,1,0,0,0,912,914,1,0,0,0,913,904,
1,0,0,0,913,914,1,0,0,0,914,198,1,0,0,0,915,919,7,4,0,0,916,918,
7,5,0,0,917,916,1,0,0,0,918,921,1,0,0,0,919,917,1,0,0,0,919,920,
1,0,0,0,920,200,1,0,0,0,921,919,1,0,0,0,922,927,5,34,0,0,923,926,
3,203,101,0,924,926,8,6,0,0,925,923,1,0,0,0,925,924,1,0,0,0,926,
929,1,0,0,0,927,925,1,0,0,0,927,928,1,0,0,0,928,930,1,0,0,0,929,
927,1,0,0,0,930,931,5,34,0,0,931,202,1,0,0,0,932,940,5,92,0,0,933,
941,7,7,0,0,934,935,5,117,0,0,935,936,3,205,102,0,936,937,3,205,
102,0,937,938,3,205,102,0,938,939,3,205,102,0,939,941,1,0,0,0,940,
933,1,0,0,0,940,934,1,0,0,0,941,204,1,0,0,0,942,943,7,8,0,0,943,
206,1,0,0,0,944,945,5,47,0,0,945,946,5,47,0,0,946,950,1,0,0,0,947,
949,8,9,0,0,948,947,1,0,0,0,949,952,1,0,0,0,950,948,1,0,0,0,950,
951,1,0,0,0,951,953,1,0,0,0,952,950,1,0,0,0,953,954,6,103,0,0,954,
208,1,0,0,0,955,956,5,47,0,0,956,957,5,42,0,0,957,961,1,0,0,0,958,
960,9,0,0,0,959,958,1,0,0,0,960,963,1,0,0,0,961,962,1,0,0,0,961,
959,1,0,0,0,962,964,1,0,0,0,963,961,1,0,0,0,964,965,5,42,0,0,965,
966,5,47,0,0,966,967,1,0,0,0,967,968,6,104,0,0,968,210,1,0,0,0,969,
971,7,10,0,0,970,969,1,0,0,0,971,972,1,0,0,0,972,970,1,0,0,0,972,
973,1,0,0,0,973,974,1,0,0,0,974,975,6,105,0,0,975,212,1,0,0,0,18,
0,876,881,884,891,894,900,902,906,911,913,919,925,927,940,950,961,
972,1,0,1,0
1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,64,
1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,
1,64,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,65,1,66,1,66,
1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,67,1,67,
1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,
1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,69,1,69,1,69,1,69,
1,69,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1,71,1,71,1,71,
1,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,
1,72,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,74,1,74,1,74,1,74,
1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,
1,75,1,75,1,75,1,75,1,75,1,75,1,76,1,76,1,76,1,76,1,76,1,76,1,76,
1,76,1,76,1,77,1,77,1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,78,1,79,
1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,81,
1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,82,1,83,1,83,
1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,85,
1,85,1,85,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,86,1,87,1,87,
1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,90,
1,90,1,91,1,91,1,92,1,92,1,93,1,93,1,94,1,94,1,95,1,95,1,96,1,96,
1,97,1,97,1,98,1,98,1,99,1,99,1,100,1,100,1,101,1,101,1,102,3,102,
948,8,102,1,102,4,102,951,8,102,11,102,12,102,952,1,103,3,103,956,
8,103,1,103,1,103,1,103,5,103,961,8,103,10,103,12,103,964,9,103,
3,103,966,8,103,1,103,1,103,4,103,970,8,103,11,103,12,103,971,3,
103,974,8,103,1,103,1,103,3,103,978,8,103,1,103,4,103,981,8,103,
11,103,12,103,982,3,103,985,8,103,1,104,1,104,5,104,989,8,104,10,
104,12,104,992,9,104,1,105,1,105,1,105,5,105,997,8,105,10,105,12,
105,1000,9,105,1,105,1,105,1,106,1,106,1,106,1,106,1,106,1,106,1,
106,1,106,3,106,1012,8,106,1,107,1,107,1,108,1,108,1,108,1,108,5,
108,1020,8,108,10,108,12,108,1023,9,108,1,108,1,108,1,109,1,109,
1,109,1,109,5,109,1031,8,109,10,109,12,109,1034,9,109,1,109,1,109,
1,109,1,109,1,109,1,110,4,110,1042,8,110,11,110,12,110,1043,1,110,
1,110,1,1032,0,111,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,
21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,
43,22,45,23,47,24,49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,
65,33,67,34,69,35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,
87,44,89,45,91,46,93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,
54,109,55,111,56,113,57,115,58,117,59,119,60,121,61,123,62,125,63,
127,64,129,65,131,66,133,67,135,68,137,69,139,70,141,71,143,72,145,
73,147,74,149,75,151,76,153,77,155,78,157,79,159,80,161,81,163,82,
165,83,167,84,169,85,171,86,173,87,175,88,177,89,179,90,181,91,183,
92,185,93,187,94,189,95,191,96,193,97,195,98,197,99,199,100,201,
101,203,102,205,103,207,104,209,105,211,106,213,0,215,0,217,107,
219,108,221,109,1,0,11,1,0,48,57,1,0,49,57,2,0,69,69,101,101,2,0,
43,43,45,45,3,0,65,90,95,95,97,122,4,0,48,57,65,90,95,95,97,122,
4,0,10,10,13,13,34,34,92,92,8,0,34,34,47,47,92,92,98,98,102,102,
110,110,114,114,116,116,3,0,48,57,65,70,97,102,2,0,10,10,13,13,3,
0,9,10,13,13,32,32,1061,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,
1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,
1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,
1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,
1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,
1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,
1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,
1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,
1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,
1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,
1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,
107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,
0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,
1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,
0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,
0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,
153,1,0,0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,
0,0,0,163,1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,
1,0,0,0,0,173,1,0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,
0,181,1,0,0,0,0,183,1,0,0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,
0,0,0,0,191,1,0,0,0,0,193,1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,
199,1,0,0,0,0,201,1,0,0,0,0,203,1,0,0,0,0,205,1,0,0,0,0,207,1,0,
0,0,0,209,1,0,0,0,0,211,1,0,0,0,0,217,1,0,0,0,0,219,1,0,0,0,0,221,
1,0,0,0,1,223,1,0,0,0,3,233,1,0,0,0,5,242,1,0,0,0,7,249,1,0,0,0,
9,258,1,0,0,0,11,263,1,0,0,0,13,273,1,0,0,0,15,284,1,0,0,0,17,292,
1,0,0,0,19,298,1,0,0,0,21,307,1,0,0,0,23,317,1,0,0,0,25,326,1,0,
0,0,27,338,1,0,0,0,29,349,1,0,0,0,31,355,1,0,0,0,33,363,1,0,0,0,
35,366,1,0,0,0,37,371,1,0,0,0,39,374,1,0,0,0,41,382,1,0,0,0,43,389,
1,0,0,0,45,395,1,0,0,0,47,400,1,0,0,0,49,411,1,0,0,0,51,416,1,0,
0,0,53,422,1,0,0,0,55,426,1,0,0,0,57,438,1,0,0,0,59,441,1,0,0,0,
61,448,1,0,0,0,63,451,1,0,0,0,65,458,1,0,0,0,67,466,1,0,0,0,69,473,
1,0,0,0,71,484,1,0,0,0,73,491,1,0,0,0,75,500,1,0,0,0,77,515,1,0,
0,0,79,525,1,0,0,0,81,538,1,0,0,0,83,544,1,0,0,0,85,561,1,0,0,0,
87,564,1,0,0,0,89,568,1,0,0,0,91,573,1,0,0,0,93,579,1,0,0,0,95,588,
1,0,0,0,97,597,1,0,0,0,99,601,1,0,0,0,101,605,1,0,0,0,103,609,1,
0,0,0,105,615,1,0,0,0,107,621,1,0,0,0,109,626,1,0,0,0,111,631,1,
0,0,0,113,637,1,0,0,0,115,645,1,0,0,0,117,653,1,0,0,0,119,664,1,
0,0,0,121,669,1,0,0,0,123,681,1,0,0,0,125,692,1,0,0,0,127,702,1,
0,0,0,129,714,1,0,0,0,131,734,1,0,0,0,133,739,1,0,0,0,135,752,1,
0,0,0,137,764,1,0,0,0,139,776,1,0,0,0,141,781,1,0,0,0,143,789,1,
0,0,0,145,794,1,0,0,0,147,807,1,0,0,0,149,815,1,0,0,0,151,824,1,
0,0,0,153,838,1,0,0,0,155,847,1,0,0,0,157,852,1,0,0,0,159,857,1,
0,0,0,161,863,1,0,0,0,163,870,1,0,0,0,165,876,1,0,0,0,167,882,1,
0,0,0,169,889,1,0,0,0,171,896,1,0,0,0,173,903,1,0,0,0,175,908,1,
0,0,0,177,914,1,0,0,0,179,919,1,0,0,0,181,922,1,0,0,0,183,924,1,
0,0,0,185,926,1,0,0,0,187,928,1,0,0,0,189,930,1,0,0,0,191,932,1,
0,0,0,193,934,1,0,0,0,195,936,1,0,0,0,197,938,1,0,0,0,199,940,1,
0,0,0,201,942,1,0,0,0,203,944,1,0,0,0,205,947,1,0,0,0,207,955,1,
0,0,0,209,986,1,0,0,0,211,993,1,0,0,0,213,1003,1,0,0,0,215,1013,
1,0,0,0,217,1015,1,0,0,0,219,1026,1,0,0,0,221,1041,1,0,0,0,223,224,
5,119,0,0,224,225,5,111,0,0,225,226,5,114,0,0,226,227,5,107,0,0,
227,228,5,115,0,0,228,229,5,112,0,0,229,230,5,97,0,0,230,231,5,99,
0,0,231,232,5,101,0,0,232,2,1,0,0,0,233,234,5,102,0,0,234,235,5,
114,0,0,235,236,5,97,0,0,236,237,5,103,0,0,237,238,5,109,0,0,238,
239,5,101,0,0,239,240,5,110,0,0,240,241,5,116,0,0,241,4,1,0,0,0,
242,243,5,105,0,0,243,244,5,109,0,0,244,245,5,112,0,0,245,246,5,
111,0,0,246,247,5,114,0,0,247,248,5,116,0,0,248,6,1,0,0,0,249,250,
5,101,0,0,250,251,5,120,0,0,251,252,5,116,0,0,252,253,5,101,0,0,
253,254,5,114,0,0,254,255,5,110,0,0,255,256,5,97,0,0,256,257,5,108,
0,0,257,8,1,0,0,0,258,259,5,97,0,0,259,260,5,116,0,0,260,261,5,111,
0,0,261,262,5,109,0,0,262,10,1,0,0,0,263,264,5,105,0,0,264,265,5,
110,0,0,265,266,5,116,0,0,266,267,5,101,0,0,267,268,5,114,0,0,268,
269,5,102,0,0,269,270,5,97,0,0,270,271,5,99,0,0,271,272,5,101,0,
0,272,12,1,0,0,0,273,274,5,105,0,0,274,275,5,110,0,0,275,276,5,116,
0,0,276,277,5,101,0,0,277,278,5,114,0,0,278,279,5,102,0,0,279,280,
5,97,0,0,280,281,5,99,0,0,281,282,5,101,0,0,282,283,5,115,0,0,283,
14,1,0,0,0,284,285,5,112,0,0,285,286,5,97,0,0,286,287,5,99,0,0,287,
288,5,107,0,0,288,289,5,97,0,0,289,290,5,103,0,0,290,291,5,101,0,
0,291,16,1,0,0,0,292,293,5,118,0,0,293,294,5,97,0,0,294,295,5,108,
0,0,295,296,5,117,0,0,296,297,5,101,0,0,297,18,1,0,0,0,298,299,5,
114,0,0,299,300,5,101,0,0,300,301,5,108,0,0,301,302,5,97,0,0,302,
303,5,116,0,0,303,304,5,105,0,0,304,305,5,111,0,0,305,306,5,110,
0,0,306,20,1,0,0,0,307,308,5,111,0,0,308,309,5,112,0,0,309,310,5,
101,0,0,310,311,5,114,0,0,311,312,5,97,0,0,312,313,5,116,0,0,313,
314,5,105,0,0,314,315,5,111,0,0,315,316,5,110,0,0,316,22,1,0,0,0,
317,318,5,102,0,0,318,319,5,117,0,0,319,320,5,110,0,0,320,321,5,
99,0,0,321,322,5,116,0,0,322,323,5,105,0,0,323,324,5,111,0,0,324,
325,5,110,0,0,325,24,1,0,0,0,326,327,5,99,0,0,327,328,5,111,0,0,
328,329,5,110,0,0,329,330,5,115,0,0,330,331,5,116,0,0,331,332,5,
114,0,0,332,333,5,117,0,0,333,334,5,99,0,0,334,335,5,116,0,0,335,
336,5,111,0,0,336,337,5,114,0,0,337,26,1,0,0,0,338,339,5,99,0,0,
339,340,5,111,0,0,340,341,5,110,0,0,341,342,5,115,0,0,342,343,5,
116,0,0,343,344,5,114,0,0,344,345,5,117,0,0,345,346,5,99,0,0,346,
347,5,116,0,0,347,348,5,115,0,0,348,28,1,0,0,0,349,350,5,105,0,0,
350,351,5,110,0,0,351,352,5,112,0,0,352,353,5,117,0,0,353,354,5,
116,0,0,354,30,1,0,0,0,355,356,5,99,0,0,356,357,5,111,0,0,357,358,
5,110,0,0,358,359,5,102,0,0,359,360,5,111,0,0,360,361,5,114,0,0,
361,362,5,109,0,0,362,32,1,0,0,0,363,364,5,97,0,0,364,365,5,115,
0,0,365,34,1,0,0,0,366,367,5,98,0,0,367,368,5,105,0,0,368,369,5,
110,0,0,369,370,5,100,0,0,370,36,1,0,0,0,371,372,5,116,0,0,372,373,
5,111,0,0,373,38,1,0,0,0,374,375,5,112,0,0,375,376,5,114,0,0,376,
377,5,105,0,0,377,378,5,118,0,0,378,379,5,97,0,0,379,380,5,116,0,
0,380,381,5,101,0,0,381,40,1,0,0,0,382,383,5,115,0,0,383,384,5,104,
0,0,384,385,5,97,0,0,385,386,5,114,0,0,386,387,5,101,0,0,387,388,
5,100,0,0,388,42,1,0,0,0,389,390,5,115,0,0,390,391,5,116,0,0,391,
392,5,97,0,0,392,393,5,116,0,0,393,394,5,101,0,0,394,44,1,0,0,0,
395,396,5,101,0,0,396,397,5,100,0,0,397,398,5,103,0,0,398,399,5,
101,0,0,399,46,1,0,0,0,400,401,5,112,0,0,401,402,5,114,0,0,402,403,
5,111,0,0,403,404,5,106,0,0,404,405,5,101,0,0,405,406,5,99,0,0,406,
407,5,116,0,0,407,408,5,105,0,0,408,409,5,111,0,0,409,410,5,110,
0,0,410,48,1,0,0,0,411,412,5,119,0,0,412,413,5,105,0,0,413,414,5,
116,0,0,414,415,5,104,0,0,415,50,1,0,0,0,416,417,5,117,0,0,417,418,
5,115,0,0,418,419,5,105,0,0,419,420,5,110,0,0,420,421,5,103,0,0,
421,52,1,0,0,0,422,423,5,118,0,0,423,424,5,105,0,0,424,425,5,97,
0,0,425,54,1,0,0,0,426,427,5,109,0,0,427,428,5,97,0,0,428,429,5,
116,0,0,429,430,5,101,0,0,430,431,5,114,0,0,431,432,5,105,0,0,432,
433,5,97,0,0,433,434,5,108,0,0,434,435,5,105,0,0,435,436,5,122,0,
0,436,437,5,101,0,0,437,56,1,0,0,0,438,439,5,105,0,0,439,440,5,102,
0,0,440,58,1,0,0,0,441,442,5,97,0,0,442,443,5,98,0,0,443,444,5,115,
0,0,444,445,5,101,0,0,445,446,5,110,0,0,446,447,5,116,0,0,447,60,
1,0,0,0,448,449,5,111,0,0,449,450,5,110,0,0,450,62,1,0,0,0,451,452,
5,112,0,0,452,453,5,111,0,0,453,454,5,108,0,0,454,455,5,105,0,0,
455,456,5,99,0,0,456,457,5,121,0,0,457,64,1,0,0,0,458,459,5,100,
0,0,459,460,5,101,0,0,460,461,5,102,0,0,461,462,5,97,0,0,462,463,
5,117,0,0,463,464,5,108,0,0,464,465,5,116,0,0,465,66,1,0,0,0,466,
467,5,115,0,0,467,468,5,111,0,0,468,469,5,117,0,0,469,470,5,114,
0,0,470,471,5,99,0,0,471,472,5,101,0,0,472,68,1,0,0,0,473,474,5,
114,0,0,474,475,5,101,0,0,475,476,5,112,0,0,476,477,5,111,0,0,477,
478,5,115,0,0,478,479,5,105,0,0,479,480,5,116,0,0,480,481,5,111,
0,0,481,482,5,114,0,0,482,483,5,121,0,0,483,70,1,0,0,0,484,485,5,
99,0,0,485,486,5,111,0,0,486,487,5,109,0,0,487,488,5,109,0,0,488,
489,5,105,0,0,489,490,5,116,0,0,490,72,1,0,0,0,491,492,5,114,0,0,
492,493,5,101,0,0,493,494,5,118,0,0,494,495,5,105,0,0,495,496,5,
115,0,0,496,497,5,105,0,0,497,498,5,111,0,0,498,499,5,110,0,0,499,
74,1,0,0,0,500,501,5,115,0,0,501,502,5,101,0,0,502,503,5,109,0,0,
503,504,5,97,0,0,504,505,5,110,0,0,505,506,5,116,0,0,506,507,5,105,
0,0,507,508,5,99,0,0,508,509,5,45,0,0,509,510,5,109,0,0,510,511,
5,97,0,0,511,512,5,106,0,0,512,513,5,111,0,0,513,514,5,114,0,0,514,
76,1,0,0,0,515,516,5,111,0,0,516,517,5,110,0,0,517,518,5,45,0,0,
518,519,5,100,0,0,519,520,5,101,0,0,520,521,5,108,0,0,521,522,5,
101,0,0,522,523,5,116,0,0,523,524,5,101,0,0,524,78,1,0,0,0,525,526,
5,114,0,0,526,527,5,101,0,0,527,528,5,116,0,0,528,529,5,97,0,0,529,
530,5,105,0,0,530,531,5,110,0,0,531,532,5,45,0,0,532,533,5,111,0,
0,533,534,5,116,0,0,534,535,5,104,0,0,535,536,5,101,0,0,536,537,
5,114,0,0,537,80,1,0,0,0,538,539,5,107,0,0,539,540,5,101,0,0,540,
541,5,121,0,0,541,542,5,101,0,0,542,543,5,100,0,0,543,82,1,0,0,0,
544,545,5,112,0,0,545,546,5,117,0,0,546,547,5,98,0,0,547,548,5,108,
0,0,548,549,5,105,0,0,549,550,5,99,0,0,550,551,5,45,0,0,551,552,
5,116,0,0,552,553,5,114,0,0,553,554,5,97,0,0,554,555,5,118,0,0,555,
556,5,101,0,0,556,557,5,114,0,0,557,558,5,115,0,0,558,559,5,97,0,
0,559,560,5,108,0,0,560,84,1,0,0,0,561,562,5,105,0,0,562,563,5,100,
0,0,563,86,1,0,0,0,564,565,5,100,0,0,565,566,5,111,0,0,566,567,5,
99,0,0,567,88,1,0,0,0,568,569,5,109,0,0,569,570,5,111,0,0,570,571,
5,100,0,0,571,572,5,101,0,0,572,90,1,0,0,0,573,574,5,101,0,0,574,
575,5,109,0,0,575,576,5,105,0,0,576,577,5,116,0,0,577,578,5,115,
0,0,578,92,1,0,0,0,579,580,5,114,0,0,580,581,5,101,0,0,581,582,5,
99,0,0,582,583,5,101,0,0,583,584,5,105,0,0,584,585,5,118,0,0,585,
586,5,101,0,0,586,587,5,114,0,0,587,94,1,0,0,0,588,589,5,114,0,0,
589,590,5,101,0,0,590,591,5,113,0,0,591,592,5,117,0,0,592,593,5,
105,0,0,593,594,5,114,0,0,594,595,5,101,0,0,595,596,5,115,0,0,596,
96,1,0,0,0,597,598,5,97,0,0,598,599,5,110,0,0,599,600,5,121,0,0,
600,98,1,0,0,0,601,602,5,103,0,0,602,603,5,101,0,0,603,604,5,116,
0,0,604,100,1,0,0,0,605,606,5,115,0,0,606,607,5,101,0,0,607,608,
5,116,0,0,608,102,1,0,0,0,609,610,5,119,0,0,610,611,5,97,0,0,611,
612,5,116,0,0,612,613,5,99,0,0,613,614,5,104,0,0,614,104,1,0,0,0,
615,616,5,115,0,0,616,617,5,116,0,0,617,618,5,97,0,0,618,619,5,114,
0,0,619,620,5,116,0,0,620,106,1,0,0,0,621,622,5,115,0,0,622,623,
5,116,0,0,623,624,5,111,0,0,624,625,5,112,0,0,625,108,1,0,0,0,626,
627,5,114,0,0,627,628,5,101,0,0,628,629,5,97,0,0,629,630,5,100,0,
0,630,110,1,0,0,0,631,632,5,119,0,0,632,633,5,114,0,0,633,634,5,
105,0,0,634,635,5,116,0,0,635,636,5,101,0,0,636,112,1,0,0,0,637,
638,5,114,0,0,638,639,5,101,0,0,639,640,5,115,0,0,640,641,5,111,
0,0,641,642,5,108,0,0,642,643,5,118,0,0,643,644,5,101,0,0,644,114,
1,0,0,0,645,646,5,99,0,0,646,647,5,111,0,0,647,648,5,110,0,0,648,
649,5,110,0,0,649,650,5,101,0,0,650,651,5,99,0,0,651,652,5,116,0,
0,652,116,1,0,0,0,653,654,5,100,0,0,654,655,5,105,0,0,655,656,5,
115,0,0,656,657,5,99,0,0,657,658,5,111,0,0,658,659,5,110,0,0,659,
660,5,110,0,0,660,661,5,101,0,0,661,662,5,99,0,0,662,663,5,116,0,
0,663,118,1,0,0,0,664,665,5,99,0,0,665,666,5,97,0,0,666,667,5,108,
0,0,667,668,5,108,0,0,668,120,1,0,0,0,669,670,5,119,0,0,670,671,
5,97,0,0,671,672,5,116,0,0,672,673,5,99,0,0,673,674,5,104,0,0,674,
675,5,45,0,0,675,676,5,115,0,0,676,677,5,116,0,0,677,678,5,97,0,
0,678,679,5,114,0,0,679,680,5,116,0,0,680,122,1,0,0,0,681,682,5,
119,0,0,682,683,5,97,0,0,683,684,5,116,0,0,684,685,5,99,0,0,685,
686,5,104,0,0,686,687,5,45,0,0,687,688,5,115,0,0,688,689,5,116,0,
0,689,690,5,111,0,0,690,691,5,112,0,0,691,124,1,0,0,0,692,693,5,
115,0,0,693,694,5,117,0,0,694,695,5,98,0,0,695,696,5,115,0,0,696,
697,5,99,0,0,697,698,5,114,0,0,698,699,5,105,0,0,699,700,5,98,0,
0,700,701,5,101,0,0,701,126,1,0,0,0,702,703,5,117,0,0,703,704,5,
110,0,0,704,705,5,115,0,0,705,706,5,117,0,0,706,707,5,98,0,0,707,
708,5,115,0,0,708,709,5,99,0,0,709,710,5,114,0,0,710,711,5,105,0,
0,711,712,5,98,0,0,712,713,5,101,0,0,713,128,1,0,0,0,714,715,5,111,
0,0,715,716,5,112,0,0,716,717,5,116,0,0,717,718,5,105,0,0,718,719,
5,109,0,0,719,720,5,105,0,0,720,721,5,115,0,0,721,722,5,116,0,0,
722,723,5,105,0,0,723,724,5,99,0,0,724,725,5,45,0,0,725,726,5,114,
0,0,726,727,5,101,0,0,727,728,5,103,0,0,728,729,5,105,0,0,729,730,
5,115,0,0,730,731,5,116,0,0,731,732,5,101,0,0,732,733,5,114,0,0,
733,130,1,0,0,0,734,735,5,99,0,0,735,736,5,114,0,0,736,737,5,100,
0,0,737,738,5,116,0,0,738,132,1,0,0,0,739,740,5,111,0,0,740,741,
5,112,0,0,741,742,5,116,0,0,742,743,5,105,0,0,743,744,5,111,0,0,
744,745,5,110,0,0,745,746,5,97,0,0,746,747,5,108,0,0,747,748,5,45,
0,0,748,749,5,111,0,0,749,750,5,110,0,0,750,751,5,101,0,0,751,134,
1,0,0,0,752,753,5,101,0,0,753,754,5,120,0,0,754,755,5,97,0,0,755,
756,5,99,0,0,756,757,5,116,0,0,757,758,5,108,0,0,758,759,5,121,0,
0,759,760,5,45,0,0,760,761,5,111,0,0,761,762,5,110,0,0,762,763,5,
101,0,0,763,136,1,0,0,0,764,765,5,109,0,0,765,766,5,97,0,0,766,767,
5,110,0,0,767,768,5,121,0,0,768,769,5,45,0,0,769,770,5,117,0,0,770,
771,5,110,0,0,771,772,5,105,0,0,772,773,5,113,0,0,773,774,5,117,
0,0,774,775,5,101,0,0,775,138,1,0,0,0,776,777,5,109,0,0,777,778,
5,97,0,0,778,779,5,110,0,0,779,780,5,121,0,0,780,140,1,0,0,0,781,
782,5,111,0,0,782,783,5,114,0,0,783,784,5,100,0,0,784,785,5,101,
0,0,785,786,5,114,0,0,786,787,5,101,0,0,787,788,5,100,0,0,788,142,
1,0,0,0,789,790,5,117,0,0,790,791,5,110,0,0,791,792,5,105,0,0,792,
793,5,116,0,0,793,144,1,0,0,0,794,795,5,119,0,0,795,796,5,97,0,0,
796,797,5,116,0,0,797,798,5,99,0,0,798,799,5,104,0,0,799,800,5,45,
0,0,800,801,5,104,0,0,801,802,5,97,0,0,802,803,5,110,0,0,803,804,
5,100,0,0,804,805,5,108,0,0,805,806,5,101,0,0,806,146,1,0,0,0,807,
808,5,109,0,0,808,809,5,101,0,0,809,810,5,115,0,0,810,811,5,115,
0,0,811,812,5,97,0,0,812,813,5,103,0,0,813,814,5,101,0,0,814,148,
1,0,0,0,815,816,5,97,0,0,816,817,5,116,0,0,817,818,5,111,0,0,818,
819,5,109,0,0,819,820,5,45,0,0,820,821,5,114,0,0,821,822,5,101,0,
0,822,823,5,102,0,0,823,150,1,0,0,0,824,825,5,105,0,0,825,826,5,
110,0,0,826,827,5,116,0,0,827,828,5,101,0,0,828,829,5,114,0,0,829,
830,5,102,0,0,830,831,5,97,0,0,831,832,5,99,0,0,832,833,5,101,0,
0,833,834,5,45,0,0,834,835,5,114,0,0,835,836,5,101,0,0,836,837,5,
102,0,0,837,152,1,0,0,0,838,839,5,111,0,0,839,840,5,112,0,0,840,
841,5,116,0,0,841,842,5,105,0,0,842,843,5,111,0,0,843,844,5,110,
0,0,844,845,5,97,0,0,845,846,5,108,0,0,846,154,1,0,0,0,847,848,5,
108,0,0,848,849,5,105,0,0,849,850,5,115,0,0,850,851,5,116,0,0,851,
156,1,0,0,0,852,853,5,98,0,0,853,854,5,111,0,0,854,855,5,111,0,0,
855,856,5,108,0,0,856,158,1,0,0,0,857,858,5,98,0,0,858,859,5,121,
0,0,859,860,5,116,0,0,860,861,5,101,0,0,861,862,5,115,0,0,862,160,
1,0,0,0,863,864,5,100,0,0,864,865,5,111,0,0,865,866,5,117,0,0,866,
867,5,98,0,0,867,868,5,108,0,0,868,869,5,101,0,0,869,162,1,0,0,0,
870,871,5,105,0,0,871,872,5,110,0,0,872,873,5,116,0,0,873,874,5,
51,0,0,874,875,5,50,0,0,875,164,1,0,0,0,876,877,5,105,0,0,877,878,
5,110,0,0,878,879,5,116,0,0,879,880,5,54,0,0,880,881,5,52,0,0,881,
166,1,0,0,0,882,883,5,115,0,0,883,884,5,116,0,0,884,885,5,114,0,
0,885,886,5,105,0,0,886,887,5,110,0,0,887,888,5,103,0,0,888,168,
1,0,0,0,889,890,5,117,0,0,890,891,5,105,0,0,891,892,5,110,0,0,892,
893,5,116,0,0,893,894,5,51,0,0,894,895,5,50,0,0,895,170,1,0,0,0,
896,897,5,117,0,0,897,898,5,105,0,0,898,899,5,110,0,0,899,900,5,
116,0,0,900,901,5,54,0,0,901,902,5,52,0,0,902,172,1,0,0,0,903,904,
5,116,0,0,904,905,5,114,0,0,905,906,5,117,0,0,906,907,5,101,0,0,
907,174,1,0,0,0,908,909,5,102,0,0,909,910,5,97,0,0,910,911,5,108,
0,0,911,912,5,115,0,0,912,913,5,101,0,0,913,176,1,0,0,0,914,915,
5,110,0,0,915,916,5,117,0,0,916,917,5,108,0,0,917,918,5,108,0,0,
918,178,1,0,0,0,919,920,5,45,0,0,920,921,5,62,0,0,921,180,1,0,0,
0,922,923,5,58,0,0,923,182,1,0,0,0,924,925,5,59,0,0,925,184,1,0,
0,0,926,927,5,44,0,0,927,186,1,0,0,0,928,929,5,46,0,0,929,188,1,
0,0,0,930,931,5,123,0,0,931,190,1,0,0,0,932,933,5,125,0,0,933,192,
1,0,0,0,934,935,5,91,0,0,935,194,1,0,0,0,936,937,5,93,0,0,937,196,
1,0,0,0,938,939,5,40,0,0,939,198,1,0,0,0,940,941,5,41,0,0,941,200,
1,0,0,0,942,943,5,60,0,0,943,202,1,0,0,0,944,945,5,62,0,0,945,204,
1,0,0,0,946,948,5,45,0,0,947,946,1,0,0,0,947,948,1,0,0,0,948,950,
1,0,0,0,949,951,7,0,0,0,950,949,1,0,0,0,951,952,1,0,0,0,952,950,
1,0,0,0,952,953,1,0,0,0,953,206,1,0,0,0,954,956,5,45,0,0,955,954,
1,0,0,0,955,956,1,0,0,0,956,965,1,0,0,0,957,966,5,48,0,0,958,962,
7,1,0,0,959,961,7,0,0,0,960,959,1,0,0,0,961,964,1,0,0,0,962,960,
1,0,0,0,962,963,1,0,0,0,963,966,1,0,0,0,964,962,1,0,0,0,965,957,
1,0,0,0,965,958,1,0,0,0,966,973,1,0,0,0,967,969,5,46,0,0,968,970,
7,0,0,0,969,968,1,0,0,0,970,971,1,0,0,0,971,969,1,0,0,0,971,972,
1,0,0,0,972,974,1,0,0,0,973,967,1,0,0,0,973,974,1,0,0,0,974,984,
1,0,0,0,975,977,7,2,0,0,976,978,7,3,0,0,977,976,1,0,0,0,977,978,
1,0,0,0,978,980,1,0,0,0,979,981,7,0,0,0,980,979,1,0,0,0,981,982,
1,0,0,0,982,980,1,0,0,0,982,983,1,0,0,0,983,985,1,0,0,0,984,975,
1,0,0,0,984,985,1,0,0,0,985,208,1,0,0,0,986,990,7,4,0,0,987,989,
7,5,0,0,988,987,1,0,0,0,989,992,1,0,0,0,990,988,1,0,0,0,990,991,
1,0,0,0,991,210,1,0,0,0,992,990,1,0,0,0,993,998,5,34,0,0,994,997,
3,213,106,0,995,997,8,6,0,0,996,994,1,0,0,0,996,995,1,0,0,0,997,
1000,1,0,0,0,998,996,1,0,0,0,998,999,1,0,0,0,999,1001,1,0,0,0,1000,
998,1,0,0,0,1001,1002,5,34,0,0,1002,212,1,0,0,0,1003,1011,5,92,0,
0,1004,1012,7,7,0,0,1005,1006,5,117,0,0,1006,1007,3,215,107,0,1007,
1008,3,215,107,0,1008,1009,3,215,107,0,1009,1010,3,215,107,0,1010,
1012,1,0,0,0,1011,1004,1,0,0,0,1011,1005,1,0,0,0,1012,214,1,0,0,
0,1013,1014,7,8,0,0,1014,216,1,0,0,0,1015,1016,5,47,0,0,1016,1017,
5,47,0,0,1017,1021,1,0,0,0,1018,1020,8,9,0,0,1019,1018,1,0,0,0,1020,
1023,1,0,0,0,1021,1019,1,0,0,0,1021,1022,1,0,0,0,1022,1024,1,0,0,
0,1023,1021,1,0,0,0,1024,1025,6,108,0,0,1025,218,1,0,0,0,1026,1027,
5,47,0,0,1027,1028,5,42,0,0,1028,1032,1,0,0,0,1029,1031,9,0,0,0,
1030,1029,1,0,0,0,1031,1034,1,0,0,0,1032,1033,1,0,0,0,1032,1030,
1,0,0,0,1033,1035,1,0,0,0,1034,1032,1,0,0,0,1035,1036,5,42,0,0,1036,
1037,5,47,0,0,1037,1038,1,0,0,0,1038,1039,6,109,0,0,1039,220,1,0,
0,0,1040,1042,7,10,0,0,1041,1040,1,0,0,0,1042,1043,1,0,0,0,1043,
1041,1,0,0,0,1043,1044,1,0,0,0,1044,1045,1,0,0,0,1045,1046,6,110,
0,0,1046,222,1,0,0,0,18,0,947,952,955,962,965,971,973,977,982,984,
990,996,998,1011,1021,1032,1043,1,0,1,0
];
private static __ATN: antlr.ATN;
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -894,6 +894,7 @@ const lowerPackage = (
return {
packageId: capabilityId.package(stringValue(context.stringLiteral(0))),
revisionId: state.packages.get(alias)!.revisionId,
...(context.INTEGER() ? { semanticMajor: Number(context.INTEGER()!.getText()) } : {}),
displayName: alias,
source,
exports,
@@ -938,12 +939,16 @@ const lowerEdgeEndpoint = (
return constraint
? {
projectionId: capabilityId.edgeProjection(
stringValue(context.stringLiteral()),
stringValue(context.stringLiteral(0)),
),
displayName: identifier(context.identifier()),
constraint,
cardinality: lowerCardinality(context.cardinality()),
ordered: Boolean(context.ORDERED()),
...(context.ON_DELETE() ? { onDelete: stringValue(context.stringLiteral(1)) as EdgeEndpoint["onDelete"] } : {}),
...(context.RETAIN_OTHER() ? { retainOther: true } : {}),
...(context.KEYED() ? {keyType: stringValue(context.stringLiteral(context.ON_DELETE() ? 2 : 1)) as EdgeEndpoint["keyType"]} : {}),
...(context.PUBLIC_TRAVERSAL() ? {publicTraversal: true} : {}),
}
: undefined;
};
@@ -1406,6 +1411,8 @@ const lowerConformance = (
return {
atomId,
interfaceRevisionId: interfaceSymbol.revisionId,
...(context.stringLiteral() ? { id: capabilityId.conformance(stringValue(context.stringLiteral()!)) } : {}),
...(context.INTEGER() ? { semanticMajor: Number(context.INTEGER()!.getText()) } : {}),
privateAttachments,
operationBindings,
relationshipMaterializations,
+249
View File
@@ -0,0 +1,249 @@
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import {randomUUID} from "node:crypto";
import {execFile as callback} from "node:child_process";
import {promisify} from "node:util";
import {loadQuixosLock, parseQuixosLockDocument} from "../resource-lock/index.js";
import {contentDigest} from "../capability-model/evolution.js";
import {planStructure, applyStructure, type StructuralRequest} from "./structural-plan.js";
import {snapshotRepository, checkResourceCandidate, checkWorkspaceCandidate} from "./candidate-check.js";
import {compileWorkspaceRepository, compileCapabilityResourceRepository, type ResolvedCapabilityResource} from "./assembly.js";
import {createGitCapabilityResolver} from "./git-resolver.js";
const execFile = promisify(callback);
type Source = {repository: string; commit: string};
export type UpgradeNode = {kind: "workspace" | "package" | "interface"; directory: string; source: Source};
export type UpgradeSpec = {nodes: UpgradeNode[]; quixos?: Source; baseline?: string; reviews?: string; bootstrap?: boolean};
type NodePlan = UpgradeNode & {treeDigest: string; dependencies: string[]; lockFiles: string[]};
export type UpgradePlan = {schemaVersion: 1; workbench: string; spec: UpgradeSpec; nodes: NodePlan[]; digest: string};
type Step = {directory: string; phase: "editing" | "prepared" | "refactor" | "checked" | "publishing" | "published"; commit?: string; treeDigest?: string; structuralJournal?: string; structuralPlan?: Awaited<ReturnType<typeof planStructure>>};
type Journal = {schemaVersion: 1; plan: UpgradePlan; steps: Step[]};
const sourceKey = (node: {kind: string; source: Source}) => JSON.stringify([node.kind, node.source.repository, node.source.commit]);
const command = async (cwd: string, tool: string, args: string[]) => (await execFile(tool, args, {cwd, maxBuffer: 16 * 1024 * 1024, env: {...process.env, GIT_TERMINAL_PROMPT: "0", QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0"}})).stdout.trim();
const validSource = (value: Source) => {
const url = new URL(value.repository);
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(value.commit)) throw new Error("Upgrade sources must be exact credential-free HTTPS revisions");
};
const location = async (root: string, directory: string) => {
if (directory !== "root" && !/^resources\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(directory)) throw new Error("Upgrade target must be a managed root/resource repository");
const resolved = await fs.realpath(path.join(root, directory));
if (resolved !== path.join(root, directory)) throw new Error("Upgrade target crosses a symlink");
return resolved;
};
const treeDigest = async (root: string) => {
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-tree-"));
try {return (await snapshotRepository(root, temporary)).treeDigest;} finally {await fs.rm(temporary, {recursive: true, force: true});}
};
const writeJournal = async (filename: string, journal: unknown) => {
const temp = `${filename}.${randomUUID()}.tmp`;
const handle = await fs.open(temp, "wx", 0o600);
try {await handle.writeFile(JSON.stringify(journal, null, 2)); await handle.sync();} finally {await handle.close();}
await fs.rename(temp, filename);
const directory = await fs.open(path.dirname(filename), "r");
try {await directory.sync();} finally {await directory.close();}
};
export const discoverUpgradeSpec = async (workbench: string): Promise<UpgradeSpec> => {
const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"));
const root = await location(workbench, "root");
const nodes: UpgradeNode[] = [{kind: "workspace", directory: "root", source: {
repository: await command(root, "git", ["remote", "get-url", "origin"]),
commit: await command(root, "jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"]),
}}, ...graph.resources.map((entry: {kind: "interface" | "package"; directory: string; source: Source}) => ({kind: entry.kind, source: entry.source,
directory: path.relative(workbench, path.resolve(workbench, entry.directory))}))];
let baseline: string | undefined;
try {
const host = JSON.parse(await fs.readFile("/etc/quixos/workspace-source.json", "utf8"));
if (await fs.realpath(host.workbenchRoot) === await fs.realpath(workbench)) baseline = JSON.parse(await fs.readFile(path.join(host.runtimeClosureRoot, "manifest.json"), "utf8")).workspacePlanPath;
} catch (error) {if (!["ENOENT", "EACCES"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error;}
return {nodes, baseline};
};
/** Read-only source plan. Repositories are selected explicitly, including any
* parallel versions of the same resource; no guesses at a floating 'latest'. */
export const planPinUpgrades = async (workbenchPath: string, spec: UpgradeSpec): Promise<UpgradePlan> => {
const workbench = await fs.realpath(workbenchPath);
if (!Array.isArray(spec.nodes) || !spec.nodes.length || spec.nodes.length > 100 || spec.nodes.filter((node) => node.kind === "workspace").length !== 1) throw new Error("Upgrade graph requires one workspace and at most 100 repositories");
if (spec.quixos) validSource(spec.quixos);
const keys = new Map<string, string>();
for (const node of spec.nodes) {
validSource(node.source);
if (!["workspace", "package", "interface"].includes(node.kind) || keys.has(sourceKey(node))) throw new Error("Duplicate/invalid upgrade resource identity");
keys.set(sourceKey(node), node.directory);
}
if (new Set(spec.nodes.map((node) => node.directory)).size !== spec.nodes.length) throw new Error("Upgrade directories must be distinct");
const nodes: NodePlan[] = [];
for (const node of spec.nodes) {
const root = await location(workbench, node.directory);
if (await command(root, "git", ["remote", "get-url", "origin"]) !== node.source.repository) throw new Error(`Upgrade origin differs from selected source: ${node.directory}`);
const loaded = await loadQuixosLock(path.join(root, "quixos.lock"));
if (!loaded.ok) throw new Error(`Invalid lock in ${node.directory}: ${loaded.diagnostics.map((entry) => entry.message).join("; ")}`);
const dependencies = loaded.lock.resources.map((resource) => keys.get(sourceKey(resource))).filter((value): value is string => Boolean(value));
nodes.push({...node, treeDigest: await treeDigest(root), dependencies: [...new Set(dependencies)], lockFiles: loaded.lock.sourceFiles ?? ["quixos.lock"]});
}
const ordered: NodePlan[] = [], remaining = [...nodes];
while (remaining.length) {
const index = remaining.findIndex((node) => node.dependencies.every((dependency) => ordered.some((entry) => entry.directory === dependency)));
if (index < 0) throw new Error("Cyclic source publication graph");
ordered.push(remaining.splice(index, 1)[0]);
}
const workspace = ordered.find((node) => node.kind === "workspace")!;
// Even unreferenced new resources are published before the root.
ordered.splice(ordered.indexOf(workspace), 1); ordered.push(workspace);
const plan = {schemaVersion: 1 as const, workbench, spec, nodes: ordered};
return {...plan, digest: contentDigest(plan)};
};
export type UpgradeEffects = {
check(node: NodePlan, root: string, output: string, spec: UpgradeSpec): Promise<void>;
snapshot(root: string): Promise<string>;
publish(root: string, commit: string): Promise<void>;
};
const effects: UpgradeEffects = {
async check(node, root, output, spec) {
if (node.kind === "workspace" && !spec.baseline && !spec.bootstrap) throw new Error("Upgrading a workspace requires its checked active baseline for major-review checks (or explicit bootstrap:true for a new workspace)");
// Publication checks consume already-published dependency revisions, never
// workbench dirty overlays masquerading as those immutable identities.
const snapshotMap = `${output}-published-dependencies.json`;
await fs.writeFile(snapshotMap, JSON.stringify({resources: []}), {flag: "wx"});
const result = node.kind === "workspace" ? await checkWorkspaceCandidate({root, output, snapshotMap, baseline: spec.baseline, reviews: spec.reviews})
: await checkResourceCandidate({root, output, kind: node.kind, source: node.source, snapshotMap});
if (result.blockers.length) throw new Error(`Refactor required in ${node.directory}: ${result.blockers.join("; ")}`);
const evolution = (result as {evolution?: {reviews: {accepted: boolean}[]}}).evolution;
if (evolution?.reviews.some((review) => !review.accepted)) throw new Error("Explicit semantic-major review required before publishing the workspace");
},
async snapshot(root) {
// Unlike checking, publication deliberately captures the working copy.
await command(root, "jj", ["status"]);
const conflicts = await command(root, "jj", ["resolve", "--list"]);
if (conflicts) throw new Error("Resolve source conflicts before publication");
const commit = await command(root, "jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"]);
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commit)) throw new Error("Publication did not resolve an exact commit");
await command(root, "git", ["diff", "--exit-code", "--no-ext-diff", "--no-textconv", commit, "--"]);
const tracked = new Set((await command(root, "git", ["ls-tree", "-r", "--name-only", "-z", commit])).split("\0"));
for (const file of (await command(root, "git", ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean)) if (!tracked.has(file)) throw new Error(`Uncaptured source file ${file}`);
return commit;
},
async publish(root, commit) {
const ref = `refs/tags/quixos-reachability/${commit}`;
const remote = await command(root, "git", ["ls-remote", "--refs", "origin", ref]);
if (remote && remote !== `${commit}\t${ref}`) throw new Error("Immutable publication ref conflict");
if (!remote) await command(root, "git", ["push", "origin", `${commit}:${ref}`]);
if (await command(root, "git", ["ls-remote", "--refs", "origin", ref]) !== `${commit}\t${ref}`) throw new Error("Publication response uncertain; retry the same journal");
},
};
/** Explicit --publish only. Append-only remote retention; never moves the
* workspace branch, activates code, or rolls back previously published nodes. */
export const applyPinUpgrades = async (plan: UpgradePlan, journalId?: string, implementation: UpgradeEffects = effects, options: {acceptEdits?: boolean} = {}) => {
if (implementation === effects && !plan.spec.baseline && !plan.spec.bootstrap) throw new Error("Publication requires an active checked baseline or explicit bootstrap:true");
const {digest, ...body} = plan;
if (contentDigest(body) !== digest) throw new Error("Upgrade plan digest mismatch");
const directory = path.join(plan.workbench, ".quixos", "upgrades");
await fs.mkdir(directory, {recursive: true, mode: 0o700});
if (await fs.realpath(directory) !== directory) throw new Error("Upgrade journals must not cross symlinks");
const lock = await fs.open(path.join(directory, "writer.lock"), "wx", 0o600);
const id = journalId ?? randomUUID();
if (!/^[a-f0-9-]{36}$/.test(id)) {await lock.close(); await fs.unlink(path.join(directory, "writer.lock")); throw new Error("Invalid upgrade journal ID");}
const filename = path.join(directory, `${id}.json`);
try {
const journal: Journal = journalId ? JSON.parse(await fs.readFile(filename, "utf8")) : {schemaVersion: 1, plan, steps: []};
if (journal.plan.digest !== plan.digest) throw new Error("Upgrade journal belongs to another plan");
if (!journalId) await writeJournal(filename, journal);
for (const node of plan.nodes) {
const root = await location(plan.workbench, node.directory);
if (await command(root, "git", ["remote", "get-url", "origin"]) !== node.source.repository) throw new Error("Upgrade remote changed after planning");
let step = journal.steps.find((entry) => entry.directory === node.directory);
if (step?.phase === "published") continue;
if (!step) {
if (await treeDigest(root) !== node.treeDigest) throw new Error(`Stale upgrade plan: ${node.directory}`);
const files: StructuralRequest["files"] = [];
for (const file of node.lockFiles) {
const parsed = parseQuixosLockDocument(await fs.readFile(path.join(root, file), "utf8"));
if (!parsed.ok) throw new Error("Invalid lock during upgrade");
const edits = [];
for (const resource of parsed.document.resources) {
const dependency = plan.nodes.find((entry) => sourceKey(entry) === sourceKey(resource));
const published = dependency && journal.steps.find((entry) => entry.directory === dependency.directory && entry.phase === "published");
if (published?.commit && published.commit !== resource.source.commit) edits.push({operation: "dependency" as const, kind: resource.kind, name: resource.binding, source: {...resource.source, commit: published.commit}});
}
if (parsed.document.kind === "root" && plan.spec.quixos) edits.push({operation: "quixos-pin" as const, source: plan.spec.quixos});
if (edits.length) files.push({file, edits});
}
const structuralPlan = files.length ? await planStructure(root, {kind: node.kind, source: node.source, files}, process.env.QUIXOS_SNAPSHOT_MAP) : undefined;
step = {directory: node.directory, phase: "editing", treeDigest: node.treeDigest, structuralPlan, structuralJournal: structuralPlan ? randomUUID() : undefined};
journal.steps.push(step); await writeJournal(filename, journal);
}
if (step.phase === "editing") {
if (step.structuralPlan) await applyStructure(step.structuralPlan, step.structuralJournal);
else if (await treeDigest(root) !== step.treeDigest) throw new Error("Source changed before upgrade editing");
step.treeDigest = await treeDigest(root);
step.phase = "prepared";
await writeJournal(filename, journal);
}
if (step.phase === "refactor") {
const current = await treeDigest(root);
if (current !== step.treeDigest && !options.acceptEdits) throw new Error("Refactored source requires --accept-edits when resuming");
step.treeDigest = current; step.phase = "prepared"; await writeJournal(filename, journal);
}
if (await treeDigest(root) !== step.treeDigest) throw new Error(`Source changed during upgrade: ${node.directory}; inspect ${filename}`);
if (step.phase === "prepared") {
try {await implementation.check(node, root, path.join(directory, `${id}-${node.directory.replaceAll("/", "-")}-${randomUUID()}`), plan.spec);}
catch (error) {step.phase = "refactor"; await writeJournal(filename, journal); throw error;}
if (await treeDigest(root) !== step.treeDigest) throw new Error("Source changed while checking");
step.phase = "checked"; await writeJournal(filename, journal);
}
if (step.phase === "checked") {
step.commit = await implementation.snapshot(root);
if (await treeDigest(root) !== step.treeDigest) throw new Error("Publication snapshot changed checked files");
step.phase = "publishing"; await writeJournal(filename, journal);
}
await implementation.publish(root, step.commit!);
step.phase = "published"; await writeJournal(filename, journal);
}
// Keep subsequent automatic upgrades associated with the newly published
// identities, without renaming repositories or changing any selected branch.
// Explicit-spec callers without a managed graph retain the journal as their
// source of revisions instead.
const graphFile = path.join(plan.workbench, ".quixos/resource-graph.json");
let graphText: string | undefined;
try {graphText = await fs.readFile(graphFile, "utf8");} catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;}
if (graphText !== undefined) {
const previous = JSON.parse(graphText);
const snapshots = await Promise.all(previous.resources.map(async (entry: {kind: string; source: Source; directory: string}) => ({kind: entry.kind, ...entry.source, directory: await location(plan.workbench, path.relative(plan.workbench, path.resolve(plan.workbench, entry.directory)))})));
for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) {
const step = journal.steps.find((entry) => entry.directory === node.directory)!;
if (await treeDigest(await location(plan.workbench, node.directory)) !== step.treeDigest) throw new Error("Published source changed before workbench graph refresh");
snapshots.push({kind: node.kind, repository: node.source.repository, commit: step.commit, directory: path.join(plan.workbench, node.directory)});
}
const unique = [...new Map(snapshots.map((entry: {kind: string; repository: string; commit: string}) => [JSON.stringify([entry.kind, entry.repository, entry.commit]), entry])).values()];
const snapshotMap = path.join(directory, `${id}-published-snapshots.json`);
await fs.writeFile(snapshotMap, JSON.stringify({resources: unique}));
const resolveResource = await createGitCapabilityResolver({checkoutRoot: path.join(directory, `${id}-graph-resources`), snapshotMap, snapshotOnly: true});
const rootNode = plan.nodes.find((node) => node.kind === "workspace")!;
if (await treeDigest(await location(plan.workbench, rootNode.directory)) !== journal.steps.find((step) => step.directory === rootNode.directory)!.treeDigest) throw new Error("Root source changed before workbench graph refresh");
const compiled = await compileWorkspaceRepository({rootDirectory: await location(plan.workbench, rootNode.directory), resolveResource});
// Managed repositories need not currently be reachable from the workspace.
// Keep them discoverable/checkpointed until explicitly removed by the user.
const resources = new Map<string, ResolvedCapabilityResource>(compiled.resources.map((node) => [node.key, node]));
for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) {
const step = journal.steps.find((entry) => entry.directory === node.directory)!;
const source = {resolver: "git" as const, repository: node.source.repository, commit: step.commit!};
const key = `${node.kind}\0${source.repository}\0${source.commit}`;
if (resources.has(key)) continue;
const directory = await location(plan.workbench, node.directory);
const standalone = await compileCapabilityResourceRepository({rootDirectory: directory, kind: node.kind as "package" | "interface", source, resolveResource});
for (const dependency of standalone.resources) resources.set(dependency.key, dependency);
resources.set(key, {key, kind: node.kind as "package" | "interface", source, directory, lock: standalone.lock, resource: standalone.resource, dependencies: standalone.directResources});
}
await writeJournal(graphFile, {formatVersion: 1, quixos: compiled.lock.quixos,
directResources: [...compiled.directResources.entries()].map(([bindingKey, node]) => {const [kind, binding] = bindingKey.split("\0"); return {kind, binding, resourceKey: node.key, directory: node.directory};}),
resources: [...resources.values()].map((node) => ({key: node.key, kind: node.kind, source: node.source, directory: node.directory,
resourceId: node.resource.kind === "interface" ? node.resource.revision.interfaceId : node.resource.revision.packageId,
revisionId: node.resource.revision.revisionId, dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({binding, resourceKey: dependency.key}))})),
});
}
return {id, journal: filename, revisions: journal.steps.map((step) => ({directory: step.directory, commit: step.commit})), activated: false};
} catch (error) {throw new Error(`${error instanceof Error ? error.message : String(error)}; upgrade journal ${filename}`, {cause: error});}
finally {await lock.close(); await fs.unlink(path.join(directory, "writer.lock"));}
};
+138
View File
@@ -0,0 +1,138 @@
import fs from "node:fs/promises";
import path from "node:path";
import {contentDigest} from "../capability-model/evolution.js";
import {validateMigrationCatalog, type MigrationCatalog, type MigrationDeclaration} from "../capability-model/migrations.js";
import {formatQuixosLock, type GitSource} from "../resource-lock/index.js";
import {parseQx, walkSyntax} from "./source.js";
import type {StructuralRequest} from "./structural-plan.js";
type Source = {repository: string; commit: string};
type Registry = {generatedBy: "qx-scaffold-v1"; name: string; id: string; revision: string; exports: {name: string; id: string; file: string; migration?: boolean}[]};
export type ScaffoldRecipe = {
source: Source; directory?: string; name?: string; id?: string; revision?: string;
declaration?: string;
tools?: {quixos: Source; protocol: Source; helpers: Source; sdk: Source};
nixifyPluginUrl?: string;
migration?: Omit<MigrationDeclaration, "implementation"> & {contracts: Record<string, unknown>};
};
const marker = "// Generated by qx-scaffold-v1\n";
const json = (value: unknown) => `${JSON.stringify(value, null, 2)}\n`;
const source = (value: Source): GitSource => {
const url = new URL(value.repository);
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit)) throw new Error("Scaffolds require credential-free HTTPS sources and full exact commits");
return {resolver: "git", ...value};
};
const nixSource = (value: Source) => `git+${source(value).repository}?ref=refs/tags/quixos-reachability/${value.commit}&rev=${value.commit}`;
const nixString = (value: string) => JSON.stringify(value).replaceAll("${", "\\${");
const safeName = (name: string | undefined): string => {
if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error("Scaffold requires a simple authored name");
return name;
};
const ownedJson = async <T>(root: string, file: string): Promise<T> => {
const target = path.join(root, file);
if (!(await fs.realpath(target)).startsWith(`${await fs.realpath(root)}/`)) throw new Error("Scaffold input escapes repository");
const value = JSON.parse(await fs.readFile(target, "utf8"));
if (value.generatedBy !== "qx-scaffold-v1") throw new Error(`Not scaffold-owned: ${file}`);
return value;
};
/** Recipes describe structural edits; planStructure owns validation/journaling.
* Implementation files are created once and never rewritten by refresh. */
export const scaffoldRecipe = async (root: string, command: "package" | "function" | "migration" | "refresh", spec: ScaffoldRecipe): Promise<StructuralRequest> => {
source(spec.source);
if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory)) throw new Error("Scaffold directory must be contained");
const prefix = spec.directory ? `${spec.directory}/` : "";
const files: StructuralRequest["files"] = [];
const create = (file: string, content: string) => files.push({file: prefix + file, create: content});
const generated = (file: string, content: string) => files.push({file: prefix + file, generated: content});
let registry: Registry;
let catalog: MigrationCatalog & {generatedBy: "qx-scaffold-v1"};
if (command === "package") {
const name = safeName(spec.name);
if (!spec.id || !spec.revision || !spec.tools) throw new Error("Package scaffold requires id, revision, and exact quixos/protocol/helpers/sdk tool sources");
Object.values(spec.tools).forEach(source);
registry = {generatedBy: "qx-scaffold-v1", name, id: spec.id, revision: spec.revision, exports: []};
catalog = {generatedBy: "qx-scaffold-v1", schemaVersion: 1, contracts: {}, migrations: []};
create("package.qx", `package ${name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`);
create("quixos.lock", formatQuixosLock({formatVersion: 1, quixos: source(spec.tools.quixos), resources: []}));
create("package.json", json({name: `@quixos/${name.toLowerCase()}`, version: "0.1.0", private: true, type: "module", packageManager: "yarn@4.18.0",
scripts: {build: "tsc -p tsconfig.json", typecheck: "tsc --noEmit"}, dependencies: {"@quixos/camino-package-runtime": `${spec.tools.sdk.repository}#commit=${spec.tools.sdk.commit}`},
devDependencies: {"@types/node": "^24", typescript: "^7.0.2"}}));
create("tsconfig.json", json({compilerOptions: {target: "ES2023", module: "NodeNext", moduleResolution: "NodeNext", strict: true, outDir: "dist", skipLibCheck: true}, include: ["src/**/*.ts"]}));
create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n");
create(".yarnrc.yml", `nodeLinker: node-modules\nenableScripts: true\nnpmMinimalAgeGate: 0\napprovedGitRepositories:\n - ${JSON.stringify(spec.tools.sdk.repository)}\nsupportedArchitectures:\n os: [current, linux]\n cpu: [current, x64, arm64]\n libc: [current, glibc]\n`);
const nixifyPluginUrl = spec.nixifyPluginUrl ?? "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js";
const plugin = new URL(nixifyPluginUrl);
if (plugin.protocol !== "https:" || plugin.username || plugin.password || plugin.search || plugin.hash || !/\/commit\/[a-f0-9]{40,64}\//.test(plugin.pathname)) throw new Error("Nixify plugin must have an exact credential-free HTTPS commit URL");
generated("quixos.toolchain.json", json({generatedBy: "qx-scaffold-v1", nixifyPluginUrl}));
create("quixos.check.json", json({backend: "typescript", bindingOutput: "src/generated-bindings.ts"}));
create("flake.nix", `{
inputs.protocol.url = ${nixString(nixSource(spec.tools.protocol))};
inputs.nixpkgs.follows = "protocol/nixpkgs";
inputs.flake-utils.follows = "protocol/flake-utils";
inputs.helpers = { url = ${nixString(nixSource(spec.tools.helpers))}; flake = false; };
outputs = inputs@{ self, protocol, nixpkgs, flake-utils, helpers, ... }:
(import (toString helpers + "/quixos-package-helpers.nix")).mkCaminoTsYarnNixifyFlake {
inherit inputs nixpkgs flake-utils; packageRoot = ./.;
bindings = { system, ... }: {
generator = (builtins.getAttr system protocol.packages).default;
repository = ${nixString(spec.source.repository)};
commit = self.rev or (throw "Publish an exact package revision before building an activation artifact");
packageRevisionId = ${nixString(spec.revision)};
output = "src/generated-bindings.ts";
resources = map (entry: entry // { directory = builtins.fetchGit { url = entry.repository; rev = entry.commit; ref = "refs/tags/quixos-reachability/" + entry.commit; }; }) (builtins.fromJSON (builtins.readFile ./quixos.resources.json)).resources;
};
bundle = { entry = "dist/server.js"; };
migrationEntrypoint = "dist/migrate.js";
installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; };
};
}\n`);
generated("quixos.resources.json", json({generatedBy: "qx-scaffold-v1", resources: []}));
} else {
registry = await ownedJson<Registry>(root, prefix + "quixos.scaffold.json");
catalog = await ownedJson<typeof catalog>(root, prefix + "quixos.migrations.json");
if (command !== "refresh") {
const name = safeName(spec.name);
if (!spec.id || registry.exports.some((entry) => entry.id === spec.id || entry.name === name)) throw new Error("New export requires a unique name and ID");
const declaration = spec.declaration ?? `function ${name} id ${JSON.stringify(spec.id)} : unit -> unit;`;
const parsed = parseQx(`package Scaffold id "scaffold" revision "scaffold@1" { ${declaration} }`);
const exports = [...walkSyntax(parsed.root)].filter((node) => ["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind));
if (parsed.diagnostics.length || exports.length !== 1) throw new Error("Expected one valid package export declaration");
const wrapped = `package Scaffold id "scaffold" revision "scaffold@1" { ${declaration} }`;
const node = exports[0];
const derived = [...walkSyntax(node)].some((entry) => entry.kind === "eventClause");
if (command === "migration" && (node.kind !== "packageFunctionExport" || spec.declaration)) throw new Error("Migration exports use the scaffold's unit function declaration and dedicated migration entrypoint");
if (wrapped.slice(node.children.find((child) => child.kind === "identifier")!.start, node.children.find((child) => child.kind === "identifier")!.end) !== name
|| JSON.parse(wrapped.slice(node.children.find((child) => child.kind === "stringLiteral")!.start, node.children.find((child) => child.kind === "stringLiteral")!.end)) !== spec.id) throw new Error("Declaration name/ID must match its registration");
files.push({file: prefix + "package.qx", edits: [{operation: "append", parent: {kind: "packageResourceDecl", id: registry.id}, source: declaration}]});
const file = `src/${command === "migration" ? "migrations" : "impl"}/${name}.ts`;
const implementation = command === "migration" ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise<void> => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n`
: `import type {Implementation} from "../generated-bindings.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`;
create(file, implementation);
registry.exports.push({name, id: spec.id, file, ...(command === "migration" ? {migration: true} : {})});
if (command === "migration") {
if (!spec.migration) throw new Error("Migration scaffold requires retained contracts and an explicit transition");
const {contracts, ...transition} = spec.migration;
for (const [digest, contract] of Object.entries(contracts)) {
if (contentDigest(contract) !== digest || (catalog.contracts[digest] && contentDigest(catalog.contracts[digest]) !== digest)) throw new Error("Retained migration contract mismatch");
catalog.contracts[digest] = contract;
}
catalog.migrations.push({...transition, implementation: {exportId: spec.id, file, digest: contentDigest(implementation)}});
}
}
if (command === "refresh") for (const migration of catalog.migrations) {
const file = path.join(root, prefix, migration.implementation.file);
if (!(await fs.realpath(file)).startsWith(`${await fs.realpath(path.join(root, prefix))}/`)) throw new Error("Migration implementation escapes package");
migration.implementation.digest = contentDigest(await fs.readFile(file, "utf8"));
}
}
validateMigrationCatalog(catalog, new Set(registry.exports.map((entry) => entry.id)));
generated("quixos.scaffold.json", json(registry));
generated("quixos.migrations.json", json(catalog));
generated("src/server.ts", marker + `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./generated-bindings.js";\n` + registry.exports.filter((entry) => !entry.migration).map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") +
`servePackageRuntime(createRuntime({\n` + registry.exports.map((entry) => ` ${JSON.stringify(entry.name)}: ${entry.migration ? 'async () => { throw new Error("Migration-only export"); }' : `impl${registry.exports.filter((value) => !value.migration).indexOf(entry)}`},`).join("\n") + `\n}));\n`);
const migrations = registry.exports.filter((entry) => entry.migration);
generated("src/migrate.ts", marker + `import {serveMigration} from "@quixos/camino-package-runtime";\n` + migrations.map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`);
generated("descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(registry.id)}\npackage_revision_id: ${JSON.stringify(registry.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + registry.exports.map((entry) => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`).join(""));
return {kind: "package", source: spec.source, resourceRoot: spec.directory, files};
};
+135
View File
@@ -0,0 +1,135 @@
import { CharStream, CommonTokenStream } from "antlr4ng";
import { QuixosLockLexer } from "../resource-lock/generated/QuixosLockLexer.js";
import { QuixosLockParser } from "../resource-lock/generated/QuixosLockParser.js";
import { parseQuixosLockDocument } from "../resource-lock/parser.js";
import { parseQx, walkSyntax, applySourceEdits, type SyntaxNode } from "./source.js";
export type StructuralSelector = { kind: string; id?: string; name?: string; names?: string[] };
export type StructuralEdit =
| { operation: "append"; parent: StructuralSelector; source: string }
| { operation: "replace"; target: StructuralSelector; source: string }
| { operation: "remove"; target: StructuralSelector }
| { operation: "import"; kind: "interface" | "package"; name: string }
| { operation: "semantic-major"; target: StructuralSelector; major: number }
| { operation: "conformance-id"; target: StructuralSelector; id: string }
| { operation: "quixos-pin"; source: {repository: string; commit: string} }
| { operation: "dependency"; kind: "interface" | "package"; name: string; source: {repository: string; commit: string} | null };
// Deliberately exclude valueType/identifier/stringLiteral: callers operate on
// declaration structure, not arbitrary token offsets or lockfile text patches.
const selectable = new Set(["workspaceDecl", "fragmentDecl", "interfaceResourceDecl", "packageResourceDecl", "atomDecl",
"valueMember", "relationshipMember", "operationMember", "packageOperationExport", "packageFunctionExport", "packageConstructorExport",
"conformanceDecl", "stateDecl", "edgeDecl", "constructorBindingDecl", "resourceImportDecl", "sourceImportDecl", "operationBindingDecl"]);
const select = (source: string, selector: StructuralSelector): {node: SyntaxNode; syntax: ReturnType<typeof parseQx>} => {
if (!selectable.has(selector.kind)) throw new Error(`Unsupported structural selector ${selector.kind}`);
const syntax = parseQx(source);
if (syntax.diagnostics.length) throw new Error("Cannot scaffold syntactically invalid QX");
const matches = [...walkSyntax(syntax.root)].filter((node) => {
if (node.kind !== selector.kind) return false;
if (selector.name && !node.children.some((child) => child.kind === "identifier" && source.slice(child.start, child.end) === selector.name)) return false;
if (selector.names && JSON.stringify(node.children.filter((child) => child.kind === "identifier").map((child) => source.slice(child.start, child.end))) !== JSON.stringify(selector.names)) return false;
if (selector.id) {
// Only an explicit ID field counts, not a coincidentally equal revision,
// default value, nested declaration, or comment.
const tokens = syntax.tokens.filter((token) => !token.trivia && token.start >= node.start && token.end <= node.end);
const literal = node.children.find((child) => child.kind === "stringLiteral" && tokens.some((token, index) => token.start === child.start && tokens[index - 1]?.kind === "ID"));
if (!literal || JSON.parse(source.slice(literal.start, literal.end)) !== selector.id) return false;
}
return true;
});
if (matches.length !== 1) throw new Error(`Structural selector must resolve exactly once (found ${matches.length})`);
return {node: matches[0], syntax};
};
/** Comment-preserving structural edits; every result is parsed before returning. */
export const editStructure = (source: string, edit: StructuralEdit): string => {
if (edit.operation === "quixos-pin") {
const parsed = parseQuixosLockDocument(source);
if (!parsed.ok || parsed.document.kind !== "root") throw new Error("Quixos pins belong in a valid root lockfile");
const parser = new QuixosLockParser(new CommonTokenStream(new QuixosLockLexer(CharStream.fromString(source))));
const entries = parser.document().quixosEntry();
if (entries.length !== 1) throw new Error("Expected one Quixos source declaration");
const literals = entries[0].quixosSourceBlock().stringLiteral();
const offsets = [0];
for (const character of source) offsets.push(offsets[offsets.length - 1] + character.length);
const result = applySourceEdits(source, [
{start: offsets[literals[0].start!.start], end: offsets[literals[0].stop!.stop + 1], text: JSON.stringify(edit.source.repository)},
{start: offsets[literals[literals.length - 1].start!.start], end: offsets[literals[literals.length - 1].stop!.stop + 1], text: JSON.stringify(edit.source.commit)},
]);
const checked = parseQuixosLockDocument(result);
if (!checked.ok) throw new Error(`Invalid Quixos pin: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`);
return result;
}
if (edit.operation === "import") {
if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) throw new Error("Invalid resource import");
const syntax = parseQx(source);
if (syntax.diagnostics.length) throw new Error("Cannot scaffold invalid QX");
const text = `import ${edit.kind} ${edit.name};`;
if ([...walkSyntax(syntax.root)].some((entry) => entry.kind === "resourceImportDecl" && source.slice(entry.start, entry.end).replace(/\s+/g, " ") === text)) return source;
const root = syntax.root.children[0];
const position = ["workspaceDecl", "fragmentDecl"].includes(root.kind) ? syntax.tokens.find((token) => token.kind === "LBRACE")!.end : root.start;
const result = applySourceEdits(source, [{start: position, end: position, text: `\n${text}\n`}]);
if (parseQx(result).diagnostics.length) throw new Error("Invalid resource import position");
return result;
}
if (edit.operation === "dependency") {
const parsed = parseQuixosLockDocument(source);
if (!parsed.ok) throw new Error("Cannot scaffold an invalid lockfile");
if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) throw new Error("Invalid dependency selector");
const parser = new QuixosLockParser(new CommonTokenStream(new QuixosLockLexer(CharStream.fromString(source))));
const tree = parser.document();
const offsets = [0];
for (const character of source) offsets.push(offsets[offsets.length - 1] + character.length);
const entries = tree.resourceEntry().filter((entry) => entry.resourceKind().getText() === edit.kind && entry.identifier().getText() === edit.name);
if (entries.length > 1) throw new Error("Ambiguous dependency selector");
const entry = entries[0];
const replacement = edit.source ? `${edit.kind} ${edit.name} source {\n repository ${JSON.stringify(edit.source.repository)};\n commit ${JSON.stringify(edit.source.commit)};\n}` : "";
if (!entry && !edit.source) throw new Error("Cannot remove an absent dependency");
const start = entry ? offsets[entry.start!.start] : offsets[tree.RBRACE().symbol.start];
const end = entry ? offsets[entry.stop!.stop + 1] : start;
const result = applySourceEdits(source, [{start, end, text: entry ? replacement : `${replacement}\n`}]);
const checked = parseQuixosLockDocument(result);
if (!checked.ok) throw new Error(`Invalid dependency change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`);
return result;
}
const {node, syntax} = select(source, edit.operation === "append" ? edit.parent : edit.target);
let result: string;
if (edit.operation === "semantic-major") {
if (!["conformanceDecl", "packageResourceDecl"].includes(node.kind) || !Number.isSafeInteger(edit.major) || edit.major < 1) throw new Error("Semantic major requires a package/conformance and positive integer");
const tokens = syntax.tokens.filter((token) => !token.trivia && token.start >= node.start && token.end <= node.end);
const marker = tokens.findIndex((token) => token.kind === "SEMANTIC_MAJOR");
const value = marker < 0 ? undefined : tokens[marker + 1];
const brace = tokens.find((token) => token.kind === "LBRACE")!;
result = applySourceEdits(source, [{start: value?.start ?? brace.start, end: value?.end ?? brace.start, text: value ? String(edit.major) : `semantic-major ${edit.major} `}]);
} else if (edit.operation === "conformance-id") {
if (node.kind !== "conformanceDecl" || !edit.id) throw new Error("Identity enrollment requires a conformance and stable ID");
const existing = node.children.find((entry) => entry.kind === "stringLiteral");
if (existing) {
if (JSON.parse(source.slice(existing.start, existing.end)) !== edit.id) throw new Error("Cannot change an enrolled conformance identity; create a new conformance explicitly");
return source;
}
const identifiers = node.children.filter((entry) => entry.kind === "identifier");
const position = identifiers[identifiers.length - 1].end;
result = applySourceEdits(source, [{start: position, end: position, text: ` id ${JSON.stringify(edit.id)}`}]);
} else if (edit.operation === "append") {
const closing = syntax.tokens.find((token) => token.kind === "RBRACE" && token.end === node.end);
if (!closing) throw new Error("Append requires a declaration with a body");
result = applySourceEdits(source, [{start: closing.start, end: closing.start, text: `\n${edit.source}\n`}]);
} else {
const wrapper = edit.operation === "remove" && ["stateDecl", "edgeDecl"].includes(node.kind)
? [...walkSyntax(syntax.root)].filter((entry) => ["conformanceItem", "sharedAttachmentDecl"].includes(entry.kind) && entry.start <= node.start && entry.end >= node.end).sort((a, b) => (a.end - a.start) - (b.end - b.start))[0]
: undefined;
result = applySourceEdits(source, [{start: wrapper?.start ?? node.start, end: wrapper?.end ?? node.end, text: edit.operation === "replace" ? edit.source : ""}]);
}
const checked = parseQx(result);
if (checked.diagnostics.length) throw new Error(`Invalid structural change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`);
return result;
};
export const scaffoldResourceSource = (kind: "interface" | "package", name: string, id: string, revision: string) => {
if (!/^[A-Z][A-Za-z0-9]*$/.test(name) || !id || !revision) throw new Error("Resource scaffold requires a PascalCase name and explicit identities");
const source = `${kind} ${name} id ${JSON.stringify(id)} revision ${JSON.stringify(revision)} {\n}\n`;
if (parseQx(source).diagnostics.length) throw new Error("Invalid resource scaffold");
return source;
};
+189
View File
@@ -0,0 +1,189 @@
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { randomUUID } from "node:crypto";
import { contentDigest } from "../capability-model/evolution.js";
import { editStructure, type StructuralEdit } from "./structural-edits.js";
import { snapshotRepository, localResourceSnapshots } from "./candidate-check.js";
import { compileWorkspaceRepository, compileCapabilityResourceRepository } from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import {bindingSchema, generateTypeScriptBindings} from "../bindings/index.js";
export type StructuralRequest = {
kind: "workspace" | "interface" | "package";
source?: {repository: string; commit: string};
resourceRoot?: string;
files: ({file: string; edits: StructuralEdit[]} | {file: string; create: string} | {file: string; generated: string})[];
};
type Change = {file: string; before: string | null; after: string; mode: number};
type Journal = {schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[]};
const safeFile = (file: string) => {
if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|json|nix|txtpb))$/.test(file)
|| file.split("/").some((part) => [".git", ".jj", ".quixos", "node_modules"].includes(part))) throw new Error(`Unsafe scaffold path ${file}`);
};
const read = async (root: string, file: string): Promise<string | null> => {
safeFile(file);
const target = path.join(root, file);
try {
const metadata = await fs.lstat(target);
if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(target)).startsWith(`${root}/`)) throw new Error(`Scaffold target is not a contained regular file: ${file}`);
return await fs.readFile(target, "utf8");
} catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; }
};
const containedParent = async (root: string, file: string) => {
let current = root;
for (const part of file.split("/").slice(0, -1)) {
current = path.join(current, part);
await fs.mkdir(current).catch((error: NodeJS.ErrnoException) => { if (error.code !== "EEXIST") throw error; });
const metadata = await fs.lstat(current);
if (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new Error("Scaffold parent must be a real directory");
}
};
const durableJson = async (file: string, value: unknown) => {
const temporary = `${file}.${randomUUID()}.tmp`;
const handle = await fs.open(temporary, "wx", 0o600);
try { await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`); await handle.sync(); } finally { await handle.close(); }
await fs.rename(temporary, file);
const directory = await fs.open(path.dirname(file), "r");
try { await directory.sync(); } finally { await directory.close(); }
};
/** Validate the entire edited resource graph in a private snapshot before writes. */
export const planStructure = async (rootPath: string, request: StructuralRequest, snapshotMap?: string) => {
const root = await fs.realpath(rootPath);
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structure-"));
try {
const snapshot = await snapshotRepository(root, path.join(temporary, "source"));
const observed = await Promise.all(snapshot.files.map(async ({name}) => ({file: name, digest: contentDigest(await fs.readFile(path.join(snapshot.directory, name), "utf8"))})));
const changes: Change[] = [];
if (!Array.isArray(request.files) || !request.files.length || request.files.length > 100) throw new Error("Structural plan requires 1100 files");
for (const input of request.files) {
safeFile(input.file);
if (changes.some((entry) => entry.file === input.file)) throw new Error("Repeated structural file target");
const before = await read(root, input.file);
let after: string;
if ("create" in input) {
if (before !== null || typeof input.create !== "string") throw new Error("Scaffold creation cannot replace an existing file");
after = input.create;
} else if ("generated" in input) {
const generated = (text: string) => text.startsWith("// Generated by qx-scaffold-v1\n") || text.startsWith("# Generated by qx-scaffold-v1\n") || (() => {try {return JSON.parse(text).generatedBy === "qx-scaffold-v1";} catch {return false;}})();
if (typeof input.generated !== "string" || !generated(input.generated) || (before !== null && !generated(before))) throw new Error("Only scaffold-owned generated files may be regenerated");
after = input.generated;
} else {
if (before === null || !Array.isArray(input.edits)) throw new Error("Structural edit requires an existing source");
after = input.edits.reduce(editStructure, before);
}
if (Buffer.byteLength(after) > 1024 * 1024) throw new Error("Scaffold file exceeds 1 MiB");
const mode = before === null ? 0o644 : (await fs.stat(path.join(root, input.file))).mode & 0o777;
changes.push({file: input.file, before, after, mode});
await containedParent(snapshot.directory, input.file);
await fs.writeFile(path.join(snapshot.directory, input.file), after);
}
const localMap = path.join(temporary, "local-resources.json");
await fs.writeFile(localMap, JSON.stringify(await localResourceSnapshots(root, snapshotMap)));
const resolveResource = await createGitCapabilityResolver({checkoutRoot: path.join(temporary, "resources"), snapshotMap: localMap});
if (request.resourceRoot && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(request.resourceRoot)) throw new Error("Resource root must be a contained relative directory");
const resourceRoot = path.join(snapshot.directory, request.resourceRoot ?? "");
if (request.kind === "workspace") await compileWorkspaceRepository({rootDirectory: resourceRoot, resolveResource});
else if (["package", "interface"].includes(request.kind) && request.source) {
const compiled = await compileCapabilityResourceRepository({rootDirectory: resourceRoot, kind: request.kind as "package" | "interface", source: {resolver: "git", ...request.source}, resolveResource});
let scaffoldOwned = false;
try { scaffoldOwned = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.scaffold.json"), "utf8")).generatedBy === "qx-scaffold-v1"; } catch { /* ordinary resource, no generated package scaffolding */ }
if (scaffoldOwned && compiled.resource.kind === "package") {
const configuration = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.check.json"), "utf8"));
const artifacts = [
{file: configuration.bindingOutput as string, after: generateTypeScriptBindings(bindingSchema(compiled), compiled.resource.revision.revisionId, configuration.options)},
{file: "quixos.resources.json", after: JSON.stringify({generatedBy: "qx-scaffold-v1", resources: compiled.resources.filter((entry) => entry.directory !== resourceRoot).map((entry) => ({kind: entry.kind, repository: entry.source.repository, commit: entry.source.commit}))}, null, 2) + "\n"},
];
for (const artifact of artifacts) {
const file = request.resourceRoot ? `${request.resourceRoot}/${artifact.file}` : artifact.file;
safeFile(file);
if (Buffer.byteLength(artifact.after) > 1024 * 1024) throw new Error("Generated scaffold file exceeds 1 MiB");
const before = await read(root, file);
if (before !== null && !before.startsWith("// Generated by quixos-codegen-ts.") && (() => {try {return JSON.parse(before).generatedBy !== "qx-scaffold-v1";} catch {return true;}})()) throw new Error(`Refusing to overwrite hand-authored generated artifact ${file}`);
const previous = changes.find((entry) => entry.file === file);
if (previous) previous.after = artifact.after;
else changes.push({file, before, after: artifact.after, mode: 0o644});
}
}
}
else throw new Error("Resource plans require kind and exact authored source identity");
if (changes.length > 100) throw new Error("Structural plan including generated artifacts exceeds 100 files");
// Validation may fetch dependencies; reject edits made while it was running.
for (const entry of changes) if (await read(root, entry.file) !== entry.before) throw new Error(`Source changed while planning: ${entry.file}`);
for (const entry of observed) if (contentDigest(await fs.readFile(path.join(root, entry.file), "utf8")) !== entry.digest) throw new Error(`Validation input changed while planning: ${entry.file}`);
return {root, changes, observed, digest: contentDigest(changes), validation: "resource-graph" as const};
} finally { await fs.rm(temporary, {recursive: true, force: true}); }
};
/** Replay only exact before/after states. A crash never loses the original text. */
const replayStructure = async (rootPath: string, id: string) => {
const root = await fs.realpath(rootPath);
if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid scaffold journal ID");
const journalPath = path.join(root, ".quixos", "scaffolds", `${id}.json`);
const journal = JSON.parse(await fs.readFile(journalPath, "utf8")) as Journal;
if (journal.schemaVersion !== 1 || journal.root !== root || journal.id !== id) throw new Error("Scaffold journal identity mismatch");
for (const entry of journal.changes) {
const current = await read(root, entry.file);
if (current !== entry.before && current !== entry.after) throw new Error(`Scaffold conflicts with newer edits: ${entry.file}; original text is retained in ${journalPath}`);
}
if (journal.phase === "complete") return {id, journalPath, phase: journal.phase};
for (const entry of journal.changes) {
if (await read(root, entry.file) === entry.after) continue;
await containedParent(root, entry.file);
const target = path.join(root, entry.file);
const temporary = `${target}.qx-${randomUUID()}.tmp`;
const handle = await fs.open(temporary, "wx", entry.mode);
try { await handle.writeFile(entry.after); await handle.sync(); } finally { await handle.close(); }
if (entry.before === null) {
// link is atomic and fails if another author created the destination.
await fs.link(temporary, target);
await fs.unlink(temporary);
} else {
if (await read(root, entry.file) !== entry.before) throw new Error(`Source changed during scaffold: ${entry.file}`);
await fs.rename(temporary, target);
}
const directory = await fs.open(path.dirname(target), "r");
try { await directory.sync(); } finally { await directory.close(); }
}
journal.phase = "complete";
await durableJson(journalPath, journal);
return {id, journalPath, phase: journal.phase};
};
const withStructureLock = async <T>(root: string, work: () => Promise<T>) => {
await containedParent(root, ".quixos/scaffolds/placeholder.json");
const lock = path.join(root, ".quixos", "scaffolds", "writer.lock");
// Never steal a possibly live writer's lock. A process crash requires the
// operator to verify that writer is gone, remove this lock, then resume its
// journal. This is deliberately fail-closed instead of guessing from a PID.
const handle = await fs.open(lock, "wx", 0o600).catch((error) => {
if ((error as NodeJS.ErrnoException).code === "EEXIST") throw new Error(`Another scaffold writer or interrupted writer owns ${lock}; verify it has exited before removing its lock and resuming`);
throw error;
});
try { await handle.writeFile(JSON.stringify({pid: process.pid})); await handle.sync(); return await work(); }
finally { await handle.close(); await fs.unlink(lock); }
};
export const resumeStructure = async (rootPath: string, id: string) => {
const root = await fs.realpath(rootPath);
return withStructureLock(root, () => replayStructure(root, id));
};
export const applyStructure = async (plan: Awaited<ReturnType<typeof planStructure>>, id: string = randomUUID()) => withStructureLock(plan.root, async () => {
if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid scaffold journal ID");
const directory = path.join(plan.root, ".quixos", "scaffolds");
try {
const existing = JSON.parse(await fs.readFile(path.join(directory, `${id}.json`), "utf8")) as Journal;
if (existing.root !== plan.root || contentDigest(existing.changes) !== plan.digest) throw new Error("Scaffold journal identity conflict");
for (const entry of plan.observed) if (!plan.changes.some((change) => change.file === entry.file) && contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest) throw new Error(`Stale scaffold validation input: ${entry.file}`);
return replayStructure(plan.root, id);
} catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;}
// An unfinished journal must be recovered before another structural mutation.
for (const file of await fs.readdir(directory)) if (file.endsWith(".json")) {
const prior = JSON.parse(await fs.readFile(path.join(directory, file), "utf8")) as Journal;
if (prior.phase !== "complete") throw new Error(`Unfinished scaffold ${prior.id}; resume it first`);
}
for (const entry of plan.changes) if (await read(plan.root, entry.file) !== entry.before) throw new Error(`Stale scaffold plan: ${entry.file}`);
for (const entry of plan.observed) if (contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest) throw new Error(`Stale scaffold validation input: ${entry.file}`);
await durableJson(path.join(directory, `${id}.json`), {schemaVersion: 1, id, root: plan.root, phase: "prepared", changes: plan.changes} satisfies Journal);
return replayStructure(plan.root, id);
});
+97 -1
View File
@@ -1,11 +1,107 @@
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import { readFile, writeFile, mkdtemp, rm } from "node:fs/promises";
import { parseQx, formatQx, lintQx } from "./source.js";
import { scaffoldAtom } from "./scaffold.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import { planEvolution } from "../capability-model/index.js";
import { checkWorkspaceCandidate, checkResourceCandidate, snapshotRepository } from "./candidate-check.js";
import {planPinUpgrades, applyPinUpgrades, discoverUpgradeSpec, type UpgradeSpec} from "./pin-upgrades.js";
import path from "node:path";
import os from "node:os";
import {spawnSync} from "node:child_process";
import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "./structural-plan.js";
import {scaffoldRecipe, type ScaffoldRecipe} from "./scaffold-recipes.js";
const main = async () => {
const [command, ...args] = process.argv.slice(2);
if (command === "source-digest") {
if (!args[0] || args.length !== 1) throw new Error("usage: quixos-qx source-digest ROOT");
const temporary = await mkdtemp(path.join(os.tmpdir(), "qx-source-digest-"));
try {process.stdout.write(`${(await snapshotRepository(args[0], temporary)).treeDigest}\n`);} finally {await rm(temporary, {recursive: true, force: true});}
return;
}
if (command === "pin-upgrade") {
const [workbench, specFile, ...flags] = args;
if (!workbench || !specFile) throw new Error("usage: quixos-qx pin-upgrade WORKBENCH SPEC_JSON [--publish] [--resume UUID]");
let publish = false, acceptEdits = false, resume: string | undefined;
for (let index = 0; index < flags.length; index++) {
if (flags[index] === "--publish") publish = true;
else if (flags[index] === "--accept-edits") acceptEdits = true;
else if (flags[index] === "--resume" && /^[a-f0-9-]{36}$/.test(flags[index + 1] ?? "")) resume = flags[++index];
else throw new Error(`Unknown pin-upgrade option ${flags[index]}`);
}
if (acceptEdits && !resume) throw new Error("--accept-edits requires an existing refactor journal (--resume)");
const plan = resume ? JSON.parse(await readFile(path.join(workbench, ".quixos/upgrades", `${resume}.json`), "utf8")).plan
: await planPinUpgrades(workbench, specFile === "auto" ? await discoverUpgradeSpec(workbench) : JSON.parse(await readFile(specFile, "utf8")) as UpgradeSpec);
if (resume && path.resolve(workbench) !== plan.workbench) throw new Error("Upgrade journal belongs to another workbench");
process.stdout.write(`${JSON.stringify(publish ? await applyPinUpgrades(plan, resume, undefined, {acceptEdits}) : plan, null, 2)}\n`);
return;
}
if (command === "check-resource") {
const [root, kind, repository, commit, output, ...extra] = args;
if (!root || !output || !["package", "interface"].includes(kind) || extra.length) throw new Error("usage: quixos-qx check-resource ROOT package|interface REPOSITORY COMMIT OUTPUT");
const result = await checkResourceCandidate({root, kind: kind as "package" | "interface", source: {repository, commit}, output, publishedOnly: true});
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
if (result.blockers.length) process.exitCode = 1;
return;
}
if (["scaffold-package", "scaffold-function", "scaffold-migration", "scaffold-refresh"].includes(command)) {
const [root, specFile, ...flags] = args;
if (!root || !specFile || flags.some((flag) => !["--write", "--install"].includes(flag)) || (flags.includes("--install") && !flags.includes("--write"))) throw new Error("usage: quixos-qx scaffold-package|function|migration|refresh ROOT SPEC_JSON [--write [--install]]");
const spec = JSON.parse(await readFile(specFile, "utf8")) as ScaffoldRecipe;
const request = await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "migration" | "refresh", spec);
const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP);
const applied = flags.includes("--write") ? await applyStructure(plan) : undefined;
if (flags.includes("--install")) {
const cwd = path.resolve(root, spec.directory ?? "");
const toolchain = JSON.parse(await readFile(path.join(cwd, "quixos.toolchain.json"), "utf8"));
if (toolchain.generatedBy !== "qx-scaffold-v1" || typeof toolchain.nixifyPluginUrl !== "string") throw new Error("Missing scaffold toolchain");
for (const [executable, args] of [["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]], ["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]], ["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]], ["corepack", ["yarn", "install"]], ["corepack", ["yarn", "typecheck"]], ["nix", ["flake", "lock"]]] as const) {
const result = spawnSync(executable, [...args], {cwd, stdio: ["inherit", 2, 2]});
if (result.error || result.status !== 0) throw new Error(`Scaffold files retained; ${executable} ${args.join(" ")} failed: ${result.error?.message ?? result.status}`);
}
}
process.stdout.write(`${JSON.stringify({...plan, applied}, null, 2)}\n`);
return;
}
if (command === "scaffold-structure") {
const [root, spec, ...flags] = args;
if (!root || !spec || flags.some((flag) => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-structure ROOT SPEC_JSON [--write]");
const request = JSON.parse(await readFile(spec, "utf8")) as StructuralRequest;
const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP);
const applied = flags.includes("--write") ? await applyStructure(plan) : undefined;
process.stdout.write(`${JSON.stringify({...plan, applied}, null, 2)}\n`);
return;
}
if (command === "scaffold-resume") {
const [root, id, ...extra] = args;
if (!root || !id || extra.length) throw new Error("usage: quixos-qx scaffold-resume ROOT JOURNAL_ID");
process.stdout.write(`${JSON.stringify(await resumeStructure(root, id), null, 2)}\n`);
return;
}
if (command === "check") {
const [root, output, ...flags] = args;
if (!root || !output || flags.length % 2) throw new Error("usage: quixos-qx check ROOT OUTPUT [--snapshot-map FILE] [--baseline FILE] [--reviews FILE]");
const values = new Map<string, string>();
for (let index = 0; index < flags.length; index += 2) {
if (!["--snapshot-map", "--baseline", "--reviews"].includes(flags[index])) throw new Error(`Unknown check option ${flags[index]}`);
values.set(flags[index], flags[index + 1]);
}
const result = await checkWorkspaceCandidate({ root, output, snapshotMap: values.get("--snapshot-map"), baseline: values.get("--baseline"), reviews: values.get("--reviews") });
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
if (result.blockers.length) process.exitCode = 1;
return;
}
if (command === "evolution") {
const [baseline, candidate, reviews, ...extra] = args;
if (!baseline || !candidate || extra.length) throw new Error("usage: quixos-qx evolution BASELINE_JSON CANDIDATE_JSON [REVIEWS_JSON]");
const before = baseline === "none" ? null : JSON.parse(await readFile(baseline, "utf8"));
const after = JSON.parse(await readFile(candidate, "utf8"));
const decisions = reviews ? JSON.parse(await readFile(reviews, "utf8")) : [];
if (!Array.isArray(decisions)) throw new Error("Reviews must be an array");
process.stdout.write(`${JSON.stringify(planEvolution(before, after, { reviews: decisions }), null, 2)}\n`);
return;
}
if (command === "scaffold-atom") {
const [root, name, id, ...flags] = args;
if (!root || !name || !id || flags.some((flag) => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-atom ROOT NAME ID [--write]");
+15 -3
View File
@@ -1,13 +1,15 @@
#!/usr/bin/env node
import { writeFile } from "node:fs/promises";
import { readFile, writeFile } from "node:fs/promises";
import process from "node:process";
import { compileWorkspaceRepository } from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import { planEvolution, runtimeContracts, type EvolutionReview, type WorkspaceRevision } from "../capability-model/index.js";
const usage = `usage: quixos-workspace-compile --root DIRECTORY --checkout-root DIRECTORY
[--snapshot-map PATH] [--graph-out PATH] [--workspace-id ID] [--workspace-revision-id ID]
[--source-root-commit GIT_REV]
[--source-root-commit GIT_REV] [--baseline PLAN_JSON] [--evolution-out PATH]
[--reviews REVIEW_JSON]
Resolves a workspace's recursive resource-lock graph, clones every exact
resource revision, validates standalone interface/package manifests, and emits
@@ -32,6 +34,9 @@ const parseArgs = (args: string[]) => {
workspaceId: values.get("--workspace-id"),
workspaceRevisionId: values.get("--workspace-revision-id"),
sourceRootCommit: values.get("--source-root-commit"),
baseline: values.get("--baseline"),
evolutionOut: values.get("--evolution-out"),
reviews: values.get("--reviews"),
};
};
@@ -77,7 +82,14 @@ const main = async () => {
})),
}, null, 2)}\n`);
}
process.stdout.write(`${JSON.stringify(assembled.workspace, null, 2)}\n`);
const candidate = { ...assembled.workspace, executionContracts: runtimeContracts(assembled.workspace) };
if (options.evolutionOut) {
const baseline = options.baseline ? JSON.parse(await readFile(options.baseline, "utf8")) as WorkspaceRevision : null;
const reviews = options.reviews ? JSON.parse(await readFile(options.reviews, "utf8")) as EvolutionReview[] : [];
if (!Array.isArray(reviews)) throw new Error("Review file must contain an array");
await writeFile(options.evolutionOut, `${JSON.stringify(planEvolution(baseline, candidate, { reviews }), null, 2)}\n`);
}
process.stdout.write(`${JSON.stringify(candidate, null, 2)}\n`);
};
main().catch((error: unknown) => {
+206
View File
@@ -0,0 +1,206 @@
import { createHash } from "node:crypto";
import type { Binding, Conformance, DependencyBinding, PersistentAttachment, WorkspaceRevision } from "./types.js";
import { validateWorkspaceRevision } from "./validation.js";
/** Content hashing is independent of JSON object insertion order, not array order. */
const compareText = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;
export const canonicalJson = (value: unknown): string => {
if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
if (typeof value === "number" && Number.isFinite(value)) return JSON.stringify(value);
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
if (typeof value === "object" && value !== null) {
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new Error("Expected a plain JSON object");
return `{${Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([a], [b]) => compareText(a, b))
.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
}
throw new Error(`Cannot hash non-JSON value: ${typeof value}`);
};
export const contentDigest = (value: unknown) => `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
const semantic = (value: unknown): unknown => {
if (Array.isArray(value)) return value.map(semantic);
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value)
.filter(([key, entry]) => entry !== undefined && key !== "displayName" && key !== "documentation")
.map(([key, entry]) => [key, semantic(entry)]));
return value;
};
const sorted = <T>(entries: readonly T[], key: (entry: T) => string) => [...entries].sort((a, b) => compareText(key(a), key(b)));
export const conformanceIdentity = (entry: Conformance): string => entry.id ?? `legacy:${entry.atomId}:${entry.interfaceRevisionId}`;
export type StorageContract = { id: string; ownerId: string; kind: "state" | "edge"; digest: string; definition: unknown };
export const storageContracts = (workspace: WorkspaceRevision): StorageContract[] => {
const result: StorageContract[] = [];
const add = (attachment: PersistentAttachment, ownerId: string) => {
const definition = semantic(attachment);
result.push({ id: attachment.id, ownerId, kind: attachment.kind, definition, digest: contentDigest({ ownerId, definition }) });
};
for (const attachment of workspace.sharedAttachments) add(attachment, "legacy:workspace");
for (const conformance of workspace.conformances) for (const attachment of conformance.privateAttachments) add(attachment, conformanceIdentity(conformance));
return sorted(result, (entry) => entry.id);
};
type GraphNode = { value: unknown; dependencies: Set<string>; reviewProviders: Set<string> };
export type RuntimeContract = { groupId: string; packageId: string; packageRevisionId: string; digest: string; reviewProviders: string[]; dependencies: Array<{ id: string; digest: string }> };
/** Build only outbound execution dependencies. Incoming callers never retain or invalidate a provider. */
export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[] => {
const nodes = new Map<string, GraphNode>();
const node = (key: string, value: unknown) => {
const result = { value: semantic(value), dependencies: new Set<string>(), reviewProviders: new Set<string>() };
nodes.set(key, result);
return result;
};
const conformanceKey = (atom: string, iface: string) => `conformance:${atom}:${iface}`;
for (const storage of storageContracts(workspace)) node(`attachment:${storage.id}`, storage);
for (const iface of workspace.interfaceImports) node(`interface:${iface.revisionId}`, {
...iface, members: sorted(iface.members, (entry) => entry.id).map((entry) => ({ ...entry, operations: sorted(entry.operations, (operation) => operation.id) })),
});
for (const pkg of workspace.packageImports) node(`package:${pkg.revisionId}`, {
...pkg, semanticMajor: pkg.semanticMajor ?? 1,
exports: sorted(pkg.exports, (entry) => entry.id).map((entry) => ({ ...entry, dependencyPorts: sorted(entry.dependencyPorts, (port) => port.id) })),
});
const dependency = (parent: GraphNode, binding: DependencyBinding, atomId: string, reviews?: Set<string>) => {
if (binding.kind === "state") parent.dependencies.add(`attachment:${binding.slotId}`);
if (binding.kind === "edge") parent.dependencies.add(`attachment:${binding.edgeTypeId}`);
if (binding.kind === "constructor") {
parent.dependencies.add(`constructor:${binding.atomId}`);
const ctor = workspace.constructors.find((entry) => entry.atomId === binding.atomId);
const pkg = workspace.packageImports.find((entry) => entry.revisionId === ctor?.packageRevisionId);
if (pkg) reviews?.add(pkg.packageId);
}
if (binding.kind !== "constructor" && binding.via) parent.dependencies.add(`attachment:${binding.via.edgeTypeId}`);
if (binding.kind === "interface") {
parent.dependencies.add(`interface:${binding.interfaceRevisionId}`);
// An edge traversal may select any matching target. Conservatively include every possible witness.
for (const conformance of workspace.conformances) {
if (conformance.interfaceRevisionId === binding.interfaceRevisionId && (binding.via || conformance.atomId === atomId)) {
parent.dependencies.add(conformanceKey(conformance.atomId, conformance.interfaceRevisionId));
reviews?.add(conformanceIdentity(conformance));
for (const operation of conformance.operationBindings) {
if (operation.binding.kind !== "package") continue;
const revisionId = operation.binding.packageRevisionId;
const pkg = workspace.packageImports.find((entry) => entry.revisionId === revisionId);
if (pkg) reviews?.add(pkg.packageId);
}
}
}
}
};
const binding = (parent: GraphNode, value: Binding, atomId: string, context: unknown) => {
if (value.kind !== "package") { dependency(parent, value, atomId); return; }
const packageNode = nodes.get(`package:${value.packageRevisionId}`)!;
parent.dependencies.add(`package:${value.packageRevisionId}`);
const normalized = { ...value, dependencies: sorted(value.dependencies, (entry) => entry.portId) };
const key = `binding:${contentDigest({ atomId, context, value: semantic(normalized) })}`;
const bound = node(key, { atomId, context, binding: normalized });
packageNode.dependencies.add(key);
for (const port of value.dependencies) dependency(bound, port.binding, atomId, packageNode.reviewProviders);
};
for (const conformance of workspace.conformances) {
const parent = node(conformanceKey(conformance.atomId, conformance.interfaceRevisionId), {
id: conformanceIdentity(conformance), semanticMajor: conformance.semanticMajor ?? 1,
atomId: conformance.atomId, interfaceRevisionId: conformance.interfaceRevisionId,
operations: sorted(conformance.operationBindings, (entry) => entry.operationId),
materializations: sorted(conformance.relationshipMaterializations, (entry) => entry.memberId),
});
parent.dependencies.add(`interface:${conformance.interfaceRevisionId}`);
for (const attachment of conformance.privateAttachments) parent.dependencies.add(`attachment:${attachment.id}`);
for (const operation of conformance.operationBindings) binding(parent, operation.binding, conformance.atomId, {
conformanceId: conformanceIdentity(conformance), semanticMajor: conformance.semanticMajor ?? 1, operationId: operation.operationId,
});
for (const materialization of conformance.relationshipMaterializations) {
parent.dependencies.add(`constructor:${materialization.constructorAtomId}`);
parent.dependencies.add(`attachment:${materialization.edgeTypeId}`);
}
}
for (const constructor of workspace.constructors) {
const parent = node(`constructor:${constructor.atomId}`, constructor);
binding(parent, { kind: "package", ...constructor }, constructor.atomId, { constructor: constructor.atomId });
}
const counts = new Map<string, number>();
for (const pkg of workspace.packageImports) counts.set(pkg.packageId, (counts.get(pkg.packageId) ?? 0) + 1);
return sorted(workspace.packageImports.map((pkg): RuntimeContract => {
const visited = new Set<string>();
const walk = (key: string) => {
if (visited.has(key)) return;
const entry = nodes.get(key);
if (!entry) throw new Error(`Unresolved execution dependency ${key}`);
visited.add(key);
for (const target of entry.dependencies) walk(target);
};
walk(`package:${pkg.revisionId}`);
const dependencies = [...visited].sort().map((id) => ({ id, digest: contentDigest(nodes.get(id)!.value) }));
return { groupId: counts.get(pkg.packageId) === 1 ? pkg.packageId : `${pkg.packageId}#${pkg.revisionId}`,
packageId: pkg.packageId, packageRevisionId: pkg.revisionId, digest: contentDigest(dependencies),
reviewProviders: [...nodes.get(`package:${pkg.revisionId}`)!.reviewProviders].sort(), dependencies };
}), (entry) => entry.groupId);
};
export type EvolutionReview = { requirementDigest: string; decision: "changed" | "accepted-unchanged"; rationale: string; agentId: string };
export type ReviewRequirement = { consumerId: string; providerId: string; oldMajor: number; newMajor: number; requirementDigest: string };
export type RuntimeAction = { groupId: string; action: "keep" | "start" | "replace" | "retire"; previous?: RuntimeContract; candidate?: RuntimeContract; reasons: string[] };
export type EvolutionReport = {
schemaVersion: 1; baselineDigest: string | null; candidateDigest: string; checkerVersion: string;
runtimeActions: RuntimeAction[];
storageChanges: Array<{ id: string; kind: "add" | "remove" | "change"; previous?: StorageContract; candidate?: StorageContract }>;
reviews: Array<ReviewRequirement & { accepted: boolean }>;
packageChecks: Array<{ groupId: string; contractDigest: string }>;
blockers: string[];
};
export const planEvolution = (baseline: WorkspaceRevision | null, candidate: WorkspaceRevision,
options: { reviews?: EvolutionReview[]; allowLegacy?: boolean } = {}): EvolutionReport => {
const issues = validateWorkspaceRevision(candidate);
if (issues.length) throw new Error(`Invalid candidate workspace:\n${issues.map((entry) => `${entry.path}: ${entry.message}`).join("\n")}`);
if (baseline && baseline.workspaceId !== candidate.workspaceId) throw new Error("Cannot evolve a different workspace");
const candidateDigest = contentDigest(candidate);
const checkerVersion = "quixos-evolution-v1";
const blockers: string[] = [];
if (!options.allowLegacy) {
if (candidate.sharedAttachments.length) blockers.push("Assign legacy workspace-shared attachments to explicit conformance owners");
for (const entry of candidate.conformances) if (!entry.id) blockers.push(`Conformance ${entry.atomId} as ${entry.interfaceRevisionId} requires an authored ID`);
}
const previousRuntimes = new Map((baseline ? runtimeContracts(baseline) : []).map((entry) => [entry.groupId, entry]));
const nextRuntimes = new Map(runtimeContracts(candidate).map((entry) => [entry.groupId, entry]));
const runtimeActions: RuntimeAction[] = [...new Set([...previousRuntimes.keys(), ...nextRuntimes.keys()])].sort().map((groupId) => {
const previous = previousRuntimes.get(groupId), next = nextRuntimes.get(groupId);
const before = new Map(previous?.dependencies.map((entry) => [entry.id, entry.digest]));
const after = new Map(next?.dependencies.map((entry) => [entry.id, entry.digest]));
const reasons = [...new Set([...before.keys(), ...after.keys()])].sort().filter((id) => before.get(id) !== after.get(id));
return { groupId, action: !previous ? "start" : !next ? "retire" : previous.digest === next.digest ? "keep" : "replace",
...(previous ? { previous } : {}), ...(next ? { candidate: next } : {}), reasons };
});
const beforeStorage = new Map((baseline ? storageContracts(baseline) : []).map((entry) => [entry.id, entry]));
const afterStorage = new Map(storageContracts(candidate).map((entry) => [entry.id, entry]));
const storageChanges: EvolutionReport["storageChanges"] = [];
for (const id of [...new Set([...beforeStorage.keys(), ...afterStorage.keys()])].sort()) {
const previous = beforeStorage.get(id), next = afterStorage.get(id);
if (previous?.digest !== next?.digest) storageChanges.push({ id, kind: !previous ? "add" : !next ? "remove" : "change",
...(previous ? { previous } : {}), ...(next ? { candidate: next } : {}) });
}
const providers = (workspace: WorkspaceRevision) => [
...workspace.packageImports.map((entry) => ({ id: entry.packageId as string, revision: entry.revisionId as string, major: entry.semanticMajor ?? 1,
node: `package:${entry.revisionId}`, digest: contentDigest(entry) })),
...workspace.conformances.map((entry) => ({ id: conformanceIdentity(entry), revision: contentDigest(entry), major: entry.semanticMajor ?? 1,
node: `conformance:${entry.atomId}:${entry.interfaceRevisionId}`, digest: contentDigest(entry) })),
];
const oldProviders = baseline ? providers(baseline) : [];
const reviews: EvolutionReport["reviews"] = [];
for (const provider of providers(candidate)) {
const old = oldProviders.filter((entry) => entry.id === provider.id);
if (old.length > 1) { blockers.push(`Ambiguous semantic-major lineage for ${provider.id}`); continue; }
if (!old[0] || old[0].major === provider.major) continue;
if (provider.major < old[0].major) blockers.push(`Semantic major decreases for ${provider.id}`);
for (const consumer of nextRuntimes.values()) {
if (consumer.packageId === provider.id || !consumer.reviewProviders.includes(provider.id)) continue;
const requirement = { consumerId: consumer.groupId, providerId: provider.id, oldMajor: old[0].major, newMajor: provider.major };
const requirementDigest = contentDigest({ ...requirement, oldProvider: old[0].digest, newProvider: provider.digest, consumer: consumer.digest, checkerVersion });
const accepted = (options.reviews ?? []).some((entry) => entry.requirementDigest === requirementDigest &&
["changed", "accepted-unchanged"].includes(entry.decision) && entry.rationale.trim() && entry.agentId.trim());
reviews.push({ ...requirement, requirementDigest, accepted });
if (!accepted) blockers.push(`Semantic-major review required: ${consumer.groupId} consumes ${provider.id}`);
}
}
return { schemaVersion: 1, baselineDigest: baseline ? contentDigest(baseline) : null, candidateDigest, checkerVersion,
runtimeActions, storageChanges, reviews, packageChecks: runtimeActions.filter((entry) => entry.candidate && entry.action !== "keep")
.map((entry) => ({ groupId: entry.groupId, contractDigest: entry.candidate!.digest })), blockers };
};
+2
View File
@@ -1,2 +1,4 @@
export * from "./types.js";
export * from "./validation.js";
export * from "./evolution.js";
export * from "./migrations.js";
+96
View File
@@ -0,0 +1,96 @@
import { contentDigest } from "./evolution.js";
import type { PersistentAttachment } from "./types.js";
/** Portable storage shape: local owner/slot/projection identities are supplied
* by the consuming workspace's explicit bindings, never baked into this hash. */
export const migrationPortContract = (attachment: PersistentAttachment): unknown => {
if (attachment.kind === "state") return {kind: "state", valueType: attachment.valueType, storagePolicy: attachment.storagePolicy,
...(attachment.defaultValue === undefined ? {} : {defaultValue: attachment.defaultValue})};
const endpoint = (value: typeof attachment.endpoints[number]) => ({constraint: value.constraint, cardinality: value.cardinality,
ordered: value.ordered, onDelete: value.onDelete ?? "restrict", retainOther: value.retainOther ?? false,
...(value.keyType ? {keyType: value.keyType} : {}), ...(value.publicTraversal ? {publicTraversal: true} : {})});
return {kind: "edge", first: endpoint(attachment.endpoints[0]), second: endpoint(attachment.endpoints[1])};
};
export type MigrationDeclaration = {
id: string;
scopeId: string;
from: string;
to: string;
implementation: { exportId: string; file: string; digest: string };
predecessors: string[];
ports: { name: string; view: "old" | "new"; access: ("read" | "write" | "create" | "edge")[]; contractDigest: string }[];
preservesOldReaders?: boolean;
preservesOldWriters?: boolean;
};
export type MigrationCatalog = {
schemaVersion: 1;
contracts: Record<string, unknown>;
migrations: MigrationDeclaration[];
};
export const validateMigrationCatalog = (value: unknown, exportIds?: ReadonlySet<string>): MigrationCatalog => {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Migration catalog must be an object");
const catalog = value as MigrationCatalog;
if (catalog.schemaVersion !== 1 || !catalog.contracts || Array.isArray(catalog.contracts) || !Array.isArray(catalog.migrations)) throw new Error("Unsupported migration catalog");
for (const [digest, contract] of Object.entries(catalog.contracts)) if (contentDigest(contract) !== digest) throw new Error(`Migration contract digest mismatch: ${digest}`);
const ids = new Set<string>();
for (const migration of catalog.migrations) {
if (!migration.id || !migration.scopeId || ids.has(migration.id)) throw new Error("Migration IDs must be stable and unique");
ids.add(migration.id);
if (!catalog.contracts[migration.from] || !catalog.contracts[migration.to] || migration.from === migration.to) throw new Error(`Migration ${migration.id} requires distinct retained source and target contracts`);
if (!migration.implementation?.exportId || !/^sha256:[0-9a-f]{64}$/.test(migration.implementation.digest)) throw new Error(`Migration ${migration.id} requires an exact implementation digest`);
if (!migration.implementation.file || migration.implementation.file.startsWith("/") || migration.implementation.file.split(/[\\/]/).some((part) => !part || part === "." || part === "..")) throw new Error("Migration implementation must be a relative package file");
if (exportIds && !exportIds.has(migration.implementation.exportId)) throw new Error(`Migration ${migration.id} refers to an undeclared package export`);
if (!Array.isArray(migration.predecessors) || !Array.isArray(migration.ports)) throw new Error(`Migration ${migration.id} requires predecessors and ports`);
if (new Set(migration.predecessors).size !== migration.predecessors.length) throw new Error(`Duplicate predecessor in ${migration.id}`);
for (const promise of [migration.preservesOldReaders, migration.preservesOldWriters]) if (promise !== undefined && typeof promise !== "boolean") throw new Error("Migration compatibility promises must be booleans");
const ports = new Set<string>();
for (const port of migration.ports) {
if (!port.name || ports.has(port.name) || !["old", "new"].includes(port.view) || !Array.isArray(port.access) || !port.access.length
|| port.access.some((access) => !["read", "write", "create", "edge"].includes(access)) || !catalog.contracts[port.contractDigest]) throw new Error(`Invalid migration port in ${migration.id}`);
if (port.view === "old" && port.access.some((access) => access !== "read")) throw new Error("Old migration views are read-only");
ports.add(port.name);
}
}
for (const migration of catalog.migrations) for (const predecessor of migration.predecessors) if (!ids.has(predecessor)) throw new Error(`Missing retained predecessor ${predecessor}`);
// Catalogs are retained across releases. Reject impossible histories at
// publication/check time, not only when someone tries to select a path.
const remaining = new Map(catalog.migrations.map((entry) => [entry.id, new Set(entry.predecessors)]));
const ready = [...remaining].filter(([, dependencies]) => dependencies.size === 0).map(([id]) => id);
for (let index = 0; index < ready.length; index++) {
remaining.delete(ready[index]);
for (const [id, dependencies] of remaining) if (dependencies.delete(ready[index]) && dependencies.size === 0) ready.push(id);
}
if (remaining.size) throw new Error(`Cyclic migration predecessors: ${[...remaining.keys()].join(", ")}`);
return catalog;
};
export type MigrationSelection = {
scopeId: string; from: string; to: string; path: string[];
bindings: Record<string, string>;
};
/** Explicit paths, not shortest-path guesses. Receipts identify code plus local scope mapping. */
export const selectMigrationPath = (catalog: MigrationCatalog, selection: MigrationSelection, previousReceipts: ReadonlyMap<string, string> = new Map()) => {
validateMigrationCatalog(catalog);
let current = selection.from;
const seen = new Set<string>();
const transitions = [];
for (const id of selection.path) {
const declaration = catalog.migrations.find((entry) => entry.id === id);
if (!declaration || declaration.scopeId !== selection.scopeId || declaration.from !== current || seen.has(id)) throw new Error(`Invalid selected migration transition ${id}`);
for (const predecessor of declaration.predecessors) if (!seen.has(predecessor) && !previousReceipts.has(predecessor)) throw new Error(`Unsatisfied predecessor ${predecessor}`);
const usedBindings: Record<string, string> = {};
for (const port of declaration.ports) {
if (!selection.bindings[port.name]) throw new Error(`Missing local migration binding ${port.name}`);
usedBindings[port.name] = selection.bindings[port.name];
}
const digest = contentDigest({ declaration, bindings: usedBindings });
const previous = previousReceipts.get(id);
if (previous && previous !== digest) throw new Error(`Migration identity ${id} was previously used with different code or scope`);
transitions.push({ declaration, bindings: usedBindings, digest, alreadyApplied: Boolean(previous) });
seen.add(id);
current = declaration.to;
}
if (current !== selection.to) throw new Error("Selected migration path does not cover the target storage contract");
return transitions;
};
+12
View File
@@ -7,6 +7,7 @@ type OpaqueId<Kind extends string> = string & {
export type WorkspaceId = OpaqueId<"WorkspaceId">;
export type WorkspaceRevisionId = OpaqueId<"WorkspaceRevisionId">;
export type AtomId = OpaqueId<"AtomId">;
export type ConformanceId = OpaqueId<"ConformanceId">;
export type InterfaceId = OpaqueId<"InterfaceId">;
export type InterfaceRevisionId = OpaqueId<"InterfaceRevisionId">;
export type MemberId = OpaqueId<"MemberId">;
@@ -31,6 +32,7 @@ export const capabilityId = {
workspaceRevision: (value: string) =>
opaque<"WorkspaceRevisionId">(value),
atom: (value: string) => opaque<"AtomId">(value),
conformance: (value: string) => opaque<"ConformanceId">(value),
interface: (value: string) => opaque<"InterfaceId">(value),
interfaceRevision: (value: string) =>
opaque<"InterfaceRevisionId">(value),
@@ -192,11 +194,15 @@ export interface StateSlotDefinition {
}
export interface EdgeEndpoint {
keyType?: "string" | "boolean" | "int64";
publicTraversal?: boolean;
projectionId: EdgeProjectionId;
displayName: string;
constraint: EdgeEndpointConstraint;
cardinality: EdgeCardinality;
ordered: boolean;
onDelete?: "restrict" | "detach" | "cascade-other";
retainOther?: boolean;
}
export interface EdgeDefinition {
@@ -283,11 +289,14 @@ export type PackageExport =
| PackageConstructorExport;
export interface PackageRevision {
migrationCatalog?: import("./migrations.js").MigrationCatalog;
packageId: PackageId;
revisionId: PackageRevisionId;
displayName: string;
source: SourceRevision;
exports: PackageExport[];
/** Author-declared implementation semantics; omitted legacy values mean 1. */
semanticMajor?: number;
}
export type DependencyBinding =
@@ -353,6 +362,9 @@ export interface OperationBinding {
}
export interface Conformance {
/** Absent only for legacy assemblies awaiting explicit ownership enrollment. */
id?: ConformanceId;
semanticMajor?: number;
atomId: AtomId;
interfaceRevisionId: InterfaceRevisionId;
privateAttachments: PersistentAttachment[];
+26 -1
View File
@@ -36,6 +36,8 @@ import type {
import { valueType } from "./types.js";
export type CapabilityValidationIssueCode =
| "invalid-semantic-major"
| "duplicate-conformance-id"
| "required-value"
| "invalid-source"
| "invalid-value-type"
@@ -562,6 +564,9 @@ const collectIdentityIndexes = (
const packages = new Map<string, PackageIndexEntry>();
for (const [packageIndex, revision] of workspace.packageImports.entries()) {
const path = `packageImports[${packageIndex}]`;
if (revision.semanticMajor !== undefined && (!Number.isSafeInteger(revision.semanticMajor) || revision.semanticMajor < 1)) {
issue(issues, "invalid-semantic-major", `${path}.semanticMajor`, "Semantic major must be a positive safe integer");
}
requireText(issues, revision.packageId, `${path}.packageId`, "Package ID");
requireText(
issues,
@@ -616,8 +621,17 @@ const collectIdentityIndexes = (
const conformances = new Map<string, Conformance>();
const conformancePaths = new Map<string, string>();
const conformanceIds = new Set<string>();
for (const [index, conformance] of workspace.conformances.entries()) {
const path = `conformances[${index}]`;
if (conformance.semanticMajor !== undefined && (!Number.isSafeInteger(conformance.semanticMajor) || conformance.semanticMajor < 1)) {
issue(issues, "invalid-semantic-major", `${path}.semanticMajor`, "Semantic major must be a positive safe integer");
}
if (conformance.id !== undefined) {
requireText(issues, conformance.id, `${path}.id`, "Conformance ID");
if (conformanceIds.has(conformance.id)) issue(issues, "duplicate-conformance-id", `${path}.id`, `Duplicate conformance ID ${conformance.id}`);
conformanceIds.add(conformance.id);
}
const key = conformanceKey(conformance.atomId, conformance.interfaceRevisionId);
if (conformances.has(key)) {
issue(
@@ -763,6 +777,10 @@ const validateAttachments = (
);
}
validateValueType(issues, attachment.valueType, `${path}.valueType`, indexes);
const containsReference = (type: ValueType): boolean => type.kind === "object-ref" || ((type.kind === "optional" || type.kind === "list") && containsReference(type.value));
if (containsReference(attachment.valueType) || (attachment.storagePolicy.kind === "crdt-document" && containsReference(attachment.storagePolicy.updateType))) {
issue(issues, "invalid-attachment", `${path}.valueType`, "Managed object references belong in graph relationships, not ordinary state");
}
if (attachment.storagePolicy.kind === "crdt-document") {
validateValueType(
issues,
@@ -794,10 +812,17 @@ const validateAttachments = (
}
for (const [endpointIndex, endpoint] of attachment.endpoints.entries()) {
const endpointPath = `${path}.endpoints[${endpointIndex}]`;
if (endpoint.keyType !== undefined && (!["string", "boolean", "int64"].includes(endpoint.keyType) || endpoint.ordered || !["many", "many-unique"].includes(endpoint.cardinality))) {
issue(issues, "invalid-attachment", `${endpointPath}.keyType`, "Keyed projections require string/boolean/int64 keys, many cardinality, and no ordering");
}
requireText(issues, endpoint.projectionId, `${endpointPath}.projectionId`, "Projection ID");
if (endpoint.onDelete !== undefined && !["restrict", "detach", "cascade-other"].includes(endpoint.onDelete)) {
issue(issues, "invalid-attachment", `${endpointPath}.onDelete`, "Deletion policy must be restrict, detach, or cascade-other");
}
requireText(issues, endpoint.displayName, `${endpointPath}.displayName`, "Projection name");
validateConstraint(issues, endpoint.constraint, `${endpointPath}.constraint`, indexes);
}
if (attachment.endpoints.every((endpoint) => endpoint.keyType)) issue(issues, "invalid-attachment", `${path}.endpoints`, "A v0 map has one keyed projection and one unkeyed inverse");
if (owner.kind === "conformance") {
if (
!attachment.endpoints.some((endpoint) =>
@@ -989,7 +1014,6 @@ const validateTraversal = (
);
return undefined;
}
validateAttachmentAccess(issues, attachment, conformance, `${path}.edgeTypeId`);
const projection = edgeProjection(attachment.attachment, traversal.projectionId);
if (!projection) {
issue(
@@ -1000,6 +1024,7 @@ const validateTraversal = (
);
return undefined;
}
if (!projection.endpoint.publicTraversal) validateAttachmentAccess(issues, attachment, conformance, `${path}.edgeTypeId`);
if (!atomSatisfiesConstraint(atomId, projection.endpoint.constraint, indexes.conformances)) {
issue(
issues,
File diff suppressed because one or more lines are too long
+42 -1
View File
@@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file camino/schema.proto.
*/
export const file_camino_schema: GenFile = /*@__PURE__*/
fileDesc("ChNjYW1pbm8vc2NoZW1hLnByb3RvEgZjYW1pbm8iNwoOQXRvbURlZmluaXRpb24SDwoHYXRvbV9pZBgBIAEoCRIUCgxkaXNwbGF5X25hbWUYAiABKAkiQQoPQXRvbUNvbmZvcm1hbmNlEg8KB2F0b21faWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJIqQBCg9TdGF0ZUF0dGFjaG1lbnQSDwoHc2xvdF9pZBgBIAEoCRIYChBhdHRhY2hlZF9hdG9tX2lkGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRIXCg92YWx1ZV90eXBlX2pzb24YBCABKAkSGwoTc3RvcmFnZV9wb2xpY3lfanNvbhgFIAEoCRIaChJkZWZhdWx0X3ZhbHVlX2pzb24YBiABKAkiUAoSRW5kcG9pbnRDb25zdHJhaW50EhEKB2F0b21faWQYASABKAlIABIfChVpbnRlcmZhY2VfcmV2aXNpb25faWQYAiABKAlIAEIGCgRraW5kIqYBCgxFZGdlRW5kcG9pbnQSFQoNcHJvamVjdGlvbl9pZBgBIAEoCRIUCgxkaXNwbGF5X25hbWUYAiABKAkSLgoKY29uc3RyYWludBgDIAEoCzIaLmNhbWluby5FbmRwb2ludENvbnN0cmFpbnQSKAoLY2FyZGluYWxpdHkYBCABKA4yEy5jYW1pbm8uQ2FyZGluYWxpdHkSDwoHb3JkZXJlZBgFIAEoCCKHAQoORWRnZUF0dGFjaG1lbnQSFAoMZWRnZV90eXBlX2lkGAEgASgJEhQKDGRpc3BsYXlfbmFtZRgCIAEoCRIjCgVmaXJzdBgDIAEoCzIULmNhbWluby5FZGdlRW5kcG9pbnQSJAoGc2Vjb25kGAQgASgLMhQuY2FtaW5vLkVkZ2VFbmRwb2ludCLsAQoPUGVyc2lzdGVuY2VQbGFuEhQKDHdvcmtzcGFjZV9pZBgBIAEoCRIdChV3b3Jrc3BhY2VfcmV2aXNpb25faWQYAiABKAkSJQoFYXRvbXMYAyADKAsyFi5jYW1pbm8uQXRvbURlZmluaXRpb24SLQoMY29uZm9ybWFuY2VzGAQgAygLMhcuY2FtaW5vLkF0b21Db25mb3JtYW5jZRInCgZzdGF0ZXMYBSADKAsyFy5jYW1pbm8uU3RhdGVBdHRhY2htZW50EiUKBWVkZ2VzGAYgAygLMhYuY2FtaW5vLkVkZ2VBdHRhY2htZW50KmgKC0NhcmRpbmFsaXR5EhsKF0NBUkRJTkFMSVRZX1VOU1BFQ0lGSUVEEAASEAoMT1BUSU9OQUxfT05FEAESDwoLRVhBQ1RMWV9PTkUQAhIICgRNQU5ZEAMSDwoLTUFOWV9VTklRVUUQBGIGcHJvdG8z");
fileDesc("ChNjYW1pbm8vc2NoZW1hLnByb3RvEgZjYW1pbm8iNwoOQXRvbURlZmluaXRpb24SDwoHYXRvbV9pZBgBIAEoCRIUCgxkaXNwbGF5X25hbWUYAiABKAkiWQoPQXRvbUNvbmZvcm1hbmNlEg8KB2F0b21faWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhYKDmNvbmZvcm1hbmNlX2lkGAMgASgJIsIBCg9TdGF0ZUF0dGFjaG1lbnQSDwoHc2xvdF9pZBgBIAEoCRIYChBhdHRhY2hlZF9hdG9tX2lkGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRIXCg92YWx1ZV90eXBlX2pzb24YBCABKAkSGwoTc3RvcmFnZV9wb2xpY3lfanNvbhgFIAEoCRIaChJkZWZhdWx0X3ZhbHVlX2pzb24YBiABKAkSHAoUb3duZXJfY29uZm9ybWFuY2VfaWQYByABKAkiUAoSRW5kcG9pbnRDb25zdHJhaW50EhEKB2F0b21faWQYASABKAlIABIfChVpbnRlcmZhY2VfcmV2aXNpb25faWQYAiABKAlIAEIGCgRraW5kIvsBCgxFZGdlRW5kcG9pbnQSFQoNcHJvamVjdGlvbl9pZBgBIAEoCRIUCgxkaXNwbGF5X25hbWUYAiABKAkSLgoKY29uc3RyYWludBgDIAEoCzIaLmNhbWluby5FbmRwb2ludENvbnN0cmFpbnQSKAoLY2FyZGluYWxpdHkYBCABKA4yEy5jYW1pbm8uQ2FyZGluYWxpdHkSDwoHb3JkZXJlZBgFIAEoCBIRCglvbl9kZWxldGUYBiABKAkSFAoMcmV0YWluX290aGVyGAcgASgIEhAKCGtleV90eXBlGAggASgJEhgKEHB1YmxpY190cmF2ZXJzYWwYCSABKAgipQEKDkVkZ2VBdHRhY2htZW50EhQKDGVkZ2VfdHlwZV9pZBgBIAEoCRIUCgxkaXNwbGF5X25hbWUYAiABKAkSIwoFZmlyc3QYAyABKAsyFC5jYW1pbm8uRWRnZUVuZHBvaW50EiQKBnNlY29uZBgEIAEoCzIULmNhbWluby5FZGdlRW5kcG9pbnQSHAoUb3duZXJfY29uZm9ybWFuY2VfaWQYBSABKAki7AEKD1BlcnNpc3RlbmNlUGxhbhIUCgx3b3Jrc3BhY2VfaWQYASABKAkSHQoVd29ya3NwYWNlX3JldmlzaW9uX2lkGAIgASgJEiUKBWF0b21zGAMgAygLMhYuY2FtaW5vLkF0b21EZWZpbml0aW9uEi0KDGNvbmZvcm1hbmNlcxgEIAMoCzIXLmNhbWluby5BdG9tQ29uZm9ybWFuY2USJwoGc3RhdGVzGAUgAygLMhcuY2FtaW5vLlN0YXRlQXR0YWNobWVudBIlCgVlZGdlcxgGIAMoCzIWLmNhbWluby5FZGdlQXR0YWNobWVudCpoCgtDYXJkaW5hbGl0eRIbChdDQVJESU5BTElUWV9VTlNQRUNJRklFRBAAEhAKDE9QVElPTkFMX09ORRABEg8KC0VYQUNUTFlfT05FEAISCAoETUFOWRADEg8KC01BTllfVU5JUVVFEARiBnByb3RvMw");
/**
* @generated from message camino.AtomDefinition
@@ -47,6 +47,11 @@ export type AtomConformance = Message<"camino.AtomConformance"> & {
* @generated from field: string interface_revision_id = 2;
*/
interfaceRevisionId: string;
/**
* @generated from field: string conformance_id = 3;
*/
conformanceId: string;
};
/**
@@ -89,6 +94,11 @@ export type StateAttachment = Message<"camino.StateAttachment"> & {
* @generated from field: string default_value_json = 6;
*/
defaultValueJson: string;
/**
* @generated from field: string owner_conformance_id = 7;
*/
ownerConformanceId: string;
};
/**
@@ -155,6 +165,32 @@ export type EdgeEndpoint = Message<"camino.EdgeEndpoint"> & {
* @generated from field: bool ordered = 5;
*/
ordered: boolean;
/**
* Empty means restrict. Direction is the endpoint being deleted.
*
* @generated from field: string on_delete = 6;
*/
onDelete: string;
/**
* @generated from field: bool retain_other = 7;
*/
retainOther: boolean;
/**
* Empty for sets/lists, otherwise string, boolean, or int64 map keys.
*
* @generated from field: string key_type = 8;
*/
keyType: string;
/**
* Explicit read-only dependency injection traversal, not mutation authority.
*
* @generated from field: bool public_traversal = 9;
*/
publicTraversal: boolean;
};
/**
@@ -187,6 +223,11 @@ export type EdgeAttachment = Message<"camino.EdgeAttachment"> & {
* @generated from field: camino.EdgeEndpoint second = 4;
*/
second?: EdgeEndpoint | undefined;
/**
* @generated from field: string owner_conformance_id = 5;
*/
ownerConformanceId: string;
};
/**
+137 -6
View File
@@ -14,7 +14,7 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file quixos/runtime.proto.
*/
export const file_quixos_runtime: GenFile = /*@__PURE__*/
fileDesc("ChRxdWl4b3MvcnVudGltZS5wcm90bxIOcXVpeG9zLnJ1bnRpbWUiMQoQSGFuZHNoYWtlUmVxdWVzdBIdChVvcmNoX3Byb3RvY29sX3ZlcnNpb24YASABKAkiZgoRSGFuZHNoYWtlUmVzcG9uc2USGwoTcGFja2FnZV9yZXZpc2lvbl9pZBgBIAEoCRIgChhydW50aW1lX3Byb3RvY29sX3ZlcnNpb24YAiABKAkSEgoKZXhwb3J0X2lkcxgDIAMoCSKLAgoNSW52b2tlUmVxdWVzdBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRI3CgVpbnB1dBgEIAMoCzIoLnF1aXhvcy5ydW50aW1lLkludm9rZVJlcXVlc3QuSW5wdXRFbnRyeRIwCgxkZXBlbmRlbmNpZXMYBSADKAsyGi5xdWl4b3MuSW5qZWN0ZWREZXBlbmRlbmN5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASKDAQoOSW52b2tlUmVzcG9uc2USCgoCb2sYASABKAgSHQoGcmVzdWx0GAIgASgLMg0uY2FtaW5vLlZhbHVlEg0KBWVycm9yGAMgASgJEjcKDGRlcGVuZGVuY2llcxgEIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5IokCCgxXYXRjaFJlcXVlc3QSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSNgoFaW5wdXQYBCADKAsyJy5xdWl4b3MucnVudGltZS5XYXRjaFJlcXVlc3QuSW5wdXRFbnRyeRIwCgxkZXBlbmRlbmNpZXMYBSADKAsyGi5xdWl4b3MuSW5qZWN0ZWREZXBlbmRlbmN5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJiChFEZXJpdmVkRGVwZW5kZW5jeRIMCgRraW5kGAEgASgJEhEKCW9iamVjdF9pZBgCIAEoCRIVCg1hdHRhY2htZW50X2lkGAMgASgJEhUKDXByb2plY3Rpb25faWQYBCABKAkilQEKCldhdGNoRXZlbnQSEAoId2F0Y2hfaWQYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWUSNwoMZGVwZW5kZW5jaWVzGAMgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSDQoFZXJyb3IYBCABKAkSDwoHaW5pdGlhbBgFIAEoCDLwAQoOUGFja2FnZVJ1bnRpbWUSUAoJSGFuZHNoYWtlEiAucXVpeG9zLnJ1bnRpbWUuSGFuZHNoYWtlUmVxdWVzdBohLnF1aXhvcy5ydW50aW1lLkhhbmRzaGFrZVJlc3BvbnNlEkcKBkludm9rZRIdLnF1aXhvcy5ydW50aW1lLkludm9rZVJlcXVlc3QaHi5xdWl4b3MucnVudGltZS5JbnZva2VSZXNwb25zZRJDCgVXYXRjaBIcLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdBoaLnF1aXhvcy5ydW50aW1lLldhdGNoRXZlbnQwAWIGcHJvdG8z", [file_camino_api, file_quixos_refs]);
fileDesc("ChRxdWl4b3MvcnVudGltZS5wcm90bxIOcXVpeG9zLnJ1bnRpbWUiQAoQSGFuZHNoYWtlUmVxdWVzdBIdChVvcmNoX3Byb3RvY29sX3ZlcnNpb24YASABKAkSDQoFbm9uY2UYAiABKAkirwEKEUhhbmRzaGFrZVJlc3BvbnNlEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYASABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAIgASgJEhIKCmV4cG9ydF9pZHMYAyADKAkSEwoLaW5zdGFuY2VfaWQYBCABKAkSHAoUYXV0aGVudGljYXRpb25fcHJvb2YYBSABKAkSFAoMY2FwYWJpbGl0aWVzGAYgAygJIpoBChFJbnZvY2F0aW9uQ29udGV4dBIXCg93b3Jrc3BhY2VfZXBvY2gYASABKAkSEwoLaW5zdGFuY2VfaWQYAiABKAkSFgoOYmluZGluZ19kaWdlc3QYAyABKAkSDQoFZ3JhbnQYBCABKAkSEgoKc2Vzc2lvbl9pZBgFIAEoCRIcChRvd25lcl9jb25mb3JtYW5jZV9pZBgGIAEoCSIxChhJbnZvY2F0aW9uQ29udHJvbFJlcXVlc3QSFQoNaW52b2NhdGlvbl9pZBgBIAEoCSI4ChBJbnZvY2F0aW9uU3RhdHVzEhUKDWludm9jYXRpb25faWQYASABKAkSDQoFc3RhdGUYAiABKAkivwIKDUludm9rZVJlcXVlc3QSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSNwoFaW5wdXQYBCADKAsyKC5xdWl4b3MucnVudGltZS5JbnZva2VSZXF1ZXN0LklucHV0RW50cnkSMAoMZGVwZW5kZW5jaWVzGAUgAygLMhoucXVpeG9zLkluamVjdGVkRGVwZW5kZW5jeRIyCgdjb250ZXh0GAYgASgLMiEucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRleHQaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIoMBCg5JbnZva2VSZXNwb25zZRIKCgJvaxgBIAEoCBIdCgZyZXN1bHQYAiABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYAyABKAkSNwoMZGVwZW5kZW5jaWVzGAQgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kivQIKDFdhdGNoUmVxdWVzdBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRI2CgVpbnB1dBgEIAMoCzInLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdC5JbnB1dEVudHJ5EjAKDGRlcGVuZGVuY2llcxgFIAMoCzIaLnF1aXhvcy5JbmplY3RlZERlcGVuZGVuY3kSMgoHY29udGV4dBgGIAEoCzIhLnF1aXhvcy5ydW50aW1lLkludm9jYXRpb25Db250ZXh0GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJiChFEZXJpdmVkRGVwZW5kZW5jeRIMCgRraW5kGAEgASgJEhEKCW9iamVjdF9pZBgCIAEoCRIVCg1hdHRhY2htZW50X2lkGAMgASgJEhUKDXByb2plY3Rpb25faWQYBCABKAkilQEKCldhdGNoRXZlbnQSEAoId2F0Y2hfaWQYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWUSNwoMZGVwZW5kZW5jaWVzGAMgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSDQoFZXJyb3IYBCABKAkSDwoHaW5pdGlhbBgFIAEoCDKzAwoOUGFja2FnZVJ1bnRpbWUSUAoJSGFuZHNoYWtlEiAucXVpeG9zLnJ1bnRpbWUuSGFuZHNoYWtlUmVxdWVzdBohLnF1aXhvcy5ydW50aW1lLkhhbmRzaGFrZVJlc3BvbnNlEkcKBkludm9rZRIdLnF1aXhvcy5ydW50aW1lLkludm9rZVJlcXVlc3QaHi5xdWl4b3MucnVudGltZS5JbnZva2VSZXNwb25zZRJDCgVXYXRjaBIcLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdBoaLnF1aXhvcy5ydW50aW1lLldhdGNoRXZlbnQwARJhChNHZXRJbnZvY2F0aW9uU3RhdHVzEigucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRyb2xSZXF1ZXN0GiAucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvblN0YXR1cxJeChBDYW5jZWxJbnZvY2F0aW9uEigucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRyb2xSZXF1ZXN0GiAucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvblN0YXR1c2IGcHJvdG8z", [file_camino_api, file_quixos_refs]);
/**
* @generated from message quixos.runtime.HandshakeRequest
@@ -24,6 +24,11 @@ export type HandshakeRequest = Message<"quixos.runtime.HandshakeRequest"> & {
* @generated from field: string orch_protocol_version = 1;
*/
orchProtocolVersion: string;
/**
* @generated from field: string nonce = 2;
*/
nonce: string;
};
/**
@@ -51,6 +56,21 @@ export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & {
* @generated from field: repeated string export_ids = 3;
*/
exportIds: string[];
/**
* @generated from field: string instance_id = 4;
*/
instanceId: string;
/**
* @generated from field: string authentication_proof = 5;
*/
authenticationProof: string;
/**
* @generated from field: repeated string capabilities = 6;
*/
capabilities: string[];
};
/**
@@ -60,6 +80,91 @@ export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & {
export const HandshakeResponseSchema: GenMessage<HandshakeResponse> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 1);
/**
* @generated from message quixos.runtime.InvocationContext
*/
export type InvocationContext = Message<"quixos.runtime.InvocationContext"> & {
/**
* @generated from field: string workspace_epoch = 1;
*/
workspaceEpoch: string;
/**
* @generated from field: string instance_id = 2;
*/
instanceId: string;
/**
* @generated from field: string binding_digest = 3;
*/
bindingDigest: string;
/**
* @generated from field: string grant = 4;
*/
grant: string;
/**
* @generated from field: string session_id = 5;
*/
sessionId: string;
/**
* Host-selected owner; packages must not invent workspace-local ownership.
*
* @generated from field: string owner_conformance_id = 6;
*/
ownerConformanceId: string;
};
/**
* Describes the message quixos.runtime.InvocationContext.
* Use `create(InvocationContextSchema)` to create a new message.
*/
export const InvocationContextSchema: GenMessage<InvocationContext> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 2);
/**
* @generated from message quixos.runtime.InvocationControlRequest
*/
export type InvocationControlRequest = Message<"quixos.runtime.InvocationControlRequest"> & {
/**
* @generated from field: string invocation_id = 1;
*/
invocationId: string;
};
/**
* Describes the message quixos.runtime.InvocationControlRequest.
* Use `create(InvocationControlRequestSchema)` to create a new message.
*/
export const InvocationControlRequestSchema: GenMessage<InvocationControlRequest> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 3);
/**
* @generated from message quixos.runtime.InvocationStatus
*/
export type InvocationStatus = Message<"quixos.runtime.InvocationStatus"> & {
/**
* @generated from field: string invocation_id = 1;
*/
invocationId: string;
/**
* unknown, running, cancellation-requested, completed, failed
*
* @generated from field: string state = 2;
*/
state: string;
};
/**
* Describes the message quixos.runtime.InvocationStatus.
* Use `create(InvocationStatusSchema)` to create a new message.
*/
export const InvocationStatusSchema: GenMessage<InvocationStatus> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 4);
/**
* @generated from message quixos.runtime.InvokeRequest
*/
@@ -88,6 +193,11 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
* @generated from field: repeated quixos.InjectedDependency dependencies = 5;
*/
dependencies: InjectedDependency[];
/**
* @generated from field: quixos.runtime.InvocationContext context = 6;
*/
context?: InvocationContext | undefined;
};
/**
@@ -95,7 +205,7 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
* Use `create(InvokeRequestSchema)` to create a new message.
*/
export const InvokeRequestSchema: GenMessage<InvokeRequest> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 2);
messageDesc(file_quixos_runtime, 5);
/**
* @generated from message quixos.runtime.InvokeResponse
@@ -127,7 +237,7 @@ export type InvokeResponse = Message<"quixos.runtime.InvokeResponse"> & {
* Use `create(InvokeResponseSchema)` to create a new message.
*/
export const InvokeResponseSchema: GenMessage<InvokeResponse> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 3);
messageDesc(file_quixos_runtime, 6);
/**
* @generated from message quixos.runtime.WatchRequest
@@ -157,6 +267,11 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
* @generated from field: repeated quixos.InjectedDependency dependencies = 5;
*/
dependencies: InjectedDependency[];
/**
* @generated from field: quixos.runtime.InvocationContext context = 6;
*/
context?: InvocationContext | undefined;
};
/**
@@ -164,7 +279,7 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
* Use `create(WatchRequestSchema)` to create a new message.
*/
export const WatchRequestSchema: GenMessage<WatchRequest> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 4);
messageDesc(file_quixos_runtime, 7);
/**
* @generated from message quixos.runtime.DerivedDependency
@@ -196,7 +311,7 @@ export type DerivedDependency = Message<"quixos.runtime.DerivedDependency"> & {
* Use `create(DerivedDependencySchema)` to create a new message.
*/
export const DerivedDependencySchema: GenMessage<DerivedDependency> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 5);
messageDesc(file_quixos_runtime, 8);
/**
* @generated from message quixos.runtime.WatchEvent
@@ -233,7 +348,7 @@ export type WatchEvent = Message<"quixos.runtime.WatchEvent"> & {
* Use `create(WatchEventSchema)` to create a new message.
*/
export const WatchEventSchema: GenMessage<WatchEvent> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 6);
messageDesc(file_quixos_runtime, 9);
/**
* @generated from service quixos.runtime.PackageRuntime
@@ -263,6 +378,22 @@ export const PackageRuntime: GenService<{
input: typeof WatchRequestSchema;
output: typeof WatchEventSchema;
},
/**
* @generated from rpc quixos.runtime.PackageRuntime.GetInvocationStatus
*/
getInvocationStatus: {
methodKind: "unary";
input: typeof InvocationControlRequestSchema;
output: typeof InvocationStatusSchema;
},
/**
* @generated from rpc quixos.runtime.PackageRuntime.CancelInvocation
*/
cancelInvocation: {
methodKind: "unary";
input: typeof InvocationControlRequestSchema;
output: typeof InvocationStatusSchema;
},
}> = /*@__PURE__*/
serviceDesc(file_quixos_runtime, 0);