Compare commits

..

37 Commits

Author SHA1 Message Date
Quixos Subtree Publisher f7fcc7d08b Publish quixos-protocol from e25eee6ce4f13702b2454a9354bc79a29eb2e4a1 2026-09-14 03:01:53 +00:00
Timothy J. Aveni 295181244f Migrate TODO implementations to generated candidate bindings 2026-09-13 20:01:53 -07:00
Quixos Subtree Publisher e9fb42dce4 Publish quixos-protocol from 61cb9aa5a580c2846d426464228c7d1082b7021a 2026-09-13 23:38:55 +00:00
Timothy J. Aveni 42af87f8bd Fix supervised credentials and declare typed RPC records for canvas placement 2026-09-13 16:38:55 -07:00
Quixos Subtree Publisher 9867bf4552 Publish quixos-protocol from 0e6bb95d903004916bd93670497ab310a076a28b 2026-09-13 19:34:15 +00:00
Timothy J. Aveni 483bc68a94 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.
2026-09-10 18:29:25 -07:00
Timothy J. Aveni 1e25f391e7 Browse immutable workspace source and jj history in Central 2026-09-10 00:51:26 -07:00
Timothy J. Aveni 4e17693d82 Generate typed QX bindings and add source editing tools 2026-09-08 15:42:20 -07:00
timothy ce793cc54f Build workspace agent, capability graph, and versioned cutovers 2026-09-06 11:12:04 -07:00
Quixos Subtree Publisher fd6fee133d Publish quixos-protocol from b384206b9c01a9ac50030a583d028b71291bc8c5 2026-09-06 07:07:06 +00:00
timothy f5ced64533 Build workspace agent, capability graph, and versioned cutovers 2026-09-06 00:07:06 -07:00
Quixos Subtree Publisher eabf721e94 Publish quixos-protocol from 33266c4ba9e0dd7b6370715522b281807e8b038b 2026-09-06 00:49:07 +00:00
timothy 0c46850973 Add the workspace coding agent, launch gate, and composable locks
- run a singleton Codex App Server adapter in each dev workspace
- gate Web Studio with a reloadable fragment-token to HttpOnly-session exchange
- add ChatGPT device login and streamed chat to Web Studio
- isolate authoring access in a declarative NixOS service account
- split Web Studio canvas, object, panel, and model concerns
- check generated Quixos protocol clients for drift
- resolve same-repository quixos.lock fragments deterministically
- preserve the collaborative canvas gesture fixes
2026-09-05 17:49:07 -07:00
timothy c4d42a0ac5 Use the client runtime for canvas state 2026-09-05 12:11:30 -07:00
Quixos Subtree Publisher fd6160ec67 Publish quixos-protocol from 3ea7dcc6a38ba0c5ddc099b966d48c5aeae732b1 2026-09-05 04:04:43 +00:00
timothy fd039f095b Introduce persisted Web Studio component capabilities 2026-09-04 21:04:43 -07:00
timothy f4987093f6 Introduce repository-backed capability workspaces
- define and validate the capability and resource-lock languages
- provision workspace source repositories through Central Gitea
- build package runtimes from pinned standalone sources
- replace legacy schema compilation with workspace persistence plans
- add stable optimistic registers and Automerge CRDT documents
- modernize TypeScript/Nix package builds and runtime activation readiness
2026-09-04 19:08:10 -07:00
Quixos Subtree Publisher 4d52789807 Publish quixos-protocol from 4af89fa7c703d9fecf36b4e97b76658a503dd5aa 2026-09-05 01:29:10 +00:00
timothy 60550bb760 Host workspace repositories on Central Gitea 2026-09-04 18:29:10 -07:00
Quixos Subtree Publisher eaccec410c Publish quixos-protocol from ce0dd80f56df35bf3db1be01236a949a704cb4c2 2026-09-04 17:41:38 +00:00
timothy c33dfc2817 Host workspace repositories on Central Gitea 2026-09-04 10:41:38 -07:00
Quixos Subtree Publisher 27373d801b Publish quixos-protocol from cfc0ae95b0be71689ae3004b6f2aa6d87e019374 2026-09-04 17:22:06 +00:00
timothy f852c8f0aa Host workspace repositories on Central Gitea 2026-09-04 10:22:06 -07:00
Timothy J. Aveni 3c2e4a7e85 Establish dev activation workflow 2026-09-03 15:20:53 -07:00
Quixos Subtree Publisher ee4d8867a3 Publish quixos-protocol from 39dff4bd6470dcdcaf3ecc8a6c9a4e747b7d14b6 2026-09-03 22:20:53 +00:00
Timothy J. Aveni ffac727009 Extend Camino interface contracts 2026-07-18 10:29:28 -07:00
Quixos Subtree Publisher e6b34535af Publish quixos-protocol from 0e5206263187b3ccd1539e3de36bbbd456d946a9 2026-07-18 17:29:28 +00:00
Timothy J. Aveni cd35e03b2d Add Camino edge materialization protocol 2026-07-15 07:52:07 -07:00
Quixos Subtree Publisher a9134843bc Publish quixos-protocol from eb28865321f04f04baf3b857e8688f71f95a9475 2026-07-15 14:52:07 +00:00
Timothy J. Aveni ea153c3cd7 Add Camino undirected edge protocol 2026-07-14 22:36:14 -07:00
Quixos Subtree Publisher ca0381d280 Publish quixos-protocol from 5c5205abc3e8a73b631ea508f92e3b288e71207c 2026-07-15 05:36:14 +00:00
Timothy J. Aveni 5587511713 Split Camino edge ordinals by endpoint 2026-07-14 20:30:32 -07:00
Quixos Subtree Publisher 5dd3948784 Publish quixos-protocol from 435d832a1e202124e2e5ac7f99a65da9d85962e2 2026-07-15 03:30:32 +00:00
Timothy J. Aveni 7dba68cff5 Extend Camino edge endpoint protocol 2026-07-14 07:55:04 -07:00
Quixos Subtree Publisher 2ad29f8ddb Publish quixos-protocol from 2249fe2b76dea4e52b099696c2418f0dd2444f52 2026-07-14 14:55:04 +00:00
Timothy J. Aveni 464442a55c Add Camino display label field metadata 2026-07-13 07:51:43 -07:00
Quixos Subtree Publisher ff07b0d793 Publish quixos-protocol from 1a3ecab531f39491ae0ce3c2a12de0d264440ed7 2026-07-13 14:51:43 +00:00
98 changed files with 22265 additions and 2402 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git", "sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
"sourceCommit": "0ff224604a054b52ad8b385764e913171bf19468", "sourceCommit": "e25eee6ce4f13702b2454a9354bc79a29eb2e4a1",
"sourcePath": "quixos-protocol", "sourcePath": "quixos-protocol",
"exportName": "quixos-protocol", "exportName": "quixos-protocol",
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git" "mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
File diff suppressed because one or more lines are too long
+24
View File
@@ -0,0 +1,24 @@
enableScripts: true
generateDefaultNix: false
individualNixPackaging: true
nodeLinker: node-modules
supportedArchitectures:
os:
- current
- linux
cpu:
- current
- x64
- arm64
libc:
- current
- glibc
plugins:
- checksum: 271de9a785321a37c564b8e2a6b5cd689850b20572d5fee8ec0a6b8e7a5d4cfb59e533524af2b4a537e00da12828abd062eaec6978556e3dd9149a020db2591b
path: .yarn/plugins/yarn-plugin-nixify.cjs
spec: "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js"
+136 -6
View File
@@ -1,8 +1,138 @@
# Quixos Protocol # Quixos protocol and capability compiler
Shared protobuf definitions for Camino, camino-orch, package descriptors, and This package owns the shared protobuf APIs for Camino, `quixos-orch`, package
future package runtime RPC. runtimes, package descriptors, and generic runtime values. It also owns the v1
capability authoring language and semantic compiler.
This package owns the canonical proto source. Current services still generate ## Capability language
local TypeScript bindings from these protos; the generated bindings here are
available for later consolidation. The grammar is `grammar/QuixosCapability.g4`. The parser lowers source into the
parser-independent records in `src/capability-model`; validation then produces
an immutable checked workspace JSON document. Runtime semantics do not depend
on parse-tree shapes or declaration order.
```sh
nix develop
quixos-workspace-compile \
--root ../quixos-instance/workspaces/todo \
--checkout-root /tmp/quixos-resolved-resources
```
`workspace.qx` contains workspace-local atoms, attachments, conformances, and
constructor bindings. Each imported interface has an `interface.qx` in its own
repository; each imported package similarly has a `package.qx`. Source
locations never appear in those declarations. `quixos.lock` supplies their
exact Git repositories and commits, and the workspace compiler resolves that
dependency graph recursively.
Inside an interface or package manifest, `import interface Named;` is a true
source dependency: the repository lock must pin it, and tooling may use the
full contract for generated types. `external atom` and `external interface`
declare nominal identities which the final importing workspace must provide.
The latter is appropriate for relationship targets and other opaque
references; it deliberately does not grant the contract needed for an
interface invocation port. This distinction permits mutually referential
interface identities without creating a cycle in the Git Merkle graph.
The compiler validates exact identities, recursive lock/source agreement,
external requirement satisfaction, interface operation coverage,
attachment ownership, native state/edge providers, package receiver and
dependency-port requirements, constructors, and the exact runtime closure.
Generics, interface composition, declarative forwarding, automatic
relationship materialization, and first-class bundles are intentionally absent
from v1.
Local workspace fragments, source-editing APIs, and generated package contracts
are documented in [QX bindings and tooling](../docs/QX_BINDINGS_AND_TOOLING.md).
`quixos-resource-compile --schema-out` exports the portable generator input;
`quixos-codegen-ts` is the first backend. `quixos-qx` provides parse, lint,
format, and nominal-atom scaffold commands.
## Repository locks
Every workspace, interface, and package repository carries a `quixos.lock`.
A resource lock is not a miniature workspace: it contains only the exact
Quixos commit that resource was authored against plus its dependency list.
It also pins exact Git sources for interfaces and packages named by that
resource's own manifest. Resource Git sources are deliberately only a
repository URL and full commit ID; the publisher-owned reachability tag and
Nix fetch details are derived from those values.
A workspace root additionally records its Quixos selection policy and ref.
Its `commit` is the exact compatibility baseline shared by the resource graph,
not necessarily the runtime candidate. `pinned` requires the ref, baseline,
and candidate to be one commit. `track-development` lets Central resolve the
named development ref to a newer candidate while preserving the authored
baseline. `track-release` is reserved for compatibility-filtered release-line
advancement and is not implemented yet.
```sh
quixos-lock-check quixos.lock
quixos-resource-compile \
--root . --kind package \
--repository https://repos.quixos.org/example/package-example.git \
--commit 1111111111111111111111111111111111111111 \
--checkout-root /tmp/quixos-resource-dependencies
qx-workspace resource publish
```
The resource compiler validates the local manifest against the recursively
resolved locks without turning the resource into a workspace. The final
command runs from a jj checkout. It performs that same recursive compilation,
snapshots the current working-copy commit, and pushes the immutable tag
`refs/tags/quixos-reachability/<commit>` without advancing an authoring
bookmark.
A workspace root may split its resource pins into same-repository fragments:
```text
quixos-lock version 1 {
quixos source {
repository "https://repos.quixos.org/quixos/quixos.git";
policy track-development;
ref "dev/alice/main";
commit "1111111111111111111111111111111111111111";
}
import "locks/web-studio.lock";
}
```
```text
quixos-lock fragment version 1 {
package CanvasRuntime source {
repository "https://repos.quixos.org/org-quixos-web-studio/package-canvas-runtime.git";
commit "2222222222222222222222222222222222222222";
}
}
```
Import paths are normalized, repository-root-relative paths. Absolute paths,
URLs, traversal, empty segments, directories, and symbolic links are rejected.
Fragments may import other fragments; cycles fail, shared fragments are loaded
once, and duplicate resource bindings fail across the flattened closure.
`quixos-lock-check` resolves the complete closure and reports its `sourceFiles`
alongside the flattened resources. The root Git commit content-addresses every
fragment, so fragments do not become independent repositories or identities.
## Package descriptors
Executable package metadata remains protobuf text format:
```sh
quixos-descriptor-check path/to/descriptor.quixos-package.txtpb
```
Descriptors identify exact package revisions and exported runtime symbols.
The checked workspace binds interface operations to those exports and supplies
their exact dependency ports.
## Tests
```sh
nix develop -c yarn test
```
The suite covers parsing/diagnostics, recursive repository assembly, external
requirement validation, source-order independence, ordinary call operations,
semantic validation, exact closure/tree-shaking, private and shared
attachments, related-object interface-port injection, and constructors.
+96 -13
View File
@@ -10,47 +10,130 @@
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let let
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs { inherit system; };
quixos-protocol = pkgs.stdenvNoCC.mkDerivation { nodejs = pkgs.nodejs_24;
pname = "quixos-protocol"; quixos-protocol = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) {
version = "0.1.0"; src = pkgs.lib.cleanSource ./.;
src = ./.; overrideAttrs = old: {
npmDeps = pkgs.importNpmLock { npmRoot = ./.; }; nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [
npmConfigHook = pkgs.importNpmLock.npmConfigHook; pkgs.diffutils
nativeBuildInputs = [ pkgs.esbuild
pkgs.importNpmLock.npmConfigHook
pkgs.nodejs_24
pkgs.protobuf pkgs.protobuf
pkgs.git
]; ];
buildPhase = '' buildPhase = ''
runHook preBuild runHook preBuild
mkdir -p "$TMPDIR/generated-before"
cp --recursive src/gen "$TMPDIR/generated-before/proto"
cp --recursive src/capability-language/generated "$TMPDIR/generated-before/capability"
cp --recursive src/resource-lock/generated "$TMPDIR/generated-before/lock"
export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:$PWD/proto''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}" export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:$PWD/proto''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}"
npm run build patchShebangs node_modules/.bin node_modules/@bufbuild/protoc-gen-es/bin
yarn build
diff --recursive --unified "$TMPDIR/generated-before/proto" src/gen
diff --recursive --unified "$TMPDIR/generated-before/capability" src/capability-language/generated
diff --recursive --unified "$TMPDIR/generated-before/lock" src/resource-lock/generated
esbuild dist/src/descriptor-check.js \
--bundle --platform=node --target=node24 --format=esm \
--outfile=quixos-descriptor-check.mjs
esbuild dist/src/capability-language/cli.js \
--bundle --platform=node --target=node24 --format=esm \
--outfile=quixos-capability-compile.mjs
esbuild dist/src/capability-language/workspace-cli.js \
--bundle --platform=node --target=node24 --format=esm \
--outfile=quixos-workspace-compile.mjs
esbuild dist/src/capability-language/resource-cli.js \
--bundle --platform=node --target=node24 --format=esm \
--outfile=quixos-resource-compile.mjs
esbuild dist/src/resource-lock/cli.js \
--bundle --platform=node --target=node24 --format=esm \
--outfile=quixos-lock-check.mjs
esbuild dist/src/bindings/cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-codegen-ts.mjs
esbuild dist/src/capability-language/tool-cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-qx.mjs
runHook postBuild runHook postBuild
''; '';
doCheck = true;
checkPhase = ''
runHook preCheck
yarn test
runHook postCheck
'';
installPhase = '' installPhase = ''
runHook preInstall runHook preInstall
mkdir -p "$out" mkdir -p "$out"
cp --reflink=auto --recursive grammar "$out/grammar"
cp --reflink=auto --recursive proto "$out/proto" cp --reflink=auto --recursive proto "$out/proto"
cp --reflink=auto --recursive dist "$out/dist" cp --reflink=auto --recursive dist "$out/dist"
cp package.json "$out/package.json" cp package.json "$out/package.json"
mkdir -p "$out/bin" mkdir -p "$out/bin"
for tool in quixos-codegen-ts quixos-qx; do
printf '#!/bin/sh\nexec ${pkgs.nodejs_24}/bin/node "%s/libexec/quixos-protocol/%s.mjs" "$@"\n' "$out" "$tool" > "$out/bin/$tool"
chmod +x "$out/bin/$tool"
done
cat > "$out/bin/quixos-descriptor-check" <<EOF cat > "$out/bin/quixos-descriptor-check" <<EOF
#!/bin/sh #!/bin/sh
export PATH="${pkgs.protobuf}/bin:\$PATH" export PATH="${pkgs.protobuf}/bin:\$PATH"
export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:$out/proto\''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}" export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:$out/proto\''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}"
exec ${pkgs.nodejs_24}/bin/node "$out/dist/src/descriptor-check.js" "\$@" exec ${pkgs.nodejs_24}/bin/node "$out/libexec/quixos-protocol/quixos-descriptor-check.mjs" "\$@"
EOF EOF
chmod +x "$out/bin/quixos-descriptor-check" chmod +x "$out/bin/quixos-descriptor-check"
cat > "$out/bin/quixos-capability-compile" <<EOF
#!/bin/sh
exec ${pkgs.nodejs_24}/bin/node "$out/libexec/quixos-protocol/quixos-capability-compile.mjs" "\$@"
EOF
chmod +x "$out/bin/quixos-capability-compile"
cat > "$out/bin/quixos-workspace-compile" <<EOF
#!/bin/sh
exec ${pkgs.nodejs_24}/bin/node "$out/libexec/quixos-protocol/quixos-workspace-compile.mjs" "\$@"
EOF
chmod +x "$out/bin/quixos-workspace-compile"
cat > "$out/bin/quixos-resource-compile" <<EOF
#!/bin/sh
exec ${pkgs.nodejs_24}/bin/node "$out/libexec/quixos-protocol/quixos-resource-compile.mjs" "\$@"
EOF
chmod +x "$out/bin/quixos-resource-compile"
cat > "$out/bin/quixos-lock-check" <<EOF
#!/bin/sh
exec ${pkgs.nodejs_24}/bin/node "$out/libexec/quixos-protocol/quixos-lock-check.mjs" "\$@"
EOF
chmod +x "$out/bin/quixos-lock-check"
mkdir -p "$out/libexec/quixos-protocol"
install -m644 quixos-codegen-ts.mjs quixos-qx.mjs "$out/libexec/quixos-protocol/"
install -m644 quixos-descriptor-check.mjs "$out/libexec/quixos-protocol/quixos-descriptor-check.mjs"
install -m644 quixos-capability-compile.mjs "$out/libexec/quixos-protocol/quixos-capability-compile.mjs"
install -m644 quixos-workspace-compile.mjs "$out/libexec/quixos-protocol/quixos-workspace-compile.mjs"
install -m644 quixos-resource-compile.mjs "$out/libexec/quixos-protocol/quixos-resource-compile.mjs"
install -m644 quixos-lock-check.mjs "$out/libexec/quixos-protocol/quixos-lock-check.mjs"
runHook postInstall runHook postInstall
''; '';
};
}; };
in { in {
packages.default = quixos-protocol; packages.default = quixos-protocol;
packages.inspector = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) {
src = pkgs.lib.fileset.toSource {
root = ./.;
fileset = pkgs.lib.fileset.unions [ ./package.json ./yarn.lock ./.yarnrc.yml ./.yarn/plugins ./src ];
};
overrideAttrs = old: {
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ pkgs.esbuild ];
doCheck = false;
buildPhase = ''
esbuild src/capability-language/inspect-cli.ts --bundle --platform=node --target=node24 --format=esm --outfile=inspect.mjs
'';
installPhase = ''
mkdir -p "$out/bin" "$out/lib"
cp inspect.mjs "$out/lib/inspect.mjs"
printf '#!${pkgs.runtimeShell}\nexec ${nodejs}/bin/node --max-old-space-size=128 %s/lib/inspect.mjs\n' "$out" > "$out/bin/quixos-qx-inspect"
chmod +x "$out/bin/quixos-qx-inspect"
'';
};
};
checks.default = quixos-protocol;
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = [ packages = [
pkgs.nodejs_24 nodejs
pkgs.protobuf pkgs.protobuf
pkgs.typescript quixos-protocol.yarn-freestanding
quixos-protocol quixos-protocol
]; ];
shellHook = '' shellHook = ''
+454
View File
@@ -0,0 +1,454 @@
grammar QuixosCapability;
document
: workspaceDecl EOF
| interfaceResourceDecl EOF
| packageResourceDecl EOF
| fragmentDecl EOF
;
fragmentDecl
: FRAGMENT LBRACE workspaceItem* RBRACE
;
sourceImportDecl
: IMPORT stringLiteral SEMI
;
workspaceDecl
: WORKSPACE identifier ID stringLiteral REVISION stringLiteral COMMIT stringLiteral
LBRACE workspaceItem* RBRACE
;
workspaceItem
: sourceImportDecl
| atomDecl
| resourceImportDecl
| sharedAttachmentDecl
| conformanceDecl
| constructorBindingDecl
;
resourceImportDecl
: IMPORT INTERFACE identifier SEMI
| IMPORT PACKAGE identifier SEMI
;
externalAtomDecl
: EXTERNAL ATOM identifier ID stringLiteral SEMI
;
externalInterfaceDecl
: EXTERNAL INTERFACE identifier REVISION stringLiteral SEMI
;
resourcePreamble
: resourceImportDecl
| externalAtomDecl
| externalInterfaceDecl
;
atomDecl
: ATOM identifier ID stringLiteral (DOC stringLiteral)? SEMI
;
interfaceResourceDecl
: resourcePreamble* INTERFACE identifier ID stringLiteral REVISION stringLiteral
LBRACE interfaceMember* RBRACE
;
interfaceMember
: valueMember
| relationshipMember
| operationMember
;
operationMember
: OPERATION identifier ID stringLiteral COLON valueType ARROW valueType
LBRACE CALL ID stringLiteral SEMI RBRACE
;
valueMember
: VALUE identifier ID stringLiteral COLON valueType
LBRACE valueMemberOperation* RBRACE
;
valueMemberOperation
: GET ID stringLiteral SEMI
| SET ID stringLiteral SEMI
| WATCH START ID stringLiteral STOP ID stringLiteral SEMI
;
relationshipMember
: RELATION identifier ID stringLiteral COLON cardinality targetConstraint ORDERED?
LBRACE relationshipOperation* RBRACE
;
relationshipOperation
: RESOLVE ID stringLiteral SEMI
| CONNECT ID stringLiteral SEMI
| DISCONNECT ID stringLiteral SEMI
| WATCH START ID stringLiteral STOP ID stringLiteral SEMI
;
targetConstraint
: ATOM identifier
| INTERFACE identifier
;
packageResourceDecl
: resourcePreamble* PACKAGE identifier ID stringLiteral REVISION stringLiteral
(SEMANTIC_MAJOR INTEGER)?
LBRACE packageExport* RBRACE
;
packageExport
: packageOperationExport
| packageFunctionExport
| packageConstructorExport
;
packageOperationExport
: OPERATION identifier ID stringLiteral COLON valueType ARROW valueType
MODE operationMode eventClause? RECEIVER receiverRequirement dependencyBlock? SEMI
;
packageFunctionExport
: FUNCTION identifier ID stringLiteral COLON valueType ARROW valueType
dependencyBlock? SEMI
;
packageConstructorExport
: CONSTRUCTOR identifier ID stringLiteral CONSTRUCTS identifier COLON valueType
dependencyBlock? SEMI
;
eventClause
: EMITS valueType
;
operationMode
: CALL
| WATCH_START
| WATCH_STOP
| SUBSCRIBE
| UNSUBSCRIBE
;
receiverRequirement
: ANY
| ATOM identifier
| INTERFACES LBRACK identifierList? RBRACK
;
identifierList
: identifier (COMMA identifier)*
;
dependencyBlock
: REQUIRES LBRACE dependencyPort* RBRACE
;
dependencyPort
: STATE identifier ID stringLiteral COLON valueType primitiveList SEMI
| EDGE identifier ID stringLiteral COLON cardinality targetConstraint primitiveList SEMI
| INTERFACE identifier ID stringLiteral COLON identifier SEMI
| CONSTRUCTOR identifier ID stringLiteral COLON identifier (INPUT valueType)? SEMI
;
primitiveList
: LBRACK primitive (COMMA primitive)* RBRACK
;
primitive
: READ
| WRITE
| RESOLVE
| CONNECT
| DISCONNECT
| WATCH_START
| WATCH_STOP
;
sharedAttachmentDecl
: SHARED attachmentDecl
;
attachmentDecl
: stateDecl
| edgeDecl
;
stateDecl
: STATE identifier ID stringLiteral ON identifier COLON valueType
POLICY storagePolicy (DEFAULT jsonLiteral)? SEMI
;
storagePolicy
: OPTIMISTIC_REGISTER
| CRDT LPAREN valueType RPAREN
;
edgeDecl
: EDGE identifier ID stringLiteral LBRACE edgeEndpoint edgeEndpoint RBRACE
;
edgeEndpoint
: targetConstraint PROJECTION identifier ID stringLiteral cardinality ORDERED?
(ON_DELETE stringLiteral)? RETAIN_OTHER? (KEYED stringLiteral)? PUBLIC_TRAVERSAL? SEMI
;
conformanceDecl
: CONFORM identifier AS identifier (ID stringLiteral)? (SEMANTIC_MAJOR INTEGER)?
LBRACE conformanceItem* RBRACE
;
conformanceItem
: PRIVATE attachmentDecl
| operationBindingDecl
| relationshipMaterializationDecl
;
relationshipMaterializationDecl
: MATERIALIZE identifier IF ABSENT USING CONSTRUCTOR identifier VIA EDGE identifier DOT identifier SEMI
;
operationBindingDecl
: BIND memberOperationRef TO operationProvider SEMI
;
memberOperationRef
: identifier DOT operationName
;
operationName
: identifier
| CALL
| GET
| SET
| RESOLVE
| CONNECT
| DISCONNECT
| WATCH_START
| WATCH_STOP
| SUBSCRIBE
| UNSUBSCRIBE
;
operationProvider
: STATE identifier DOT statePrimitive
| EDGE identifier DOT identifier DOT edgePrimitive
| PACKAGE identifier DOT identifier dependencyBindingBlock?
;
statePrimitive
: READ
| WRITE
| WATCH_START
| WATCH_STOP
;
edgePrimitive
: RESOLVE
| CONNECT
| DISCONNECT
| WATCH_START
| WATCH_STOP
;
dependencyBindingBlock
: WITH LBRACE dependencyBinding* RBRACE
;
dependencyBinding
: identifier TO STATE identifier (VIA EDGE identifier DOT identifier)? SEMI
| identifier TO EDGE identifier DOT identifier (VIA EDGE identifier DOT identifier)? SEMI
| identifier TO INTERFACE identifier (VIA EDGE identifier DOT identifier)? SEMI
| identifier TO CONSTRUCTOR identifier SEMI
;
constructorBindingDecl
: CONSTRUCTOR identifier TO identifier DOT identifier dependencyBindingBlock? SEMI
;
valueType
: scalarType
| UNIT
| WATCH_HANDLE
| MESSAGE stringLiteral
| ATOM_REF LT identifier GT
| INTERFACE_REF LT identifier GT
| OPTIONAL LT valueType GT
| LIST LT valueType GT
| RECORD LBRACE recordField* RBRACE
;
recordField
: identifier COLON valueType SEMI
;
scalarType
: BOOL
| BYTES
| DOUBLE
| INT32
| INT64
| STRING
| UINT32
| UINT64
;
cardinality
: OPTIONAL_ONE
| EXACTLY_ONE
| MANY
| MANY_UNIQUE
;
jsonLiteral
: stringLiteral
| INTEGER
| JSON_NUMBER
| TRUE
| FALSE
| NULL
| jsonObject
| jsonArray
;
jsonObject
: LBRACE (jsonMember (COMMA jsonMember)*)? RBRACE
;
jsonMember
: stringLiteral COLON jsonLiteral
;
jsonArray
: LBRACK (jsonLiteral (COMMA jsonLiteral)*)? RBRACK
;
identifier
: IDENTIFIER
| SOURCE
;
stringLiteral
: STRING_LITERAL
;
WORKSPACE: 'workspace';
FRAGMENT: 'fragment';
IMPORT: 'import';
EXTERNAL: 'external';
ATOM: 'atom';
INTERFACE: 'interface';
INTERFACES: 'interfaces';
PACKAGE: 'package';
VALUE: 'value';
RELATION: 'relation';
OPERATION: 'operation';
FUNCTION: 'function';
CONSTRUCTOR: 'constructor';
CONSTRUCTS: 'constructs';
INPUT: 'input';
CONFORM: 'conform';
AS: 'as';
BIND: 'bind';
TO: 'to';
PRIVATE: 'private';
SHARED: 'shared';
STATE: 'state';
EDGE: 'edge';
PROJECTION: 'projection';
WITH: 'with';
USING: 'using';
VIA: 'via';
MATERIALIZE: 'materialize';
IF: 'if';
ABSENT: 'absent';
ON: 'on';
POLICY: 'policy';
DEFAULT: 'default';
SOURCE: 'source';
REPOSITORY: 'repository';
COMMIT: 'commit';
REVISION: 'revision';
SEMANTIC_MAJOR: 'semantic-major';
ON_DELETE: 'on-delete';
RETAIN_OTHER: 'retain-other';
KEYED: 'keyed';
PUBLIC_TRAVERSAL: 'public-traversal';
ID: 'id';
DOC: 'doc';
MODE: 'mode';
EMITS: 'emits';
RECEIVER: 'receiver';
REQUIRES: 'requires';
ANY: 'any';
GET: 'get';
SET: 'set';
WATCH: 'watch';
START: 'start';
STOP: 'stop';
READ: 'read';
WRITE: 'write';
RESOLVE: 'resolve';
CONNECT: 'connect';
DISCONNECT: 'disconnect';
CALL: 'call';
WATCH_START: 'watch-start';
WATCH_STOP: 'watch-stop';
SUBSCRIBE: 'subscribe';
UNSUBSCRIBE: 'unsubscribe';
OPTIMISTIC_REGISTER: 'optimistic-register';
CRDT: 'crdt';
OPTIONAL_ONE: 'optional-one';
EXACTLY_ONE: 'exactly-one';
MANY_UNIQUE: 'many-unique';
MANY: 'many';
ORDERED: 'ordered';
UNIT: 'unit';
WATCH_HANDLE: 'watch-handle';
MESSAGE: 'message';
ATOM_REF: 'atom-ref';
INTERFACE_REF: 'interface-ref';
OPTIONAL: 'optional';
LIST: 'list';
RECORD: 'record';
BOOL: 'bool';
BYTES: 'bytes';
DOUBLE: 'double';
INT32: 'int32';
INT64: 'int64';
STRING: 'string';
UINT32: 'uint32';
UINT64: 'uint64';
TRUE: 'true';
FALSE: 'false';
NULL: 'null';
ARROW: '->';
COLON: ':';
SEMI: ';';
COMMA: ',';
DOT: '.';
LBRACE: '{';
RBRACE: '}';
LBRACK: '[';
RBRACK: ']';
LPAREN: '(';
RPAREN: ')';
LT: '<';
GT: '>';
INTEGER: '-'? [0-9]+;
JSON_NUMBER: '-'? ('0' | [1-9] [0-9]*) ('.' [0-9]+)? ([eE] [+-]? [0-9]+)?;
IDENTIFIER: [A-Za-z_] [A-Za-z0-9_]*;
STRING_LITERAL: '"' (ESC | ~["\\\r\n])* '"';
fragment ESC: '\\' (["\\/bfnrt] | 'u' HEX HEX HEX HEX);
fragment HEX: [0-9a-fA-F];
LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN);
BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
WS: [ \t\r\n]+ -> channel(HIDDEN);
+81
View File
@@ -0,0 +1,81 @@
grammar QuixosLock;
document
: QUIXOS_LOCK FRAGMENT? VERSION INTEGER LBRACE
(quixosEntry | importEntry | resourceEntry)* RBRACE EOF
;
quixosEntry
: QUIXOS SOURCE quixosSourceBlock
;
resourceEntry
: resourceKind identifier SOURCE sourceBlock
;
importEntry
: IMPORT stringLiteral SEMI
;
resourceKind
: INTERFACE
| PACKAGE
;
sourceBlock
: LBRACE
REPOSITORY stringLiteral SEMI
COMMIT stringLiteral SEMI
RBRACE
;
quixosSourceBlock
: LBRACE
REPOSITORY stringLiteral SEMI
(POLICY sourcePolicy SEMI REF stringLiteral SEMI)?
COMMIT stringLiteral SEMI
RBRACE
;
sourcePolicy
: PINNED
| TRACK_RELEASE
| TRACK_DEVELOPMENT
;
identifier
: IDENTIFIER
;
stringLiteral
: STRING_LITERAL
;
QUIXOS_LOCK: 'quixos-lock';
FRAGMENT: 'fragment';
VERSION: 'version';
QUIXOS: 'quixos';
SOURCE: 'source';
INTERFACE: 'interface';
PACKAGE: 'package';
REPOSITORY: 'repository';
COMMIT: 'commit';
POLICY: 'policy';
REF: 'ref';
PINNED: 'pinned';
TRACK_RELEASE: 'track-release';
TRACK_DEVELOPMENT: 'track-development';
IMPORT: 'import';
LBRACE: '{';
RBRACE: '}';
SEMI: ';';
INTEGER: [0-9]+;
IDENTIFIER: [A-Za-z_] [A-Za-z0-9_]*;
STRING_LITERAL: '"' (ESC | ~["\\\r\n])* '"';
fragment ESC: '\\' (["\\/bfnrt] | 'u' HEX HEX HEX HEX);
fragment HEX: [0-9a-fA-F];
LINE_COMMENT: '//' ~[\r\n]* -> skip;
BLOCK_COMMENT: '/*' .*? '*/' -> skip;
WS: [ \t\r\n]+ -> skip;
-142
View File
@@ -1,142 +0,0 @@
{
"name": "@quixos/quixos-protocol",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@quixos/quixos-protocol",
"version": "0.1.0",
"dependencies": {
"@bufbuild/protobuf": "^2.12.1",
"@bufbuild/protoc-gen-es": "^2.12.1"
},
"bin": {
"quixos-descriptor-check": "dist/src/descriptor-check.js"
},
"devDependencies": {
"@types/node": "^24",
"typescript": "^5.9.3"
}
},
"node_modules/@bufbuild/protobuf": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.12.1.tgz",
"integrity": "sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==",
"license": "(Apache-2.0 AND BSD-3-Clause)"
},
"node_modules/@bufbuild/protoc-gen-es": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-2.12.1.tgz",
"integrity": "sha512-SWa7XvRYRouMo+vBQmpNFZ+ZEqQ8AC0LpL4QWAo1gvstLhFh/Y7Nf/a+MK7ZxDq5LZSThwfk974L1sFxO3OaGw==",
"license": "Apache-2.0",
"dependencies": {
"@bufbuild/protobuf": "2.12.1",
"@bufbuild/protoplugin": "2.12.1"
},
"bin": {
"protoc-gen-es": "bin/protoc-gen-es"
},
"engines": {
"node": ">=20"
},
"peerDependencies": {
"@bufbuild/protobuf": "2.12.1"
},
"peerDependenciesMeta": {
"@bufbuild/protobuf": {
"optional": true
}
}
},
"node_modules/@bufbuild/protoplugin": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.12.1.tgz",
"integrity": "sha512-PY58KxQVAD1BnnKtStOctsMoegEVGfBnY5AOqVQOIu711nA13oYtTqJM8df5lUQg2J1DR3XxUXptE+fWX5oLdA==",
"license": "Apache-2.0",
"dependencies": {
"@bufbuild/protobuf": "2.12.1",
"@typescript/vfs": "^1.6.2",
"typescript": "5.4.5"
}
},
"node_modules/@bufbuild/protoplugin/node_modules/typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/@types/node": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.18.0"
}
},
"node_modules/@typescript/vfs": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.4.tgz",
"integrity": "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.3"
},
"peerDependencies": {
"typescript": "*"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
}
}
}
+22 -6
View File
@@ -2,24 +2,40 @@
"name": "@quixos/quixos-protocol", "name": "@quixos/quixos-protocol",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"packageManager": "yarn@4.18.0",
"type": "module", "type": "module",
"bin": { "bin": {
"quixos-descriptor-check": "dist/src/descriptor-check.js" "quixos-qx": "dist/src/capability-language/tool-cli.js",
"quixos-codegen-ts": "dist/src/bindings/cli.js",
"quixos-capability-compile": "dist/src/capability-language/cli.js",
"quixos-descriptor-check": "dist/src/descriptor-check.js",
"quixos-lock-check": "dist/src/resource-lock/cli.js",
"quixos-resource-compile": "dist/src/capability-language/resource-cli.js",
"quixos-workspace-compile": "dist/src/capability-language/workspace-cli.js"
}, },
"exports": { "exports": {
"./bindings": "./dist/src/bindings/index.js",
"./capability-model": "./dist/src/capability-model/index.js",
"./capability-language": "./dist/src/capability-language/index.js",
"./resource-lock": "./dist/src/resource-lock/index.js",
"./gen/*": "./dist/src/gen/*" "./gen/*": "./dist/src/gen/*"
}, },
"scripts": { "scripts": {
"build": "npm run generate && tsc -p tsconfig.build.json", "build": "yarn generate && tsc -p tsconfig.build.json",
"generate": "rm -rf src/gen && mkdir -p src/gen && protoc --plugin=protoc-gen-es=./node_modules/.bin/protoc-gen-es --es_out=src/gen --es_opt=target=ts,import_extension=js --proto_path=proto --proto_path=$QUIXOS_PROTO_PATH proto/quixos/refs.proto proto/quixos/package.proto proto/quixos/orch.proto proto/quixos/runtime.proto proto/camino/schema.proto proto/camino/api.proto proto/camino/options.proto", "generate": "yarn generate:proto && yarn generate:parser",
"typecheck": "npm run generate && tsc --noEmit" "generate:proto": "rm -rf src/gen && mkdir -p src/gen && protoc --experimental_allow_proto3_optional --plugin=protoc-gen-es=./node_modules/.bin/protoc-gen-es --es_out=src/gen --es_opt=target=ts,import_extension=js --proto_path=proto --proto_path=$QUIXOS_PROTO_PATH proto/quixos/refs.proto proto/quixos/package.proto proto/quixos/orch.proto proto/quixos/runtime.proto proto/camino/schema.proto proto/camino/api.proto",
"generate:parser": "rm -rf src/capability-language/generated src/resource-lock/generated && mkdir -p src/capability-language/generated src/resource-lock/generated && antlr-ng -D language=TypeScript --generate-visitor --generate-listener false --output-directory src/capability-language/generated grammar/QuixosCapability.g4 && antlr-ng -D language=TypeScript --generate-visitor --generate-listener false --output-directory src/resource-lock/generated grammar/QuixosLock.g4",
"test": "yarn generate && tsc -p tsconfig.test.json && node --test dist/test/*.test.js",
"typecheck": "yarn generate && tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@bufbuild/protobuf": "^2.12.1", "@bufbuild/protobuf": "^2.12.1",
"@bufbuild/protoc-gen-es": "^2.12.1" "@bufbuild/protoc-gen-es": "^2.12.1",
"antlr4ng": "^3.0.16"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24", "@types/node": "^24",
"typescript": "^5.9.3" "antlr-ng": "^1.0.10",
"typescript": "^7.0.2"
} }
} }
+86 -117
View File
@@ -3,74 +3,47 @@ syntax = "proto3";
package camino; package camino;
import "camino/schema.proto"; import "camino/schema.proto";
import "quixos/refs.proto";
service CaminoService { service CaminoService {
rpc RegisterSchema(RegisterSchemaRequest) returns (RegisterSchemaResponse); rpc InstallPersistencePlan(InstallPersistencePlanRequest) returns (InstallPersistencePlanResponse);
rpc ListClasses(ListClassesRequest) returns (ListClassesResponse); rpc GetPersistencePlan(GetPersistencePlanRequest) returns (GetPersistencePlanResponse);
rpc CreateObject(CreateObjectRequest) returns (CreateObjectResponse); rpc CreateObject(CreateObjectRequest) returns (CreateObjectResponse);
rpc GetObject(GetObjectRequest) returns (GetObjectResponse); rpc GetObject(GetObjectRequest) returns (GetObjectResponse);
rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse);
rpc SetField(SetFieldRequest) returns (SetFieldResponse); rpc ReadState(ReadStateRequest) returns (ReadStateResponse);
rpc AddEdge(AddEdgeRequest) returns (AddEdgeResponse); rpc WriteState(WriteStateRequest) returns (WriteStateResponse);
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse); rpc ConnectEdge(ConnectEdgeRequest) returns (ConnectEdgeResponse);
rpc RemoveEdge(RemoveEdgeRequest) returns (RemoveEdgeResponse); rpc ResolveEdge(ResolveEdgeRequest) returns (ResolveEdgeResponse);
rpc DisconnectEdge(DisconnectEdgeRequest) returns (DisconnectEdgeResponse);
rpc ReadCollection(ResolveEdgeRequest) returns (ReadCollectionResponse);
rpc ReplaceCollection(ReplaceCollectionRequest) returns (ReadCollectionResponse);
rpc ListOps(ListOpsRequest) returns (ListOpsResponse); rpc ListOps(ListOpsRequest) returns (ListOpsResponse);
rpc WatchObject(WatchObjectRequest) returns (stream WatchObjectEvent); rpc WatchObject(WatchObjectRequest) returns (stream WatchObjectEvent);
} }
message RegisterSchemaRequest {
string source_file = 1;
}
message RegisterSchemaResponse {
repeated string class_ids = 1;
repeated ClassSchema classes = 2;
}
message ListClassesRequest {}
message RegisteredClass {
string id = 1;
ClassSchema schema = 2;
}
message ListClassesResponse {
repeated RegisteredClass classes = 1;
}
message NullValue {} message NullValue {}
message ObjectValue { map<string, Value> fields = 1; }
message ObjectValue { message ListValue { repeated Value values = 1; }
map<string, Value> fields = 1; message RefValue { string object_id = 1; }
}
message ListValue {
repeated Value values = 1;
}
message RefValue {
string object_id = 1;
}
message CrdtValue { message CrdtValue {
string type = 1; string type = 1;
string encoding = 2; string encoding = 2;
bytes payload = 3; bytes payload = 3;
} }
message FieldValueSource { message StateValueSource {
string object_id = 1; string object_id = 1;
string field_name = 2; string slot_id = 2;
string field_type = 3; string value_type_json = 3;
string field_storage = 4; string storage_policy_json = 4;
string conflict_strategy = 5; uint64 revision = 5;
uint64 revision = 6; // CRDT-backed state is still read as its materialized Value. The snapshot
// travels with the writable source identity so a client can retain and
// advance a local replica without exposing the document to package code.
CrdtValue crdt_snapshot = 6;
} }
message ValueSource { message ValueSource { StateValueSource state = 1; }
FieldValueSource field = 1;
}
message Value { message Value {
oneof kind { oneof kind {
@@ -88,84 +61,82 @@ message Value {
ValueSource source = 11; ValueSource source = 11;
} }
message InstallPersistencePlanRequest { PersistencePlan plan = 1; }
message InstallPersistencePlanResponse { PersistencePlan plan = 1; }
message GetPersistencePlanRequest {}
message GetPersistencePlanResponse { PersistencePlan plan = 1; }
message CreateObjectRequest { message CreateObjectRequest {
string class_id = 1; string atom_id = 1;
map<string, Value> fields = 2; map<string, Value> initial_state = 2;
} }
message CreateObjectResponse { CaminoObject object = 1; }
message GetObjectRequest { string object_id = 1; }
message GetObjectResponse { CaminoObject object = 1; }
message ListObjectsRequest { string atom_id = 1; }
message ListObjectsResponse { repeated CaminoObject objects = 1; }
message CreateObjectResponse { message ReadStateRequest {
CaminoObject object = 1;
}
message GetObjectRequest {
string object_id = 1; string object_id = 1;
string slot_id = 2;
} }
message ReadStateResponse { Value value = 1; }
message GetObjectResponse { message WriteStateRequest {
CaminoObject object = 1;
}
message ListObjectsRequest {
string class_id = 1;
}
message ListObjectsResponse {
repeated CaminoObject objects = 1;
}
message SetFieldRequest {
string object_id = 1; string object_id = 1;
string field_name = 2; string slot_id = 2;
Value value = 3; Value value = 3;
string client_mutation_id = 4; string client_mutation_id = 4;
} }
message WriteStateResponse {
message SetFieldResponse { Value value = 1;
CaminoObject object = 1; CaminoObject object = 2;
} }
message AddEdgeRequest { message ConnectEdgeRequest {
string from_object_id = 1; string edge_type_id = 1;
string source_field = 2; string projection_id = 2;
string to_object_id = 3; string object_id = 3;
map<string, Value> fields = 4; string target_object_id = 4;
optional int32 ordinal = 5; optional int32 ordinal = 5;
optional int32 target_ordinal = 6;
} }
message ConnectEdgeResponse { CaminoEdge edge = 1; }
message AddEdgeResponse { message ResolveEdgeRequest {
CaminoEdge edge = 1;
}
message ListEdgesRequest {
string object_id = 1; string object_id = 1;
string edge_type_id = 2;
string projection_id = 3;
} }
message ResolveEdgeResponse { repeated CaminoEdge edges = 1; }
message DisconnectEdgeRequest { string edge_id = 1; }
message DisconnectEdgeResponse { string edge_id = 1; }
message ListEdgesResponse { message CollectionEntry {
repeated CaminoEdge edges = 1; // Existing entry identity to preserve; empty allocates a new canonical edge.
}
message RemoveEdgeRequest {
string edge_id = 1; string edge_id = 1;
string target_object_id = 2;
Value key = 3;
} }
message ReadCollectionResponse {
message RemoveEdgeResponse { uint64 revision = 1;
string edge_id = 1; repeated CollectionEntry entries = 2;
} }
message ReplaceCollectionRequest {
message ListOpsRequest {
string object_id = 1; string object_id = 1;
string edge_type_id = 2;
string projection_id = 3;
uint64 expected_revision = 4;
repeated CollectionEntry entries = 5;
} }
message ListOpsResponse { message ListOpsRequest { string object_id = 1; }
repeated CaminoOp ops = 1; message ListOpsResponse { repeated CaminoOp ops = 1; }
}
message WatchObjectRequest { message WatchObjectRequest {
string object_id = 1; string object_id = 1;
string after_op_id = 2; string after_op_id = 2;
bool include_snapshot = 3; bool include_snapshot = 3;
// Managed runtimes must watch only their injected attachments.
repeated string attachment_ids = 4;
} }
message WatchObjectEvent { message WatchObjectEvent {
string object_id = 1; string object_id = 1;
CaminoObject snapshot = 2; CaminoObject snapshot = 2;
@@ -174,27 +145,26 @@ message WatchObjectEvent {
message CaminoObject { message CaminoObject {
string id = 1; string id = 1;
string class_id = 2; string atom_id = 2;
uint32 schema_version = 3; string workspace_revision_id = 3;
string created_at = 4; string created_at = 4;
string updated_at = 5; string updated_at = 5;
map<string, Value> fields = 6; map<string, Value> state = 6;
map<string, uint64> field_revisions = 7; map<string, uint64> state_revisions = 7;
map<string, string> field_conflicts = 8;
} }
message CaminoEdge { message CaminoEdge {
string id = 1; string id = 1;
string edge_type_id = 2; string edge_type_id = 2;
string from_object_id = 3; string first_object_id = 3;
string to_object_id = 4; string second_object_id = 4;
string source_field = 5; string first_projection_id = 5;
string cardinality = 6; string second_projection_id = 6;
string inverse = 7; optional int32 first_ordinal = 7;
map<string, Value> fields = 8; optional int32 second_ordinal = 8;
optional int32 ordinal = 9; string created_at = 9;
string created_at = 10; string first_key_json = 10;
string updated_at = 11; string second_key_json = 11;
} }
message CaminoOp { message CaminoOp {
@@ -203,7 +173,6 @@ message CaminoOp {
string op_kind = 3; string op_kind = 3;
Value payload = 4; Value payload = 4;
string actor = 5; string actor = 5;
quixos.FunctionRef function_ref = 6; string created_at = 6;
string created_at = 7; string client_mutation_id = 7;
string client_mutation_id = 8;
} }
-70
View File
@@ -1,70 +0,0 @@
syntax = "proto3";
package camino;
import "google/protobuf/descriptor.proto";
import "camino/schema.proto";
import "quixos/refs.proto";
extend google.protobuf.FileOptions {
string schema_namespace = 51000;
string schema_version = 51001;
}
extend google.protobuf.MessageOptions {
ClassOptions class = 51010;
repeated MethodOptions method = 51011;
repeated MigrationOptions migration = 51012;
InterfaceOptions interface = 51013;
repeated ImplementsOptions implements = 51014;
ConstructorOptions constructor = 51015;
}
extend google.protobuf.FieldOptions {
ConflictStrategy conflict = 51020;
EdgeOptions edge = 51021;
FieldStorage field_storage = 51022;
FieldOps field_ops = 51023;
InterfaceFieldContract interface_field = 51024;
}
extend google.protobuf.MethodOptions {
quixos.FunctionRef impl = 51030;
}
message ClassOptions {
uint32 version = 1;
SymbolRef id = 2;
}
message InterfaceOptions {
uint32 version = 1;
SymbolRef id = 2;
}
message ImplementsOptions {
SymbolRef interface = 1;
repeated TypeBinding type_binding = 2;
}
message ConstructorOptions {
quixos.FunctionRef function = 1;
}
message EdgeOptions {
SymbolRef id = 1;
SymbolRef target = 2;
Cardinality cardinality = 3;
string inverse = 4;
}
message MethodOptions {
string name = 1;
quixos.FunctionRef function = 2;
}
message MigrationOptions {
uint32 from_version = 1;
uint32 to_version = 2;
quixos.FunctionRef function = 3;
}
+48 -165
View File
@@ -2,189 +2,72 @@ syntax = "proto3";
package camino; package camino;
import "quixos/refs.proto";
message SymbolRef {
string namespace = 1;
string name = 2;
string version = 3;
string hash = 4;
}
enum ConflictStrategy {
CONFLICT_STRATEGY_UNSPECIFIED = 0;
REPLACE = 1;
PRESERVE_CONFLICTS = 2;
CRDT = 3;
}
enum Cardinality { enum Cardinality {
CARDINALITY_UNSPECIFIED = 0; CARDINALITY_UNSPECIFIED = 0;
OPTIONAL_ONE = 1; OPTIONAL_ONE = 1;
EXACTLY_ONE = 2; EXACTLY_ONE = 2;
MANY = 3; MANY = 3;
MANY_UNIQUE = 4; MANY_UNIQUE = 4;
MANY_ORDERED = 5;
} }
enum DocRefStrength { message AtomDefinition {
DOC_REF_STRENGTH_UNSPECIFIED = 0; string atom_id = 1;
SOFT_REF = 1; string display_name = 2;
PROJECTED_EDGE = 2;
} }
enum FieldStorageKind { message AtomConformance {
FIELD_STORAGE_KIND_UNSPECIFIED = 0; string atom_id = 1;
STORED = 1; string interface_revision_id = 2;
DERIVED = 2; string conformance_id = 3;
LAZY = 3;
EXTERNAL = 4;
STATIC_FINAL = 5;
} }
message Ref { message StateAttachment {
string object_id = 1; string slot_id = 1;
string attached_atom_id = 2;
string display_name = 3;
string value_type_json = 4;
string storage_policy_json = 5;
string default_value_json = 6;
string owner_conformance_id = 7;
} }
message CustomField {} message EndpointConstraint {
oneof kind {
message WatchStartResult { string atom_id = 1;
string watch_id = 1; string interface_revision_id = 2;
}
} }
message WatchStopRequest { message EdgeEndpoint {
string watch_id = 1; string projection_id = 1;
string display_name = 2;
EndpointConstraint constraint = 3;
Cardinality cardinality = 4;
bool ordered = 5;
// Empty means restrict. Direction is the endpoint being deleted.
string on_delete = 6;
bool retain_other = 7;
// Empty for sets/lists, otherwise string, boolean, or int64 map keys.
string key_type = 8;
// Explicit read-only dependency injection traversal, not mutation authority.
bool public_traversal = 9;
} }
message WatchStopResult {} message EdgeAttachment {
string edge_type_id = 1;
message CrdtText { string display_name = 2;
bytes payload = 1; EdgeEndpoint first = 3;
EdgeEndpoint second = 4;
string owner_conformance_id = 5;
} }
message CrdtRichText { // Camino consumes this persistence-only projection of a checked workspace.
bytes payload = 1; // Interfaces, operation bindings, packages, and constructors stay in orch.
} message PersistencePlan {
string workspace_id = 1;
message CrdtJson { string workspace_revision_id = 2;
bytes payload = 1; repeated AtomDefinition atoms = 3;
} repeated AtomConformance conformances = 4;
repeated StateAttachment states = 5;
message ClassSchema { repeated EdgeAttachment edges = 6;
SymbolRef id = 1;
uint32 version = 2;
repeated FieldSchema fields = 3;
repeated EdgeSchema edges = 4;
repeated MethodSchema methods = 5;
repeated MigrationSpec migrations = 6;
string source_file = 7;
string proto_message = 8;
repeated OperationServiceSchema operation_services = 9;
repeated InterfaceImplementationSchema implements = 10;
ConstructorSpec constructor = 11;
}
message FieldSchema {
string name = 1;
uint32 tag = 2;
TypeRef type = 3;
ConflictStrategy conflict = 4;
bool repeated = 5;
bool optional = 6;
FieldStorage storage = 7;
FieldOps ops = 8;
InterfaceFieldContract interface_contract = 9;
}
message FieldStorage {
FieldStorageKind kind = 1;
quixos.FunctionRef resolver = 2;
bool cache = 3;
}
message TypeRef {
string proto_type = 1;
SymbolRef symbol = 2;
}
message TypeBinding {
string name = 1;
TypeRef type = 2;
}
message InterfaceFieldContract {
bool required = 1;
bool readable = 2;
bool writable = 3;
bool watchable = 4;
string type_param = 5;
TypeRef type = 6;
}
message InterfaceImplementationSchema {
SymbolRef interface = 1;
repeated TypeBinding type_bindings = 2;
}
message InterfaceSchema {
SymbolRef id = 1;
uint32 version = 2;
repeated FieldSchema fields = 3;
string source_file = 4;
string proto_message = 5;
}
enum FieldImplementation {
FIELD_IMPLEMENTATION_UNSPECIFIED = 0;
SERVICE = 1;
}
message FieldOps {
FieldImplementation implementation = 1;
string service = 2;
}
message EdgeSchema {
SymbolRef id = 1;
string source_field = 2;
SymbolRef from_class = 3;
SymbolRef to_class = 4;
Cardinality cardinality = 5;
string inverse = 6;
repeated FieldSchema fields = 7;
}
message MethodSchema {
string name = 1;
quixos.FunctionRef function = 2;
}
message MigrationSpec {
uint32 from_version = 1;
uint32 to_version = 2;
quixos.FunctionRef function = 3;
}
message ConstructorSpec {
quixos.FunctionRef function = 1;
}
message OperationSchema {
string name = 1;
TypeRef input_type = 2;
TypeRef output_type = 3;
quixos.FunctionRef function = 4;
}
message OperationServiceSchema {
string name = 1;
string full_name = 2;
repeated OperationSchema operations = 3;
}
message DocRefDeclaration {
string local_name = 1;
SymbolRef type = 2;
SymbolRef target_class = 3;
DocRefStrength strength = 4;
} }
+55 -48
View File
@@ -8,35 +8,56 @@ import "quixos/refs.proto";
import "quixos/runtime.proto"; import "quixos/runtime.proto";
service OrchestratorRuntime { service OrchestratorRuntime {
rpc InvokeFunction(InvokeFunctionRequest) returns (InvokeFunctionResponse); rpc InvokeCapability(InvokeCapabilityRequest) returns (InvokeCapabilityResponse);
rpc WatchFunction(WatchFunctionRequest) returns (stream WatchFunctionEvent); rpc WatchCapability(WatchCapabilityRequest) returns (stream WatchCapabilityEvent);
rpc ConstructObject(ConstructObjectRequest) returns (ConstructObjectResponse);
rpc ResolveOrConstructRelatedObject(ResolveOrConstructRelatedObjectRequest) returns (ResolveOrConstructRelatedObjectResponse);
rpc GetWorkspace(GetWorkspaceRequest) returns (GetWorkspaceResponse);
rpc ListPackageDescriptors(ListPackageDescriptorsRequest) returns (ListPackageDescriptorsResponse); rpc ListPackageDescriptors(ListPackageDescriptorsRequest) returns (ListPackageDescriptorsResponse);
rpc ListPackageRuntimes(ListPackageRuntimesRequest) returns (ListPackageRuntimesResponse); rpc ListPackageRuntimes(ListPackageRuntimesRequest) returns (ListPackageRuntimesResponse);
rpc ListActivations(ListActivationsRequest) returns (ListActivationsResponse); rpc ListActivations(ListActivationsRequest) returns (ListActivationsResponse);
rpc CloseActivation(CloseActivationRequest) returns (CloseActivationResponse); rpc CloseActivation(CloseActivationRequest) returns (CloseActivationResponse);
} }
message InvokeFunctionRequest { message ConstructObjectRequest {
quixos.FunctionRef function = 1; string atom_id = 1;
string object_id = 2; map<string, camino.Value> input = 2;
map<string, camino.Value> input = 3; }
message ConstructObjectResponse { camino.CaminoObject object = 1; }
message ResolveOrConstructRelatedObjectRequest {
string object_id = 1;
string interface_revision_id = 2;
string member_id = 3;
}
message ResolveOrConstructRelatedObjectResponse {
camino.CaminoObject object = 1;
bool constructed = 2;
} }
message InvokeFunctionResponse { message InvokeCapabilityRequest {
quixos.CapabilityRef capability = 1;
string object_id = 2;
map<string, camino.Value> input = 3;
// Identifies one logical client mutation across the capability and Camino
// layers so a live-value controller can recognize its own confirmation.
string client_mutation_id = 4;
}
message InvokeCapabilityResponse {
string invocation_id = 1; string invocation_id = 1;
Activation activation = 2; Activation activation = 2;
bool ok = 3; bool ok = 3;
camino.Value result = 4; camino.Value result = 4;
string error = 5; string error = 5;
repeated quixos.runtime.DerivedDependency dependencies = 6;
} }
message WatchFunctionRequest { message WatchCapabilityRequest {
quixos.FunctionRef function = 1; quixos.CapabilityRef capability = 1;
string object_id = 2; string object_id = 2;
map<string, camino.Value> input = 3; map<string, camino.Value> input = 3;
} }
message WatchCapabilityEvent {
message WatchFunctionEvent {
string invocation_id = 1; string invocation_id = 1;
Activation activation = 2; Activation activation = 2;
string watch_id = 3; string watch_id = 3;
@@ -46,36 +67,24 @@ message WatchFunctionEvent {
bool initial = 7; bool initial = 7;
} }
message GetWorkspaceRequest {}
message GetWorkspaceResponse {
string workspace_id = 1;
string workspace_revision_id = 2;
string source_root_commit = 3;
}
message ListActivationsRequest {} message ListActivationsRequest {}
message ListPackageDescriptorsRequest {} message ListPackageDescriptorsRequest {}
message ListPackageDescriptorsResponse { repeated quixos.PackageDescriptor descriptors = 1; }
message ListPackageDescriptorsResponse {
repeated quixos.PackageDescriptor descriptors = 1;
}
message ListPackageRuntimesRequest {} message ListPackageRuntimesRequest {}
message ListPackageRuntimesResponse { repeated PackageRuntimeStatus runtimes = 1; }
message ListPackageRuntimesResponse { message ListActivationsResponse { repeated Activation activations = 1; }
repeated PackageRuntimeStatus runtimes = 1; message CloseActivationRequest { string activation_id = 1; string reason = 2; }
} message CloseActivationResponse { Activation activation = 1; }
message ListActivationsResponse {
repeated Activation activations = 1;
}
message CloseActivationRequest {
string activation_id = 1;
string reason = 2;
}
message CloseActivationResponse {
Activation activation = 1;
}
message Activation { message Activation {
string activation_id = 1; string activation_id = 1;
quixos.FunctionRef function = 2; quixos.PackageExportRef export = 2;
string object_id = 3; string object_id = 3;
string state = 4; string state = 4;
uint32 demand = 5; uint32 demand = 5;
@@ -88,17 +97,15 @@ message Activation {
message PackageRuntimeStatus { message PackageRuntimeStatus {
string runtime_key = 1; string runtime_key = 1;
string package_namespace = 2; string package_revision_id = 2;
string package_name = 3; string source_repository = 3;
uint32 descriptor_version = 4; string source_commit = 4;
string source_repo = 5; string build_target = 5;
string server_installable = 6; string server_path = 6;
string source_path = 7; uint32 pid = 7;
string server_path = 8; string state = 8;
uint32 pid = 9; string started_at = 9;
string state = 10; string last_handshake_at = 10;
string started_at = 11; string runtime_protocol_version = 11;
string last_handshake_at = 12; uint32 advertised_export_count = 12;
string runtime_protocol_version = 13;
uint32 advertised_function_count = 14;
} }
+7 -63
View File
@@ -2,70 +2,14 @@ syntax = "proto3";
package quixos; package quixos;
import "camino/schema.proto";
import "quixos/refs.proto";
message PackageRef {
string package_namespace = 1;
string package_name = 2;
uint32 descriptor_version = 3;
string version_ref = 4;
}
message PackageDescriptor { message PackageDescriptor {
string package_namespace = 1; string package_id = 1;
string package_name = 2; string package_revision_id = 2;
uint32 descriptor_version = 3; string runtime_protocol_version = 3;
uint32 interface_compatible_back_to = 4; repeated RuntimeExport exports = 4;
uint32 runner_compatible_back_to = 5;
string source_repo = 6;
string source_ref = 7;
string resolved_source_ref = 8;
string server_installable = 9;
string runtime_protocol_version = 10;
repeated ClassExport class_exports = 11;
repeated FunctionExport function_exports = 12;
repeated ComponentExport component_exports = 13;
repeated SidecarExport sidecar_exports = 14;
repeated PackageDependency dependencies = 15;
} }
enum FunctionCapabilityKind { message RuntimeExport {
FUNCTION_CAPABILITY_KIND_UNSPECIFIED = 0; string export_id = 1;
METHOD = 1; string runtime_symbol = 2;
FIELD_RESOLVER = 2;
MIGRATION = 3;
CONSTRUCTOR = 4;
}
message ClassExport {
camino.SymbolRef class_id = 1;
string source_file = 2;
}
message FunctionExport {
FunctionRef function = 1;
string input_type = 2;
string output_type = 3;
uint32 interface_version = 4;
uint32 interface_compatible_back_to = 5;
FunctionCapabilityKind capability_kind = 6;
}
message ComponentExport {
string symbol = 1;
string props_type = 2;
repeated camino.SymbolRef supported_classes = 3;
}
message SidecarExport {
string symbol = 1;
string side_effect_mode = 2;
}
message PackageDependency {
PackageRef package = 1;
uint32 interface_compatible_back_to = 2;
bool refactor_required = 3;
string compatibility_note = 4;
} }
+26 -6
View File
@@ -2,10 +2,30 @@ syntax = "proto3";
package quixos; package quixos;
message FunctionRef { message CapabilityRef {
string package_namespace = 1; string interface_revision_id = 1;
string package_name = 2; string operation_id = 2;
string symbol = 3; }
string version_ref = 4;
string operation = 5; message PackageExportRef {
string package_revision_id = 1;
string export_id = 2;
}
message InjectedDependency {
string port_id = 1;
oneof binding {
string state_slot_id = 2;
EdgeDependency edge = 3;
string interface_revision_id = 4;
string constructor_atom_id = 5;
}
// Defaults to the invocation receiver. A checked dependency traversal can
// select a related object explicitly before the package runtime starts.
string object_id = 6;
}
message EdgeDependency {
string edge_type_id = 1;
string projection_id = 2;
} }
+34 -10
View File
@@ -9,44 +9,68 @@ service PackageRuntime {
rpc Handshake(HandshakeRequest) returns (HandshakeResponse); rpc Handshake(HandshakeRequest) returns (HandshakeResponse);
rpc Invoke(InvokeRequest) returns (InvokeResponse); rpc Invoke(InvokeRequest) returns (InvokeResponse);
rpc Watch(WatchRequest) returns (stream WatchEvent); rpc Watch(WatchRequest) returns (stream WatchEvent);
rpc GetInvocationStatus(InvocationControlRequest) returns (InvocationStatus);
rpc CancelInvocation(InvocationControlRequest) returns (InvocationStatus);
} }
message HandshakeRequest { message HandshakeRequest {
string orch_protocol_version = 1; string orch_protocol_version = 1;
string nonce = 2;
}
message HandshakeResponse {
string package_revision_id = 1;
string runtime_protocol_version = 2;
repeated string export_ids = 3;
string instance_id = 4;
string authentication_proof = 5;
repeated string capabilities = 6;
} }
message HandshakeResponse { message InvocationContext {
string package_namespace = 1; string workspace_epoch = 1;
string package_name = 2; string instance_id = 2;
string runtime_protocol_version = 3; string binding_digest = 3;
repeated quixos.FunctionRef functions = 4; string grant = 4;
string session_id = 5;
// Host-selected owner; packages must not invent workspace-local ownership.
string owner_conformance_id = 6;
}
message InvocationControlRequest { string invocation_id = 1; }
message InvocationStatus {
string invocation_id = 1;
// unknown, running, cancellation-requested, completed, failed
string state = 2;
} }
message InvokeRequest { message InvokeRequest {
string invocation_id = 1; string invocation_id = 1;
quixos.FunctionRef function = 2; quixos.PackageExportRef export = 2;
string object_id = 3; string object_id = 3;
map<string, camino.Value> input = 4; map<string, camino.Value> input = 4;
repeated quixos.InjectedDependency dependencies = 5;
InvocationContext context = 6;
} }
message InvokeResponse { message InvokeResponse {
bool ok = 1; bool ok = 1;
camino.Value result = 2; camino.Value result = 2;
string error = 3; string error = 3;
repeated DerivedDependency dependencies = 4;
} }
message WatchRequest { message WatchRequest {
string invocation_id = 1; string invocation_id = 1;
quixos.FunctionRef function = 2; quixos.PackageExportRef export = 2;
string object_id = 3; string object_id = 3;
map<string, camino.Value> input = 4; map<string, camino.Value> input = 4;
repeated quixos.InjectedDependency dependencies = 5;
InvocationContext context = 6;
} }
message DerivedDependency { message DerivedDependency {
string kind = 1; string kind = 1;
string object_id = 2; string object_id = 2;
string field_name = 3; string attachment_id = 3;
string source_field = 4; string projection_id = 4;
} }
message WatchEvent { message WatchEvent {
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import { generateTypeScriptBindings } from "./index.js";
const main = async () => {
const [schema, revision, output, options, ...rest] = process.argv.slice(2);
if (!schema || !revision || !output || rest.length) throw new Error(
"usage: quixos-codegen-ts SCHEMA.json PACKAGE_REVISION OUTPUT.ts [OPTIONS.json]");
const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision,
options ? JSON.parse(await readFile(options, "utf8")) : {});
await writeFile(output, generated);
};
main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; });
+138
View File
@@ -0,0 +1,138 @@
import type { InterfaceRevision, PackageRevision, ValueType, DependencyPort } from "../capability-model/types.js";
import type { CompiledCapabilityResourceRepository } from "../capability-language/assembly.js";
/** Portable generator input. New backends consume this instead of the parser or TS runtime. */
export type BindingSchema = {
format: "quixos-bindings";
version: 1;
interfaces: InterfaceRevision[];
packages: PackageRevision[];
};
export const bindingSchema = (compiled: CompiledCapabilityResourceRepository): BindingSchema => ({
format: "quixos-bindings", version: 1,
interfaces: compiled.resources.flatMap((node) => node.resource.kind === "interface" ? [node.resource.revision] : []),
packages: compiled.resources.flatMap((node) => node.resource.kind === "package" ? [node.resource.revision] : []),
});
export type TypeScriptBindingOptions = {
runtimeModule?: string;
/** Each export must implement MessageBinding<T>, providing both TS type and wire codec. */
messages?: Record<string, { module: string; export: string }>;
};
const q = JSON.stringify;
const object = (entries: [string, string][]) => `{ ${entries.map(([key, value]) => `${q(key)}: ${value}`).join("; ")} }`;
const unit = (type: ValueType) => type.kind === "builtin" && type.name === "unit";
export const generateTypeScriptBindings = (
schema: BindingSchema, packageRevisionId: string, options: TypeScriptBindingOptions = {},
) => {
if (schema.format !== "quixos-bindings" || schema.version !== 1) throw new Error("Unsupported binding schema version");
const pkg = schema.packages.find((entry) => entry.revisionId === packageRevisionId);
if (!pkg) throw new Error(`Unknown package revision ${packageRevisionId}`);
const messages = new Map<string, string>();
const type = (value: ValueType): string => {
switch (value.kind) {
case "builtin": return value.name === "unit" ? "null" : "QxWatchHandle";
case "scalar": return ({ bool: "boolean", bytes: "Uint8Array", string: "string", int64: "bigint", uint64: "bigint",
double: "number", int32: "number", uint32: "number" })[value.name];
case "object-ref": return `QxObjectRef<${q(value.expectation.kind === "atom" ? `atom:${value.expectation.atomId}` : `interface:${value.expectation.interfaceRevisionId}`)}>`;
case "optional": return `(${type(value.value)} | null)`;
case "list": return `Array<${type(value.value)}>`;
case "record": return `{ ${Object.entries(value.fields).map(([name, field]) => `${q(name)}: ${type(field)}`).join("; ")} }`;
case "message": {
if (!options.messages?.[value.descriptorId]) throw new Error(`Missing TypeScript message binding for ${value.descriptorId}`);
if (!messages.has(value.descriptorId)) messages.set(value.descriptorId, `message${messages.size}`);
return `BindingValue<typeof ${messages.get(value.descriptorId)}>`;
}
}
};
const ref = (target: { kind: "atom"; atomId: string } | { kind: "interface"; interfaceRevisionId: string }) =>
`QxObjectRef<${q(target.kind === "atom" ? `atom:${target.atomId}` : `interface:${target.interfaceRevisionId}`)}>`;
const params = (input: ValueType) => unit(input) ? "" : `input: ${type(input)}`;
const port = (entry: DependencyPort): { type: string; spec: unknown } => {
const requirement = entry.requirement;
switch (requirement.kind) {
case "state": {
const methods = requirement.primitives.map((primitive): [string, string] => {
if (primitive === "read") return ["get", `() => Promise<${type(requirement.valueType)}>`];
if (primitive === "write") return ["set", `(value: ${type(requirement.valueType)}) => Promise<void>`];
throw new Error(`State primitive ${primitive} is not supported by the TypeScript runtime binding yet`);
});
if (requirement.primitives.includes("read")) methods.push(["live", "() => Promise<QxLiveValue>"]);
return { type: object(methods), spec: { ...requirement, id: entry.id } };
}
case "edge": {
const methods = requirement.primitives.map((primitive): [string, string] => {
if (primitive === "resolve") return [primitive, `() => Promise<Array<${ref(requirement.target)}>>`];
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": {
const contract = schema.interfaces.find((candidate) => candidate.revisionId === requirement.interfaceRevisionId);
if (!contract) throw new Error(`Missing imported interface contract ${requirement.interfaceRevisionId}`);
// Streaming ports need a future streaming ABI; ordinary calls are fully typed today.
const operations = contract.members.flatMap((member) => member.operations.filter((operation) => operation.mode === "call")
.map((operation) => ({ ...operation, name: `${member.displayName}.${operation.displayName}` })));
return { type: object([
["objectId", ref({ kind: "interface", interfaceRevisionId: requirement.interfaceRevisionId })],
["live", object(operations.map(operation => [operation.name, `(${params(operation.inputType)}) => Promise<QxLiveValue>`]))],
...operations.map((operation): [string, string] => [operation.name, `(${params(operation.inputType)}) => Promise<${type(operation.outputType)}>`]),
]),
spec: { kind: "interface", id: entry.id, operations: Object.fromEntries(operations.map(({name, id, inputType, outputType}) =>
[name, {id, inputType, outputType}])) } };
}
case "constructor": {
const input = requirement.inputType;
if (!input) throw new Error(`Constructor port ${entry.id} needs an explicit input contract: add 'input TYPE' after ${requirement.atomId} in QX`);
return { type: object([["construct", `(${params(input)}) => Promise<${ref({ kind: "atom", atomId: requirement.atomId })}>`]]),
spec: { kind: "constructor", id: entry.id, inputType: input } };
}
}
};
const exports = [...pkg.exports].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
const names = new Set<string>();
const specs: Record<string, unknown> = {};
const contexts: [string, string][] = [];
const handlers: [string, string][] = [];
for (const entry of exports) {
if (names.has(entry.displayName)) throw new Error(`Duplicate export name ${entry.displayName}`);
names.add(entry.displayName);
const ports = entry.dependencyPorts.map((dependency) => ({ name: dependency.displayName, ...port(dependency) }));
if (new Set(ports.map((p) => p.name)).size !== ports.length) throw new Error(`Duplicate dependency name in ${entry.displayName}`);
const receiver = entry.kind === "constructor" ? ref({ kind: "atom", atomId: entry.constructsAtom }) :
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"}>` : "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);
// Watch-start handlers produce events through the runtime's derived stream protocol.
handlers.push([entry.displayName, event ? `QxDerived<${contextType}, ${outputType}>` :
`QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`]);
specs[entry.displayName] = { inputType: entry.inputType, outputType: entry.outputType,
...(event ? { eventType: event } : {}), ports: Object.fromEntries(ports.map((port) => [port.name, port.spec])) };
}
const imports = [...messages].map(([id, alias]) => {
const binding = options.messages![id]!;
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(binding.export)) throw new Error(`Invalid message binding export ${binding.export}`);
return `import { ${binding.export} as ${alias} } from ${q(binding.module)};`;
});
const signatures = `${object(contexts)} ${object(handlers)}`;
const typeImports = ["BindingValue", "QxObjectRef", "QxWatchHandle", "QxHandler", "QxDerived", "QxContextLifecycle", "QxLiveValue", "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` +
`export type Contexts = ${object(contexts)};\nexport type Implementation = ${object(handlers)};\n` +
`const messages = { ${[...messages].map(([id, alias]) => `${q(id)}: ${alias}`).join(", ")} } satisfies QxMessages;\n` +
`const specs = ${JSON.stringify(specs, null, 2)} satisfies Record<string, QxHandlerSpec>;\n` +
`export const createRuntime = (implementation: Implementation) => ({\n packageRevisionId,\n exports: {\n` +
exports.map((entry) => ` ${q(entry.id)}: bindQxHandler(specs[${q(entry.displayName)}], implementation[${q(entry.displayName)}], messages),`).join("\n") +
`\n },\n});\n`;
};
+386
View File
@@ -0,0 +1,386 @@
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,
type PackageRevision,
type SourceRevision,
type WorkspaceRevision,
} from "../capability-model/index.js";
import {
loadQuixosLock,
type GitSource,
type LockedResource,
type QuixosRepositoryLock,
} from "../resource-lock/index.js";
import {
compileCapabilityResourceSource,
compileCapabilitySource,
type CapabilityImportEnvironment,
type CapabilityResource,
type CapabilityResourceImport,
} from "./parser.js";
export type CapabilityRepositorySnapshot = {
directory: string;
};
export type CapabilityRepositoryResolver = (
source: GitSource,
kind: LockedResource["kind"],
) => Promise<CapabilityRepositorySnapshot>;
export type ResolvedCapabilityResource = {
key: string;
kind: LockedResource["kind"];
source: GitSource;
directory: string;
lock: QuixosRepositoryLock;
resource: CapabilityResource;
dependencies: ReadonlyMap<string, ResolvedCapabilityResource>;
};
export type CompiledWorkspaceRepository = {
workspace: WorkspaceRevision;
plan: CompiledWorkspaceRevision;
lock: QuixosRepositoryLock;
resources: ResolvedCapabilityResource[];
directResources: ReadonlyMap<string, ResolvedCapabilityResource>;
};
export type CompiledCapabilityResourceRepository = {
resource: CapabilityResource;
lock: QuixosRepositoryLock;
resources: ResolvedCapabilityResource[];
directResources: ReadonlyMap<string, ResolvedCapabilityResource>;
};
const sourceKey = (kind: LockedResource["kind"], source: GitSource) =>
`${kind}\0${source.repository}\0${source.commit.toLowerCase()}`;
const bindingKey = (kind: LockedResource["kind"], binding: string) =>
`${kind}\0${binding}`;
const importsKey = (entry: CapabilityResourceImport | LockedResource) =>
bindingKey(entry.kind, entry.binding);
const assertImportsMatchLock = (
label: string,
imports: readonly CapabilityResourceImport[],
resources: readonly LockedResource[],
) => {
const authored = [...imports].map(importsKey).sort();
const locked = [...resources].map(importsKey).sort();
if (
authored.length !== locked.length ||
authored.some((entry, index) => entry !== locked[index])
) {
throw new Error(
`${label} imports do not match quixos.lock:\n` +
`authored: ${authored.join(", ") || "none"}\n` +
`locked: ${locked.join(", ") || "none"}`,
);
}
};
const exactRevisions = <Revision extends {
revisionId: string;
source: SourceRevision;
}>(revisions: readonly Revision[]) => {
const seen = new Set<string>();
return revisions.filter((revision) => {
const key = `${revision.revisionId}\0${revision.source.repository}\0${revision.source.commit}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
};
const exactAtoms = (atoms: readonly AtomDefinition[]) => {
const seen = new Set<string>();
return atoms.filter((atom) => {
if (seen.has(atom.id)) return false;
seen.add(atom.id);
return true;
});
};
const diagnosticsMessage = (
label: string,
diagnostics: readonly {
fileName: string;
line: number;
column: number;
phase: string;
code: string;
message: string;
path?: string;
}[],
) => `${label} did not compile:\n${diagnostics.map((entry) => {
const location = entry.line > 0
? `${entry.fileName}:${entry.line}:${entry.column + 1}`
: `${entry.fileName}${entry.path ? `:${entry.path}` : ""}`;
return `${location}: ${entry.phase} ${entry.code}: ${entry.message}`;
}).join("\n")}`;
const revisionFor = (node: ResolvedCapabilityResource) =>
node.resource.revision;
const resourceClosure = (
roots: readonly ResolvedCapabilityResource[],
): ResolvedCapabilityResource[] => {
const result: ResolvedCapabilityResource[] = [];
const seen = new Set<string>();
const visit = (node: ResolvedCapabilityResource) => {
if (seen.has(node.key)) return;
seen.add(node.key);
for (const dependency of node.dependencies.values()) visit(dependency);
result.push(node);
};
for (const root of roots) visit(root);
return result;
};
const environmentFor = (
direct: readonly [LockedResource, ResolvedCapabilityResource][],
closure: readonly ResolvedCapabilityResource[],
): CapabilityImportEnvironment => ({
interfaces: new Map(direct.flatMap(([locked, node]) =>
node.resource.kind === "interface"
? [[locked.binding, node.resource.revision] as const]
: [])),
packages: new Map(direct.flatMap(([locked, node]) =>
node.resource.kind === "package"
? [[locked.binding, node.resource.revision] as const]
: [])),
interfaceClosure: exactRevisions(closure.flatMap((node) =>
node.resource.kind === "interface" ? [node.resource.revision] : [])),
packageClosure: exactRevisions(closure.flatMap((node) =>
node.resource.kind === "package" ? [node.resource.revision] : [])),
externalAtoms: exactAtoms(closure.flatMap((node) => node.resource.externalAtoms)),
});
const createResourceGraphResolver = (
quixosCommit: string,
resolveResource: CapabilityRepositoryResolver,
) => {
const resolved = new Map<string, Promise<ResolvedCapabilityResource>>();
const active: string[] = [];
const visit = async (
locked: LockedResource,
suppliedSnapshot?: CapabilityRepositorySnapshot,
): Promise<ResolvedCapabilityResource> => {
const key = sourceKey(locked.kind, locked.source);
const cached = resolved.get(key);
if (cached) {
if (active.includes(key)) {
throw new Error(`Resource dependency cycle: ${[...active, key].join(" -> ")}`);
}
return await cached;
}
const pending = (async () => {
active.push(key);
try {
const snapshot = suppliedSnapshot ?? await resolveResource(locked.source, locked.kind);
const lockResult = await loadQuixosLock(path.join(snapshot.directory, "quixos.lock"));
if (!lockResult.ok) {
throw new Error(diagnosticsMessage(`${locked.kind} ${locked.binding} lock`, lockResult.diagnostics));
}
if (lockResult.lock.quixos.policy) {
throw new Error(
`${locked.kind} ${locked.binding} lock declares workspace Quixos policy ` +
`${lockResult.lock.quixos.policy}; resource locks may only declare their exact authored-against commit`,
);
}
if (lockResult.lock.quixos.commit.toLowerCase() !== quixosCommit.toLowerCase()) {
throw new Error(
`${locked.kind} ${locked.binding} selects Quixos ${lockResult.lock.quixos.commit}, ` +
`but the repository graph selects ${quixosCommit}`,
);
}
const directPairs: Array<[LockedResource, ResolvedCapabilityResource]> = [];
for (const dependency of lockResult.lock.resources) {
directPairs.push([dependency, await visit(dependency)]);
}
const dependencyClosure = resourceClosure(
directPairs.map(([, node]) => node),
);
const environment = environmentFor(directPairs, dependencyClosure);
const manifestName = locked.kind === "interface" ? "interface.qx" : "package.qx";
const manifestPath = path.join(snapshot.directory, manifestName);
const sourceText = await readFile(manifestPath, "utf8");
const compiled = compileCapabilityResourceSource(sourceText, {
source: locked.source,
fileName: manifestPath,
environment,
});
if (!compiled.ok) {
throw new Error(diagnosticsMessage(`${locked.kind} ${locked.binding}`, compiled.diagnostics));
}
if (compiled.resource.kind !== locked.kind) {
throw new Error(
`${manifestPath} declares ${compiled.resource.kind}, not ${locked.kind}`,
);
}
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,
source: locked.source,
directory: snapshot.directory,
lock: lockResult.lock,
resource: compiled.resource,
dependencies: new Map(directPairs.map(([dependency, node]) => [
bindingKey(dependency.kind, dependency.binding),
node,
])),
};
} finally {
active.pop();
}
})();
resolved.set(key, pending);
return await pending;
};
return {
visit,
nodes: async () => await Promise.all(resolved.values()),
};
};
export const compileCapabilityResourceRepository = async (options: {
rootDirectory: string;
kind: LockedResource["kind"];
source: GitSource;
resolveResource: CapabilityRepositoryResolver;
}): Promise<CompiledCapabilityResourceRepository> => {
const lockResult = await loadQuixosLock(path.join(options.rootDirectory, "quixos.lock"));
if (!lockResult.ok) {
throw new Error(diagnosticsMessage("Resource lock", lockResult.diagnostics));
}
const resolver = createResourceGraphResolver(
lockResult.lock.quixos.commit,
options.resolveResource,
);
const root = await resolver.visit({
kind: options.kind,
binding: "<root>",
source: options.source,
}, { directory: options.rootDirectory });
return {
resource: root.resource,
lock: root.lock,
resources: await resolver.nodes(),
directResources: root.dependencies,
};
};
export const compileWorkspaceRepository = async (options: {
rootDirectory: string;
resolveResource: CapabilityRepositoryResolver;
workspaceId?: string;
workspaceRevisionId?: string;
sourceRootCommit?: string;
/** An editor's proposed source snapshot; locks and dependency revisions remain exact. */
readSource?: (name: string) => Promise<string>;
}): Promise<CompiledWorkspaceRepository> => {
const rootLockResult = await loadQuixosLock(
path.join(options.rootDirectory, "quixos.lock"),
);
if (!rootLockResult.ok) {
throw new Error(diagnosticsMessage("Workspace lock", rootLockResult.diagnostics));
}
const rootLock = rootLockResult.lock;
const resolver = createResourceGraphResolver(rootLock.quixos.commit, options.resolveResource);
const directPairs: Array<[LockedResource, ResolvedCapabilityResource]> = [];
for (const locked of rootLock.resources) {
directPairs.push([locked, await resolver.visit(locked)]);
}
const nodes = await resolver.nodes();
const environment = environmentFor(directPairs, nodes);
const workspacePath = path.join(options.rootDirectory, "workspace.qx");
const sources = options.readSource ? await resolveQxSources(options.readSource) : await loadQxSources(options.rootDirectory);
const compiled = compileCapabilitySource(sources.source, workspacePath, environment);
if (!compiled.ok) {
throw new Error(diagnosticsMessage("Workspace", compiled.diagnostics.map((diagnostic) =>
diagnostic.line > 0 ? { ...diagnostic, ...sources.originalPosition(diagnostic.line, diagnostic.column) } : diagnostic)));
}
assertImportsMatchLock(workspacePath, compiled.imports, rootLock.resources);
const availableInterfaceIds = new Set(
nodes.flatMap((node) => node.resource.kind === "interface"
? [node.resource.revision.revisionId]
: []),
);
const availableAtomIds = new Set(compiled.workspace.atoms.map((atom) => atom.id));
for (const node of nodes) {
for (const requirement of node.resource.externalInterfaces) {
if (!availableInterfaceIds.has(requirement.revisionId)) {
throw new Error(
`${node.kind} ${node.resource.revision.displayName} requires external interface ` +
`${requirement.binding} (${requirement.revisionId}), but the workspace resource graph does not provide it`,
);
}
}
for (const atom of node.resource.externalAtoms) {
if (!availableAtomIds.has(atom.id)) {
throw new Error(
`${node.kind} ${node.resource.revision.displayName} requires external atom ` +
`${atom.displayName} (${atom.id}), but the workspace does not define it`,
);
}
}
}
const workspace: WorkspaceRevision = {
...compiled.workspace,
...(options.workspaceId
? { workspaceId: capabilityId.workspace(options.workspaceId) }
: {}),
...(options.workspaceRevisionId
? { id: capabilityId.workspaceRevision(options.workspaceRevisionId) }
: {}),
...(options.sourceRootCommit
? { sourceRootCommit: options.sourceRootCommit }
: {}),
};
const checked = compileWorkspaceRevision(workspace);
if (!checked.ok) {
throw new Error(
`Instantiated workspace did not compile:\n${checked.issues.map((entry) =>
`${entry.path}: ${entry.message}`).join("\n")}`,
);
}
return {
workspace,
plan: checked.plan,
lock: rootLock,
resources: nodes,
directResources: new Map(directPairs.map(([locked, node]) => [
bindingKey(locked.kind, locked.binding),
node,
])),
};
};
+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 }); }
};
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env node
import { readFile } from "node:fs/promises";
import process from "node:process";
import { compileCapabilityResourceSource, compileCapabilitySource } from "./parser.js";
import { capabilityId } from "../capability-model/types.js";
import { compileWorkspaceRevision } from "../capability-model/validation.js";
const usage = `usage: quixos-capability-compile [--check] [--resource]
[--workspace-id ID] [--workspace-revision-id ID]
[--source-root-commit GIT_REV] <workspace.qx | ->
Parses, lowers, and validates a Quixos capability workspace. --resource instead
expects one standalone interface or package document. By default the checked
semantic workspace revision or resource is written as JSON. --check emits no JSON.
The identity overrides instantiate a checked built-in/template assembly for one
real workspace; both must be supplied together.`;
const parseArgs = (args: string[]) => {
let checkOnly = false;
let resource = false;
let workspaceId: string | undefined;
let workspaceRevisionId: string | undefined;
let sourceRootCommit: string | undefined;
const positional: string[] = [];
for (let index = 0; index < args.length; index += 1) {
const argument = args[index]!;
if (argument === "--check") checkOnly = true;
else if (argument === "--resource") resource = true;
else if (argument === "--workspace-id") workspaceId = args[++index];
else if (argument === "--workspace-revision-id") workspaceRevisionId = args[++index];
else if (argument === "--source-root-commit") sourceRootCommit = args[++index];
else if (argument === "-") positional.push(argument);
else if (argument.startsWith("-")) throw new Error(usage);
else positional.push(argument);
}
if (
positional.length !== 1
|| Boolean(workspaceId) !== Boolean(workspaceRevisionId)
|| (resource && Boolean(workspaceId || workspaceRevisionId || sourceRootCommit))
) {
throw new Error(usage);
}
return { checkOnly, resource, workspaceId, workspaceRevisionId, sourceRootCommit, fileName: positional[0]! };
};
const main = async () => {
const args = process.argv.slice(2);
if (args.includes("--help") || args.includes("-h")) {
process.stdout.write(`${usage}\n`);
return;
}
const {
checkOnly,
resource,
workspaceId,
workspaceRevisionId,
sourceRootCommit,
fileName,
} = parseArgs(args);
const source =
fileName === "-"
? await new Promise<string>((resolve, reject) => {
const chunks: Buffer[] = [];
process.stdin.on("data", (chunk: Buffer) => chunks.push(chunk));
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
process.stdin.on("error", reject);
})
: await readFile(fileName, "utf8");
const reportDiagnostics = (diagnostics: readonly {
fileName: string;
line: number;
column: number;
phase: string;
code: string;
message: string;
path?: string;
}[]) => {
for (const diagnostic of diagnostics) {
const location = diagnostic.line
? `${diagnostic.fileName}:${diagnostic.line}:${diagnostic.column + 1}`
: `${diagnostic.fileName}${diagnostic.path ? `:${diagnostic.path}` : ""}`;
process.stderr.write(
`${location}: ${diagnostic.phase} ${diagnostic.code}: ${diagnostic.message}\n`,
);
}
process.exitCode = 1;
};
if (resource) {
const result = compileCapabilityResourceSource(source, {
source: {
repository: "https://compiler.invalid/resource.git",
commit: "0000000000000000000000000000000000000000",
},
fileName,
});
if (!result.ok) {
reportDiagnostics(result.diagnostics);
return;
}
if (!checkOnly) process.stdout.write(`${JSON.stringify(result.resource, null, 2)}\n`);
return;
}
const result = compileCapabilitySource(source, fileName);
if (!result.ok) {
reportDiagnostics(result.diagnostics);
return;
}
const workspace = workspaceId && workspaceRevisionId
? {
...result.workspace,
workspaceId: capabilityId.workspace(workspaceId),
id: capabilityId.workspaceRevision(workspaceRevisionId),
...(sourceRootCommit ? { sourceRootCommit } : {}),
}
: { ...result.workspace, ...(sourceRootCommit ? { sourceRootCommit } : {}) };
const instantiated = compileWorkspaceRevision(workspace);
if (!instantiated.ok) {
throw new Error(instantiated.issues.map((issue) => `${issue.path}: ${issue.message}`).join("\n"));
}
if (!checkOnly) {
process.stdout.write(`${JSON.stringify(workspace, null, 2)}\n`);
}
};
main().catch((error: unknown) => {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
});
File diff suppressed because one or more lines are too long
@@ -0,0 +1,213 @@
WORKSPACE=1
FRAGMENT=2
IMPORT=3
EXTERNAL=4
ATOM=5
INTERFACE=6
INTERFACES=7
PACKAGE=8
VALUE=9
RELATION=10
OPERATION=11
FUNCTION=12
CONSTRUCTOR=13
CONSTRUCTS=14
INPUT=15
CONFORM=16
AS=17
BIND=18
TO=19
PRIVATE=20
SHARED=21
STATE=22
EDGE=23
PROJECTION=24
WITH=25
USING=26
VIA=27
MATERIALIZE=28
IF=29
ABSENT=30
ON=31
POLICY=32
DEFAULT=33
SOURCE=34
REPOSITORY=35
COMMIT=36
REVISION=37
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
RECORD=79
BOOL=80
BYTES=81
DOUBLE=82
INT32=83
INT64=84
STRING=85
UINT32=86
UINT64=87
TRUE=88
FALSE=89
NULL=90
ARROW=91
COLON=92
SEMI=93
COMMA=94
DOT=95
LBRACE=96
RBRACE=97
LBRACK=98
RBRACK=99
LPAREN=100
RPAREN=101
LT=102
GT=103
INTEGER=104
JSON_NUMBER=105
IDENTIFIER=106
STRING_LITERAL=107
LINE_COMMENT=108
BLOCK_COMMENT=109
WS=110
'workspace'=1
'fragment'=2
'import'=3
'external'=4
'atom'=5
'interface'=6
'interfaces'=7
'package'=8
'value'=9
'relation'=10
'operation'=11
'function'=12
'constructor'=13
'constructs'=14
'input'=15
'conform'=16
'as'=17
'bind'=18
'to'=19
'private'=20
'shared'=21
'state'=22
'edge'=23
'projection'=24
'with'=25
'using'=26
'via'=27
'materialize'=28
'if'=29
'absent'=30
'on'=31
'policy'=32
'default'=33
'source'=34
'repository'=35
'commit'=36
'revision'=37
'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
'record'=79
'bool'=80
'bytes'=81
'double'=82
'int32'=83
'int64'=84
'string'=85
'uint32'=86
'uint64'=87
'true'=88
'false'=89
'null'=90
'->'=91
':'=92
';'=93
','=94
'.'=95
'{'=96
'}'=97
'['=98
']'=99
'('=100
')'=101
'<'=102
'>'=103
File diff suppressed because one or more lines are too long
@@ -0,0 +1,213 @@
WORKSPACE=1
FRAGMENT=2
IMPORT=3
EXTERNAL=4
ATOM=5
INTERFACE=6
INTERFACES=7
PACKAGE=8
VALUE=9
RELATION=10
OPERATION=11
FUNCTION=12
CONSTRUCTOR=13
CONSTRUCTS=14
INPUT=15
CONFORM=16
AS=17
BIND=18
TO=19
PRIVATE=20
SHARED=21
STATE=22
EDGE=23
PROJECTION=24
WITH=25
USING=26
VIA=27
MATERIALIZE=28
IF=29
ABSENT=30
ON=31
POLICY=32
DEFAULT=33
SOURCE=34
REPOSITORY=35
COMMIT=36
REVISION=37
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
RECORD=79
BOOL=80
BYTES=81
DOUBLE=82
INT32=83
INT64=84
STRING=85
UINT32=86
UINT64=87
TRUE=88
FALSE=89
NULL=90
ARROW=91
COLON=92
SEMI=93
COMMA=94
DOT=95
LBRACE=96
RBRACE=97
LBRACK=98
RBRACK=99
LPAREN=100
RPAREN=101
LT=102
GT=103
INTEGER=104
JSON_NUMBER=105
IDENTIFIER=106
STRING_LITERAL=107
LINE_COMMENT=108
BLOCK_COMMENT=109
WS=110
'workspace'=1
'fragment'=2
'import'=3
'external'=4
'atom'=5
'interface'=6
'interfaces'=7
'package'=8
'value'=9
'relation'=10
'operation'=11
'function'=12
'constructor'=13
'constructs'=14
'input'=15
'conform'=16
'as'=17
'bind'=18
'to'=19
'private'=20
'shared'=21
'state'=22
'edge'=23
'projection'=24
'with'=25
'using'=26
'via'=27
'materialize'=28
'if'=29
'absent'=30
'on'=31
'policy'=32
'default'=33
'source'=34
'repository'=35
'commit'=36
'revision'=37
'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
'record'=79
'bool'=80
'bytes'=81
'double'=82
'int32'=83
'int64'=84
'string'=85
'uint32'=86
'uint64'=87
'true'=88
'false'=89
'null'=90
'->'=91
':'=92
';'=93
','=94
'.'=95
'{'=96
'}'=97
'['=98
']'=99
'('=100
')'=101
'<'=102
'>'=103
@@ -0,0 +1,606 @@
import * as antlr from "antlr4ng";
import { Token } from "antlr4ng";
export class QuixosCapabilityLexer extends antlr.Lexer {
public static readonly WORKSPACE = 1;
public static readonly FRAGMENT = 2;
public static readonly IMPORT = 3;
public static readonly EXTERNAL = 4;
public static readonly ATOM = 5;
public static readonly INTERFACE = 6;
public static readonly INTERFACES = 7;
public static readonly PACKAGE = 8;
public static readonly VALUE = 9;
public static readonly RELATION = 10;
public static readonly OPERATION = 11;
public static readonly FUNCTION = 12;
public static readonly CONSTRUCTOR = 13;
public static readonly CONSTRUCTS = 14;
public static readonly INPUT = 15;
public static readonly CONFORM = 16;
public static readonly AS = 17;
public static readonly BIND = 18;
public static readonly TO = 19;
public static readonly PRIVATE = 20;
public static readonly SHARED = 21;
public static readonly STATE = 22;
public static readonly EDGE = 23;
public static readonly PROJECTION = 24;
public static readonly WITH = 25;
public static readonly USING = 26;
public static readonly VIA = 27;
public static readonly MATERIALIZE = 28;
public static readonly IF = 29;
public static readonly ABSENT = 30;
public static readonly ON = 31;
public static readonly POLICY = 32;
public static readonly DEFAULT = 33;
public static readonly SOURCE = 34;
public static readonly REPOSITORY = 35;
public static readonly COMMIT = 36;
public static readonly REVISION = 37;
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 RECORD = 79;
public static readonly BOOL = 80;
public static readonly BYTES = 81;
public static readonly DOUBLE = 82;
public static readonly INT32 = 83;
public static readonly INT64 = 84;
public static readonly STRING = 85;
public static readonly UINT32 = 86;
public static readonly UINT64 = 87;
public static readonly TRUE = 88;
public static readonly FALSE = 89;
public static readonly NULL = 90;
public static readonly ARROW = 91;
public static readonly COLON = 92;
public static readonly SEMI = 93;
public static readonly COMMA = 94;
public static readonly DOT = 95;
public static readonly LBRACE = 96;
public static readonly RBRACE = 97;
public static readonly LBRACK = 98;
public static readonly RBRACK = 99;
public static readonly LPAREN = 100;
public static readonly RPAREN = 101;
public static readonly LT = 102;
public static readonly GT = 103;
public static readonly INTEGER = 104;
public static readonly JSON_NUMBER = 105;
public static readonly IDENTIFIER = 106;
public static readonly STRING_LITERAL = 107;
public static readonly LINE_COMMENT = 108;
public static readonly BLOCK_COMMENT = 109;
public static readonly WS = 110;
public static readonly channelNames = [
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
];
public static readonly literalNames = [
null, "'workspace'", "'fragment'", "'import'", "'external'", "'atom'",
"'interface'", "'interfaces'", "'package'", "'value'", "'relation'",
"'operation'", "'function'", "'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'", "'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'", "'record'", "'bool'",
"'bytes'", "'double'", "'int32'", "'int64'", "'string'", "'uint32'",
"'uint64'", "'true'", "'false'", "'null'", "'->'", "':'", "';'",
"','", "'.'", "'{'", "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'"
];
public static readonly symbolicNames = [
null, "WORKSPACE", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE",
"INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION",
"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", "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", "RECORD", "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 = [
"DEFAULT_MODE",
];
public static readonly ruleNames = [
"WORKSPACE", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE",
"INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION",
"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", "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", "RECORD", "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",
];
public constructor(input: antlr.CharStream) {
super(input);
this.interpreter = new antlr.LexerATNSimulator(this, QuixosCapabilityLexer._ATN, QuixosCapabilityLexer.decisionsToDFA, new antlr.PredictionContextCache());
}
public get grammarFileName(): string { return "QuixosCapability.g4"; }
public get literalNames(): (string | null)[] { return QuixosCapabilityLexer.literalNames; }
public get symbolicNames(): (string | null)[] { return QuixosCapabilityLexer.symbolicNames; }
public get ruleNames(): string[] { return QuixosCapabilityLexer.ruleNames; }
public get serializedATN(): number[] { return QuixosCapabilityLexer._serializedATN; }
public get channelNames(): string[] { return QuixosCapabilityLexer.channelNames; }
public get modeNames(): string[] { return QuixosCapabilityLexer.modeNames; }
public static readonly _serializedATN: number[] = [
4,0,110,1056,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,2,111,7,111,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,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,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,78,1,78,1,79,1,79,1,79,1,79,1,79,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,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,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,86,1,86,1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,
88,1,89,1,89,1,89,1,89,1,89,1,90,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,1,102,1,103,3,103,957,8,103,1,103,
4,103,960,8,103,11,103,12,103,961,1,104,3,104,965,8,104,1,104,1,
104,1,104,5,104,970,8,104,10,104,12,104,973,9,104,3,104,975,8,104,
1,104,1,104,4,104,979,8,104,11,104,12,104,980,3,104,983,8,104,1,
104,1,104,3,104,987,8,104,1,104,4,104,990,8,104,11,104,12,104,991,
3,104,994,8,104,1,105,1,105,5,105,998,8,105,10,105,12,105,1001,9,
105,1,106,1,106,1,106,5,106,1006,8,106,10,106,12,106,1009,9,106,
1,106,1,106,1,107,1,107,1,107,1,107,1,107,1,107,1,107,1,107,3,107,
1021,8,107,1,108,1,108,1,109,1,109,1,109,1,109,5,109,1029,8,109,
10,109,12,109,1032,9,109,1,109,1,109,1,110,1,110,1,110,1,110,5,110,
1040,8,110,10,110,12,110,1043,9,110,1,110,1,110,1,110,1,110,1,110,
1,111,4,111,1051,8,111,11,111,12,111,1052,1,111,1,111,1,1041,0,112,
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,107,215,0,217,0,219,108,221,109,223,
110,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,1070,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,213,1,0,0,0,0,219,1,0,0,0,0,221,1,0,0,0,0,223,1,
0,0,0,1,225,1,0,0,0,3,235,1,0,0,0,5,244,1,0,0,0,7,251,1,0,0,0,9,
260,1,0,0,0,11,265,1,0,0,0,13,275,1,0,0,0,15,286,1,0,0,0,17,294,
1,0,0,0,19,300,1,0,0,0,21,309,1,0,0,0,23,319,1,0,0,0,25,328,1,0,
0,0,27,340,1,0,0,0,29,351,1,0,0,0,31,357,1,0,0,0,33,365,1,0,0,0,
35,368,1,0,0,0,37,373,1,0,0,0,39,376,1,0,0,0,41,384,1,0,0,0,43,391,
1,0,0,0,45,397,1,0,0,0,47,402,1,0,0,0,49,413,1,0,0,0,51,418,1,0,
0,0,53,424,1,0,0,0,55,428,1,0,0,0,57,440,1,0,0,0,59,443,1,0,0,0,
61,450,1,0,0,0,63,453,1,0,0,0,65,460,1,0,0,0,67,468,1,0,0,0,69,475,
1,0,0,0,71,486,1,0,0,0,73,493,1,0,0,0,75,502,1,0,0,0,77,517,1,0,
0,0,79,527,1,0,0,0,81,540,1,0,0,0,83,546,1,0,0,0,85,563,1,0,0,0,
87,566,1,0,0,0,89,570,1,0,0,0,91,575,1,0,0,0,93,581,1,0,0,0,95,590,
1,0,0,0,97,599,1,0,0,0,99,603,1,0,0,0,101,607,1,0,0,0,103,611,1,
0,0,0,105,617,1,0,0,0,107,623,1,0,0,0,109,628,1,0,0,0,111,633,1,
0,0,0,113,639,1,0,0,0,115,647,1,0,0,0,117,655,1,0,0,0,119,666,1,
0,0,0,121,671,1,0,0,0,123,683,1,0,0,0,125,694,1,0,0,0,127,704,1,
0,0,0,129,716,1,0,0,0,131,736,1,0,0,0,133,741,1,0,0,0,135,754,1,
0,0,0,137,766,1,0,0,0,139,778,1,0,0,0,141,783,1,0,0,0,143,791,1,
0,0,0,145,796,1,0,0,0,147,809,1,0,0,0,149,817,1,0,0,0,151,826,1,
0,0,0,153,840,1,0,0,0,155,849,1,0,0,0,157,854,1,0,0,0,159,861,1,
0,0,0,161,866,1,0,0,0,163,872,1,0,0,0,165,879,1,0,0,0,167,885,1,
0,0,0,169,891,1,0,0,0,171,898,1,0,0,0,173,905,1,0,0,0,175,912,1,
0,0,0,177,917,1,0,0,0,179,923,1,0,0,0,181,928,1,0,0,0,183,931,1,
0,0,0,185,933,1,0,0,0,187,935,1,0,0,0,189,937,1,0,0,0,191,939,1,
0,0,0,193,941,1,0,0,0,195,943,1,0,0,0,197,945,1,0,0,0,199,947,1,
0,0,0,201,949,1,0,0,0,203,951,1,0,0,0,205,953,1,0,0,0,207,956,1,
0,0,0,209,964,1,0,0,0,211,995,1,0,0,0,213,1002,1,0,0,0,215,1012,
1,0,0,0,217,1022,1,0,0,0,219,1024,1,0,0,0,221,1035,1,0,0,0,223,1050,
1,0,0,0,225,226,5,119,0,0,226,227,5,111,0,0,227,228,5,114,0,0,228,
229,5,107,0,0,229,230,5,115,0,0,230,231,5,112,0,0,231,232,5,97,0,
0,232,233,5,99,0,0,233,234,5,101,0,0,234,2,1,0,0,0,235,236,5,102,
0,0,236,237,5,114,0,0,237,238,5,97,0,0,238,239,5,103,0,0,239,240,
5,109,0,0,240,241,5,101,0,0,241,242,5,110,0,0,242,243,5,116,0,0,
243,4,1,0,0,0,244,245,5,105,0,0,245,246,5,109,0,0,246,247,5,112,
0,0,247,248,5,111,0,0,248,249,5,114,0,0,249,250,5,116,0,0,250,6,
1,0,0,0,251,252,5,101,0,0,252,253,5,120,0,0,253,254,5,116,0,0,254,
255,5,101,0,0,255,256,5,114,0,0,256,257,5,110,0,0,257,258,5,97,0,
0,258,259,5,108,0,0,259,8,1,0,0,0,260,261,5,97,0,0,261,262,5,116,
0,0,262,263,5,111,0,0,263,264,5,109,0,0,264,10,1,0,0,0,265,266,5,
105,0,0,266,267,5,110,0,0,267,268,5,116,0,0,268,269,5,101,0,0,269,
270,5,114,0,0,270,271,5,102,0,0,271,272,5,97,0,0,272,273,5,99,0,
0,273,274,5,101,0,0,274,12,1,0,0,0,275,276,5,105,0,0,276,277,5,110,
0,0,277,278,5,116,0,0,278,279,5,101,0,0,279,280,5,114,0,0,280,281,
5,102,0,0,281,282,5,97,0,0,282,283,5,99,0,0,283,284,5,101,0,0,284,
285,5,115,0,0,285,14,1,0,0,0,286,287,5,112,0,0,287,288,5,97,0,0,
288,289,5,99,0,0,289,290,5,107,0,0,290,291,5,97,0,0,291,292,5,103,
0,0,292,293,5,101,0,0,293,16,1,0,0,0,294,295,5,118,0,0,295,296,5,
97,0,0,296,297,5,108,0,0,297,298,5,117,0,0,298,299,5,101,0,0,299,
18,1,0,0,0,300,301,5,114,0,0,301,302,5,101,0,0,302,303,5,108,0,0,
303,304,5,97,0,0,304,305,5,116,0,0,305,306,5,105,0,0,306,307,5,111,
0,0,307,308,5,110,0,0,308,20,1,0,0,0,309,310,5,111,0,0,310,311,5,
112,0,0,311,312,5,101,0,0,312,313,5,114,0,0,313,314,5,97,0,0,314,
315,5,116,0,0,315,316,5,105,0,0,316,317,5,111,0,0,317,318,5,110,
0,0,318,22,1,0,0,0,319,320,5,102,0,0,320,321,5,117,0,0,321,322,5,
110,0,0,322,323,5,99,0,0,323,324,5,116,0,0,324,325,5,105,0,0,325,
326,5,111,0,0,326,327,5,110,0,0,327,24,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,111,0,0,338,339,5,114,0,0,339,26,1,0,0,0,
340,341,5,99,0,0,341,342,5,111,0,0,342,343,5,110,0,0,343,344,5,115,
0,0,344,345,5,116,0,0,345,346,5,114,0,0,346,347,5,117,0,0,347,348,
5,99,0,0,348,349,5,116,0,0,349,350,5,115,0,0,350,28,1,0,0,0,351,
352,5,105,0,0,352,353,5,110,0,0,353,354,5,112,0,0,354,355,5,117,
0,0,355,356,5,116,0,0,356,30,1,0,0,0,357,358,5,99,0,0,358,359,5,
111,0,0,359,360,5,110,0,0,360,361,5,102,0,0,361,362,5,111,0,0,362,
363,5,114,0,0,363,364,5,109,0,0,364,32,1,0,0,0,365,366,5,97,0,0,
366,367,5,115,0,0,367,34,1,0,0,0,368,369,5,98,0,0,369,370,5,105,
0,0,370,371,5,110,0,0,371,372,5,100,0,0,372,36,1,0,0,0,373,374,5,
116,0,0,374,375,5,111,0,0,375,38,1,0,0,0,376,377,5,112,0,0,377,378,
5,114,0,0,378,379,5,105,0,0,379,380,5,118,0,0,380,381,5,97,0,0,381,
382,5,116,0,0,382,383,5,101,0,0,383,40,1,0,0,0,384,385,5,115,0,0,
385,386,5,104,0,0,386,387,5,97,0,0,387,388,5,114,0,0,388,389,5,101,
0,0,389,390,5,100,0,0,390,42,1,0,0,0,391,392,5,115,0,0,392,393,5,
116,0,0,393,394,5,97,0,0,394,395,5,116,0,0,395,396,5,101,0,0,396,
44,1,0,0,0,397,398,5,101,0,0,398,399,5,100,0,0,399,400,5,103,0,0,
400,401,5,101,0,0,401,46,1,0,0,0,402,403,5,112,0,0,403,404,5,114,
0,0,404,405,5,111,0,0,405,406,5,106,0,0,406,407,5,101,0,0,407,408,
5,99,0,0,408,409,5,116,0,0,409,410,5,105,0,0,410,411,5,111,0,0,411,
412,5,110,0,0,412,48,1,0,0,0,413,414,5,119,0,0,414,415,5,105,0,0,
415,416,5,116,0,0,416,417,5,104,0,0,417,50,1,0,0,0,418,419,5,117,
0,0,419,420,5,115,0,0,420,421,5,105,0,0,421,422,5,110,0,0,422,423,
5,103,0,0,423,52,1,0,0,0,424,425,5,118,0,0,425,426,5,105,0,0,426,
427,5,97,0,0,427,54,1,0,0,0,428,429,5,109,0,0,429,430,5,97,0,0,430,
431,5,116,0,0,431,432,5,101,0,0,432,433,5,114,0,0,433,434,5,105,
0,0,434,435,5,97,0,0,435,436,5,108,0,0,436,437,5,105,0,0,437,438,
5,122,0,0,438,439,5,101,0,0,439,56,1,0,0,0,440,441,5,105,0,0,441,
442,5,102,0,0,442,58,1,0,0,0,443,444,5,97,0,0,444,445,5,98,0,0,445,
446,5,115,0,0,446,447,5,101,0,0,447,448,5,110,0,0,448,449,5,116,
0,0,449,60,1,0,0,0,450,451,5,111,0,0,451,452,5,110,0,0,452,62,1,
0,0,0,453,454,5,112,0,0,454,455,5,111,0,0,455,456,5,108,0,0,456,
457,5,105,0,0,457,458,5,99,0,0,458,459,5,121,0,0,459,64,1,0,0,0,
460,461,5,100,0,0,461,462,5,101,0,0,462,463,5,102,0,0,463,464,5,
97,0,0,464,465,5,117,0,0,465,466,5,108,0,0,466,467,5,116,0,0,467,
66,1,0,0,0,468,469,5,115,0,0,469,470,5,111,0,0,470,471,5,117,0,0,
471,472,5,114,0,0,472,473,5,99,0,0,473,474,5,101,0,0,474,68,1,0,
0,0,475,476,5,114,0,0,476,477,5,101,0,0,477,478,5,112,0,0,478,479,
5,111,0,0,479,480,5,115,0,0,480,481,5,105,0,0,481,482,5,116,0,0,
482,483,5,111,0,0,483,484,5,114,0,0,484,485,5,121,0,0,485,70,1,0,
0,0,486,487,5,99,0,0,487,488,5,111,0,0,488,489,5,109,0,0,489,490,
5,109,0,0,490,491,5,105,0,0,491,492,5,116,0,0,492,72,1,0,0,0,493,
494,5,114,0,0,494,495,5,101,0,0,495,496,5,118,0,0,496,497,5,105,
0,0,497,498,5,115,0,0,498,499,5,105,0,0,499,500,5,111,0,0,500,501,
5,110,0,0,501,74,1,0,0,0,502,503,5,115,0,0,503,504,5,101,0,0,504,
505,5,109,0,0,505,506,5,97,0,0,506,507,5,110,0,0,507,508,5,116,0,
0,508,509,5,105,0,0,509,510,5,99,0,0,510,511,5,45,0,0,511,512,5,
109,0,0,512,513,5,97,0,0,513,514,5,106,0,0,514,515,5,111,0,0,515,
516,5,114,0,0,516,76,1,0,0,0,517,518,5,111,0,0,518,519,5,110,0,0,
519,520,5,45,0,0,520,521,5,100,0,0,521,522,5,101,0,0,522,523,5,108,
0,0,523,524,5,101,0,0,524,525,5,116,0,0,525,526,5,101,0,0,526,78,
1,0,0,0,527,528,5,114,0,0,528,529,5,101,0,0,529,530,5,116,0,0,530,
531,5,97,0,0,531,532,5,105,0,0,532,533,5,110,0,0,533,534,5,45,0,
0,534,535,5,111,0,0,535,536,5,116,0,0,536,537,5,104,0,0,537,538,
5,101,0,0,538,539,5,114,0,0,539,80,1,0,0,0,540,541,5,107,0,0,541,
542,5,101,0,0,542,543,5,121,0,0,543,544,5,101,0,0,544,545,5,100,
0,0,545,82,1,0,0,0,546,547,5,112,0,0,547,548,5,117,0,0,548,549,5,
98,0,0,549,550,5,108,0,0,550,551,5,105,0,0,551,552,5,99,0,0,552,
553,5,45,0,0,553,554,5,116,0,0,554,555,5,114,0,0,555,556,5,97,0,
0,556,557,5,118,0,0,557,558,5,101,0,0,558,559,5,114,0,0,559,560,
5,115,0,0,560,561,5,97,0,0,561,562,5,108,0,0,562,84,1,0,0,0,563,
564,5,105,0,0,564,565,5,100,0,0,565,86,1,0,0,0,566,567,5,100,0,0,
567,568,5,111,0,0,568,569,5,99,0,0,569,88,1,0,0,0,570,571,5,109,
0,0,571,572,5,111,0,0,572,573,5,100,0,0,573,574,5,101,0,0,574,90,
1,0,0,0,575,576,5,101,0,0,576,577,5,109,0,0,577,578,5,105,0,0,578,
579,5,116,0,0,579,580,5,115,0,0,580,92,1,0,0,0,581,582,5,114,0,0,
582,583,5,101,0,0,583,584,5,99,0,0,584,585,5,101,0,0,585,586,5,105,
0,0,586,587,5,118,0,0,587,588,5,101,0,0,588,589,5,114,0,0,589,94,
1,0,0,0,590,591,5,114,0,0,591,592,5,101,0,0,592,593,5,113,0,0,593,
594,5,117,0,0,594,595,5,105,0,0,595,596,5,114,0,0,596,597,5,101,
0,0,597,598,5,115,0,0,598,96,1,0,0,0,599,600,5,97,0,0,600,601,5,
110,0,0,601,602,5,121,0,0,602,98,1,0,0,0,603,604,5,103,0,0,604,605,
5,101,0,0,605,606,5,116,0,0,606,100,1,0,0,0,607,608,5,115,0,0,608,
609,5,101,0,0,609,610,5,116,0,0,610,102,1,0,0,0,611,612,5,119,0,
0,612,613,5,97,0,0,613,614,5,116,0,0,614,615,5,99,0,0,615,616,5,
104,0,0,616,104,1,0,0,0,617,618,5,115,0,0,618,619,5,116,0,0,619,
620,5,97,0,0,620,621,5,114,0,0,621,622,5,116,0,0,622,106,1,0,0,0,
623,624,5,115,0,0,624,625,5,116,0,0,625,626,5,111,0,0,626,627,5,
112,0,0,627,108,1,0,0,0,628,629,5,114,0,0,629,630,5,101,0,0,630,
631,5,97,0,0,631,632,5,100,0,0,632,110,1,0,0,0,633,634,5,119,0,0,
634,635,5,114,0,0,635,636,5,105,0,0,636,637,5,116,0,0,637,638,5,
101,0,0,638,112,1,0,0,0,639,640,5,114,0,0,640,641,5,101,0,0,641,
642,5,115,0,0,642,643,5,111,0,0,643,644,5,108,0,0,644,645,5,118,
0,0,645,646,5,101,0,0,646,114,1,0,0,0,647,648,5,99,0,0,648,649,5,
111,0,0,649,650,5,110,0,0,650,651,5,110,0,0,651,652,5,101,0,0,652,
653,5,99,0,0,653,654,5,116,0,0,654,116,1,0,0,0,655,656,5,100,0,0,
656,657,5,105,0,0,657,658,5,115,0,0,658,659,5,99,0,0,659,660,5,111,
0,0,660,661,5,110,0,0,661,662,5,110,0,0,662,663,5,101,0,0,663,664,
5,99,0,0,664,665,5,116,0,0,665,118,1,0,0,0,666,667,5,99,0,0,667,
668,5,97,0,0,668,669,5,108,0,0,669,670,5,108,0,0,670,120,1,0,0,0,
671,672,5,119,0,0,672,673,5,97,0,0,673,674,5,116,0,0,674,675,5,99,
0,0,675,676,5,104,0,0,676,677,5,45,0,0,677,678,5,115,0,0,678,679,
5,116,0,0,679,680,5,97,0,0,680,681,5,114,0,0,681,682,5,116,0,0,682,
122,1,0,0,0,683,684,5,119,0,0,684,685,5,97,0,0,685,686,5,116,0,0,
686,687,5,99,0,0,687,688,5,104,0,0,688,689,5,45,0,0,689,690,5,115,
0,0,690,691,5,116,0,0,691,692,5,111,0,0,692,693,5,112,0,0,693,124,
1,0,0,0,694,695,5,115,0,0,695,696,5,117,0,0,696,697,5,98,0,0,697,
698,5,115,0,0,698,699,5,99,0,0,699,700,5,114,0,0,700,701,5,105,0,
0,701,702,5,98,0,0,702,703,5,101,0,0,703,126,1,0,0,0,704,705,5,117,
0,0,705,706,5,110,0,0,706,707,5,115,0,0,707,708,5,117,0,0,708,709,
5,98,0,0,709,710,5,115,0,0,710,711,5,99,0,0,711,712,5,114,0,0,712,
713,5,105,0,0,713,714,5,98,0,0,714,715,5,101,0,0,715,128,1,0,0,0,
716,717,5,111,0,0,717,718,5,112,0,0,718,719,5,116,0,0,719,720,5,
105,0,0,720,721,5,109,0,0,721,722,5,105,0,0,722,723,5,115,0,0,723,
724,5,116,0,0,724,725,5,105,0,0,725,726,5,99,0,0,726,727,5,45,0,
0,727,728,5,114,0,0,728,729,5,101,0,0,729,730,5,103,0,0,730,731,
5,105,0,0,731,732,5,115,0,0,732,733,5,116,0,0,733,734,5,101,0,0,
734,735,5,114,0,0,735,130,1,0,0,0,736,737,5,99,0,0,737,738,5,114,
0,0,738,739,5,100,0,0,739,740,5,116,0,0,740,132,1,0,0,0,741,742,
5,111,0,0,742,743,5,112,0,0,743,744,5,116,0,0,744,745,5,105,0,0,
745,746,5,111,0,0,746,747,5,110,0,0,747,748,5,97,0,0,748,749,5,108,
0,0,749,750,5,45,0,0,750,751,5,111,0,0,751,752,5,110,0,0,752,753,
5,101,0,0,753,134,1,0,0,0,754,755,5,101,0,0,755,756,5,120,0,0,756,
757,5,97,0,0,757,758,5,99,0,0,758,759,5,116,0,0,759,760,5,108,0,
0,760,761,5,121,0,0,761,762,5,45,0,0,762,763,5,111,0,0,763,764,5,
110,0,0,764,765,5,101,0,0,765,136,1,0,0,0,766,767,5,109,0,0,767,
768,5,97,0,0,768,769,5,110,0,0,769,770,5,121,0,0,770,771,5,45,0,
0,771,772,5,117,0,0,772,773,5,110,0,0,773,774,5,105,0,0,774,775,
5,113,0,0,775,776,5,117,0,0,776,777,5,101,0,0,777,138,1,0,0,0,778,
779,5,109,0,0,779,780,5,97,0,0,780,781,5,110,0,0,781,782,5,121,0,
0,782,140,1,0,0,0,783,784,5,111,0,0,784,785,5,114,0,0,785,786,5,
100,0,0,786,787,5,101,0,0,787,788,5,114,0,0,788,789,5,101,0,0,789,
790,5,100,0,0,790,142,1,0,0,0,791,792,5,117,0,0,792,793,5,110,0,
0,793,794,5,105,0,0,794,795,5,116,0,0,795,144,1,0,0,0,796,797,5,
119,0,0,797,798,5,97,0,0,798,799,5,116,0,0,799,800,5,99,0,0,800,
801,5,104,0,0,801,802,5,45,0,0,802,803,5,104,0,0,803,804,5,97,0,
0,804,805,5,110,0,0,805,806,5,100,0,0,806,807,5,108,0,0,807,808,
5,101,0,0,808,146,1,0,0,0,809,810,5,109,0,0,810,811,5,101,0,0,811,
812,5,115,0,0,812,813,5,115,0,0,813,814,5,97,0,0,814,815,5,103,0,
0,815,816,5,101,0,0,816,148,1,0,0,0,817,818,5,97,0,0,818,819,5,116,
0,0,819,820,5,111,0,0,820,821,5,109,0,0,821,822,5,45,0,0,822,823,
5,114,0,0,823,824,5,101,0,0,824,825,5,102,0,0,825,150,1,0,0,0,826,
827,5,105,0,0,827,828,5,110,0,0,828,829,5,116,0,0,829,830,5,101,
0,0,830,831,5,114,0,0,831,832,5,102,0,0,832,833,5,97,0,0,833,834,
5,99,0,0,834,835,5,101,0,0,835,836,5,45,0,0,836,837,5,114,0,0,837,
838,5,101,0,0,838,839,5,102,0,0,839,152,1,0,0,0,840,841,5,111,0,
0,841,842,5,112,0,0,842,843,5,116,0,0,843,844,5,105,0,0,844,845,
5,111,0,0,845,846,5,110,0,0,846,847,5,97,0,0,847,848,5,108,0,0,848,
154,1,0,0,0,849,850,5,108,0,0,850,851,5,105,0,0,851,852,5,115,0,
0,852,853,5,116,0,0,853,156,1,0,0,0,854,855,5,114,0,0,855,856,5,
101,0,0,856,857,5,99,0,0,857,858,5,111,0,0,858,859,5,114,0,0,859,
860,5,100,0,0,860,158,1,0,0,0,861,862,5,98,0,0,862,863,5,111,0,0,
863,864,5,111,0,0,864,865,5,108,0,0,865,160,1,0,0,0,866,867,5,98,
0,0,867,868,5,121,0,0,868,869,5,116,0,0,869,870,5,101,0,0,870,871,
5,115,0,0,871,162,1,0,0,0,872,873,5,100,0,0,873,874,5,111,0,0,874,
875,5,117,0,0,875,876,5,98,0,0,876,877,5,108,0,0,877,878,5,101,0,
0,878,164,1,0,0,0,879,880,5,105,0,0,880,881,5,110,0,0,881,882,5,
116,0,0,882,883,5,51,0,0,883,884,5,50,0,0,884,166,1,0,0,0,885,886,
5,105,0,0,886,887,5,110,0,0,887,888,5,116,0,0,888,889,5,54,0,0,889,
890,5,52,0,0,890,168,1,0,0,0,891,892,5,115,0,0,892,893,5,116,0,0,
893,894,5,114,0,0,894,895,5,105,0,0,895,896,5,110,0,0,896,897,5,
103,0,0,897,170,1,0,0,0,898,899,5,117,0,0,899,900,5,105,0,0,900,
901,5,110,0,0,901,902,5,116,0,0,902,903,5,51,0,0,903,904,5,50,0,
0,904,172,1,0,0,0,905,906,5,117,0,0,906,907,5,105,0,0,907,908,5,
110,0,0,908,909,5,116,0,0,909,910,5,54,0,0,910,911,5,52,0,0,911,
174,1,0,0,0,912,913,5,116,0,0,913,914,5,114,0,0,914,915,5,117,0,
0,915,916,5,101,0,0,916,176,1,0,0,0,917,918,5,102,0,0,918,919,5,
97,0,0,919,920,5,108,0,0,920,921,5,115,0,0,921,922,5,101,0,0,922,
178,1,0,0,0,923,924,5,110,0,0,924,925,5,117,0,0,925,926,5,108,0,
0,926,927,5,108,0,0,927,180,1,0,0,0,928,929,5,45,0,0,929,930,5,62,
0,0,930,182,1,0,0,0,931,932,5,58,0,0,932,184,1,0,0,0,933,934,5,59,
0,0,934,186,1,0,0,0,935,936,5,44,0,0,936,188,1,0,0,0,937,938,5,46,
0,0,938,190,1,0,0,0,939,940,5,123,0,0,940,192,1,0,0,0,941,942,5,
125,0,0,942,194,1,0,0,0,943,944,5,91,0,0,944,196,1,0,0,0,945,946,
5,93,0,0,946,198,1,0,0,0,947,948,5,40,0,0,948,200,1,0,0,0,949,950,
5,41,0,0,950,202,1,0,0,0,951,952,5,60,0,0,952,204,1,0,0,0,953,954,
5,62,0,0,954,206,1,0,0,0,955,957,5,45,0,0,956,955,1,0,0,0,956,957,
1,0,0,0,957,959,1,0,0,0,958,960,7,0,0,0,959,958,1,0,0,0,960,961,
1,0,0,0,961,959,1,0,0,0,961,962,1,0,0,0,962,208,1,0,0,0,963,965,
5,45,0,0,964,963,1,0,0,0,964,965,1,0,0,0,965,974,1,0,0,0,966,975,
5,48,0,0,967,971,7,1,0,0,968,970,7,0,0,0,969,968,1,0,0,0,970,973,
1,0,0,0,971,969,1,0,0,0,971,972,1,0,0,0,972,975,1,0,0,0,973,971,
1,0,0,0,974,966,1,0,0,0,974,967,1,0,0,0,975,982,1,0,0,0,976,978,
5,46,0,0,977,979,7,0,0,0,978,977,1,0,0,0,979,980,1,0,0,0,980,978,
1,0,0,0,980,981,1,0,0,0,981,983,1,0,0,0,982,976,1,0,0,0,982,983,
1,0,0,0,983,993,1,0,0,0,984,986,7,2,0,0,985,987,7,3,0,0,986,985,
1,0,0,0,986,987,1,0,0,0,987,989,1,0,0,0,988,990,7,0,0,0,989,988,
1,0,0,0,990,991,1,0,0,0,991,989,1,0,0,0,991,992,1,0,0,0,992,994,
1,0,0,0,993,984,1,0,0,0,993,994,1,0,0,0,994,210,1,0,0,0,995,999,
7,4,0,0,996,998,7,5,0,0,997,996,1,0,0,0,998,1001,1,0,0,0,999,997,
1,0,0,0,999,1000,1,0,0,0,1000,212,1,0,0,0,1001,999,1,0,0,0,1002,
1007,5,34,0,0,1003,1006,3,215,107,0,1004,1006,8,6,0,0,1005,1003,
1,0,0,0,1005,1004,1,0,0,0,1006,1009,1,0,0,0,1007,1005,1,0,0,0,1007,
1008,1,0,0,0,1008,1010,1,0,0,0,1009,1007,1,0,0,0,1010,1011,5,34,
0,0,1011,214,1,0,0,0,1012,1020,5,92,0,0,1013,1021,7,7,0,0,1014,1015,
5,117,0,0,1015,1016,3,217,108,0,1016,1017,3,217,108,0,1017,1018,
3,217,108,0,1018,1019,3,217,108,0,1019,1021,1,0,0,0,1020,1013,1,
0,0,0,1020,1014,1,0,0,0,1021,216,1,0,0,0,1022,1023,7,8,0,0,1023,
218,1,0,0,0,1024,1025,5,47,0,0,1025,1026,5,47,0,0,1026,1030,1,0,
0,0,1027,1029,8,9,0,0,1028,1027,1,0,0,0,1029,1032,1,0,0,0,1030,1028,
1,0,0,0,1030,1031,1,0,0,0,1031,1033,1,0,0,0,1032,1030,1,0,0,0,1033,
1034,6,109,0,0,1034,220,1,0,0,0,1035,1036,5,47,0,0,1036,1037,5,42,
0,0,1037,1041,1,0,0,0,1038,1040,9,0,0,0,1039,1038,1,0,0,0,1040,1043,
1,0,0,0,1041,1042,1,0,0,0,1041,1039,1,0,0,0,1042,1044,1,0,0,0,1043,
1041,1,0,0,0,1044,1045,5,42,0,0,1045,1046,5,47,0,0,1046,1047,1,0,
0,0,1047,1048,6,110,0,0,1048,222,1,0,0,0,1049,1051,7,10,0,0,1050,
1049,1,0,0,0,1051,1052,1,0,0,0,1052,1050,1,0,0,0,1052,1053,1,0,0,
0,1053,1054,1,0,0,0,1054,1055,6,111,0,0,1055,224,1,0,0,0,18,0,956,
961,964,971,974,980,982,986,991,993,999,1005,1007,1020,1030,1041,
1052,1,0,1,0
];
private static __ATN: antlr.ATN;
public static get _ATN(): antlr.ATN {
if (!QuixosCapabilityLexer.__ATN) {
QuixosCapabilityLexer.__ATN = new antlr.ATNDeserializer().deserialize(QuixosCapabilityLexer._serializedATN);
}
return QuixosCapabilityLexer.__ATN;
}
private static readonly vocabulary = new antlr.Vocabulary(QuixosCapabilityLexer.literalNames, QuixosCapabilityLexer.symbolicNames, []);
public override get vocabulary(): antlr.Vocabulary {
return QuixosCapabilityLexer.vocabulary;
}
private static readonly decisionsToDFA = QuixosCapabilityLexer._ATN.decisionToState.map( (ds: antlr.DecisionState, index: number) => new antlr.DFA(ds, index) );
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,429 @@
import { AbstractParseTreeVisitor } from "antlr4ng";
import { DocumentContext } from "./QuixosCapabilityParser.js";
import { FragmentDeclContext } from "./QuixosCapabilityParser.js";
import { SourceImportDeclContext } from "./QuixosCapabilityParser.js";
import { WorkspaceDeclContext } from "./QuixosCapabilityParser.js";
import { WorkspaceItemContext } from "./QuixosCapabilityParser.js";
import { ResourceImportDeclContext } from "./QuixosCapabilityParser.js";
import { ExternalAtomDeclContext } from "./QuixosCapabilityParser.js";
import { ExternalInterfaceDeclContext } from "./QuixosCapabilityParser.js";
import { ResourcePreambleContext } from "./QuixosCapabilityParser.js";
import { AtomDeclContext } from "./QuixosCapabilityParser.js";
import { InterfaceResourceDeclContext } from "./QuixosCapabilityParser.js";
import { InterfaceMemberContext } from "./QuixosCapabilityParser.js";
import { OperationMemberContext } from "./QuixosCapabilityParser.js";
import { ValueMemberContext } from "./QuixosCapabilityParser.js";
import { ValueMemberOperationContext } from "./QuixosCapabilityParser.js";
import { RelationshipMemberContext } from "./QuixosCapabilityParser.js";
import { RelationshipOperationContext } from "./QuixosCapabilityParser.js";
import { TargetConstraintContext } from "./QuixosCapabilityParser.js";
import { PackageResourceDeclContext } from "./QuixosCapabilityParser.js";
import { PackageExportContext } from "./QuixosCapabilityParser.js";
import { PackageOperationExportContext } from "./QuixosCapabilityParser.js";
import { PackageFunctionExportContext } from "./QuixosCapabilityParser.js";
import { PackageConstructorExportContext } from "./QuixosCapabilityParser.js";
import { EventClauseContext } from "./QuixosCapabilityParser.js";
import { OperationModeContext } from "./QuixosCapabilityParser.js";
import { ReceiverRequirementContext } from "./QuixosCapabilityParser.js";
import { IdentifierListContext } from "./QuixosCapabilityParser.js";
import { DependencyBlockContext } from "./QuixosCapabilityParser.js";
import { DependencyPortContext } from "./QuixosCapabilityParser.js";
import { PrimitiveListContext } from "./QuixosCapabilityParser.js";
import { PrimitiveContext } from "./QuixosCapabilityParser.js";
import { SharedAttachmentDeclContext } from "./QuixosCapabilityParser.js";
import { AttachmentDeclContext } from "./QuixosCapabilityParser.js";
import { StateDeclContext } from "./QuixosCapabilityParser.js";
import { StoragePolicyContext } from "./QuixosCapabilityParser.js";
import { EdgeDeclContext } from "./QuixosCapabilityParser.js";
import { EdgeEndpointContext } from "./QuixosCapabilityParser.js";
import { ConformanceDeclContext } from "./QuixosCapabilityParser.js";
import { ConformanceItemContext } from "./QuixosCapabilityParser.js";
import { RelationshipMaterializationDeclContext } from "./QuixosCapabilityParser.js";
import { OperationBindingDeclContext } from "./QuixosCapabilityParser.js";
import { MemberOperationRefContext } from "./QuixosCapabilityParser.js";
import { OperationNameContext } from "./QuixosCapabilityParser.js";
import { OperationProviderContext } from "./QuixosCapabilityParser.js";
import { StatePrimitiveContext } from "./QuixosCapabilityParser.js";
import { EdgePrimitiveContext } from "./QuixosCapabilityParser.js";
import { DependencyBindingBlockContext } from "./QuixosCapabilityParser.js";
import { DependencyBindingContext } from "./QuixosCapabilityParser.js";
import { ConstructorBindingDeclContext } from "./QuixosCapabilityParser.js";
import { ValueTypeContext } from "./QuixosCapabilityParser.js";
import { RecordFieldContext } from "./QuixosCapabilityParser.js";
import { ScalarTypeContext } from "./QuixosCapabilityParser.js";
import { CardinalityContext } from "./QuixosCapabilityParser.js";
import { JsonLiteralContext } from "./QuixosCapabilityParser.js";
import { JsonObjectContext } from "./QuixosCapabilityParser.js";
import { JsonMemberContext } from "./QuixosCapabilityParser.js";
import { JsonArrayContext } from "./QuixosCapabilityParser.js";
import { IdentifierContext } from "./QuixosCapabilityParser.js";
import { StringLiteralContext } from "./QuixosCapabilityParser.js";
/**
* This interface defines a complete generic visitor for a parse tree produced
* by `QuixosCapabilityParser`.
*
* @param <Result> The return type of the visit operation. Use `void` for
* operations with no return type.
*/
export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Result> {
/**
* Visit a parse tree produced by `QuixosCapabilityParser.document`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDocument?: (ctx: DocumentContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.fragmentDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitFragmentDecl?: (ctx: FragmentDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.sourceImportDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSourceImportDecl?: (ctx: SourceImportDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.workspaceDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitWorkspaceDecl?: (ctx: WorkspaceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.workspaceItem`.
* @param ctx the parse tree
* @return the visitor result
*/
visitWorkspaceItem?: (ctx: WorkspaceItemContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.resourceImportDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitResourceImportDecl?: (ctx: ResourceImportDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.externalAtomDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitExternalAtomDecl?: (ctx: ExternalAtomDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.externalInterfaceDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitExternalInterfaceDecl?: (ctx: ExternalInterfaceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.resourcePreamble`.
* @param ctx the parse tree
* @return the visitor result
*/
visitResourcePreamble?: (ctx: ResourcePreambleContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.atomDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitAtomDecl?: (ctx: AtomDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.interfaceResourceDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitInterfaceResourceDecl?: (ctx: InterfaceResourceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.interfaceMember`.
* @param ctx the parse tree
* @return the visitor result
*/
visitInterfaceMember?: (ctx: InterfaceMemberContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.operationMember`.
* @param ctx the parse tree
* @return the visitor result
*/
visitOperationMember?: (ctx: OperationMemberContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.valueMember`.
* @param ctx the parse tree
* @return the visitor result
*/
visitValueMember?: (ctx: ValueMemberContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.valueMemberOperation`.
* @param ctx the parse tree
* @return the visitor result
*/
visitValueMemberOperation?: (ctx: ValueMemberOperationContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.relationshipMember`.
* @param ctx the parse tree
* @return the visitor result
*/
visitRelationshipMember?: (ctx: RelationshipMemberContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.relationshipOperation`.
* @param ctx the parse tree
* @return the visitor result
*/
visitRelationshipOperation?: (ctx: RelationshipOperationContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.targetConstraint`.
* @param ctx the parse tree
* @return the visitor result
*/
visitTargetConstraint?: (ctx: TargetConstraintContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.packageResourceDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPackageResourceDecl?: (ctx: PackageResourceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.packageExport`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPackageExport?: (ctx: PackageExportContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.packageOperationExport`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPackageOperationExport?: (ctx: PackageOperationExportContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.packageFunctionExport`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPackageFunctionExport?: (ctx: PackageFunctionExportContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.packageConstructorExport`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPackageConstructorExport?: (ctx: PackageConstructorExportContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.eventClause`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEventClause?: (ctx: EventClauseContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.operationMode`.
* @param ctx the parse tree
* @return the visitor result
*/
visitOperationMode?: (ctx: OperationModeContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.receiverRequirement`.
* @param ctx the parse tree
* @return the visitor result
*/
visitReceiverRequirement?: (ctx: ReceiverRequirementContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.identifierList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitIdentifierList?: (ctx: IdentifierListContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.dependencyBlock`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDependencyBlock?: (ctx: DependencyBlockContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.dependencyPort`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDependencyPort?: (ctx: DependencyPortContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.primitiveList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPrimitiveList?: (ctx: PrimitiveListContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.primitive`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPrimitive?: (ctx: PrimitiveContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.sharedAttachmentDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSharedAttachmentDecl?: (ctx: SharedAttachmentDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.attachmentDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitAttachmentDecl?: (ctx: AttachmentDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.stateDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitStateDecl?: (ctx: StateDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.storagePolicy`.
* @param ctx the parse tree
* @return the visitor result
*/
visitStoragePolicy?: (ctx: StoragePolicyContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.edgeDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEdgeDecl?: (ctx: EdgeDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.edgeEndpoint`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEdgeEndpoint?: (ctx: EdgeEndpointContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.conformanceDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitConformanceDecl?: (ctx: ConformanceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.conformanceItem`.
* @param ctx the parse tree
* @return the visitor result
*/
visitConformanceItem?: (ctx: ConformanceItemContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.relationshipMaterializationDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitRelationshipMaterializationDecl?: (ctx: RelationshipMaterializationDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.operationBindingDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitOperationBindingDecl?: (ctx: OperationBindingDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.memberOperationRef`.
* @param ctx the parse tree
* @return the visitor result
*/
visitMemberOperationRef?: (ctx: MemberOperationRefContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.operationName`.
* @param ctx the parse tree
* @return the visitor result
*/
visitOperationName?: (ctx: OperationNameContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.operationProvider`.
* @param ctx the parse tree
* @return the visitor result
*/
visitOperationProvider?: (ctx: OperationProviderContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.statePrimitive`.
* @param ctx the parse tree
* @return the visitor result
*/
visitStatePrimitive?: (ctx: StatePrimitiveContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.edgePrimitive`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEdgePrimitive?: (ctx: EdgePrimitiveContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.dependencyBindingBlock`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDependencyBindingBlock?: (ctx: DependencyBindingBlockContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.dependencyBinding`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDependencyBinding?: (ctx: DependencyBindingContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.constructorBindingDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitConstructorBindingDecl?: (ctx: ConstructorBindingDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.valueType`.
* @param ctx the parse tree
* @return the visitor result
*/
visitValueType?: (ctx: ValueTypeContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.recordField`.
* @param ctx the parse tree
* @return the visitor result
*/
visitRecordField?: (ctx: RecordFieldContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.scalarType`.
* @param ctx the parse tree
* @return the visitor result
*/
visitScalarType?: (ctx: ScalarTypeContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.cardinality`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCardinality?: (ctx: CardinalityContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.jsonLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitJsonLiteral?: (ctx: JsonLiteralContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.jsonObject`.
* @param ctx the parse tree
* @return the visitor result
*/
visitJsonObject?: (ctx: JsonObjectContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.jsonMember`.
* @param ctx the parse tree
* @return the visitor result
*/
visitJsonMember?: (ctx: JsonMemberContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.jsonArray`.
* @param ctx the parse tree
* @return the visitor result
*/
visitJsonArray?: (ctx: JsonArrayContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.identifier`.
* @param ctx the parse tree
* @return the visitor result
*/
visitIdentifier?: (ctx: IdentifierContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.stringLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitStringLiteral?: (ctx: StringLiteralContext) => Result;
}
+88
View File
@@ -0,0 +1,88 @@
import childProcess from "node:child_process";
import crypto from "node:crypto";
import { mkdir, readFile, realpath } from "node:fs/promises";
import path from "node:path";
import { promisify } from "node:util";
import type { CapabilityRepositoryResolver } from "./assembly.js";
const execFile = promisify(childProcess.execFile);
const sourceKey = (kind: string, repository: string, commit: string) =>
`${kind}\0${repository}\0${commit.toLowerCase()}`;
const checkoutName = (kind: string, repository: string, commit: string) => {
const digest = crypto
.createHash("sha256")
.update(sourceKey(kind, repository, commit))
.digest("hex")
.slice(0, 24);
return `${kind}-${digest}`;
};
export const createGitCapabilityResolver = async (options: {
checkoutRoot: string;
snapshotMap?: string;
snapshotOnly?: boolean;
}): Promise<CapabilityRepositoryResolver> => {
await mkdir(options.checkoutRoot, { recursive: true });
const checkoutRoot = await realpath(options.checkoutRoot);
const snapshots = new Map<string, string>();
if (options.snapshotMap) {
const snapshotMapPath = await realpath(options.snapshotMap);
const document = JSON.parse(await readFile(snapshotMapPath, "utf8")) as {
resources?: Array<{
kind: string;
repository: string;
commit: string;
directory: string;
}>;
};
for (const entry of document.resources ?? []) {
if (entry.kind !== "interface" && entry.kind !== "package") {
throw new Error(`Snapshot map has unsupported resource kind ${entry.kind}`);
}
const key = sourceKey(entry.kind, entry.repository, entry.commit);
if (snapshots.has(key)) throw new Error(`Snapshot map repeats ${key}`);
const directory = path.resolve(path.dirname(snapshotMapPath), entry.directory);
snapshots.set(key, await realpath(directory));
}
}
const checkouts = new Map<string, Promise<{ directory: string }>>();
return async (source, kind) => {
const key = sourceKey(kind, source.repository, source.commit);
const snapshot = snapshots.get(key);
if (snapshot) return { directory: snapshot };
if (options.snapshotOnly) throw new Error(`No offline snapshot for ${kind} ${source.repository}@${source.commit}`);
const existing = checkouts.get(key);
if (existing) return await existing;
const pending = (async () => {
const directory = path.join(
checkoutRoot,
checkoutName(kind, source.repository, source.commit),
);
await execFile("git", [
"-c",
"advice.detachedHead=false",
"clone",
"--depth",
"1",
"--single-branch",
"--branch",
`quixos-reachability/${source.commit.toLowerCase()}`,
source.repository,
directory,
]);
const { stdout } = await execFile("git", ["-C", directory, "rev-parse", "HEAD"]);
if (stdout.trim().toLowerCase() !== source.commit.toLowerCase()) {
throw new Error(
`Locked commit mismatch for ${source.repository}: ` +
`wanted ${source.commit}, fetched ${stdout.trim()}`,
);
}
return { directory };
})();
checkouts.set(key, pending);
return await pending;
};
};
+5
View File
@@ -0,0 +1,5 @@
export * from "./parser.js";
export * from "./assembly.js";
export * from "./source.js";
export * from "./source-loader.js";
export * from "./scaffold.js";
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env node
// Data only: never load files, resolve repositories, or evaluate code.
import { parseQx } from "./source.js";
import { parseQuixosLockDocument } from "../resource-lock/parser.js";
let input = "";
for await (const chunk of process.stdin) {
input += chunk;
if (Buffer.byteLength(input) > 6 * 1024 * 1024) throw new Error("Inspection input exceeds limit");
}
const files: { path: string; source: string }[] = JSON.parse(input);
if (!Array.isArray(files) || files.length > 128) throw new Error("Too many source files");
const result = files.map(({ path, source }) => {
if (typeof path !== "string" || typeof source !== "string" || source.length > 262144)
throw new Error("Invalid source input");
if (path.endsWith(".qx")) {
const parsed = parseQx(source, path);
return { path, root: parsed.root, diagnostics: parsed.diagnostics };
}
return { path, lock: parseQuixosLockDocument(source, path) };
});
process.stdout.write(JSON.stringify(result));
File diff suppressed because it is too large Load Diff
+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"));}
};
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env node
import { writeFile } from "node:fs/promises";
import process from "node:process";
import { bindingSchema } from "../bindings/index.js";
import {
compileCapabilityResourceRepository,
type ResolvedCapabilityResource,
} from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
const usage = `usage: quixos-resource-compile --root DIRECTORY --kind interface|package
--repository URL --commit GIT_REV --checkout-root DIRECTORY
[--snapshot-map PATH] [--snapshot-only true] [--graph-out PATH] [--schema-out PATH]
Resolves a standalone resource's recursive dependency-lock graph, validates
its interface.qx or package.qx manifest, and emits the checked resource as JSON.`;
const parseArgs = (args: string[]) => {
const values = new Map<string, string>();
for (let index = 0; index < args.length; index += 2) {
const key = args[index];
const value = args[index + 1];
if (!key?.startsWith("--") || !value) throw new Error(usage);
if (!["--root", "--kind", "--repository", "--commit", "--checkout-root", "--snapshot-map", "--snapshot-only", "--graph-out", "--schema-out"].includes(key) || values.has(key)) throw new Error(usage);
values.set(key, value);
}
const rootDirectory = values.get("--root");
const kind = values.get("--kind");
const repository = values.get("--repository");
const commit = values.get("--commit")?.toLowerCase();
const checkoutRoot = values.get("--checkout-root");
if (!rootDirectory || !repository || !commit || !checkoutRoot ||
(kind !== "interface" && kind !== "package")) {
throw new Error(usage);
}
if (!/^([0-9a-f]{40}|[0-9a-f]{64})$/.test(commit)) {
throw new Error("--commit must be a full Git object ID");
}
if (values.has("--snapshot-only") && values.get("--snapshot-only") !== "true") throw new Error("--snapshot-only accepts true");
return {
rootDirectory,
kind,
repository,
commit,
checkoutRoot,
snapshotMap: values.get("--snapshot-map"),
snapshotOnly: values.get("--snapshot-only") === "true",
graphOut: values.get("--graph-out"),
schemaOut: values.get("--schema-out"),
} as const;
};
const graphEntry = (node: ResolvedCapabilityResource) => ({
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,
})),
});
const main = async () => {
if (process.argv.includes("--help") || process.argv.includes("-h")) {
process.stdout.write(`${usage}\n`);
return;
}
const options = parseArgs(process.argv.slice(2));
const resolveResource = await createGitCapabilityResolver({
checkoutRoot: options.checkoutRoot,
snapshotMap: options.snapshotMap,
snapshotOnly: options.snapshotOnly,
});
const compiled = await compileCapabilityResourceRepository({
rootDirectory: options.rootDirectory,
kind: options.kind,
source: {
resolver: "git",
repository: options.repository,
commit: options.commit,
},
resolveResource,
});
if (options.graphOut) {
await writeFile(options.graphOut, `${JSON.stringify({
formatVersion: 1,
quixos: compiled.lock.quixos,
root: graphEntry(compiled.resources.find((node) =>
node.source.repository === options.repository &&
node.source.commit === options.commit &&
node.kind === options.kind)!),
resources: compiled.resources.map(graphEntry),
}, null, 2)}\n`);
}
if (options.schemaOut) await writeFile(options.schemaOut, `${JSON.stringify(bindingSchema(compiled), null, 2)}\n`);
process.stdout.write(`${JSON.stringify(compiled.resource, null, 2)}\n`);
};
main().catch((error: unknown) => {
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
process.exitCode = 1;
});
+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};
};
+66
View File
@@ -0,0 +1,66 @@
import { lstat, open, rename, unlink } from "node:fs/promises";
import path from "node:path";
import { randomUUID } from "node:crypto";
import { addWorkspaceImport } from "./source.js";
import { readQxSource } from "./source-loader.js";
import { compileWorkspaceRepository, type CapabilityRepositoryResolver } from "./assembly.js";
export const planAtomScaffold = (workspace: string, name: string, id: string) => {
if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) throw new Error("Atom name must be PascalCase");
if (!id.trim()) throw new Error("Atom ID must not be empty");
const fileName = `${name}.qx`;
return {
before: workspace,
workspace: addWorkspaceImport(workspace, fileName),
fileName,
fragment: `fragment {\n atom ${name} id ${JSON.stringify(id)};\n}\n`,
};
};
/** Validate an in-memory proposal before creating files. Never replace a fragment. */
export const scaffoldAtom = async (options: {
root: string; name: string; id: string; write: boolean; resolveResource: CapabilityRepositoryResolver;
}) => {
const before = await readQxSource(options.root, "workspace.qx");
const plan = planAtomScaffold(before, options.name, options.id);
const fragmentPath = path.join(options.root, plan.fileName);
try {
await lstat(fragmentPath);
throw new Error(`Scaffold target already exists: ${plan.fileName}`);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
const observed = new Map<string, string>();
await compileWorkspaceRepository({ rootDirectory: options.root, resolveResource: options.resolveResource,
readSource: async (name) => {
if (name === "workspace.qx") return plan.workspace;
if (name === plan.fileName) return plan.fragment;
const text = await readQxSource(options.root, name);
observed.set(name, text);
return text;
} });
if (!options.write) return plan;
const workspacePath = path.join(options.root, "workspace.qx");
const temporary = path.join(options.root, `.qx-scaffold-${randomUUID()}.tmp`);
let createdFragment = false;
let createdTemporary = false;
try {
const fragment = await open(fragmentPath, "wx");
createdFragment = true;
try { await fragment.writeFile(plan.fragment); } finally { await fragment.close(); }
const file = await open(temporary, "wx", (await lstat(workspacePath)).mode);
createdTemporary = true;
try { await file.writeFile(plan.workspace); } finally { await file.close(); }
observed.set("workspace.qx", before);
for (const [name, text] of observed) {
if (await readQxSource(options.root, name) !== text) throw new Error(`QX source changed during scaffolding: ${name}`);
}
await rename(temporary, workspacePath);
createdTemporary = false;
createdFragment = false;
} finally {
if (createdTemporary) await unlink(temporary);
if (createdFragment) await unlink(fragmentPath);
}
return plan;
};
+77
View File
@@ -0,0 +1,77 @@
import { lstat, readFile } from "node:fs/promises";
import path from "node:path";
import { parseQx, validateQxImportPath, walkSyntax } from "./source.js";
export const readQxSource = async (root: string, name: string) => {
validateQxImportPath(name);
let current = path.resolve(root);
const segments = name.split("/");
for (const [index, segment] of segments.entries()) {
current = path.join(current, segment);
const stat = await lstat(current);
if (stat.isSymbolicLink() || (index === segments.length - 1 ? !stat.isFile() : !stat.isDirectory()))
throw new Error(`QX imports must be ordinary files beneath ordinary directories: ${name}`);
}
return readFile(current, "utf8");
};
/** Local imports share one workspace scope; paths are always repository-relative. */
export const resolveQxSources = async (
read: (name: string) => Promise<string>, entry = "workspace.qx",
) => {
const files = new Map<string, string>();
const active: string[] = [];
const visited = new Set<string>();
const segments: { start: number; end: number; fileName: string; sourceStart: number }[] = [];
let source = "";
const append = (fileName: string, text: string, sourceStart: number) => {
segments.push({ start: source.length, end: source.length + text.length, fileName, sourceStart });
source += text;
};
const visit = async (name: string, root: boolean) => {
validateQxImportPath(name);
if (active.includes(name)) throw new Error(`QX import cycle: ${[...active, name].join(" -> ")}`);
if (visited.has(name)) return;
const text = await read(name);
const syntax = parseQx(text, name);
if (syntax.diagnostics.length) throw new Error(syntax.diagnostics.map((d) =>
`${name}:${d.line}:${d.column + 1}: ${d.message}`).join("\n"));
const declaration = syntax.root.children[0]!;
if (declaration.kind !== (root ? "workspaceDecl" : "fragmentDecl"))
throw new Error(`${name}: expected ${root ? "workspace" : "fragment"} document`);
files.set(name, text);
active.push(name);
visited.add(name);
const braces = syntax.tokens.filter((token) => !token.trivia);
let cursor = root ? 0 : braces.find((token) => token.kind === "LBRACE")!.end;
const end = root ? text.length : braces.filter((token) => token.kind === "RBRACE").at(-1)!.start;
for (const node of walkSyntax(declaration)) {
if (node.kind !== "sourceImportDecl") continue;
append(name, text.slice(cursor, node.start), cursor);
const literal = node.children.find((child) => child.kind === "stringLiteral")!;
// Separators prevent adjacent tokens/comments joining across files.
append(name, "\n", node.start);
await visit(JSON.parse(text.slice(literal.start, literal.end)), false);
append(name, "\n", node.end);
cursor = node.end;
}
append(name, text.slice(cursor, end), cursor);
active.pop();
};
await visit(entry, true);
return {
source, sourceFiles: [...files.keys()], files,
originalPosition(line: number, column: number) {
const lines = source.split("\n");
const offset = lines.slice(0, line - 1).reduce((sum, value) => sum + value.length + 1, 0) +
[...(lines[line - 1] ?? "")].slice(0, column).join("").length;
const segment = segments.find((entry) => entry.start <= offset && entry.end > offset);
if (!segment) return { fileName: entry, line, column };
const prefix = files.get(segment.fileName)!.slice(0, segment.sourceStart + offset - segment.start);
return { fileName: segment.fileName, line: prefix.split("\n").length,
column: prefix.length - prefix.lastIndexOf("\n") - 1 };
},
};
};
export const loadQxSources = (root: string) => resolveQxSources((name) => readQxSource(root, name));
+128
View File
@@ -0,0 +1,128 @@
import { ParserRuleContext } from "antlr4ng";
import { parseDocument } from "./parser.js";
import { QuixosCapabilityParser } from "./generated/QuixosCapabilityParser.js";
/** Offsets and columns use UTF-16, as do JavaScript and LSP. End is exclusive. */
export type SourceRange = { start: number; end: number };
export type SourceEdit = SourceRange & { text: string };
export type SyntaxNode = SourceRange & { kind: string; children: SyntaxNode[] };
export const parseQx = (source: string, fileName = "<memory>") => {
const parsed = parseDocument(source, fileName);
// ANTLR indexes Unicode code points; editors index UTF-16 code units.
const offsets = [0];
for (const character of source) offsets.push(offsets[offsets.length - 1]! + character.length);
const offset = (index: number) => offsets[Math.max(0, index)] ?? source.length;
const node = (context: ParserRuleContext): SyntaxNode => ({
kind: QuixosCapabilityParser.ruleNames[context.ruleIndex]!,
start: offset(context.start?.start ?? 0),
end: offset((context.stop?.stop ?? -1) + 1),
children: context.children.flatMap((child) => child instanceof ParserRuleContext ? [node(child)] : []),
});
return {
source, fileName,
root: node(parsed.tree),
diagnostics: parsed.diagnostics.map((diagnostic) => {
const line = source.split("\n")[diagnostic.line - 1] ?? "";
return { ...diagnostic, column: [...line].slice(0, diagnostic.column).join("").length };
}),
tokens: parsed.tokens.getTokens().filter((token) => token.type !== -1).map((token) => ({
kind: QuixosCapabilityParser.symbolicNames[token.type] ?? "token",
start: offset(token.start), end: offset(token.stop + 1),
text: token.text ?? "", trivia: token.channel !== 0,
})),
};
};
/** Edits refer to one immutable source snapshot; overlapping edits are errors. */
export const applySourceEdits = (source: string, edits: readonly SourceEdit[]) => {
const sorted = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
let end = 0;
let previousStart = -1;
let result = "";
for (const edit of sorted) {
if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end) ||
edit.start < end || edit.start === previousStart || edit.end < edit.start || edit.end > source.length) {
throw new Error("Invalid or overlapping source edits");
}
result += source.slice(end, edit.start) + edit.text;
end = edit.end;
previousStart = edit.start;
}
return result + source.slice(end);
};
export const walkSyntax = function* (node: SyntaxNode): Generator<SyntaxNode> {
yield node;
for (const child of node.children) yield* walkSyntax(child);
};
export const lintQx = (source: string, fileName = "<memory>") => {
const syntax = parseQx(source, fileName);
const diagnostics = syntax.diagnostics.map((entry) => ({ ...entry, severity: "error" as "error" | "warning" }));
if (diagnostics.length) return diagnostics;
const seen = new Set<string>();
for (const node of walkSyntax(syntax.root)) {
if (node.kind !== "sourceImportDecl") continue;
const literal = node.children.find((child) => child.kind === "stringLiteral")!;
const importPath: string = JSON.parse(source.slice(literal.start, literal.end));
let message: string | undefined;
let code = "invalid-source-import";
try { validateQxImportPath(importPath); } catch (error) { message = (error as Error).message; }
if (!message && seen.has(importPath)) { code = "duplicate-source-import"; message = `Repeated local import ${importPath}`; }
seen.add(importPath);
if (message) {
const prefix = source.slice(0, node.start);
diagnostics.push({ phase: "syntax", code, message, fileName,
line: prefix.split("\n").length, column: prefix.length - prefix.lastIndexOf("\n") - 1,
severity: code === "duplicate-source-import" ? "warning" : "error" });
}
}
return diagnostics;
};
/** Conservative formatter: indentation only, preserving strings and comments verbatim. */
export const formatQx = (source: string) => {
const syntax = parseQx(source);
if (syntax.diagnostics.length) throw new Error("Cannot format QX with syntax errors");
const edits: SourceEdit[] = [];
let depth = 0;
let lineStart = 0;
for (const token of syntax.tokens) {
if (token.kind === "WS") continue;
lineStart = source.lastIndexOf("\n", token.start - 1) + 1;
if (/^[ \t]*$/.test(source.slice(lineStart, token.start))) {
const indentation = " ".repeat(Math.max(0, depth - (token.kind === "RBRACE" ? 1 : 0)));
if (source.slice(lineStart, token.start) !== indentation)
edits.push({ start: lineStart, end: token.start, text: indentation });
}
if (!token.trivia) {
if (token.kind === "LBRACE") depth++;
if (token.kind === "RBRACE") depth--;
}
}
return applySourceEdits(source, edits);
};
export const addWorkspaceImport = (source: string, importPath: string) => {
validateQxImportPath(importPath);
const syntax = parseQx(source);
if (syntax.diagnostics.length || syntax.root.children[0]?.kind !== "workspaceDecl")
throw new Error("Expected a syntactically valid workspace");
for (const node of walkSyntax(syntax.root)) {
if (node.kind === "sourceImportDecl") {
const literal = node.children.find((child) => child.kind === "stringLiteral")!;
if (JSON.parse(source.slice(literal.start, literal.end)) === importPath) return source;
}
}
const brace = syntax.tokens.find((token) => token.kind === "LBRACE")!;
const newline = source.includes("\r\n") ? "\r\n" : "\n";
return applySourceEdits(source, [{ start: brace.end, end: brace.end,
text: `${newline} import ${JSON.stringify(importPath)};` }]);
};
export const validateQxImportPath = (value: string) => {
if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.qx$/.test(value) ||
value.split("/").some((part) => part === "." || part === ".."))
throw new Error(`Invalid repository-relative QX import path: ${value}`);
};
+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);
});
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env node
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]");
const resolveResource = await createGitCapabilityResolver({ checkoutRoot: `${root}/.quixos/resource-checkouts` });
const plan = await scaffoldAtom({ root, name, id, write: flags.includes("--write"), resolveResource });
process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`);
return;
}
const [file, flag, ...rest] = args;
if (!file || rest.length || (flag && flag !== "--write") || !["parse", "lint", "format"].includes(command ?? "") ||
(flag && command !== "format")) throw new Error("usage: quixos-qx parse|lint|format FILE [--write (format only)]");
const source = await readFile(file, "utf8");
if (command === "format") {
const formatted = formatQx(source);
if (flag) await writeFile(file, formatted); else process.stdout.write(formatted);
} else {
const result = command === "parse" ? parseQx(source, file) : lintQx(source, file);
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
if (Array.isArray(result) ? result.some((entry) => entry.severity === "error") : result.diagnostics.length) process.exitCode = 1;
}
};
main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; });
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env node
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] [--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
the checked workspace plan as JSON.`;
const parseArgs = (args: string[]) => {
const values = new Map<string, string>();
for (let index = 0; index < args.length; index += 2) {
const key = args[index];
const value = args[index + 1];
if (!key?.startsWith("--") || !value) throw new Error(usage);
values.set(key, value);
}
const rootDirectory = values.get("--root");
const checkoutRoot = values.get("--checkout-root");
if (!rootDirectory || !checkoutRoot) throw new Error(usage);
return {
rootDirectory,
checkoutRoot,
graphOut: values.get("--graph-out"),
snapshotMap: values.get("--snapshot-map"),
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"),
};
};
const main = async () => {
if (process.argv.includes("--help") || process.argv.includes("-h")) {
process.stdout.write(`${usage}\n`);
return;
}
const options = parseArgs(process.argv.slice(2));
const resolveResource = await createGitCapabilityResolver({
checkoutRoot: options.checkoutRoot,
snapshotMap: options.snapshotMap,
});
const assembled = await compileWorkspaceRepository({
rootDirectory: options.rootDirectory,
workspaceId: options.workspaceId,
workspaceRevisionId: options.workspaceRevisionId,
sourceRootCommit: options.sourceRootCommit,
resolveResource,
});
if (options.graphOut) {
await writeFile(options.graphOut, `${JSON.stringify({
formatVersion: 1,
quixos: assembled.lock.quixos,
directResources: [...assembled.directResources.entries()].map(([bindingKey, node]) => {
const [kind, binding] = bindingKey.split("\0");
return { kind, binding, resourceKey: node.key, directory: node.directory };
}),
resources: assembled.resources.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,
})),
})),
}, 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) => {
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
process.exitCode = 1;
});
+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 };
};
+4
View File
@@ -0,0 +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;
};
+407
View File
@@ -0,0 +1,407 @@
declare const opaqueIdBrand: unique symbol;
type OpaqueId<Kind extends string> = string & {
readonly [opaqueIdBrand]: Kind;
};
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">;
export type OperationId = OpaqueId<"OperationId">;
export type SlotId = OpaqueId<"SlotId">;
export type EdgeTypeId = OpaqueId<"EdgeTypeId">;
export type EdgeProjectionId = OpaqueId<"EdgeProjectionId">;
export type PackageId = OpaqueId<"PackageId">;
export type PackageRevisionId = OpaqueId<"PackageRevisionId">;
export type PackageExportId = OpaqueId<"PackageExportId">;
export type DependencyPortId = OpaqueId<"DependencyPortId">;
export type ObjectId = OpaqueId<"ObjectId">;
const opaque = <Kind extends string>(value: string) => value as OpaqueId<Kind>;
/**
* Textual IDs remain opaque to the semantic model. Authoring tools may mint
* UUID-backed values later without changing the checked IR.
*/
export const capabilityId = {
workspace: (value: string) => opaque<"WorkspaceId">(value),
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),
member: (value: string) => opaque<"MemberId">(value),
operation: (value: string) => opaque<"OperationId">(value),
slot: (value: string) => opaque<"SlotId">(value),
edgeType: (value: string) => opaque<"EdgeTypeId">(value),
edgeProjection: (value: string) => opaque<"EdgeProjectionId">(value),
package: (value: string) => opaque<"PackageId">(value),
packageRevision: (value: string) => opaque<"PackageRevisionId">(value),
packageExport: (value: string) => opaque<"PackageExportId">(value),
dependencyPort: (value: string) => opaque<"DependencyPortId">(value),
object: (value: string) => opaque<"ObjectId">(value),
} as const;
export interface SourceRevision {
repository: string;
commit: string;
}
export type ScalarValueTypeName =
| "bool"
| "bytes"
| "double"
| "int32"
| "int64"
| "string"
| "uint32"
| "uint64";
export type ObjectExpectation =
| { kind: "atom"; atomId: AtomId }
| {
kind: "interface";
interfaceRevisionId: InterfaceRevisionId;
};
export type ValueType =
| { kind: "builtin"; name: "unit" | "watch-handle" }
| { kind: "scalar"; name: ScalarValueTypeName }
| { kind: "message"; descriptorId: string }
| { kind: "record"; fields: Record<string, ValueType> }
| { kind: "object-ref"; expectation: ObjectExpectation }
| { kind: "optional"; value: ValueType }
| { kind: "list"; value: ValueType };
export const valueType = {
unit: { kind: "builtin", name: "unit" } as const,
watchHandle: { kind: "builtin", name: "watch-handle" } as const,
bool: { kind: "scalar", name: "bool" } as const,
bytes: { kind: "scalar", name: "bytes" } as const,
double: { kind: "scalar", name: "double" } as const,
int32: { kind: "scalar", name: "int32" } as const,
int64: { kind: "scalar", name: "int64" } as const,
string: { kind: "scalar", name: "string" } as const,
uint32: { kind: "scalar", name: "uint32" } as const,
uint64: { kind: "scalar", name: "uint64" } as const,
message: (descriptorId: string): ValueType => ({
kind: "message",
descriptorId,
}),
atomRef: (atomId: AtomId): ValueType => ({
kind: "object-ref",
expectation: { kind: "atom", atomId },
}),
interfaceRef: (interfaceRevisionId: InterfaceRevisionId): ValueType => ({
kind: "object-ref",
expectation: { kind: "interface", interfaceRevisionId },
}),
optional: (value: ValueType): ValueType => ({ kind: "optional", value }),
list: (value: ValueType): ValueType => ({ kind: "list", value }),
} as const;
export interface AtomDefinition {
id: AtomId;
displayName: string;
documentation?: string;
}
export type InterfaceOperationMode =
| "call"
| "watch-start"
| "watch-stop"
| "subscribe"
| "unsubscribe";
export interface InterfaceOperation {
id: OperationId;
displayName: string;
inputType: ValueType;
outputType: ValueType;
mode: InterfaceOperationMode;
/** Required for watch-start/subscribe and absent for other modes. */
eventType?: ValueType;
}
interface InterfaceMemberBase {
id: MemberId;
displayName: string;
operations: InterfaceOperation[];
}
export interface ValueInterfaceMember extends InterfaceMemberBase {
kind: "value";
valueType: ValueType;
}
export type EdgeCardinality =
| "optional-one"
| "exactly-one"
| "many"
| "many-unique";
export type EdgeEndpointConstraint =
| { kind: "atom"; atomId: AtomId }
| {
kind: "interface";
interfaceRevisionId: InterfaceRevisionId;
};
export interface RelationshipInterfaceMember extends InterfaceMemberBase {
kind: "relationship";
target: EdgeEndpointConstraint;
cardinality: EdgeCardinality;
ordered: boolean;
}
/** A named callable capability that is not value or relationship sugar. */
export interface OperationInterfaceMember extends InterfaceMemberBase {
kind: "operation";
inputType: ValueType;
outputType: ValueType;
}
export type InterfaceMember =
| ValueInterfaceMember
| RelationshipInterfaceMember
| OperationInterfaceMember;
export interface InterfaceRevision {
interfaceId: InterfaceId;
revisionId: InterfaceRevisionId;
displayName: string;
source: SourceRevision;
members: InterfaceMember[];
}
export type StoragePolicy =
| { kind: "optimistic-register" }
| { kind: "crdt-document"; updateType: ValueType };
export interface StateSlotDefinition {
kind: "state";
id: SlotId;
attachedTo: AtomId;
displayName: string;
valueType: ValueType;
storagePolicy: StoragePolicy;
defaultValue?: unknown;
}
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 {
kind: "edge";
id: EdgeTypeId;
displayName: string;
endpoints: [EdgeEndpoint, EdgeEndpoint];
}
export type PersistentAttachment = StateSlotDefinition | EdgeDefinition;
export type StatePrimitive =
| "read"
| "write"
| "watch-start"
| "watch-stop";
export type EdgePrimitive =
| "resolve"
| "connect"
| "disconnect"
| "watch-start"
| "watch-stop";
export type PackageReceiverRequirement =
| { kind: "any-object" }
| { kind: "exact-atom"; atomId: AtomId }
| {
kind: "all-interfaces";
interfaceRevisionIds: InterfaceRevisionId[];
};
export type DependencyPortRequirement =
| {
kind: "state";
valueType: ValueType;
primitives: StatePrimitive[];
}
| {
kind: "edge";
target: EdgeEndpointConstraint;
cardinality: EdgeCardinality;
primitives: EdgePrimitive[];
}
| {
kind: "interface";
interfaceRevisionId: InterfaceRevisionId;
}
| { kind: "constructor"; atomId: AtomId; inputType?: ValueType };
export interface DependencyPort {
id: DependencyPortId;
displayName: string;
requirement: DependencyPortRequirement;
}
interface PackageExportBase {
id: PackageExportId;
displayName: string;
inputType: ValueType;
outputType: ValueType;
dependencyPorts: DependencyPort[];
}
export interface PackageOperationExport extends PackageExportBase {
kind: "operation";
mode: InterfaceOperationMode;
eventType?: ValueType;
receiverRequirement: PackageReceiverRequirement;
}
export interface PackageFunctionExport extends PackageExportBase {
kind: "function";
}
export interface PackageConstructorExport extends PackageExportBase {
kind: "constructor";
constructsAtom: AtomId;
}
export type PackageExport =
| PackageOperationExport
| PackageFunctionExport
| 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 =
| { kind: "state"; slotId: SlotId; via?: EdgeTraversal }
| {
kind: "edge";
edgeTypeId: EdgeTypeId;
projectionId: EdgeProjectionId;
via?: EdgeTraversal;
}
| {
kind: "interface";
interfaceRevisionId: InterfaceRevisionId;
via?: EdgeTraversal;
}
| { kind: "constructor"; atomId: AtomId };
export interface BoundDependency {
portId: DependencyPortId;
binding: DependencyBinding;
}
/** Selects the object at the other end of an exactly-one relationship. */
export interface EdgeTraversal {
edgeTypeId: EdgeTypeId;
projectionId: EdgeProjectionId;
}
/**
* Provisional get-or-create recipe for a relationship implementation. The
* host projection is supplied by the relationship's resolve binding; this
* projection originates at the object constructed for the relationship.
*/
export interface RelationshipMaterialization {
memberId: MemberId;
constructorAtomId: AtomId;
edgeTypeId: EdgeTypeId;
constructedProjectionId: EdgeProjectionId;
}
export type Binding =
| {
kind: "state";
slotId: SlotId;
primitive: StatePrimitive;
}
| {
kind: "edge";
edgeTypeId: EdgeTypeId;
projectionId: EdgeProjectionId;
primitive: EdgePrimitive;
}
| {
kind: "package";
packageRevisionId: PackageRevisionId;
exportId: PackageExportId;
dependencies: BoundDependency[];
};
export interface OperationBinding {
operationId: OperationId;
binding: Binding;
}
export interface Conformance {
/** Absent only for legacy assemblies awaiting explicit ownership enrollment. */
id?: ConformanceId;
semanticMajor?: number;
atomId: AtomId;
interfaceRevisionId: InterfaceRevisionId;
privateAttachments: PersistentAttachment[];
operationBindings: OperationBinding[];
relationshipMaterializations: RelationshipMaterialization[];
}
export interface AtomConstructorBinding {
atomId: AtomId;
packageRevisionId: PackageRevisionId;
exportId: PackageExportId;
dependencies: BoundDependency[];
}
export interface WorkspaceRevision {
id: WorkspaceRevisionId;
workspaceId: WorkspaceId;
parentRevisionIds: WorkspaceRevisionId[];
sourceRootCommit: string;
atoms: AtomDefinition[];
sharedAttachments: PersistentAttachment[];
interfaceImports: InterfaceRevision[];
packageImports: PackageRevision[];
conformances: Conformance[];
constructors: AtomConstructorBinding[];
}
export type AttachmentOwner =
| { kind: "workspace" }
| {
kind: "conformance";
atomId: AtomId;
interfaceRevisionId: InterfaceRevisionId;
};
export interface OwnedAttachment {
attachment: PersistentAttachment;
owner: AttachmentOwner;
}
File diff suppressed because it is too large Load Diff
+15 -93
View File
@@ -3,23 +3,7 @@ import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import { fromBinary, toJsonString } from "@bufbuild/protobuf"; import { fromBinary, toJsonString } from "@bufbuild/protobuf";
import type { PackageDescriptor } from "./gen/quixos/package_pb.js"; import type { PackageDescriptor } from "./gen/quixos/package_pb.js";
import { import { PackageDescriptorSchema } from "./gen/quixos/package_pb.js";
FunctionCapabilityKind,
PackageDescriptorSchema,
} from "./gen/quixos/package_pb.js";
const functionKey = (value: {
packageNamespace: string;
packageName: string;
symbol: string;
versionRef: string;
}) =>
[
value.packageNamespace,
value.packageName,
value.symbol,
value.versionRef,
].join(":");
const protoPaths = () => { const protoPaths = () => {
const candidates = [ const candidates = [
@@ -76,87 +60,25 @@ export const validatePackageDescriptor = (
descriptor: PackageDescriptor, descriptor: PackageDescriptor,
): string[] => { ): string[] => {
const errors: string[] = []; const errors: string[] = [];
if (!descriptor.packageNamespace) { if (!descriptor.packageId) {
errors.push("packageNamespace is required"); errors.push("packageId is required");
} }
if (!descriptor.packageName) { if (!descriptor.packageRevisionId) {
errors.push("packageName is required"); errors.push("packageRevisionId is required");
} }
if (descriptor.descriptorVersion === 0) { if (!descriptor.runtimeProtocolVersion) {
errors.push("descriptorVersion must be greater than zero"); errors.push("runtimeProtocolVersion is required");
} }
if ( const seenExports = new Set<string>();
descriptor.interfaceCompatibleBackTo !== 0 && for (const [index, entry] of descriptor.exports.entries()) {
descriptor.interfaceCompatibleBackTo > descriptor.descriptorVersion if (!entry.exportId) errors.push(`exports[${index}].exportId is required`);
) { if (!entry.runtimeSymbol) {
errors.push("interfaceCompatibleBackTo must not exceed descriptorVersion"); errors.push(`exports[${index}].runtimeSymbol is required`);
}
if (
descriptor.runnerCompatibleBackTo !== 0 &&
descriptor.runnerCompatibleBackTo > descriptor.descriptorVersion
) {
errors.push("runnerCompatibleBackTo must not exceed descriptorVersion");
}
const seenFunctions = new Set<string>();
for (const [index, entry] of descriptor.functionExports.entries()) {
const fn = entry.function;
if (!fn) {
errors.push(`functionExports[${index}].function is required`);
continue;
} }
if (!fn.packageNamespace) { if (seenExports.has(entry.exportId)) {
fn.packageNamespace = descriptor.packageNamespace; errors.push(`duplicate export: ${entry.exportId}`);
}
if (!fn.packageName) {
fn.packageName = descriptor.packageName;
}
if (
fn.packageNamespace !== descriptor.packageNamespace ||
fn.packageName !== descriptor.packageName
) {
errors.push(
`functionExports[${index}] must export from this package (${descriptor.packageNamespace}/${descriptor.packageName})`,
);
}
if (!fn.symbol) {
errors.push(`functionExports[${index}].function.symbol is required`);
}
if (entry.interfaceVersion === 0) {
errors.push(`functionExports[${index}].interfaceVersion is required`);
}
if (entry.capabilityKind === FunctionCapabilityKind.FUNCTION_CAPABILITY_KIND_UNSPECIFIED) {
errors.push(`functionExports[${index}].capabilityKind is required`);
}
if (
entry.interfaceCompatibleBackTo !== 0 &&
entry.interfaceCompatibleBackTo > entry.interfaceVersion
) {
errors.push(
`functionExports[${index}].interfaceCompatibleBackTo must not exceed interfaceVersion`,
);
}
const key = functionKey(fn);
if (seenFunctions.has(key)) {
errors.push(`duplicate function export: ${key}`);
}
seenFunctions.add(key);
}
for (const [index, entry] of descriptor.dependencies.entries()) {
if (!entry.package) {
errors.push(`dependencies[${index}].package is required`);
continue;
}
if (!entry.package.packageNamespace) {
errors.push(`dependencies[${index}].package.packageNamespace is required`);
}
if (!entry.package.packageName) {
errors.push(`dependencies[${index}].package.packageName is required`);
}
if (entry.package.descriptorVersion === 0) {
errors.push(`dependencies[${index}].package.descriptorVersion is required`);
} }
seenExports.add(entry.exportId);
} }
return errors; return errors;
+445 -260
View File
File diff suppressed because one or more lines are too long
-268
View File
@@ -1,268 +0,0 @@
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js"
// @generated from file camino/options.proto (package camino, syntax proto3)
/* eslint-disable */
import type { GenExtension, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
import { extDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
import type { FieldOptions, FileOptions, MessageOptions, MethodOptions as MethodOptions$1 } from "@bufbuild/protobuf/wkt";
import { file_google_protobuf_descriptor } from "@bufbuild/protobuf/wkt";
import type { Cardinality, ConflictStrategy, FieldOps, FieldStorage, InterfaceFieldContract, SymbolRef, TypeBinding } from "./schema_pb.js";
import { file_camino_schema } from "./schema_pb.js";
import type { FunctionRef } from "../quixos/refs_pb.js";
import { file_quixos_refs } from "../quixos/refs_pb.js";
import type { Message } from "@bufbuild/protobuf";
/**
* Describes the file camino/options.proto.
*/
export const file_camino_options: GenFile = /*@__PURE__*/
fileDesc("ChRjYW1pbm8vb3B0aW9ucy5wcm90bxIGY2FtaW5vIj4KDENsYXNzT3B0aW9ucxIPCgd2ZXJzaW9uGAEgASgNEh0KAmlkGAIgASgLMhEuY2FtaW5vLlN5bWJvbFJlZiJCChBJbnRlcmZhY2VPcHRpb25zEg8KB3ZlcnNpb24YASABKA0SHQoCaWQYAiABKAsyES5jYW1pbm8uU3ltYm9sUmVmImQKEUltcGxlbWVudHNPcHRpb25zEiQKCWludGVyZmFjZRgBIAEoCzIRLmNhbWluby5TeW1ib2xSZWYSKQoMdHlwZV9iaW5kaW5nGAIgAygLMhMuY2FtaW5vLlR5cGVCaW5kaW5nIjsKEkNvbnN0cnVjdG9yT3B0aW9ucxIlCghmdW5jdGlvbhgBIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZiKKAQoLRWRnZU9wdGlvbnMSHQoCaWQYASABKAsyES5jYW1pbm8uU3ltYm9sUmVmEiEKBnRhcmdldBgCIAEoCzIRLmNhbWluby5TeW1ib2xSZWYSKAoLY2FyZGluYWxpdHkYAyABKA4yEy5jYW1pbm8uQ2FyZGluYWxpdHkSDwoHaW52ZXJzZRgEIAEoCSJECg1NZXRob2RPcHRpb25zEgwKBG5hbWUYASABKAkSJQoIZnVuY3Rpb24YAiABKAsyEy5xdWl4b3MuRnVuY3Rpb25SZWYiYwoQTWlncmF0aW9uT3B0aW9ucxIUCgxmcm9tX3ZlcnNpb24YASABKA0SEgoKdG9fdmVyc2lvbhgCIAEoDRIlCghmdW5jdGlvbhgDIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZjpJChBzY2hlbWFfbmFtZXNwYWNlEhwuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRpb25zGLiOAyABKAlSD3NjaGVtYU5hbWVzcGFjZTpFCg5zY2hlbWFfdmVyc2lvbhIcLmdvb2dsZS5wcm90b2J1Zi5GaWxlT3B0aW9ucxi5jgMgASgJUg1zY2hlbWFWZXJzaW9uOk0KBWNsYXNzEh8uZ29vZ2xlLnByb3RvYnVmLk1lc3NhZ2VPcHRpb25zGMKOAyABKAsyFC5jYW1pbm8uQ2xhc3NPcHRpb25zUgVjbGFzczpQCgZtZXRob2QSHy5nb29nbGUucHJvdG9idWYuTWVzc2FnZU9wdGlvbnMYw44DIAMoCzIVLmNhbWluby5NZXRob2RPcHRpb25zUgZtZXRob2Q6WQoJbWlncmF0aW9uEh8uZ29vZ2xlLnByb3RvYnVmLk1lc3NhZ2VPcHRpb25zGMSOAyADKAsyGC5jYW1pbm8uTWlncmF0aW9uT3B0aW9uc1IJbWlncmF0aW9uOlkKCWludGVyZmFjZRIfLmdvb2dsZS5wcm90b2J1Zi5NZXNzYWdlT3B0aW9ucxjFjgMgASgLMhguY2FtaW5vLkludGVyZmFjZU9wdGlvbnNSCWludGVyZmFjZTpcCgppbXBsZW1lbnRzEh8uZ29vZ2xlLnByb3RvYnVmLk1lc3NhZ2VPcHRpb25zGMaOAyADKAsyGS5jYW1pbm8uSW1wbGVtZW50c09wdGlvbnNSCmltcGxlbWVudHM6XwoLY29uc3RydWN0b3ISHy5nb29nbGUucHJvdG9idWYuTWVzc2FnZU9wdGlvbnMYx44DIAEoCzIaLmNhbWluby5Db25zdHJ1Y3Rvck9wdGlvbnNSC2NvbnN0cnVjdG9yOlUKCGNvbmZsaWN0Eh0uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucxjMjgMgASgOMhguY2FtaW5vLkNvbmZsaWN0U3RyYXRlZ3lSCGNvbmZsaWN0OkgKBGVkZ2USHS5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zGM2OAyABKAsyEy5jYW1pbm8uRWRnZU9wdGlvbnNSBGVkZ2U6WgoNZmllbGRfc3RvcmFnZRIdLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMYzo4DIAEoCzIULmNhbWluby5GaWVsZFN0b3JhZ2VSDGZpZWxkU3RvcmFnZTpOCglmaWVsZF9vcHMSHS5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zGM+OAyABKAsyEC5jYW1pbm8uRmllbGRPcHNSCGZpZWxkT3BzOmgKD2ludGVyZmFjZV9maWVsZBIdLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMY0I4DIAEoCzIeLmNhbWluby5JbnRlcmZhY2VGaWVsZENvbnRyYWN0Ug5pbnRlcmZhY2VGaWVsZDpJCgRpbXBsEh4uZ29vZ2xlLnByb3RvYnVmLk1ldGhvZE9wdGlvbnMY1o4DIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZlIEaW1wbGIGcHJvdG8z", [file_google_protobuf_descriptor, file_camino_schema, file_quixos_refs]);
/**
* @generated from message camino.ClassOptions
*/
export type ClassOptions = Message<"camino.ClassOptions"> & {
/**
* @generated from field: uint32 version = 1;
*/
version: number;
/**
* @generated from field: camino.SymbolRef id = 2;
*/
id?: SymbolRef | undefined;
};
/**
* Describes the message camino.ClassOptions.
* Use `create(ClassOptionsSchema)` to create a new message.
*/
export const ClassOptionsSchema: GenMessage<ClassOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 0);
/**
* @generated from message camino.InterfaceOptions
*/
export type InterfaceOptions = Message<"camino.InterfaceOptions"> & {
/**
* @generated from field: uint32 version = 1;
*/
version: number;
/**
* @generated from field: camino.SymbolRef id = 2;
*/
id?: SymbolRef | undefined;
};
/**
* Describes the message camino.InterfaceOptions.
* Use `create(InterfaceOptionsSchema)` to create a new message.
*/
export const InterfaceOptionsSchema: GenMessage<InterfaceOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 1);
/**
* @generated from message camino.ImplementsOptions
*/
export type ImplementsOptions = Message<"camino.ImplementsOptions"> & {
/**
* @generated from field: camino.SymbolRef interface = 1;
*/
interface?: SymbolRef | undefined;
/**
* @generated from field: repeated camino.TypeBinding type_binding = 2;
*/
typeBinding: TypeBinding[];
};
/**
* Describes the message camino.ImplementsOptions.
* Use `create(ImplementsOptionsSchema)` to create a new message.
*/
export const ImplementsOptionsSchema: GenMessage<ImplementsOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 2);
/**
* @generated from message camino.ConstructorOptions
*/
export type ConstructorOptions = Message<"camino.ConstructorOptions"> & {
/**
* @generated from field: quixos.FunctionRef function = 1;
*/
function?: FunctionRef | undefined;
};
/**
* Describes the message camino.ConstructorOptions.
* Use `create(ConstructorOptionsSchema)` to create a new message.
*/
export const ConstructorOptionsSchema: GenMessage<ConstructorOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 3);
/**
* @generated from message camino.EdgeOptions
*/
export type EdgeOptions = Message<"camino.EdgeOptions"> & {
/**
* @generated from field: camino.SymbolRef id = 1;
*/
id?: SymbolRef | undefined;
/**
* @generated from field: camino.SymbolRef target = 2;
*/
target?: SymbolRef | undefined;
/**
* @generated from field: camino.Cardinality cardinality = 3;
*/
cardinality: Cardinality;
/**
* @generated from field: string inverse = 4;
*/
inverse: string;
};
/**
* Describes the message camino.EdgeOptions.
* Use `create(EdgeOptionsSchema)` to create a new message.
*/
export const EdgeOptionsSchema: GenMessage<EdgeOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 4);
/**
* @generated from message camino.MethodOptions
*/
export type MethodOptions = Message<"camino.MethodOptions"> & {
/**
* @generated from field: string name = 1;
*/
name: string;
/**
* @generated from field: quixos.FunctionRef function = 2;
*/
function?: FunctionRef | undefined;
};
/**
* Describes the message camino.MethodOptions.
* Use `create(MethodOptionsSchema)` to create a new message.
*/
export const MethodOptionsSchema: GenMessage<MethodOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 5);
/**
* @generated from message camino.MigrationOptions
*/
export type MigrationOptions = Message<"camino.MigrationOptions"> & {
/**
* @generated from field: uint32 from_version = 1;
*/
fromVersion: number;
/**
* @generated from field: uint32 to_version = 2;
*/
toVersion: number;
/**
* @generated from field: quixos.FunctionRef function = 3;
*/
function?: FunctionRef | undefined;
};
/**
* Describes the message camino.MigrationOptions.
* Use `create(MigrationOptionsSchema)` to create a new message.
*/
export const MigrationOptionsSchema: GenMessage<MigrationOptions> = /*@__PURE__*/
messageDesc(file_camino_options, 6);
/**
* @generated from extension: string schema_namespace = 51000;
*/
export const schema_namespace: GenExtension<FileOptions, string> = /*@__PURE__*/
extDesc(file_camino_options, 0);
/**
* @generated from extension: string schema_version = 51001;
*/
export const schema_version: GenExtension<FileOptions, string> = /*@__PURE__*/
extDesc(file_camino_options, 1);
/**
* @generated from extension: camino.ClassOptions class = 51010;
*/
export const class$: GenExtension<MessageOptions, ClassOptions> = /*@__PURE__*/
extDesc(file_camino_options, 2);
/**
* @generated from extension: repeated camino.MethodOptions method = 51011;
*/
export const method: GenExtension<MessageOptions, MethodOptions[]> = /*@__PURE__*/
extDesc(file_camino_options, 3);
/**
* @generated from extension: repeated camino.MigrationOptions migration = 51012;
*/
export const migration: GenExtension<MessageOptions, MigrationOptions[]> = /*@__PURE__*/
extDesc(file_camino_options, 4);
/**
* @generated from extension: camino.InterfaceOptions interface = 51013;
*/
export const interface$: GenExtension<MessageOptions, InterfaceOptions> = /*@__PURE__*/
extDesc(file_camino_options, 5);
/**
* @generated from extension: repeated camino.ImplementsOptions implements = 51014;
*/
export const implements$: GenExtension<MessageOptions, ImplementsOptions[]> = /*@__PURE__*/
extDesc(file_camino_options, 6);
/**
* @generated from extension: camino.ConstructorOptions constructor = 51015;
*/
export const constructor: GenExtension<MessageOptions, ConstructorOptions> = /*@__PURE__*/
extDesc(file_camino_options, 7);
/**
* @generated from extension: camino.ConflictStrategy conflict = 51020;
*/
export const conflict: GenExtension<FieldOptions, ConflictStrategy> = /*@__PURE__*/
extDesc(file_camino_options, 8);
/**
* @generated from extension: camino.EdgeOptions edge = 51021;
*/
export const edge: GenExtension<FieldOptions, EdgeOptions> = /*@__PURE__*/
extDesc(file_camino_options, 9);
/**
* @generated from extension: camino.FieldStorage field_storage = 51022;
*/
export const field_storage: GenExtension<FieldOptions, FieldStorage> = /*@__PURE__*/
extDesc(file_camino_options, 10);
/**
* @generated from extension: camino.FieldOps field_ops = 51023;
*/
export const field_ops: GenExtension<FieldOptions, FieldOps> = /*@__PURE__*/
extDesc(file_camino_options, 11);
/**
* @generated from extension: camino.InterfaceFieldContract interface_field = 51024;
*/
export const interface_field: GenExtension<FieldOptions, InterfaceFieldContract> = /*@__PURE__*/
extDesc(file_camino_options, 12);
/**
* @generated from extension: quixos.FunctionRef impl = 51030;
*/
export const impl: GenExtension<MethodOptions$1, FunctionRef> = /*@__PURE__*/
extDesc(file_camino_options, 13);
File diff suppressed because it is too large Load Diff
+233 -78
View File
@@ -1,14 +1,14 @@
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js" // @generated by protoc-gen-es v2.14.1 with parameter "target=ts,import_extension=js"
// @generated from file quixos/orch.proto (package quixos.orch, syntax proto3) // @generated from file quixos/orch.proto (package quixos.orch, syntax proto3)
/* eslint-disable */ /* eslint-disable */
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
import type { Value } from "../camino/api_pb.js"; import type { CaminoObject, Value } from "../camino/api_pb.js";
import { file_camino_api } from "../camino/api_pb.js"; import { file_camino_api } from "../camino/api_pb.js";
import type { PackageDescriptor } from "./package_pb.js"; import type { PackageDescriptor } from "./package_pb.js";
import { file_quixos_package } from "./package_pb.js"; import { file_quixos_package } from "./package_pb.js";
import type { FunctionRef } from "./refs_pb.js"; import type { CapabilityRef, PackageExportRef } from "./refs_pb.js";
import { file_quixos_refs } from "./refs_pb.js"; import { file_quixos_refs } from "./refs_pb.js";
import type { DerivedDependency } from "./runtime_pb.js"; import type { DerivedDependency } from "./runtime_pb.js";
import { file_quixos_runtime } from "./runtime_pb.js"; import { file_quixos_runtime } from "./runtime_pb.js";
@@ -18,16 +18,104 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file quixos/orch.proto. * Describes the file quixos/orch.proto.
*/ */
export const file_quixos_orch: GenFile = /*@__PURE__*/ export const file_quixos_orch: GenFile = /*@__PURE__*/
fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gizAEKFUludm9rZUZ1bmN0aW9uUmVxdWVzdBIlCghmdW5jdGlvbhgBIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZhIRCglvYmplY3RfaWQYAiABKAkSPAoFaW5wdXQYAyADKAsyLS5xdWl4b3Mub3JjaC5JbnZva2VGdW5jdGlvblJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEilgEKFkludm9rZUZ1bmN0aW9uUmVzcG9uc2USFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIKCgJvaxgDIAEoCBIdCgZyZXN1bHQYBCABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYBSABKAkiygEKFFdhdGNoRnVuY3Rpb25SZXF1ZXN0EiUKCGZ1bmN0aW9uGAEgASgLMhMucXVpeG9zLkZ1bmN0aW9uUmVmEhEKCW9iamVjdF9pZBgCIAEoCRI7CgVpbnB1dBgDIAMoCzIsLnF1aXhvcy5vcmNoLldhdGNoRnVuY3Rpb25SZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIuEBChJXYXRjaEZ1bmN0aW9uRXZlbnQSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIQCgh3YXRjaF9pZBgDIAEoCRIcCgV2YWx1ZRgEIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYBSADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgGIAEoCRIPCgdpbml0aWFsGAcgASgIIhgKFkxpc3RBY3RpdmF0aW9uc1JlcXVlc3QiHwodTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1JlcXVlc3QiUAoeTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1Jlc3BvbnNlEi4KC2Rlc2NyaXB0b3JzGAEgAygLMhkucXVpeG9zLlBhY2thZ2VEZXNjcmlwdG9yIhwKGkxpc3RQYWNrYWdlUnVudGltZXNSZXF1ZXN0IlIKG0xpc3RQYWNrYWdlUnVudGltZXNSZXNwb25zZRIzCghydW50aW1lcxgBIAMoCzIhLnF1aXhvcy5vcmNoLlBhY2thZ2VSdW50aW1lU3RhdHVzIkcKF0xpc3RBY3RpdmF0aW9uc1Jlc3BvbnNlEiwKC2FjdGl2YXRpb25zGAEgAygLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbiI/ChZDbG9zZUFjdGl2YXRpb25SZXF1ZXN0EhUKDWFjdGl2YXRpb25faWQYASABKAkSDgoGcmVhc29uGAIgASgJIkYKF0Nsb3NlQWN0aXZhdGlvblJlc3BvbnNlEisKCmFjdGl2YXRpb24YASABKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uIugBCgpBY3RpdmF0aW9uEhUKDWFjdGl2YXRpb25faWQYASABKAkSJQoIZnVuY3Rpb24YAiABKAsyEy5xdWl4b3MuRnVuY3Rpb25SZWYSEQoJb2JqZWN0X2lkGAMgASgJEg0KBXN0YXRlGAQgASgJEg4KBmRlbWFuZBgFIAEoDRIRCglvcGVuZWRfYXQYBiABKAkSFAoMbGFzdF91c2VkX2F0GAcgASgJEhgKEGlkbGVfZGVhZGxpbmVfYXQYCCABKAkSEQoJY2xvc2VkX2F0GAkgASgJEhQKDGNsb3NlX3JlYXNvbhgKIAEoCSLjAgoUUGFja2FnZVJ1bnRpbWVTdGF0dXMSEwoLcnVudGltZV9rZXkYASABKAkSGQoRcGFja2FnZV9uYW1lc3BhY2UYAiABKAkSFAoMcGFja2FnZV9uYW1lGAMgASgJEhoKEmRlc2NyaXB0b3JfdmVyc2lvbhgEIAEoDRITCgtzb3VyY2VfcmVwbxgFIAEoCRIaChJzZXJ2ZXJfaW5zdGFsbGFibGUYBiABKAkSEwoLc291cmNlX3BhdGgYByABKAkSEwoLc2VydmVyX3BhdGgYCCABKAkSCwoDcGlkGAkgASgNEg0KBXN0YXRlGAogASgJEhIKCnN0YXJ0ZWRfYXQYCyABKAkSGQoRbGFzdF9oYW5kc2hha2VfYXQYDCABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGA0gASgJEiEKGWFkdmVydGlzZWRfZnVuY3Rpb25fY291bnQYDiABKA0y4AQKE09yY2hlc3RyYXRvclJ1bnRpbWUSWQoOSW52b2tlRnVuY3Rpb24SIi5xdWl4b3Mub3JjaC5JbnZva2VGdW5jdGlvblJlcXVlc3QaIy5xdWl4b3Mub3JjaC5JbnZva2VGdW5jdGlvblJlc3BvbnNlElUKDVdhdGNoRnVuY3Rpb24SIS5xdWl4b3Mub3JjaC5XYXRjaEZ1bmN0aW9uUmVxdWVzdBofLnF1aXhvcy5vcmNoLldhdGNoRnVuY3Rpb25FdmVudDABEnEKFkxpc3RQYWNrYWdlRGVzY3JpcHRvcnMSKi5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdBorLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRJoChNMaXN0UGFja2FnZVJ1bnRpbWVzEicucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QaKC5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USXAoPTGlzdEFjdGl2YXRpb25zEiMucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVxdWVzdBokLnF1aXhvcy5vcmNoLkxpc3RBY3RpdmF0aW9uc1Jlc3BvbnNlElwKD0Nsb3NlQWN0aXZhdGlvbhIjLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlcXVlc3QaJC5xdWl4b3Mub3JjaC5DbG9zZUFjdGl2YXRpb25SZXNwb25zZWIGcHJvdG8z", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLRAQoYSW52b2tlQ2FwYWJpbGl0eVJlc3BvbnNlEhUKDWludm9jYXRpb25faWQYASABKAkSKwoKYWN0aXZhdGlvbhgCIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24SCgoCb2sYAyABKAgSHQoGcmVzdWx0GAQgASgLMg0uY2FtaW5vLlZhbHVlEg0KBWVycm9yGAUgASgJEjcKDGRlcGVuZGVuY2llcxgGIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5ItIBChZXYXRjaENhcGFiaWxpdHlSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSPQoFaW5wdXQYAyADKAsyLi5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIuMBChRXYXRjaENhcGFiaWxpdHlFdmVudBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEisKCmFjdGl2YXRpb24YAiABKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uEhAKCHdhdGNoX2lkGAMgASgJEhwKBXZhbHVlGAQgASgLMg0uY2FtaW5vLlZhbHVlEjcKDGRlcGVuZGVuY2llcxgFIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5Eg0KBWVycm9yGAYgASgJEg8KB2luaXRpYWwYByABKAgiFQoTR2V0V29ya3NwYWNlUmVxdWVzdCJnChRHZXRXb3Jrc3BhY2VSZXNwb25zZRIUCgx3b3Jrc3BhY2VfaWQYASABKAkSHQoVd29ya3NwYWNlX3JldmlzaW9uX2lkGAIgASgJEhoKEnNvdXJjZV9yb290X2NvbW1pdBgDIAEoCSIYChZMaXN0QWN0aXZhdGlvbnNSZXF1ZXN0Ih8KHUxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0IlAKHkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRIuCgtkZXNjcmlwdG9ycxgBIAMoCzIZLnF1aXhvcy5QYWNrYWdlRGVzY3JpcHRvciIcChpMaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdCJSChtMaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USMwoIcnVudGltZXMYASADKAsyIS5xdWl4b3Mub3JjaC5QYWNrYWdlUnVudGltZVN0YXR1cyJHChdMaXN0QWN0aXZhdGlvbnNSZXNwb25zZRIsCgthY3RpdmF0aW9ucxgBIAMoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24iPwoWQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEg4KBnJlYXNvbhgCIAEoCSJGChdDbG9zZUFjdGl2YXRpb25SZXNwb25zZRIrCgphY3RpdmF0aW9uGAEgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbiLrAQoKQWN0aXZhdGlvbhIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRINCgVzdGF0ZRgEIAEoCRIOCgZkZW1hbmQYBSABKA0SEQoJb3BlbmVkX2F0GAYgASgJEhQKDGxhc3RfdXNlZF9hdBgHIAEoCRIYChBpZGxlX2RlYWRsaW5lX2F0GAggASgJEhEKCWNsb3NlZF9hdBgJIAEoCRIUCgxjbG9zZV9yZWFzb24YCiABKAkiswIKFFBhY2thZ2VSdW50aW1lU3RhdHVzEhMKC3J1bnRpbWVfa2V5GAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSGQoRc291cmNlX3JlcG9zaXRvcnkYAyABKAkSFQoNc291cmNlX2NvbW1pdBgEIAEoCRIUCgxidWlsZF90YXJnZXQYBSABKAkSEwoLc2VydmVyX3BhdGgYBiABKAkSCwoDcGlkGAcgASgNEg0KBXN0YXRlGAggASgJEhIKCnN0YXJ0ZWRfYXQYCSABKAkSGQoRbGFzdF9oYW5kc2hha2VfYXQYCiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAsgASgJEh8KF2FkdmVydGlzZWRfZXhwb3J0X2NvdW50GAwgASgNMq4HChNPcmNoZXN0cmF0b3JSdW50aW1lEl8KEEludm9rZUNhcGFiaWxpdHkSJC5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJbCg9XYXRjaENhcGFiaWxpdHkSIy5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0GiEucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5RXZlbnQwARJcCg9Db25zdHJ1Y3RPYmplY3QSIy5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RPYmplY3RSZXF1ZXN0GiQucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USjAEKH1Jlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3QSMy5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBo0LnF1aXhvcy5vcmNoLlJlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3RSZXNwb25zZRJTCgxHZXRXb3Jrc3BhY2USIC5xdWl4b3Mub3JjaC5HZXRXb3Jrc3BhY2VSZXF1ZXN0GiEucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVzcG9uc2UScQoWTGlzdFBhY2thZ2VEZXNjcmlwdG9ycxIqLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0GisucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1Jlc3BvbnNlEmgKE0xpc3RQYWNrYWdlUnVudGltZXMSJy5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdBooLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlUnVudGltZXNSZXNwb25zZRJcCg9MaXN0QWN0aXZhdGlvbnMSIy5xdWl4b3Mub3JjaC5MaXN0QWN0aXZhdGlvbnNSZXF1ZXN0GiQucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USXAoPQ2xvc2VBY3RpdmF0aW9uEiMucXVpeG9zLm9yY2guQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBokLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlc3BvbnNlYgZwcm90bzM", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]);
/** /**
* @generated from message quixos.orch.InvokeFunctionRequest * @generated from message quixos.orch.ConstructObjectRequest
*/ */
export type InvokeFunctionRequest = Message<"quixos.orch.InvokeFunctionRequest"> & { export type ConstructObjectRequest = Message<"quixos.orch.ConstructObjectRequest"> & {
/** /**
* @generated from field: quixos.FunctionRef function = 1; * @generated from field: string atom_id = 1;
*/ */
function?: FunctionRef | undefined; atomId: string;
/**
* @generated from field: map<string, camino.Value> input = 2;
*/
input: { [key: string]: Value };
};
/**
* Describes the message quixos.orch.ConstructObjectRequest.
* Use `create(ConstructObjectRequestSchema)` to create a new message.
*/
export const ConstructObjectRequestSchema: GenMessage<ConstructObjectRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 0);
/**
* @generated from message quixos.orch.ConstructObjectResponse
*/
export type ConstructObjectResponse = Message<"quixos.orch.ConstructObjectResponse"> & {
/**
* @generated from field: camino.CaminoObject object = 1;
*/
object?: CaminoObject | undefined;
};
/**
* Describes the message quixos.orch.ConstructObjectResponse.
* Use `create(ConstructObjectResponseSchema)` to create a new message.
*/
export const ConstructObjectResponseSchema: GenMessage<ConstructObjectResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 1);
/**
* @generated from message quixos.orch.ResolveOrConstructRelatedObjectRequest
*/
export type ResolveOrConstructRelatedObjectRequest = Message<"quixos.orch.ResolveOrConstructRelatedObjectRequest"> & {
/**
* @generated from field: string object_id = 1;
*/
objectId: string;
/**
* @generated from field: string interface_revision_id = 2;
*/
interfaceRevisionId: string;
/**
* @generated from field: string member_id = 3;
*/
memberId: string;
};
/**
* Describes the message quixos.orch.ResolveOrConstructRelatedObjectRequest.
* Use `create(ResolveOrConstructRelatedObjectRequestSchema)` to create a new message.
*/
export const ResolveOrConstructRelatedObjectRequestSchema: GenMessage<ResolveOrConstructRelatedObjectRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 2);
/**
* @generated from message quixos.orch.ResolveOrConstructRelatedObjectResponse
*/
export type ResolveOrConstructRelatedObjectResponse = Message<"quixos.orch.ResolveOrConstructRelatedObjectResponse"> & {
/**
* @generated from field: camino.CaminoObject object = 1;
*/
object?: CaminoObject | undefined;
/**
* @generated from field: bool constructed = 2;
*/
constructed: boolean;
};
/**
* Describes the message quixos.orch.ResolveOrConstructRelatedObjectResponse.
* Use `create(ResolveOrConstructRelatedObjectResponseSchema)` to create a new message.
*/
export const ResolveOrConstructRelatedObjectResponseSchema: GenMessage<ResolveOrConstructRelatedObjectResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 3);
/**
* @generated from message quixos.orch.InvokeCapabilityRequest
*/
export type InvokeCapabilityRequest = Message<"quixos.orch.InvokeCapabilityRequest"> & {
/**
* @generated from field: quixos.CapabilityRef capability = 1;
*/
capability?: CapabilityRef | undefined;
/** /**
* @generated from field: string object_id = 2; * @generated from field: string object_id = 2;
@@ -38,19 +126,27 @@ export type InvokeFunctionRequest = Message<"quixos.orch.InvokeFunctionRequest">
* @generated from field: map<string, camino.Value> input = 3; * @generated from field: map<string, camino.Value> input = 3;
*/ */
input: { [key: string]: Value }; input: { [key: string]: Value };
/**
* Identifies one logical client mutation across the capability and Camino
* layers so a live-value controller can recognize its own confirmation.
*
* @generated from field: string client_mutation_id = 4;
*/
clientMutationId: string;
}; };
/** /**
* Describes the message quixos.orch.InvokeFunctionRequest. * Describes the message quixos.orch.InvokeCapabilityRequest.
* Use `create(InvokeFunctionRequestSchema)` to create a new message. * Use `create(InvokeCapabilityRequestSchema)` to create a new message.
*/ */
export const InvokeFunctionRequestSchema: GenMessage<InvokeFunctionRequest> = /*@__PURE__*/ export const InvokeCapabilityRequestSchema: GenMessage<InvokeCapabilityRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 0); messageDesc(file_quixos_orch, 4);
/** /**
* @generated from message quixos.orch.InvokeFunctionResponse * @generated from message quixos.orch.InvokeCapabilityResponse
*/ */
export type InvokeFunctionResponse = Message<"quixos.orch.InvokeFunctionResponse"> & { export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResponse"> & {
/** /**
* @generated from field: string invocation_id = 1; * @generated from field: string invocation_id = 1;
*/ */
@@ -75,23 +171,28 @@ export type InvokeFunctionResponse = Message<"quixos.orch.InvokeFunctionResponse
* @generated from field: string error = 5; * @generated from field: string error = 5;
*/ */
error: string; error: string;
/**
* @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 6;
*/
dependencies: DerivedDependency[];
}; };
/** /**
* Describes the message quixos.orch.InvokeFunctionResponse. * Describes the message quixos.orch.InvokeCapabilityResponse.
* Use `create(InvokeFunctionResponseSchema)` to create a new message. * Use `create(InvokeCapabilityResponseSchema)` to create a new message.
*/ */
export const InvokeFunctionResponseSchema: GenMessage<InvokeFunctionResponse> = /*@__PURE__*/ export const InvokeCapabilityResponseSchema: GenMessage<InvokeCapabilityResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 1); messageDesc(file_quixos_orch, 5);
/** /**
* @generated from message quixos.orch.WatchFunctionRequest * @generated from message quixos.orch.WatchCapabilityRequest
*/ */
export type WatchFunctionRequest = Message<"quixos.orch.WatchFunctionRequest"> & { export type WatchCapabilityRequest = Message<"quixos.orch.WatchCapabilityRequest"> & {
/** /**
* @generated from field: quixos.FunctionRef function = 1; * @generated from field: quixos.CapabilityRef capability = 1;
*/ */
function?: FunctionRef | undefined; capability?: CapabilityRef | undefined;
/** /**
* @generated from field: string object_id = 2; * @generated from field: string object_id = 2;
@@ -105,16 +206,16 @@ export type WatchFunctionRequest = Message<"quixos.orch.WatchFunctionRequest"> &
}; };
/** /**
* Describes the message quixos.orch.WatchFunctionRequest. * Describes the message quixos.orch.WatchCapabilityRequest.
* Use `create(WatchFunctionRequestSchema)` to create a new message. * Use `create(WatchCapabilityRequestSchema)` to create a new message.
*/ */
export const WatchFunctionRequestSchema: GenMessage<WatchFunctionRequest> = /*@__PURE__*/ export const WatchCapabilityRequestSchema: GenMessage<WatchCapabilityRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 2); messageDesc(file_quixos_orch, 6);
/** /**
* @generated from message quixos.orch.WatchFunctionEvent * @generated from message quixos.orch.WatchCapabilityEvent
*/ */
export type WatchFunctionEvent = Message<"quixos.orch.WatchFunctionEvent"> & { export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & {
/** /**
* @generated from field: string invocation_id = 1; * @generated from field: string invocation_id = 1;
*/ */
@@ -152,11 +253,51 @@ export type WatchFunctionEvent = Message<"quixos.orch.WatchFunctionEvent"> & {
}; };
/** /**
* Describes the message quixos.orch.WatchFunctionEvent. * Describes the message quixos.orch.WatchCapabilityEvent.
* Use `create(WatchFunctionEventSchema)` to create a new message. * Use `create(WatchCapabilityEventSchema)` to create a new message.
*/ */
export const WatchFunctionEventSchema: GenMessage<WatchFunctionEvent> = /*@__PURE__*/ export const WatchCapabilityEventSchema: GenMessage<WatchCapabilityEvent> = /*@__PURE__*/
messageDesc(file_quixos_orch, 3); messageDesc(file_quixos_orch, 7);
/**
* @generated from message quixos.orch.GetWorkspaceRequest
*/
export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & {
};
/**
* Describes the message quixos.orch.GetWorkspaceRequest.
* Use `create(GetWorkspaceRequestSchema)` to create a new message.
*/
export const GetWorkspaceRequestSchema: GenMessage<GetWorkspaceRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 8);
/**
* @generated from message quixos.orch.GetWorkspaceResponse
*/
export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & {
/**
* @generated from field: string workspace_id = 1;
*/
workspaceId: string;
/**
* @generated from field: string workspace_revision_id = 2;
*/
workspaceRevisionId: string;
/**
* @generated from field: string source_root_commit = 3;
*/
sourceRootCommit: string;
};
/**
* Describes the message quixos.orch.GetWorkspaceResponse.
* Use `create(GetWorkspaceResponseSchema)` to create a new message.
*/
export const GetWorkspaceResponseSchema: GenMessage<GetWorkspaceResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 9);
/** /**
* @generated from message quixos.orch.ListActivationsRequest * @generated from message quixos.orch.ListActivationsRequest
@@ -169,7 +310,7 @@ export type ListActivationsRequest = Message<"quixos.orch.ListActivationsRequest
* Use `create(ListActivationsRequestSchema)` to create a new message. * Use `create(ListActivationsRequestSchema)` to create a new message.
*/ */
export const ListActivationsRequestSchema: GenMessage<ListActivationsRequest> = /*@__PURE__*/ export const ListActivationsRequestSchema: GenMessage<ListActivationsRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 4); messageDesc(file_quixos_orch, 10);
/** /**
* @generated from message quixos.orch.ListPackageDescriptorsRequest * @generated from message quixos.orch.ListPackageDescriptorsRequest
@@ -182,7 +323,7 @@ export type ListPackageDescriptorsRequest = Message<"quixos.orch.ListPackageDesc
* Use `create(ListPackageDescriptorsRequestSchema)` to create a new message. * Use `create(ListPackageDescriptorsRequestSchema)` to create a new message.
*/ */
export const ListPackageDescriptorsRequestSchema: GenMessage<ListPackageDescriptorsRequest> = /*@__PURE__*/ export const ListPackageDescriptorsRequestSchema: GenMessage<ListPackageDescriptorsRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 5); messageDesc(file_quixos_orch, 11);
/** /**
* @generated from message quixos.orch.ListPackageDescriptorsResponse * @generated from message quixos.orch.ListPackageDescriptorsResponse
@@ -199,7 +340,7 @@ export type ListPackageDescriptorsResponse = Message<"quixos.orch.ListPackageDes
* Use `create(ListPackageDescriptorsResponseSchema)` to create a new message. * Use `create(ListPackageDescriptorsResponseSchema)` to create a new message.
*/ */
export const ListPackageDescriptorsResponseSchema: GenMessage<ListPackageDescriptorsResponse> = /*@__PURE__*/ export const ListPackageDescriptorsResponseSchema: GenMessage<ListPackageDescriptorsResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 6); messageDesc(file_quixos_orch, 12);
/** /**
* @generated from message quixos.orch.ListPackageRuntimesRequest * @generated from message quixos.orch.ListPackageRuntimesRequest
@@ -212,7 +353,7 @@ export type ListPackageRuntimesRequest = Message<"quixos.orch.ListPackageRuntime
* Use `create(ListPackageRuntimesRequestSchema)` to create a new message. * Use `create(ListPackageRuntimesRequestSchema)` to create a new message.
*/ */
export const ListPackageRuntimesRequestSchema: GenMessage<ListPackageRuntimesRequest> = /*@__PURE__*/ export const ListPackageRuntimesRequestSchema: GenMessage<ListPackageRuntimesRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 7); messageDesc(file_quixos_orch, 13);
/** /**
* @generated from message quixos.orch.ListPackageRuntimesResponse * @generated from message quixos.orch.ListPackageRuntimesResponse
@@ -229,7 +370,7 @@ export type ListPackageRuntimesResponse = Message<"quixos.orch.ListPackageRuntim
* Use `create(ListPackageRuntimesResponseSchema)` to create a new message. * Use `create(ListPackageRuntimesResponseSchema)` to create a new message.
*/ */
export const ListPackageRuntimesResponseSchema: GenMessage<ListPackageRuntimesResponse> = /*@__PURE__*/ export const ListPackageRuntimesResponseSchema: GenMessage<ListPackageRuntimesResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 8); messageDesc(file_quixos_orch, 14);
/** /**
* @generated from message quixos.orch.ListActivationsResponse * @generated from message quixos.orch.ListActivationsResponse
@@ -246,7 +387,7 @@ export type ListActivationsResponse = Message<"quixos.orch.ListActivationsRespon
* Use `create(ListActivationsResponseSchema)` to create a new message. * Use `create(ListActivationsResponseSchema)` to create a new message.
*/ */
export const ListActivationsResponseSchema: GenMessage<ListActivationsResponse> = /*@__PURE__*/ export const ListActivationsResponseSchema: GenMessage<ListActivationsResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 9); messageDesc(file_quixos_orch, 15);
/** /**
* @generated from message quixos.orch.CloseActivationRequest * @generated from message quixos.orch.CloseActivationRequest
@@ -268,7 +409,7 @@ export type CloseActivationRequest = Message<"quixos.orch.CloseActivationRequest
* Use `create(CloseActivationRequestSchema)` to create a new message. * Use `create(CloseActivationRequestSchema)` to create a new message.
*/ */
export const CloseActivationRequestSchema: GenMessage<CloseActivationRequest> = /*@__PURE__*/ export const CloseActivationRequestSchema: GenMessage<CloseActivationRequest> = /*@__PURE__*/
messageDesc(file_quixos_orch, 10); messageDesc(file_quixos_orch, 16);
/** /**
* @generated from message quixos.orch.CloseActivationResponse * @generated from message quixos.orch.CloseActivationResponse
@@ -285,7 +426,7 @@ export type CloseActivationResponse = Message<"quixos.orch.CloseActivationRespon
* Use `create(CloseActivationResponseSchema)` to create a new message. * Use `create(CloseActivationResponseSchema)` to create a new message.
*/ */
export const CloseActivationResponseSchema: GenMessage<CloseActivationResponse> = /*@__PURE__*/ export const CloseActivationResponseSchema: GenMessage<CloseActivationResponse> = /*@__PURE__*/
messageDesc(file_quixos_orch, 11); messageDesc(file_quixos_orch, 17);
/** /**
* @generated from message quixos.orch.Activation * @generated from message quixos.orch.Activation
@@ -297,9 +438,9 @@ export type Activation = Message<"quixos.orch.Activation"> & {
activationId: string; activationId: string;
/** /**
* @generated from field: quixos.FunctionRef function = 2; * @generated from field: quixos.PackageExportRef export = 2;
*/ */
function?: FunctionRef | undefined; export?: PackageExportRef | undefined;
/** /**
* @generated from field: string object_id = 3; * @generated from field: string object_id = 3;
@@ -347,7 +488,7 @@ export type Activation = Message<"quixos.orch.Activation"> & {
* Use `create(ActivationSchema)` to create a new message. * Use `create(ActivationSchema)` to create a new message.
*/ */
export const ActivationSchema: GenMessage<Activation> = /*@__PURE__*/ export const ActivationSchema: GenMessage<Activation> = /*@__PURE__*/
messageDesc(file_quixos_orch, 12); messageDesc(file_quixos_orch, 18);
/** /**
* @generated from message quixos.orch.PackageRuntimeStatus * @generated from message quixos.orch.PackageRuntimeStatus
@@ -359,69 +500,59 @@ export type PackageRuntimeStatus = Message<"quixos.orch.PackageRuntimeStatus"> &
runtimeKey: string; runtimeKey: string;
/** /**
* @generated from field: string package_namespace = 2; * @generated from field: string package_revision_id = 2;
*/ */
packageNamespace: string; packageRevisionId: string;
/** /**
* @generated from field: string package_name = 3; * @generated from field: string source_repository = 3;
*/ */
packageName: string; sourceRepository: string;
/** /**
* @generated from field: uint32 descriptor_version = 4; * @generated from field: string source_commit = 4;
*/ */
descriptorVersion: number; sourceCommit: string;
/** /**
* @generated from field: string source_repo = 5; * @generated from field: string build_target = 5;
*/ */
sourceRepo: string; buildTarget: string;
/** /**
* @generated from field: string server_installable = 6; * @generated from field: string server_path = 6;
*/
serverInstallable: string;
/**
* @generated from field: string source_path = 7;
*/
sourcePath: string;
/**
* @generated from field: string server_path = 8;
*/ */
serverPath: string; serverPath: string;
/** /**
* @generated from field: uint32 pid = 9; * @generated from field: uint32 pid = 7;
*/ */
pid: number; pid: number;
/** /**
* @generated from field: string state = 10; * @generated from field: string state = 8;
*/ */
state: string; state: string;
/** /**
* @generated from field: string started_at = 11; * @generated from field: string started_at = 9;
*/ */
startedAt: string; startedAt: string;
/** /**
* @generated from field: string last_handshake_at = 12; * @generated from field: string last_handshake_at = 10;
*/ */
lastHandshakeAt: string; lastHandshakeAt: string;
/** /**
* @generated from field: string runtime_protocol_version = 13; * @generated from field: string runtime_protocol_version = 11;
*/ */
runtimeProtocolVersion: string; runtimeProtocolVersion: string;
/** /**
* @generated from field: uint32 advertised_function_count = 14; * @generated from field: uint32 advertised_export_count = 12;
*/ */
advertisedFunctionCount: number; advertisedExportCount: number;
}; };
/** /**
@@ -429,27 +560,51 @@ export type PackageRuntimeStatus = Message<"quixos.orch.PackageRuntimeStatus"> &
* Use `create(PackageRuntimeStatusSchema)` to create a new message. * Use `create(PackageRuntimeStatusSchema)` to create a new message.
*/ */
export const PackageRuntimeStatusSchema: GenMessage<PackageRuntimeStatus> = /*@__PURE__*/ export const PackageRuntimeStatusSchema: GenMessage<PackageRuntimeStatus> = /*@__PURE__*/
messageDesc(file_quixos_orch, 13); messageDesc(file_quixos_orch, 19);
/** /**
* @generated from service quixos.orch.OrchestratorRuntime * @generated from service quixos.orch.OrchestratorRuntime
*/ */
export const OrchestratorRuntime: GenService<{ export const OrchestratorRuntime: GenService<{
/** /**
* @generated from rpc quixos.orch.OrchestratorRuntime.InvokeFunction * @generated from rpc quixos.orch.OrchestratorRuntime.InvokeCapability
*/ */
invokeFunction: { invokeCapability: {
methodKind: "unary"; methodKind: "unary";
input: typeof InvokeFunctionRequestSchema; input: typeof InvokeCapabilityRequestSchema;
output: typeof InvokeFunctionResponseSchema; output: typeof InvokeCapabilityResponseSchema;
}, },
/** /**
* @generated from rpc quixos.orch.OrchestratorRuntime.WatchFunction * @generated from rpc quixos.orch.OrchestratorRuntime.WatchCapability
*/ */
watchFunction: { watchCapability: {
methodKind: "server_streaming"; methodKind: "server_streaming";
input: typeof WatchFunctionRequestSchema; input: typeof WatchCapabilityRequestSchema;
output: typeof WatchFunctionEventSchema; output: typeof WatchCapabilityEventSchema;
},
/**
* @generated from rpc quixos.orch.OrchestratorRuntime.ConstructObject
*/
constructObject: {
methodKind: "unary";
input: typeof ConstructObjectRequestSchema;
output: typeof ConstructObjectResponseSchema;
},
/**
* @generated from rpc quixos.orch.OrchestratorRuntime.ResolveOrConstructRelatedObject
*/
resolveOrConstructRelatedObject: {
methodKind: "unary";
input: typeof ResolveOrConstructRelatedObjectRequestSchema;
output: typeof ResolveOrConstructRelatedObjectResponseSchema;
},
/**
* @generated from rpc quixos.orch.OrchestratorRuntime.GetWorkspace
*/
getWorkspace: {
methodKind: "unary";
input: typeof GetWorkspaceRequestSchema;
output: typeof GetWorkspaceResponseSchema;
}, },
/** /**
* @generated from rpc quixos.orch.OrchestratorRuntime.ListPackageDescriptors * @generated from rpc quixos.orch.OrchestratorRuntime.ListPackageDescriptors
+33 -283
View File
@@ -1,131 +1,40 @@
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js" // @generated by protoc-gen-es v2.14.1 with parameter "target=ts,import_extension=js"
// @generated from file quixos/package.proto (package quixos, syntax proto3) // @generated from file quixos/package.proto (package quixos, syntax proto3)
/* eslint-disable */ /* eslint-disable */
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
import type { SymbolRef } from "../camino/schema_pb.js";
import { file_camino_schema } from "../camino/schema_pb.js";
import type { FunctionRef } from "./refs_pb.js";
import { file_quixos_refs } from "./refs_pb.js";
import type { Message } from "@bufbuild/protobuf"; import type { Message } from "@bufbuild/protobuf";
/** /**
* Describes the file quixos/package.proto. * Describes the file quixos/package.proto.
*/ */
export const file_quixos_package: GenFile = /*@__PURE__*/ export const file_quixos_package: GenFile = /*@__PURE__*/
fileDesc("ChRxdWl4b3MvcGFja2FnZS5wcm90bxIGcXVpeG9zIm4KClBhY2thZ2VSZWYSGQoRcGFja2FnZV9uYW1lc3BhY2UYASABKAkSFAoMcGFja2FnZV9uYW1lGAIgASgJEhoKEmRlc2NyaXB0b3JfdmVyc2lvbhgDIAEoDRITCgt2ZXJzaW9uX3JlZhgEIAEoCSKgBAoRUGFja2FnZURlc2NyaXB0b3ISGQoRcGFja2FnZV9uYW1lc3BhY2UYASABKAkSFAoMcGFja2FnZV9uYW1lGAIgASgJEhoKEmRlc2NyaXB0b3JfdmVyc2lvbhgDIAEoDRIkChxpbnRlcmZhY2VfY29tcGF0aWJsZV9iYWNrX3RvGAQgASgNEiEKGXJ1bm5lcl9jb21wYXRpYmxlX2JhY2tfdG8YBSABKA0SEwoLc291cmNlX3JlcG8YBiABKAkSEgoKc291cmNlX3JlZhgHIAEoCRIbChNyZXNvbHZlZF9zb3VyY2VfcmVmGAggASgJEhoKEnNlcnZlcl9pbnN0YWxsYWJsZRgJIAEoCRIgChhydW50aW1lX3Byb3RvY29sX3ZlcnNpb24YCiABKAkSKgoNY2xhc3NfZXhwb3J0cxgLIAMoCzITLnF1aXhvcy5DbGFzc0V4cG9ydBIwChBmdW5jdGlvbl9leHBvcnRzGAwgAygLMhYucXVpeG9zLkZ1bmN0aW9uRXhwb3J0EjIKEWNvbXBvbmVudF9leHBvcnRzGA0gAygLMhcucXVpeG9zLkNvbXBvbmVudEV4cG9ydBIuCg9zaWRlY2FyX2V4cG9ydHMYDiADKAsyFS5xdWl4b3MuU2lkZWNhckV4cG9ydBIvCgxkZXBlbmRlbmNpZXMYDyADKAsyGS5xdWl4b3MuUGFja2FnZURlcGVuZGVuY3kiRwoLQ2xhc3NFeHBvcnQSIwoIY2xhc3NfaWQYASABKAsyES5jYW1pbm8uU3ltYm9sUmVmEhMKC3NvdXJjZV9maWxlGAIgASgJItoBCg5GdW5jdGlvbkV4cG9ydBIlCghmdW5jdGlvbhgBIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZhISCgppbnB1dF90eXBlGAIgASgJEhMKC291dHB1dF90eXBlGAMgASgJEhkKEWludGVyZmFjZV92ZXJzaW9uGAQgASgNEiQKHGludGVyZmFjZV9jb21wYXRpYmxlX2JhY2tfdG8YBSABKA0SNwoPY2FwYWJpbGl0eV9raW5kGAYgASgOMh4ucXVpeG9zLkZ1bmN0aW9uQ2FwYWJpbGl0eUtpbmQiYwoPQ29tcG9uZW50RXhwb3J0Eg4KBnN5bWJvbBgBIAEoCRISCgpwcm9wc190eXBlGAIgASgJEiwKEXN1cHBvcnRlZF9jbGFzc2VzGAMgAygLMhEuY2FtaW5vLlN5bWJvbFJlZiI5Cg1TaWRlY2FyRXhwb3J0Eg4KBnN5bWJvbBgBIAEoCRIYChBzaWRlX2VmZmVjdF9tb2RlGAIgASgJIpUBChFQYWNrYWdlRGVwZW5kZW5jeRIjCgdwYWNrYWdlGAEgASgLMhIucXVpeG9zLlBhY2thZ2VSZWYSJAocaW50ZXJmYWNlX2NvbXBhdGlibGVfYmFja190bxgCIAEoDRIZChFyZWZhY3Rvcl9yZXF1aXJlZBgDIAEoCBIaChJjb21wYXRpYmlsaXR5X25vdGUYBCABKAkqggEKFkZ1bmN0aW9uQ2FwYWJpbGl0eUtpbmQSKAokRlVOQ1RJT05fQ0FQQUJJTElUWV9LSU5EX1VOU1BFQ0lGSUVEEAASCgoGTUVUSE9EEAESEgoORklFTERfUkVTT0xWRVIQAhINCglNSUdSQVRJT04QAxIPCgtDT05TVFJVQ1RPUhAEYgZwcm90bzM", [file_camino_schema, file_quixos_refs]); fileDesc("ChRxdWl4b3MvcGFja2FnZS5wcm90bxIGcXVpeG9zIo4BChFQYWNrYWdlRGVzY3JpcHRvchISCgpwYWNrYWdlX2lkGAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAMgASgJEiYKB2V4cG9ydHMYBCADKAsyFS5xdWl4b3MuUnVudGltZUV4cG9ydCI6Cg1SdW50aW1lRXhwb3J0EhEKCWV4cG9ydF9pZBgBIAEoCRIWCg5ydW50aW1lX3N5bWJvbBgCIAEoCWIGcHJvdG8z");
/**
* @generated from message quixos.PackageRef
*/
export type PackageRef = Message<"quixos.PackageRef"> & {
/**
* @generated from field: string package_namespace = 1;
*/
packageNamespace: string;
/**
* @generated from field: string package_name = 2;
*/
packageName: string;
/**
* @generated from field: uint32 descriptor_version = 3;
*/
descriptorVersion: number;
/**
* @generated from field: string version_ref = 4;
*/
versionRef: string;
};
/**
* Describes the message quixos.PackageRef.
* Use `create(PackageRefSchema)` to create a new message.
*/
export const PackageRefSchema: GenMessage<PackageRef> = /*@__PURE__*/
messageDesc(file_quixos_package, 0);
/** /**
* @generated from message quixos.PackageDescriptor * @generated from message quixos.PackageDescriptor
*/ */
export type PackageDescriptor = Message<"quixos.PackageDescriptor"> & { export type PackageDescriptor = Message<"quixos.PackageDescriptor"> & {
/** /**
* @generated from field: string package_namespace = 1; * @generated from field: string package_id = 1;
*/ */
packageNamespace: string; packageId: string;
/** /**
* @generated from field: string package_name = 2; * @generated from field: string package_revision_id = 2;
*/ */
packageName: string; packageRevisionId: string;
/** /**
* @generated from field: uint32 descriptor_version = 3; * @generated from field: string runtime_protocol_version = 3;
*/
descriptorVersion: number;
/**
* @generated from field: uint32 interface_compatible_back_to = 4;
*/
interfaceCompatibleBackTo: number;
/**
* @generated from field: uint32 runner_compatible_back_to = 5;
*/
runnerCompatibleBackTo: number;
/**
* @generated from field: string source_repo = 6;
*/
sourceRepo: string;
/**
* @generated from field: string source_ref = 7;
*/
sourceRef: string;
/**
* @generated from field: string resolved_source_ref = 8;
*/
resolvedSourceRef: string;
/**
* @generated from field: string server_installable = 9;
*/
serverInstallable: string;
/**
* @generated from field: string runtime_protocol_version = 10;
*/ */
runtimeProtocolVersion: string; runtimeProtocolVersion: string;
/** /**
* @generated from field: repeated quixos.ClassExport class_exports = 11; * @generated from field: repeated quixos.RuntimeExport exports = 4;
*/ */
classExports: ClassExport[]; exports: RuntimeExport[];
/**
* @generated from field: repeated quixos.FunctionExport function_exports = 12;
*/
functionExports: FunctionExport[];
/**
* @generated from field: repeated quixos.ComponentExport component_exports = 13;
*/
componentExports: ComponentExport[];
/**
* @generated from field: repeated quixos.SidecarExport sidecar_exports = 14;
*/
sidecarExports: SidecarExport[];
/**
* @generated from field: repeated quixos.PackageDependency dependencies = 15;
*/
dependencies: PackageDependency[];
}; };
/** /**
@@ -133,186 +42,27 @@ export type PackageDescriptor = Message<"quixos.PackageDescriptor"> & {
* Use `create(PackageDescriptorSchema)` to create a new message. * Use `create(PackageDescriptorSchema)` to create a new message.
*/ */
export const PackageDescriptorSchema: GenMessage<PackageDescriptor> = /*@__PURE__*/ export const PackageDescriptorSchema: GenMessage<PackageDescriptor> = /*@__PURE__*/
messageDesc(file_quixos_package, 0);
/**
* @generated from message quixos.RuntimeExport
*/
export type RuntimeExport = Message<"quixos.RuntimeExport"> & {
/**
* @generated from field: string export_id = 1;
*/
exportId: string;
/**
* @generated from field: string runtime_symbol = 2;
*/
runtimeSymbol: string;
};
/**
* Describes the message quixos.RuntimeExport.
* Use `create(RuntimeExportSchema)` to create a new message.
*/
export const RuntimeExportSchema: GenMessage<RuntimeExport> = /*@__PURE__*/
messageDesc(file_quixos_package, 1); messageDesc(file_quixos_package, 1);
/**
* @generated from message quixos.ClassExport
*/
export type ClassExport = Message<"quixos.ClassExport"> & {
/**
* @generated from field: camino.SymbolRef class_id = 1;
*/
classId?: SymbolRef | undefined;
/**
* @generated from field: string source_file = 2;
*/
sourceFile: string;
};
/**
* Describes the message quixos.ClassExport.
* Use `create(ClassExportSchema)` to create a new message.
*/
export const ClassExportSchema: GenMessage<ClassExport> = /*@__PURE__*/
messageDesc(file_quixos_package, 2);
/**
* @generated from message quixos.FunctionExport
*/
export type FunctionExport = Message<"quixos.FunctionExport"> & {
/**
* @generated from field: quixos.FunctionRef function = 1;
*/
function?: FunctionRef | undefined;
/**
* @generated from field: string input_type = 2;
*/
inputType: string;
/**
* @generated from field: string output_type = 3;
*/
outputType: string;
/**
* @generated from field: uint32 interface_version = 4;
*/
interfaceVersion: number;
/**
* @generated from field: uint32 interface_compatible_back_to = 5;
*/
interfaceCompatibleBackTo: number;
/**
* @generated from field: quixos.FunctionCapabilityKind capability_kind = 6;
*/
capabilityKind: FunctionCapabilityKind;
};
/**
* Describes the message quixos.FunctionExport.
* Use `create(FunctionExportSchema)` to create a new message.
*/
export const FunctionExportSchema: GenMessage<FunctionExport> = /*@__PURE__*/
messageDesc(file_quixos_package, 3);
/**
* @generated from message quixos.ComponentExport
*/
export type ComponentExport = Message<"quixos.ComponentExport"> & {
/**
* @generated from field: string symbol = 1;
*/
symbol: string;
/**
* @generated from field: string props_type = 2;
*/
propsType: string;
/**
* @generated from field: repeated camino.SymbolRef supported_classes = 3;
*/
supportedClasses: SymbolRef[];
};
/**
* Describes the message quixos.ComponentExport.
* Use `create(ComponentExportSchema)` to create a new message.
*/
export const ComponentExportSchema: GenMessage<ComponentExport> = /*@__PURE__*/
messageDesc(file_quixos_package, 4);
/**
* @generated from message quixos.SidecarExport
*/
export type SidecarExport = Message<"quixos.SidecarExport"> & {
/**
* @generated from field: string symbol = 1;
*/
symbol: string;
/**
* @generated from field: string side_effect_mode = 2;
*/
sideEffectMode: string;
};
/**
* Describes the message quixos.SidecarExport.
* Use `create(SidecarExportSchema)` to create a new message.
*/
export const SidecarExportSchema: GenMessage<SidecarExport> = /*@__PURE__*/
messageDesc(file_quixos_package, 5);
/**
* @generated from message quixos.PackageDependency
*/
export type PackageDependency = Message<"quixos.PackageDependency"> & {
/**
* @generated from field: quixos.PackageRef package = 1;
*/
package?: PackageRef | undefined;
/**
* @generated from field: uint32 interface_compatible_back_to = 2;
*/
interfaceCompatibleBackTo: number;
/**
* @generated from field: bool refactor_required = 3;
*/
refactorRequired: boolean;
/**
* @generated from field: string compatibility_note = 4;
*/
compatibilityNote: string;
};
/**
* Describes the message quixos.PackageDependency.
* Use `create(PackageDependencySchema)` to create a new message.
*/
export const PackageDependencySchema: GenMessage<PackageDependency> = /*@__PURE__*/
messageDesc(file_quixos_package, 6);
/**
* @generated from enum quixos.FunctionCapabilityKind
*/
export enum FunctionCapabilityKind {
/**
* @generated from enum value: FUNCTION_CAPABILITY_KIND_UNSPECIFIED = 0;
*/
FUNCTION_CAPABILITY_KIND_UNSPECIFIED = 0,
/**
* @generated from enum value: METHOD = 1;
*/
METHOD = 1,
/**
* @generated from enum value: FIELD_RESOLVER = 2;
*/
FIELD_RESOLVER = 2,
/**
* @generated from enum value: MIGRATION = 3;
*/
MIGRATION = 3,
/**
* @generated from enum value: CONSTRUCTOR = 4;
*/
CONSTRUCTOR = 4,
}
/**
* Describes the enum quixos.FunctionCapabilityKind.
*/
export const FunctionCapabilityKindSchema: GenEnum<FunctionCapabilityKind> = /*@__PURE__*/
enumDesc(file_quixos_package, 0);
+109 -26
View File
@@ -1,4 +1,4 @@
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js" // @generated by protoc-gen-es v2.14.1 with parameter "target=ts,import_extension=js"
// @generated from file quixos/refs.proto (package quixos, syntax proto3) // @generated from file quixos/refs.proto (package quixos, syntax proto3)
/* eslint-disable */ /* eslint-disable */
@@ -10,42 +10,125 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file quixos/refs.proto. * Describes the file quixos/refs.proto.
*/ */
export const file_quixos_refs: GenFile = /*@__PURE__*/ export const file_quixos_refs: GenFile = /*@__PURE__*/
fileDesc("ChFxdWl4b3MvcmVmcy5wcm90bxIGcXVpeG9zInYKC0Z1bmN0aW9uUmVmEhkKEXBhY2thZ2VfbmFtZXNwYWNlGAEgASgJEhQKDHBhY2thZ2VfbmFtZRgCIAEoCRIOCgZzeW1ib2wYAyABKAkSEwoLdmVyc2lvbl9yZWYYBCABKAkSEQoJb3BlcmF0aW9uGAUgASgJYgZwcm90bzM"); fileDesc("ChFxdWl4b3MvcmVmcy5wcm90bxIGcXVpeG9zIkQKDUNhcGFiaWxpdHlSZWYSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAEgASgJEhQKDG9wZXJhdGlvbl9pZBgCIAEoCSJCChBQYWNrYWdlRXhwb3J0UmVmEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYASABKAkSEQoJZXhwb3J0X2lkGAIgASgJIsQBChJJbmplY3RlZERlcGVuZGVuY3kSDwoHcG9ydF9pZBgBIAEoCRIXCg1zdGF0ZV9zbG90X2lkGAIgASgJSAASJgoEZWRnZRgDIAEoCzIWLnF1aXhvcy5FZGdlRGVwZW5kZW5jeUgAEh8KFWludGVyZmFjZV9yZXZpc2lvbl9pZBgEIAEoCUgAEh0KE2NvbnN0cnVjdG9yX2F0b21faWQYBSABKAlIABIRCglvYmplY3RfaWQYBiABKAlCCQoHYmluZGluZyI9Cg5FZGdlRGVwZW5kZW5jeRIUCgxlZGdlX3R5cGVfaWQYASABKAkSFQoNcHJvamVjdGlvbl9pZBgCIAEoCWIGcHJvdG8z");
/** /**
* @generated from message quixos.FunctionRef * @generated from message quixos.CapabilityRef
*/ */
export type FunctionRef = Message<"quixos.FunctionRef"> & { export type CapabilityRef = Message<"quixos.CapabilityRef"> & {
/** /**
* @generated from field: string package_namespace = 1; * @generated from field: string interface_revision_id = 1;
*/ */
packageNamespace: string; interfaceRevisionId: string;
/** /**
* @generated from field: string package_name = 2; * @generated from field: string operation_id = 2;
*/ */
packageName: string; operationId: string;
/**
* @generated from field: string symbol = 3;
*/
symbol: string;
/**
* @generated from field: string version_ref = 4;
*/
versionRef: string;
/**
* @generated from field: string operation = 5;
*/
operation: string;
}; };
/** /**
* Describes the message quixos.FunctionRef. * Describes the message quixos.CapabilityRef.
* Use `create(FunctionRefSchema)` to create a new message. * Use `create(CapabilityRefSchema)` to create a new message.
*/ */
export const FunctionRefSchema: GenMessage<FunctionRef> = /*@__PURE__*/ export const CapabilityRefSchema: GenMessage<CapabilityRef> = /*@__PURE__*/
messageDesc(file_quixos_refs, 0); messageDesc(file_quixos_refs, 0);
/**
* @generated from message quixos.PackageExportRef
*/
export type PackageExportRef = Message<"quixos.PackageExportRef"> & {
/**
* @generated from field: string package_revision_id = 1;
*/
packageRevisionId: string;
/**
* @generated from field: string export_id = 2;
*/
exportId: string;
};
/**
* Describes the message quixos.PackageExportRef.
* Use `create(PackageExportRefSchema)` to create a new message.
*/
export const PackageExportRefSchema: GenMessage<PackageExportRef> = /*@__PURE__*/
messageDesc(file_quixos_refs, 1);
/**
* @generated from message quixos.InjectedDependency
*/
export type InjectedDependency = Message<"quixos.InjectedDependency"> & {
/**
* @generated from field: string port_id = 1;
*/
portId: string;
/**
* @generated from oneof quixos.InjectedDependency.binding
*/
binding: {
/**
* @generated from field: string state_slot_id = 2;
*/
value: string;
case: "stateSlotId";
} | {
/**
* @generated from field: quixos.EdgeDependency edge = 3;
*/
value: EdgeDependency;
case: "edge";
} | {
/**
* @generated from field: string interface_revision_id = 4;
*/
value: string;
case: "interfaceRevisionId";
} | {
/**
* @generated from field: string constructor_atom_id = 5;
*/
value: string;
case: "constructorAtomId";
} | { case: undefined; value?: undefined };
/**
* Defaults to the invocation receiver. A checked dependency traversal can
* select a related object explicitly before the package runtime starts.
*
* @generated from field: string object_id = 6;
*/
objectId: string;
};
/**
* Describes the message quixos.InjectedDependency.
* Use `create(InjectedDependencySchema)` to create a new message.
*/
export const InjectedDependencySchema: GenMessage<InjectedDependency> = /*@__PURE__*/
messageDesc(file_quixos_refs, 2);
/**
* @generated from message quixos.EdgeDependency
*/
export type EdgeDependency = Message<"quixos.EdgeDependency"> & {
/**
* @generated from field: string edge_type_id = 1;
*/
edgeTypeId: string;
/**
* @generated from field: string projection_id = 2;
*/
projectionId: string;
};
/**
* Describes the message quixos.EdgeDependency.
* Use `create(EdgeDependencySchema)` to create a new message.
*/
export const EdgeDependencySchema: GenMessage<EdgeDependency> = /*@__PURE__*/
messageDesc(file_quixos_refs, 3);
+167 -26
View File
@@ -1,4 +1,4 @@
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js" // @generated by protoc-gen-es v2.14.1 with parameter "target=ts,import_extension=js"
// @generated from file quixos/runtime.proto (package quixos.runtime, syntax proto3) // @generated from file quixos/runtime.proto (package quixos.runtime, syntax proto3)
/* eslint-disable */ /* eslint-disable */
@@ -6,7 +6,7 @@ import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegen
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
import type { Value } from "../camino/api_pb.js"; import type { Value } from "../camino/api_pb.js";
import { file_camino_api } from "../camino/api_pb.js"; import { file_camino_api } from "../camino/api_pb.js";
import type { FunctionRef } from "./refs_pb.js"; import type { InjectedDependency, PackageExportRef } from "./refs_pb.js";
import { file_quixos_refs } from "./refs_pb.js"; import { file_quixos_refs } from "./refs_pb.js";
import type { Message } from "@bufbuild/protobuf"; import type { Message } from "@bufbuild/protobuf";
@@ -14,7 +14,7 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file quixos/runtime.proto. * Describes the file quixos/runtime.proto.
*/ */
export const file_quixos_runtime: GenFile = /*@__PURE__*/ export const file_quixos_runtime: GenFile = /*@__PURE__*/
fileDesc("ChRxdWl4b3MvcnVudGltZS5wcm90bxIOcXVpeG9zLnJ1bnRpbWUiMQoQSGFuZHNoYWtlUmVxdWVzdBIdChVvcmNoX3Byb3RvY29sX3ZlcnNpb24YASABKAkijgEKEUhhbmRzaGFrZVJlc3BvbnNlEhkKEXBhY2thZ2VfbmFtZXNwYWNlGAEgASgJEhQKDHBhY2thZ2VfbmFtZRgCIAEoCRIgChhydW50aW1lX3Byb3RvY29sX3ZlcnNpb24YAyABKAkSJgoJZnVuY3Rpb25zGAQgAygLMhMucXVpeG9zLkZ1bmN0aW9uUmVmItYBCg1JbnZva2VSZXF1ZXN0EhUKDWludm9jYXRpb25faWQYASABKAkSJQoIZnVuY3Rpb24YAiABKAsyEy5xdWl4b3MuRnVuY3Rpb25SZWYSEQoJb2JqZWN0X2lkGAMgASgJEjcKBWlucHV0GAQgAygLMigucXVpeG9zLnJ1bnRpbWUuSW52b2tlUmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJKCg5JbnZva2VSZXNwb25zZRIKCgJvaxgBIAEoCBIdCgZyZXN1bHQYAiABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYAyABKAki1AEKDFdhdGNoUmVxdWVzdBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEiUKCGZ1bmN0aW9uGAIgASgLMhMucXVpeG9zLkZ1bmN0aW9uUmVmEhEKCW9iamVjdF9pZBgDIAEoCRI2CgVpbnB1dBgEIAMoCzInLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJeChFEZXJpdmVkRGVwZW5kZW5jeRIMCgRraW5kGAEgASgJEhEKCW9iamVjdF9pZBgCIAEoCRISCgpmaWVsZF9uYW1lGAMgASgJEhQKDHNvdXJjZV9maWVsZBgEIAEoCSKVAQoKV2F0Y2hFdmVudBIQCgh3YXRjaF9pZBgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYAyADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgEIAEoCRIPCgdpbml0aWFsGAUgASgIMvABCg5QYWNrYWdlUnVudGltZRJQCglIYW5kc2hha2USIC5xdWl4b3MucnVudGltZS5IYW5kc2hha2VSZXF1ZXN0GiEucXVpeG9zLnJ1bnRpbWUuSGFuZHNoYWtlUmVzcG9uc2USRwoGSW52b2tlEh0ucXVpeG9zLnJ1bnRpbWUuSW52b2tlUmVxdWVzdBoeLnF1aXhvcy5ydW50aW1lLkludm9rZVJlc3BvbnNlEkMKBVdhdGNoEhwucXVpeG9zLnJ1bnRpbWUuV2F0Y2hSZXF1ZXN0GhoucXVpeG9zLnJ1bnRpbWUuV2F0Y2hFdmVudDABYgZwcm90bzM", [file_camino_api, file_quixos_refs]); fileDesc("ChRxdWl4b3MvcnVudGltZS5wcm90bxIOcXVpeG9zLnJ1bnRpbWUiQAoQSGFuZHNoYWtlUmVxdWVzdBIdChVvcmNoX3Byb3RvY29sX3ZlcnNpb24YASABKAkSDQoFbm9uY2UYAiABKAkirwEKEUhhbmRzaGFrZVJlc3BvbnNlEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYASABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAIgASgJEhIKCmV4cG9ydF9pZHMYAyADKAkSEwoLaW5zdGFuY2VfaWQYBCABKAkSHAoUYXV0aGVudGljYXRpb25fcHJvb2YYBSABKAkSFAoMY2FwYWJpbGl0aWVzGAYgAygJIpoBChFJbnZvY2F0aW9uQ29udGV4dBIXCg93b3Jrc3BhY2VfZXBvY2gYASABKAkSEwoLaW5zdGFuY2VfaWQYAiABKAkSFgoOYmluZGluZ19kaWdlc3QYAyABKAkSDQoFZ3JhbnQYBCABKAkSEgoKc2Vzc2lvbl9pZBgFIAEoCRIcChRvd25lcl9jb25mb3JtYW5jZV9pZBgGIAEoCSIxChhJbnZvY2F0aW9uQ29udHJvbFJlcXVlc3QSFQoNaW52b2NhdGlvbl9pZBgBIAEoCSI4ChBJbnZvY2F0aW9uU3RhdHVzEhUKDWludm9jYXRpb25faWQYASABKAkSDQoFc3RhdGUYAiABKAkivwIKDUludm9rZVJlcXVlc3QSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSNwoFaW5wdXQYBCADKAsyKC5xdWl4b3MucnVudGltZS5JbnZva2VSZXF1ZXN0LklucHV0RW50cnkSMAoMZGVwZW5kZW5jaWVzGAUgAygLMhoucXVpeG9zLkluamVjdGVkRGVwZW5kZW5jeRIyCgdjb250ZXh0GAYgASgLMiEucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRleHQaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIoMBCg5JbnZva2VSZXNwb25zZRIKCgJvaxgBIAEoCBIdCgZyZXN1bHQYAiABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYAyABKAkSNwoMZGVwZW5kZW5jaWVzGAQgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kivQIKDFdhdGNoUmVxdWVzdBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRI2CgVpbnB1dBgEIAMoCzInLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdC5JbnB1dEVudHJ5EjAKDGRlcGVuZGVuY2llcxgFIAMoCzIaLnF1aXhvcy5JbmplY3RlZERlcGVuZGVuY3kSMgoHY29udGV4dBgGIAEoCzIhLnF1aXhvcy5ydW50aW1lLkludm9jYXRpb25Db250ZXh0GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJiChFEZXJpdmVkRGVwZW5kZW5jeRIMCgRraW5kGAEgASgJEhEKCW9iamVjdF9pZBgCIAEoCRIVCg1hdHRhY2htZW50X2lkGAMgASgJEhUKDXByb2plY3Rpb25faWQYBCABKAkilQEKCldhdGNoRXZlbnQSEAoId2F0Y2hfaWQYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWUSNwoMZGVwZW5kZW5jaWVzGAMgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSDQoFZXJyb3IYBCABKAkSDwoHaW5pdGlhbBgFIAEoCDKzAwoOUGFja2FnZVJ1bnRpbWUSUAoJSGFuZHNoYWtlEiAucXVpeG9zLnJ1bnRpbWUuSGFuZHNoYWtlUmVxdWVzdBohLnF1aXhvcy5ydW50aW1lLkhhbmRzaGFrZVJlc3BvbnNlEkcKBkludm9rZRIdLnF1aXhvcy5ydW50aW1lLkludm9rZVJlcXVlc3QaHi5xdWl4b3MucnVudGltZS5JbnZva2VSZXNwb25zZRJDCgVXYXRjaBIcLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdBoaLnF1aXhvcy5ydW50aW1lLldhdGNoRXZlbnQwARJhChNHZXRJbnZvY2F0aW9uU3RhdHVzEigucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRyb2xSZXF1ZXN0GiAucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvblN0YXR1cxJeChBDYW5jZWxJbnZvY2F0aW9uEigucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvbkNvbnRyb2xSZXF1ZXN0GiAucXVpeG9zLnJ1bnRpbWUuSW52b2NhdGlvblN0YXR1c2IGcHJvdG8z", [file_camino_api, file_quixos_refs]);
/** /**
* @generated from message quixos.runtime.HandshakeRequest * @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; * @generated from field: string orch_protocol_version = 1;
*/ */
orchProtocolVersion: string; orchProtocolVersion: string;
/**
* @generated from field: string nonce = 2;
*/
nonce: string;
}; };
/** /**
@@ -38,24 +43,34 @@ export const HandshakeRequestSchema: GenMessage<HandshakeRequest> = /*@__PURE__*
*/ */
export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & { export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & {
/** /**
* @generated from field: string package_namespace = 1; * @generated from field: string package_revision_id = 1;
*/ */
packageNamespace: string; packageRevisionId: string;
/** /**
* @generated from field: string package_name = 2; * @generated from field: string runtime_protocol_version = 2;
*/
packageName: string;
/**
* @generated from field: string runtime_protocol_version = 3;
*/ */
runtimeProtocolVersion: string; runtimeProtocolVersion: string;
/** /**
* @generated from field: repeated quixos.FunctionRef functions = 4; * @generated from field: repeated string export_ids = 3;
*/ */
functions: FunctionRef[]; 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[];
}; };
/** /**
@@ -65,6 +80,91 @@ export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & {
export const HandshakeResponseSchema: GenMessage<HandshakeResponse> = /*@__PURE__*/ export const HandshakeResponseSchema: GenMessage<HandshakeResponse> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 1); 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 * @generated from message quixos.runtime.InvokeRequest
*/ */
@@ -75,9 +175,9 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
invocationId: string; invocationId: string;
/** /**
* @generated from field: quixos.FunctionRef function = 2; * @generated from field: quixos.PackageExportRef export = 2;
*/ */
function?: FunctionRef | undefined; export?: PackageExportRef | undefined;
/** /**
* @generated from field: string object_id = 3; * @generated from field: string object_id = 3;
@@ -88,6 +188,16 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
* @generated from field: map<string, camino.Value> input = 4; * @generated from field: map<string, camino.Value> input = 4;
*/ */
input: { [key: string]: Value }; input: { [key: string]: Value };
/**
* @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. * Use `create(InvokeRequestSchema)` to create a new message.
*/ */
export const InvokeRequestSchema: GenMessage<InvokeRequest> = /*@__PURE__*/ export const InvokeRequestSchema: GenMessage<InvokeRequest> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 2); messageDesc(file_quixos_runtime, 5);
/** /**
* @generated from message quixos.runtime.InvokeResponse * @generated from message quixos.runtime.InvokeResponse
@@ -115,6 +225,11 @@ export type InvokeResponse = Message<"quixos.runtime.InvokeResponse"> & {
* @generated from field: string error = 3; * @generated from field: string error = 3;
*/ */
error: string; error: string;
/**
* @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 4;
*/
dependencies: DerivedDependency[];
}; };
/** /**
@@ -122,7 +237,7 @@ export type InvokeResponse = Message<"quixos.runtime.InvokeResponse"> & {
* Use `create(InvokeResponseSchema)` to create a new message. * Use `create(InvokeResponseSchema)` to create a new message.
*/ */
export const InvokeResponseSchema: GenMessage<InvokeResponse> = /*@__PURE__*/ export const InvokeResponseSchema: GenMessage<InvokeResponse> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 3); messageDesc(file_quixos_runtime, 6);
/** /**
* @generated from message quixos.runtime.WatchRequest * @generated from message quixos.runtime.WatchRequest
@@ -134,9 +249,9 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
invocationId: string; invocationId: string;
/** /**
* @generated from field: quixos.FunctionRef function = 2; * @generated from field: quixos.PackageExportRef export = 2;
*/ */
function?: FunctionRef | undefined; export?: PackageExportRef | undefined;
/** /**
* @generated from field: string object_id = 3; * @generated from field: string object_id = 3;
@@ -147,6 +262,16 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
* @generated from field: map<string, camino.Value> input = 4; * @generated from field: map<string, camino.Value> input = 4;
*/ */
input: { [key: string]: Value }; input: { [key: string]: Value };
/**
* @generated from field: repeated quixos.InjectedDependency dependencies = 5;
*/
dependencies: InjectedDependency[];
/**
* @generated from field: quixos.runtime.InvocationContext context = 6;
*/
context?: InvocationContext | undefined;
}; };
/** /**
@@ -154,7 +279,7 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
* Use `create(WatchRequestSchema)` to create a new message. * Use `create(WatchRequestSchema)` to create a new message.
*/ */
export const WatchRequestSchema: GenMessage<WatchRequest> = /*@__PURE__*/ export const WatchRequestSchema: GenMessage<WatchRequest> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 4); messageDesc(file_quixos_runtime, 7);
/** /**
* @generated from message quixos.runtime.DerivedDependency * @generated from message quixos.runtime.DerivedDependency
@@ -171,14 +296,14 @@ export type DerivedDependency = Message<"quixos.runtime.DerivedDependency"> & {
objectId: string; objectId: string;
/** /**
* @generated from field: string field_name = 3; * @generated from field: string attachment_id = 3;
*/ */
fieldName: string; attachmentId: string;
/** /**
* @generated from field: string source_field = 4; * @generated from field: string projection_id = 4;
*/ */
sourceField: string; projectionId: string;
}; };
/** /**
@@ -186,7 +311,7 @@ export type DerivedDependency = Message<"quixos.runtime.DerivedDependency"> & {
* Use `create(DerivedDependencySchema)` to create a new message. * Use `create(DerivedDependencySchema)` to create a new message.
*/ */
export const DerivedDependencySchema: GenMessage<DerivedDependency> = /*@__PURE__*/ export const DerivedDependencySchema: GenMessage<DerivedDependency> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 5); messageDesc(file_quixos_runtime, 8);
/** /**
* @generated from message quixos.runtime.WatchEvent * @generated from message quixos.runtime.WatchEvent
@@ -223,7 +348,7 @@ export type WatchEvent = Message<"quixos.runtime.WatchEvent"> & {
* Use `create(WatchEventSchema)` to create a new message. * Use `create(WatchEventSchema)` to create a new message.
*/ */
export const WatchEventSchema: GenMessage<WatchEvent> = /*@__PURE__*/ export const WatchEventSchema: GenMessage<WatchEvent> = /*@__PURE__*/
messageDesc(file_quixos_runtime, 6); messageDesc(file_quixos_runtime, 9);
/** /**
* @generated from service quixos.runtime.PackageRuntime * @generated from service quixos.runtime.PackageRuntime
@@ -253,6 +378,22 @@ export const PackageRuntime: GenService<{
input: typeof WatchRequestSchema; input: typeof WatchRequestSchema;
output: typeof WatchEventSchema; 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__*/ }> = /*@__PURE__*/
serviceDesc(file_quixos_runtime, 0); serviceDesc(file_quixos_runtime, 0);
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env node
import { loadQuixosLock } from "./loader.js";
const fileName = process.argv[2];
if (!fileName || process.argv.length !== 3) {
console.error("Usage: quixos-lock-check <quixos.lock>");
process.exit(2);
}
const result = await loadQuixosLock(fileName);
if (!result.ok) {
for (const diagnostic of result.diagnostics) {
console.error(
`${diagnostic.fileName}:${diagnostic.line}:${diagnostic.column + 1}: ${diagnostic.phase}/${diagnostic.code}: ${diagnostic.message}`,
);
}
process.exit(1);
}
console.log(JSON.stringify(result.lock, null, 2));
@@ -0,0 +1,69 @@
token literal names:
null
'quixos-lock'
'fragment'
'version'
'quixos'
'source'
'interface'
'package'
'repository'
'commit'
'policy'
'ref'
'pinned'
'track-release'
'track-development'
'import'
'{'
'}'
';'
null
null
null
null
null
null
token symbolic names:
null
QUIXOS_LOCK
FRAGMENT
VERSION
QUIXOS
SOURCE
INTERFACE
PACKAGE
REPOSITORY
COMMIT
POLICY
REF
PINNED
TRACK_RELEASE
TRACK_DEVELOPMENT
IMPORT
LBRACE
RBRACE
SEMI
INTEGER
IDENTIFIER
STRING_LITERAL
LINE_COMMENT
BLOCK_COMMENT
WS
rule names:
document
quixosEntry
resourceEntry
importEntry
resourceKind
sourceBlock
quixosSourceBlock
sourcePolicy
identifier
stringLiteral
atn:
[4, 1, 24, 87, 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, 1, 0, 1, 0, 3, 0, 23, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 5, 0, 31, 8, 0, 10, 0, 12, 0, 34, 9, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 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, 3, 6, 74, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 0, 0, 10, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 0, 2, 1, 0, 6, 7, 1, 0, 12, 14, 81, 0, 20, 1, 0, 0, 0, 2, 38, 1, 0, 0, 0, 4, 42, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 51, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 62, 1, 0, 0, 0, 14, 80, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 84, 1, 0, 0, 0, 20, 22, 5, 1, 0, 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 25, 5, 3, 0, 0, 25, 26, 5, 19, 0, 0, 26, 32, 5, 16, 0, 0, 27, 31, 3, 2, 1, 0, 28, 31, 3, 6, 3, 0, 29, 31, 3, 4, 2, 0, 30, 27, 1, 0, 0, 0, 30, 28, 1, 0, 0, 0, 30, 29, 1, 0, 0, 0, 31, 34, 1, 0, 0, 0, 32, 30, 1, 0, 0, 0, 32, 33, 1, 0, 0, 0, 33, 35, 1, 0, 0, 0, 34, 32, 1, 0, 0, 0, 35, 36, 5, 17, 0, 0, 36, 37, 5, 0, 0, 1, 37, 1, 1, 0, 0, 0, 38, 39, 5, 4, 0, 0, 39, 40, 5, 5, 0, 0, 40, 41, 3, 12, 6, 0, 41, 3, 1, 0, 0, 0, 42, 43, 3, 8, 4, 0, 43, 44, 3, 16, 8, 0, 44, 45, 5, 5, 0, 0, 45, 46, 3, 10, 5, 0, 46, 5, 1, 0, 0, 0, 47, 48, 5, 15, 0, 0, 48, 49, 3, 18, 9, 0, 49, 50, 5, 18, 0, 0, 50, 7, 1, 0, 0, 0, 51, 52, 7, 0, 0, 0, 52, 9, 1, 0, 0, 0, 53, 54, 5, 16, 0, 0, 54, 55, 5, 8, 0, 0, 55, 56, 3, 18, 9, 0, 56, 57, 5, 18, 0, 0, 57, 58, 5, 9, 0, 0, 58, 59, 3, 18, 9, 0, 59, 60, 5, 18, 0, 0, 60, 61, 5, 17, 0, 0, 61, 11, 1, 0, 0, 0, 62, 63, 5, 16, 0, 0, 63, 64, 5, 8, 0, 0, 64, 65, 3, 18, 9, 0, 65, 73, 5, 18, 0, 0, 66, 67, 5, 10, 0, 0, 67, 68, 3, 14, 7, 0, 68, 69, 5, 18, 0, 0, 69, 70, 5, 11, 0, 0, 70, 71, 3, 18, 9, 0, 71, 72, 5, 18, 0, 0, 72, 74, 1, 0, 0, 0, 73, 66, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 76, 5, 9, 0, 0, 76, 77, 3, 18, 9, 0, 77, 78, 5, 18, 0, 0, 78, 79, 5, 17, 0, 0, 79, 13, 1, 0, 0, 0, 80, 81, 7, 1, 0, 0, 81, 15, 1, 0, 0, 0, 82, 83, 5, 20, 0, 0, 83, 17, 1, 0, 0, 0, 84, 85, 5, 21, 0, 0, 85, 19, 1, 0, 0, 0, 4, 22, 30, 32, 73]
@@ -0,0 +1,42 @@
QUIXOS_LOCK=1
FRAGMENT=2
VERSION=3
QUIXOS=4
SOURCE=5
INTERFACE=6
PACKAGE=7
REPOSITORY=8
COMMIT=9
POLICY=10
REF=11
PINNED=12
TRACK_RELEASE=13
TRACK_DEVELOPMENT=14
IMPORT=15
LBRACE=16
RBRACE=17
SEMI=18
INTEGER=19
IDENTIFIER=20
STRING_LITERAL=21
LINE_COMMENT=22
BLOCK_COMMENT=23
WS=24
'quixos-lock'=1
'fragment'=2
'version'=3
'quixos'=4
'source'=5
'interface'=6
'package'=7
'repository'=8
'commit'=9
'policy'=10
'ref'=11
'pinned'=12
'track-release'=13
'track-development'=14
'import'=15
'{'=16
'}'=17
';'=18
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
QUIXOS_LOCK=1
FRAGMENT=2
VERSION=3
QUIXOS=4
SOURCE=5
INTERFACE=6
PACKAGE=7
REPOSITORY=8
COMMIT=9
POLICY=10
REF=11
PINNED=12
TRACK_RELEASE=13
TRACK_DEVELOPMENT=14
IMPORT=15
LBRACE=16
RBRACE=17
SEMI=18
INTEGER=19
IDENTIFIER=20
STRING_LITERAL=21
LINE_COMMENT=22
BLOCK_COMMENT=23
WS=24
'quixos-lock'=1
'fragment'=2
'version'=3
'quixos'=4
'source'=5
'interface'=6
'package'=7
'repository'=8
'commit'=9
'policy'=10
'ref'=11
'pinned'=12
'track-release'=13
'track-development'=14
'import'=15
'{'=16
'}'=17
';'=18
@@ -0,0 +1,194 @@
import * as antlr from "antlr4ng";
import { Token } from "antlr4ng";
export class QuixosLockLexer extends antlr.Lexer {
public static readonly QUIXOS_LOCK = 1;
public static readonly FRAGMENT = 2;
public static readonly VERSION = 3;
public static readonly QUIXOS = 4;
public static readonly SOURCE = 5;
public static readonly INTERFACE = 6;
public static readonly PACKAGE = 7;
public static readonly REPOSITORY = 8;
public static readonly COMMIT = 9;
public static readonly POLICY = 10;
public static readonly REF = 11;
public static readonly PINNED = 12;
public static readonly TRACK_RELEASE = 13;
public static readonly TRACK_DEVELOPMENT = 14;
public static readonly IMPORT = 15;
public static readonly LBRACE = 16;
public static readonly RBRACE = 17;
public static readonly SEMI = 18;
public static readonly INTEGER = 19;
public static readonly IDENTIFIER = 20;
public static readonly STRING_LITERAL = 21;
public static readonly LINE_COMMENT = 22;
public static readonly BLOCK_COMMENT = 23;
public static readonly WS = 24;
public static readonly channelNames = [
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
];
public static readonly literalNames = [
null, "'quixos-lock'", "'fragment'", "'version'", "'quixos'", "'source'",
"'interface'", "'package'", "'repository'", "'commit'", "'policy'",
"'ref'", "'pinned'", "'track-release'", "'track-development'", "'import'",
"'{'", "'}'", "';'"
];
public static readonly symbolicNames = [
null, "QUIXOS_LOCK", "FRAGMENT", "VERSION", "QUIXOS", "SOURCE",
"INTERFACE", "PACKAGE", "REPOSITORY", "COMMIT", "POLICY", "REF",
"PINNED", "TRACK_RELEASE", "TRACK_DEVELOPMENT", "IMPORT", "LBRACE",
"RBRACE", "SEMI", "INTEGER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT",
"BLOCK_COMMENT", "WS"
];
public static readonly modeNames = [
"DEFAULT_MODE",
];
public static readonly ruleNames = [
"QUIXOS_LOCK", "FRAGMENT", "VERSION", "QUIXOS", "SOURCE", "INTERFACE",
"PACKAGE", "REPOSITORY", "COMMIT", "POLICY", "REF", "PINNED", "TRACK_RELEASE",
"TRACK_DEVELOPMENT", "IMPORT", "LBRACE", "RBRACE", "SEMI", "INTEGER",
"IDENTIFIER", "STRING_LITERAL", "ESC", "HEX", "LINE_COMMENT", "BLOCK_COMMENT",
"WS",
];
public constructor(input: antlr.CharStream) {
super(input);
this.interpreter = new antlr.LexerATNSimulator(this, QuixosLockLexer._ATN, QuixosLockLexer.decisionsToDFA, new antlr.PredictionContextCache());
}
public get grammarFileName(): string { return "QuixosLock.g4"; }
public get literalNames(): (string | null)[] { return QuixosLockLexer.literalNames; }
public get symbolicNames(): (string | null)[] { return QuixosLockLexer.symbolicNames; }
public get ruleNames(): string[] { return QuixosLockLexer.ruleNames; }
public get serializedATN(): number[] { return QuixosLockLexer._serializedATN; }
public get channelNames(): string[] { return QuixosLockLexer.channelNames; }
public get modeNames(): string[] { return QuixosLockLexer.modeNames; }
public static readonly _serializedATN: number[] = [
4,0,24,261,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,1,
0,1,0,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,2,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,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,7,1,7,1,7,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,8,1,9,1,
9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,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,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,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,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,4,18,197,8,18,11,
18,12,18,198,1,19,1,19,5,19,203,8,19,10,19,12,19,206,9,19,1,20,1,
20,1,20,5,20,211,8,20,10,20,12,20,214,9,20,1,20,1,20,1,21,1,21,1,
21,1,21,1,21,1,21,1,21,1,21,3,21,226,8,21,1,22,1,22,1,23,1,23,1,
23,1,23,5,23,234,8,23,10,23,12,23,237,9,23,1,23,1,23,1,24,1,24,1,
24,1,24,5,24,245,8,24,10,24,12,24,248,9,24,1,24,1,24,1,24,1,24,1,
24,1,25,4,25,256,8,25,11,25,12,25,257,1,25,1,25,1,246,0,26,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,0,45,0,47,22,49,23,
51,24,1,0,8,1,0,48,57,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,266,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,47,1,0,0,0,0,49,1,0,0,0,
0,51,1,0,0,0,1,53,1,0,0,0,3,65,1,0,0,0,5,74,1,0,0,0,7,82,1,0,0,0,
9,89,1,0,0,0,11,96,1,0,0,0,13,106,1,0,0,0,15,114,1,0,0,0,17,125,
1,0,0,0,19,132,1,0,0,0,21,139,1,0,0,0,23,143,1,0,0,0,25,150,1,0,
0,0,27,164,1,0,0,0,29,182,1,0,0,0,31,189,1,0,0,0,33,191,1,0,0,0,
35,193,1,0,0,0,37,196,1,0,0,0,39,200,1,0,0,0,41,207,1,0,0,0,43,217,
1,0,0,0,45,227,1,0,0,0,47,229,1,0,0,0,49,240,1,0,0,0,51,255,1,0,
0,0,53,54,5,113,0,0,54,55,5,117,0,0,55,56,5,105,0,0,56,57,5,120,
0,0,57,58,5,111,0,0,58,59,5,115,0,0,59,60,5,45,0,0,60,61,5,108,0,
0,61,62,5,111,0,0,62,63,5,99,0,0,63,64,5,107,0,0,64,2,1,0,0,0,65,
66,5,102,0,0,66,67,5,114,0,0,67,68,5,97,0,0,68,69,5,103,0,0,69,70,
5,109,0,0,70,71,5,101,0,0,71,72,5,110,0,0,72,73,5,116,0,0,73,4,1,
0,0,0,74,75,5,118,0,0,75,76,5,101,0,0,76,77,5,114,0,0,77,78,5,115,
0,0,78,79,5,105,0,0,79,80,5,111,0,0,80,81,5,110,0,0,81,6,1,0,0,0,
82,83,5,113,0,0,83,84,5,117,0,0,84,85,5,105,0,0,85,86,5,120,0,0,
86,87,5,111,0,0,87,88,5,115,0,0,88,8,1,0,0,0,89,90,5,115,0,0,90,
91,5,111,0,0,91,92,5,117,0,0,92,93,5,114,0,0,93,94,5,99,0,0,94,95,
5,101,0,0,95,10,1,0,0,0,96,97,5,105,0,0,97,98,5,110,0,0,98,99,5,
116,0,0,99,100,5,101,0,0,100,101,5,114,0,0,101,102,5,102,0,0,102,
103,5,97,0,0,103,104,5,99,0,0,104,105,5,101,0,0,105,12,1,0,0,0,106,
107,5,112,0,0,107,108,5,97,0,0,108,109,5,99,0,0,109,110,5,107,0,
0,110,111,5,97,0,0,111,112,5,103,0,0,112,113,5,101,0,0,113,14,1,
0,0,0,114,115,5,114,0,0,115,116,5,101,0,0,116,117,5,112,0,0,117,
118,5,111,0,0,118,119,5,115,0,0,119,120,5,105,0,0,120,121,5,116,
0,0,121,122,5,111,0,0,122,123,5,114,0,0,123,124,5,121,0,0,124,16,
1,0,0,0,125,126,5,99,0,0,126,127,5,111,0,0,127,128,5,109,0,0,128,
129,5,109,0,0,129,130,5,105,0,0,130,131,5,116,0,0,131,18,1,0,0,0,
132,133,5,112,0,0,133,134,5,111,0,0,134,135,5,108,0,0,135,136,5,
105,0,0,136,137,5,99,0,0,137,138,5,121,0,0,138,20,1,0,0,0,139,140,
5,114,0,0,140,141,5,101,0,0,141,142,5,102,0,0,142,22,1,0,0,0,143,
144,5,112,0,0,144,145,5,105,0,0,145,146,5,110,0,0,146,147,5,110,
0,0,147,148,5,101,0,0,148,149,5,100,0,0,149,24,1,0,0,0,150,151,5,
116,0,0,151,152,5,114,0,0,152,153,5,97,0,0,153,154,5,99,0,0,154,
155,5,107,0,0,155,156,5,45,0,0,156,157,5,114,0,0,157,158,5,101,0,
0,158,159,5,108,0,0,159,160,5,101,0,0,160,161,5,97,0,0,161,162,5,
115,0,0,162,163,5,101,0,0,163,26,1,0,0,0,164,165,5,116,0,0,165,166,
5,114,0,0,166,167,5,97,0,0,167,168,5,99,0,0,168,169,5,107,0,0,169,
170,5,45,0,0,170,171,5,100,0,0,171,172,5,101,0,0,172,173,5,118,0,
0,173,174,5,101,0,0,174,175,5,108,0,0,175,176,5,111,0,0,176,177,
5,112,0,0,177,178,5,109,0,0,178,179,5,101,0,0,179,180,5,110,0,0,
180,181,5,116,0,0,181,28,1,0,0,0,182,183,5,105,0,0,183,184,5,109,
0,0,184,185,5,112,0,0,185,186,5,111,0,0,186,187,5,114,0,0,187,188,
5,116,0,0,188,30,1,0,0,0,189,190,5,123,0,0,190,32,1,0,0,0,191,192,
5,125,0,0,192,34,1,0,0,0,193,194,5,59,0,0,194,36,1,0,0,0,195,197,
7,0,0,0,196,195,1,0,0,0,197,198,1,0,0,0,198,196,1,0,0,0,198,199,
1,0,0,0,199,38,1,0,0,0,200,204,7,1,0,0,201,203,7,2,0,0,202,201,1,
0,0,0,203,206,1,0,0,0,204,202,1,0,0,0,204,205,1,0,0,0,205,40,1,0,
0,0,206,204,1,0,0,0,207,212,5,34,0,0,208,211,3,43,21,0,209,211,8,
3,0,0,210,208,1,0,0,0,210,209,1,0,0,0,211,214,1,0,0,0,212,210,1,
0,0,0,212,213,1,0,0,0,213,215,1,0,0,0,214,212,1,0,0,0,215,216,5,
34,0,0,216,42,1,0,0,0,217,225,5,92,0,0,218,226,7,4,0,0,219,220,5,
117,0,0,220,221,3,45,22,0,221,222,3,45,22,0,222,223,3,45,22,0,223,
224,3,45,22,0,224,226,1,0,0,0,225,218,1,0,0,0,225,219,1,0,0,0,226,
44,1,0,0,0,227,228,7,5,0,0,228,46,1,0,0,0,229,230,5,47,0,0,230,231,
5,47,0,0,231,235,1,0,0,0,232,234,8,6,0,0,233,232,1,0,0,0,234,237,
1,0,0,0,235,233,1,0,0,0,235,236,1,0,0,0,236,238,1,0,0,0,237,235,
1,0,0,0,238,239,6,23,0,0,239,48,1,0,0,0,240,241,5,47,0,0,241,242,
5,42,0,0,242,246,1,0,0,0,243,245,9,0,0,0,244,243,1,0,0,0,245,248,
1,0,0,0,246,247,1,0,0,0,246,244,1,0,0,0,247,249,1,0,0,0,248,246,
1,0,0,0,249,250,5,42,0,0,250,251,5,47,0,0,251,252,1,0,0,0,252,253,
6,24,0,0,253,50,1,0,0,0,254,256,7,7,0,0,255,254,1,0,0,0,256,257,
1,0,0,0,257,255,1,0,0,0,257,258,1,0,0,0,258,259,1,0,0,0,259,260,
6,25,0,0,260,52,1,0,0,0,9,0,198,204,210,212,225,235,246,257,1,6,
0,0
];
private static __ATN: antlr.ATN;
public static get _ATN(): antlr.ATN {
if (!QuixosLockLexer.__ATN) {
QuixosLockLexer.__ATN = new antlr.ATNDeserializer().deserialize(QuixosLockLexer._serializedATN);
}
return QuixosLockLexer.__ATN;
}
private static readonly vocabulary = new antlr.Vocabulary(QuixosLockLexer.literalNames, QuixosLockLexer.symbolicNames, []);
public override get vocabulary(): antlr.Vocabulary {
return QuixosLockLexer.vocabulary;
}
private static readonly decisionsToDFA = QuixosLockLexer._ATN.decisionToState.map( (ds: antlr.DecisionState, index: number) => new antlr.DFA(ds, index) );
}
@@ -0,0 +1,830 @@
import * as antlr from "antlr4ng";
import { Token } from "antlr4ng";
import { QuixosLockVisitor } from "./QuixosLockVisitor.js";
// for running tests with parameters, TODO: discuss strategy for typed parameters in CI
// eslint-disable-next-line no-unused-vars
type int = number;
export class QuixosLockParser extends antlr.Parser {
public static readonly QUIXOS_LOCK = 1;
public static readonly FRAGMENT = 2;
public static readonly VERSION = 3;
public static readonly QUIXOS = 4;
public static readonly SOURCE = 5;
public static readonly INTERFACE = 6;
public static readonly PACKAGE = 7;
public static readonly REPOSITORY = 8;
public static readonly COMMIT = 9;
public static readonly POLICY = 10;
public static readonly REF = 11;
public static readonly PINNED = 12;
public static readonly TRACK_RELEASE = 13;
public static readonly TRACK_DEVELOPMENT = 14;
public static readonly IMPORT = 15;
public static readonly LBRACE = 16;
public static readonly RBRACE = 17;
public static readonly SEMI = 18;
public static readonly INTEGER = 19;
public static readonly IDENTIFIER = 20;
public static readonly STRING_LITERAL = 21;
public static readonly LINE_COMMENT = 22;
public static readonly BLOCK_COMMENT = 23;
public static readonly WS = 24;
public static readonly RULE_document = 0;
public static readonly RULE_quixosEntry = 1;
public static readonly RULE_resourceEntry = 2;
public static readonly RULE_importEntry = 3;
public static readonly RULE_resourceKind = 4;
public static readonly RULE_sourceBlock = 5;
public static readonly RULE_quixosSourceBlock = 6;
public static readonly RULE_sourcePolicy = 7;
public static readonly RULE_identifier = 8;
public static readonly RULE_stringLiteral = 9;
public static readonly literalNames = [
null, "'quixos-lock'", "'fragment'", "'version'", "'quixos'", "'source'",
"'interface'", "'package'", "'repository'", "'commit'", "'policy'",
"'ref'", "'pinned'", "'track-release'", "'track-development'", "'import'",
"'{'", "'}'", "';'"
];
public static readonly symbolicNames = [
null, "QUIXOS_LOCK", "FRAGMENT", "VERSION", "QUIXOS", "SOURCE",
"INTERFACE", "PACKAGE", "REPOSITORY", "COMMIT", "POLICY", "REF",
"PINNED", "TRACK_RELEASE", "TRACK_DEVELOPMENT", "IMPORT", "LBRACE",
"RBRACE", "SEMI", "INTEGER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT",
"BLOCK_COMMENT", "WS"
];
public static readonly ruleNames = [
"document", "quixosEntry", "resourceEntry", "importEntry", "resourceKind",
"sourceBlock", "quixosSourceBlock", "sourcePolicy", "identifier",
"stringLiteral",
];
public get grammarFileName(): string { return "QuixosLock.g4"; }
public get literalNames(): (string | null)[] { return QuixosLockParser.literalNames; }
public get symbolicNames(): (string | null)[] { return QuixosLockParser.symbolicNames; }
public get ruleNames(): string[] { return QuixosLockParser.ruleNames; }
public get serializedATN(): number[] { return QuixosLockParser._serializedATN; }
protected createFailedPredicateException(predicate?: string, message?: string): antlr.FailedPredicateException {
return new antlr.FailedPredicateException(this, predicate, message);
}
public constructor(input: antlr.TokenStream) {
super(input);
this.interpreter = new antlr.ParserATNSimulator(this, QuixosLockParser._ATN, QuixosLockParser.decisionsToDFA, new antlr.PredictionContextCache());
}
public document(): DocumentContext {
let localContext = new DocumentContext(this.context, this.state);
this.enterRule(localContext, 0, QuixosLockParser.RULE_document);
let _la: number;
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 20;
this.match(QuixosLockParser.QUIXOS_LOCK);
this.state = 22;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
if (_la === 2) {
{
this.state = 21;
this.match(QuixosLockParser.FRAGMENT);
}
}
this.state = 24;
this.match(QuixosLockParser.VERSION);
this.state = 25;
this.match(QuixosLockParser.INTEGER);
this.state = 26;
this.match(QuixosLockParser.LBRACE);
this.state = 32;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 32976) !== 0)) {
{
this.state = 30;
this.errorHandler.sync(this);
switch (this.tokenStream.LA(1)) {
case QuixosLockParser.QUIXOS:
{
this.state = 27;
this.quixosEntry();
}
break;
case QuixosLockParser.IMPORT:
{
this.state = 28;
this.importEntry();
}
break;
case QuixosLockParser.INTERFACE:
case QuixosLockParser.PACKAGE:
{
this.state = 29;
this.resourceEntry();
}
break;
default:
throw new antlr.NoViableAltException(this);
}
}
this.state = 34;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
}
this.state = 35;
this.match(QuixosLockParser.RBRACE);
this.state = 36;
this.match(QuixosLockParser.EOF);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public quixosEntry(): QuixosEntryContext {
let localContext = new QuixosEntryContext(this.context, this.state);
this.enterRule(localContext, 2, QuixosLockParser.RULE_quixosEntry);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 38;
this.match(QuixosLockParser.QUIXOS);
this.state = 39;
this.match(QuixosLockParser.SOURCE);
this.state = 40;
this.quixosSourceBlock();
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public resourceEntry(): ResourceEntryContext {
let localContext = new ResourceEntryContext(this.context, this.state);
this.enterRule(localContext, 4, QuixosLockParser.RULE_resourceEntry);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 42;
this.resourceKind();
this.state = 43;
this.identifier();
this.state = 44;
this.match(QuixosLockParser.SOURCE);
this.state = 45;
this.sourceBlock();
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public importEntry(): ImportEntryContext {
let localContext = new ImportEntryContext(this.context, this.state);
this.enterRule(localContext, 6, QuixosLockParser.RULE_importEntry);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 47;
this.match(QuixosLockParser.IMPORT);
this.state = 48;
this.stringLiteral();
this.state = 49;
this.match(QuixosLockParser.SEMI);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public resourceKind(): ResourceKindContext {
let localContext = new ResourceKindContext(this.context, this.state);
this.enterRule(localContext, 8, QuixosLockParser.RULE_resourceKind);
let _la: number;
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 51;
_la = this.tokenStream.LA(1);
if(!(_la === 6 || _la === 7)) {
this.errorHandler.recoverInline(this);
}
else {
this.errorHandler.reportMatch(this);
this.consume();
}
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public sourceBlock(): SourceBlockContext {
let localContext = new SourceBlockContext(this.context, this.state);
this.enterRule(localContext, 10, QuixosLockParser.RULE_sourceBlock);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 53;
this.match(QuixosLockParser.LBRACE);
this.state = 54;
this.match(QuixosLockParser.REPOSITORY);
this.state = 55;
this.stringLiteral();
this.state = 56;
this.match(QuixosLockParser.SEMI);
this.state = 57;
this.match(QuixosLockParser.COMMIT);
this.state = 58;
this.stringLiteral();
this.state = 59;
this.match(QuixosLockParser.SEMI);
this.state = 60;
this.match(QuixosLockParser.RBRACE);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public quixosSourceBlock(): QuixosSourceBlockContext {
let localContext = new QuixosSourceBlockContext(this.context, this.state);
this.enterRule(localContext, 12, QuixosLockParser.RULE_quixosSourceBlock);
let _la: number;
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 62;
this.match(QuixosLockParser.LBRACE);
this.state = 63;
this.match(QuixosLockParser.REPOSITORY);
this.state = 64;
this.stringLiteral();
this.state = 65;
this.match(QuixosLockParser.SEMI);
this.state = 73;
this.errorHandler.sync(this);
_la = this.tokenStream.LA(1);
if (_la === 10) {
{
this.state = 66;
this.match(QuixosLockParser.POLICY);
this.state = 67;
this.sourcePolicy();
this.state = 68;
this.match(QuixosLockParser.SEMI);
this.state = 69;
this.match(QuixosLockParser.REF);
this.state = 70;
this.stringLiteral();
this.state = 71;
this.match(QuixosLockParser.SEMI);
}
}
this.state = 75;
this.match(QuixosLockParser.COMMIT);
this.state = 76;
this.stringLiteral();
this.state = 77;
this.match(QuixosLockParser.SEMI);
this.state = 78;
this.match(QuixosLockParser.RBRACE);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public sourcePolicy(): SourcePolicyContext {
let localContext = new SourcePolicyContext(this.context, this.state);
this.enterRule(localContext, 14, QuixosLockParser.RULE_sourcePolicy);
let _la: number;
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 80;
_la = this.tokenStream.LA(1);
if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 28672) !== 0))) {
this.errorHandler.recoverInline(this);
}
else {
this.errorHandler.reportMatch(this);
this.consume();
}
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public identifier(): IdentifierContext {
let localContext = new IdentifierContext(this.context, this.state);
this.enterRule(localContext, 16, QuixosLockParser.RULE_identifier);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 82;
this.match(QuixosLockParser.IDENTIFIER);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public stringLiteral(): StringLiteralContext {
let localContext = new StringLiteralContext(this.context, this.state);
this.enterRule(localContext, 18, QuixosLockParser.RULE_stringLiteral);
try {
this.enterOuterAlt(localContext, 1);
{
this.state = 84;
this.match(QuixosLockParser.STRING_LITERAL);
}
}
catch (re) {
if (re instanceof antlr.RecognitionException) {
this.errorHandler.reportError(this, re);
this.errorHandler.recover(this, re);
} else {
throw re;
}
}
finally {
this.exitRule();
}
return localContext;
}
public static readonly _serializedATN: number[] = [
4,1,24,87,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,1,0,1,0,3,0,23,8,0,1,0,1,0,1,0,1,0,1,0,
1,0,5,0,31,8,0,10,0,12,0,34,9,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,2,
1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,4,1,4,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,3,6,74,8,
6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,8,1,8,1,9,1,9,1,9,0,0,10,0,2,4,6,
8,10,12,14,16,18,0,2,1,0,6,7,1,0,12,14,81,0,20,1,0,0,0,2,38,1,0,
0,0,4,42,1,0,0,0,6,47,1,0,0,0,8,51,1,0,0,0,10,53,1,0,0,0,12,62,1,
0,0,0,14,80,1,0,0,0,16,82,1,0,0,0,18,84,1,0,0,0,20,22,5,1,0,0,21,
23,5,2,0,0,22,21,1,0,0,0,22,23,1,0,0,0,23,24,1,0,0,0,24,25,5,3,0,
0,25,26,5,19,0,0,26,32,5,16,0,0,27,31,3,2,1,0,28,31,3,6,3,0,29,31,
3,4,2,0,30,27,1,0,0,0,30,28,1,0,0,0,30,29,1,0,0,0,31,34,1,0,0,0,
32,30,1,0,0,0,32,33,1,0,0,0,33,35,1,0,0,0,34,32,1,0,0,0,35,36,5,
17,0,0,36,37,5,0,0,1,37,1,1,0,0,0,38,39,5,4,0,0,39,40,5,5,0,0,40,
41,3,12,6,0,41,3,1,0,0,0,42,43,3,8,4,0,43,44,3,16,8,0,44,45,5,5,
0,0,45,46,3,10,5,0,46,5,1,0,0,0,47,48,5,15,0,0,48,49,3,18,9,0,49,
50,5,18,0,0,50,7,1,0,0,0,51,52,7,0,0,0,52,9,1,0,0,0,53,54,5,16,0,
0,54,55,5,8,0,0,55,56,3,18,9,0,56,57,5,18,0,0,57,58,5,9,0,0,58,59,
3,18,9,0,59,60,5,18,0,0,60,61,5,17,0,0,61,11,1,0,0,0,62,63,5,16,
0,0,63,64,5,8,0,0,64,65,3,18,9,0,65,73,5,18,0,0,66,67,5,10,0,0,67,
68,3,14,7,0,68,69,5,18,0,0,69,70,5,11,0,0,70,71,3,18,9,0,71,72,5,
18,0,0,72,74,1,0,0,0,73,66,1,0,0,0,73,74,1,0,0,0,74,75,1,0,0,0,75,
76,5,9,0,0,76,77,3,18,9,0,77,78,5,18,0,0,78,79,5,17,0,0,79,13,1,
0,0,0,80,81,7,1,0,0,81,15,1,0,0,0,82,83,5,20,0,0,83,17,1,0,0,0,84,
85,5,21,0,0,85,19,1,0,0,0,4,22,30,32,73
];
private static __ATN: antlr.ATN;
public static get _ATN(): antlr.ATN {
if (!QuixosLockParser.__ATN) {
QuixosLockParser.__ATN = new antlr.ATNDeserializer().deserialize(QuixosLockParser._serializedATN);
}
return QuixosLockParser.__ATN;
}
private static readonly vocabulary = new antlr.Vocabulary(QuixosLockParser.literalNames, QuixosLockParser.symbolicNames, []);
public override get vocabulary(): antlr.Vocabulary {
return QuixosLockParser.vocabulary;
}
private static readonly decisionsToDFA = QuixosLockParser._ATN.decisionToState.map( (ds: antlr.DecisionState, index: number) => new antlr.DFA(ds, index) );
}
export class DocumentContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public QUIXOS_LOCK(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.QUIXOS_LOCK, 0)!;
}
public VERSION(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.VERSION, 0)!;
}
public INTEGER(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.INTEGER, 0)!;
}
public LBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.LBRACE, 0)!;
}
public RBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.RBRACE, 0)!;
}
public EOF(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.EOF, 0)!;
}
public FRAGMENT(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.FRAGMENT, 0);
}
public quixosEntry(): QuixosEntryContext[];
public quixosEntry(i: number): QuixosEntryContext | null;
public quixosEntry(i?: number): QuixosEntryContext[] | QuixosEntryContext | null {
if (i === undefined) {
return this.getRuleContexts(QuixosEntryContext);
}
return this.getRuleContext(i, QuixosEntryContext);
}
public importEntry(): ImportEntryContext[];
public importEntry(i: number): ImportEntryContext | null;
public importEntry(i?: number): ImportEntryContext[] | ImportEntryContext | null {
if (i === undefined) {
return this.getRuleContexts(ImportEntryContext);
}
return this.getRuleContext(i, ImportEntryContext);
}
public resourceEntry(): ResourceEntryContext[];
public resourceEntry(i: number): ResourceEntryContext | null;
public resourceEntry(i?: number): ResourceEntryContext[] | ResourceEntryContext | null {
if (i === undefined) {
return this.getRuleContexts(ResourceEntryContext);
}
return this.getRuleContext(i, ResourceEntryContext);
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_document;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitDocument) {
return visitor.visitDocument(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class QuixosEntryContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public QUIXOS(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.QUIXOS, 0)!;
}
public SOURCE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.SOURCE, 0)!;
}
public quixosSourceBlock(): QuixosSourceBlockContext {
return this.getRuleContext(0, QuixosSourceBlockContext)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_quixosEntry;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitQuixosEntry) {
return visitor.visitQuixosEntry(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class ResourceEntryContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public resourceKind(): ResourceKindContext {
return this.getRuleContext(0, ResourceKindContext)!;
}
public identifier(): IdentifierContext {
return this.getRuleContext(0, IdentifierContext)!;
}
public SOURCE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.SOURCE, 0)!;
}
public sourceBlock(): SourceBlockContext {
return this.getRuleContext(0, SourceBlockContext)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_resourceEntry;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitResourceEntry) {
return visitor.visitResourceEntry(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class ImportEntryContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public IMPORT(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.IMPORT, 0)!;
}
public stringLiteral(): StringLiteralContext {
return this.getRuleContext(0, StringLiteralContext)!;
}
public SEMI(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.SEMI, 0)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_importEntry;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitImportEntry) {
return visitor.visitImportEntry(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class ResourceKindContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public INTERFACE(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.INTERFACE, 0);
}
public PACKAGE(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.PACKAGE, 0);
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_resourceKind;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitResourceKind) {
return visitor.visitResourceKind(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class SourceBlockContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public LBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.LBRACE, 0)!;
}
public REPOSITORY(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.REPOSITORY, 0)!;
}
public stringLiteral(): StringLiteralContext[];
public stringLiteral(i: number): StringLiteralContext | null;
public stringLiteral(i?: number): StringLiteralContext[] | StringLiteralContext | null {
if (i === undefined) {
return this.getRuleContexts(StringLiteralContext);
}
return this.getRuleContext(i, StringLiteralContext);
}
public SEMI(): antlr.TerminalNode[];
public SEMI(i: number): antlr.TerminalNode | null;
public SEMI(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] {
if (i === undefined) {
return this.getTokens(QuixosLockParser.SEMI);
} else {
return this.getToken(QuixosLockParser.SEMI, i);
}
}
public COMMIT(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.COMMIT, 0)!;
}
public RBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.RBRACE, 0)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_sourceBlock;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitSourceBlock) {
return visitor.visitSourceBlock(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class QuixosSourceBlockContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public LBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.LBRACE, 0)!;
}
public REPOSITORY(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.REPOSITORY, 0)!;
}
public stringLiteral(): StringLiteralContext[];
public stringLiteral(i: number): StringLiteralContext | null;
public stringLiteral(i?: number): StringLiteralContext[] | StringLiteralContext | null {
if (i === undefined) {
return this.getRuleContexts(StringLiteralContext);
}
return this.getRuleContext(i, StringLiteralContext);
}
public SEMI(): antlr.TerminalNode[];
public SEMI(i: number): antlr.TerminalNode | null;
public SEMI(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] {
if (i === undefined) {
return this.getTokens(QuixosLockParser.SEMI);
} else {
return this.getToken(QuixosLockParser.SEMI, i);
}
}
public COMMIT(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.COMMIT, 0)!;
}
public RBRACE(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.RBRACE, 0)!;
}
public POLICY(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.POLICY, 0);
}
public sourcePolicy(): SourcePolicyContext | null {
return this.getRuleContext(0, SourcePolicyContext);
}
public REF(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.REF, 0);
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_quixosSourceBlock;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitQuixosSourceBlock) {
return visitor.visitQuixosSourceBlock(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class SourcePolicyContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public PINNED(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.PINNED, 0);
}
public TRACK_RELEASE(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.TRACK_RELEASE, 0);
}
public TRACK_DEVELOPMENT(): antlr.TerminalNode | null {
return this.getToken(QuixosLockParser.TRACK_DEVELOPMENT, 0);
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_sourcePolicy;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitSourcePolicy) {
return visitor.visitSourcePolicy(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class IdentifierContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public IDENTIFIER(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.IDENTIFIER, 0)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_identifier;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitIdentifier) {
return visitor.visitIdentifier(this);
} else {
return visitor.visitChildren(this);
}
}
}
export class StringLiteralContext extends antlr.ParserRuleContext {
public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) {
super(parent, invokingState);
}
public STRING_LITERAL(): antlr.TerminalNode {
return this.getToken(QuixosLockParser.STRING_LITERAL, 0)!;
}
public override get ruleIndex(): number {
return QuixosLockParser.RULE_stringLiteral;
}
public override accept<Result>(visitor: QuixosLockVisitor<Result>): Result | null {
if (visitor.visitStringLiteral) {
return visitor.visitStringLiteral(this);
} else {
return visitor.visitChildren(this);
}
}
}
@@ -0,0 +1,86 @@
import { AbstractParseTreeVisitor } from "antlr4ng";
import { DocumentContext } from "./QuixosLockParser.js";
import { QuixosEntryContext } from "./QuixosLockParser.js";
import { ResourceEntryContext } from "./QuixosLockParser.js";
import { ImportEntryContext } from "./QuixosLockParser.js";
import { ResourceKindContext } from "./QuixosLockParser.js";
import { SourceBlockContext } from "./QuixosLockParser.js";
import { QuixosSourceBlockContext } from "./QuixosLockParser.js";
import { SourcePolicyContext } from "./QuixosLockParser.js";
import { IdentifierContext } from "./QuixosLockParser.js";
import { StringLiteralContext } from "./QuixosLockParser.js";
/**
* This interface defines a complete generic visitor for a parse tree produced
* by `QuixosLockParser`.
*
* @param <Result> The return type of the visit operation. Use `void` for
* operations with no return type.
*/
export class QuixosLockVisitor<Result> extends AbstractParseTreeVisitor<Result> {
/**
* Visit a parse tree produced by `QuixosLockParser.document`.
* @param ctx the parse tree
* @return the visitor result
*/
visitDocument?: (ctx: DocumentContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.quixosEntry`.
* @param ctx the parse tree
* @return the visitor result
*/
visitQuixosEntry?: (ctx: QuixosEntryContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.resourceEntry`.
* @param ctx the parse tree
* @return the visitor result
*/
visitResourceEntry?: (ctx: ResourceEntryContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.importEntry`.
* @param ctx the parse tree
* @return the visitor result
*/
visitImportEntry?: (ctx: ImportEntryContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.resourceKind`.
* @param ctx the parse tree
* @return the visitor result
*/
visitResourceKind?: (ctx: ResourceKindContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.sourceBlock`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSourceBlock?: (ctx: SourceBlockContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.quixosSourceBlock`.
* @param ctx the parse tree
* @return the visitor result
*/
visitQuixosSourceBlock?: (ctx: QuixosSourceBlockContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.sourcePolicy`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSourcePolicy?: (ctx: SourcePolicyContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.identifier`.
* @param ctx the parse tree
* @return the visitor result
*/
visitIdentifier?: (ctx: IdentifierContext) => Result;
/**
* Visit a parse tree produced by `QuixosLockParser.stringLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitStringLiteral?: (ctx: StringLiteralContext) => Result;
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./loader.js";
export * from "./parser.js";
export * from "./types.js";
+150
View File
@@ -0,0 +1,150 @@
import { lstat, readFile } from "node:fs/promises";
import path from "node:path";
import {
parseQuixosLockDocument,
type QuixosLockDiagnostic,
type QuixosLockParseResult,
} from "./parser.js";
import type {
LockedResource,
QuixosLockDocument,
QuixosRepositoryLock,
} from "./types.js";
export type QuixosLockSourceReader = (relativePath: string) => Promise<string>;
const diagnostic = (
code: string,
message: string,
fileName: string,
path?: string,
): QuixosLockDiagnostic => ({
phase: "resolution",
code,
message,
fileName,
line: 1,
column: 0,
path,
});
export const resolveQuixosLock = async (
rootSource: string,
readSource: QuixosLockSourceReader,
rootFileName = "quixos.lock",
): Promise<QuixosLockParseResult> => {
const root = parseQuixosLockDocument(rootSource, rootFileName);
if (!root.ok) return root;
if (root.document.kind !== "root") {
return {
ok: false,
diagnostics: [diagnostic(
"expected-root-lock",
"The entrypoint must be a root Quixos lock, not a fragment",
rootFileName,
)],
};
}
const diagnostics: QuixosLockDiagnostic[] = [];
const resources: LockedResource[] = [];
const sourceFiles = [rootFileName];
const visited = new Set<string>();
const active: string[] = [];
const addResources = (document: QuixosLockDocument, fileName: string) => {
for (const resource of document.resources) {
const duplicate = resources.find((entry) =>
entry.kind === resource.kind && entry.binding === resource.binding);
if (duplicate) {
diagnostics.push(diagnostic(
"duplicate-resource-binding",
`Duplicate ${resource.kind} binding ${resource.binding} across imported lock files`,
fileName,
`${resource.kind}.${resource.binding}`,
));
} else {
resources.push(resource);
}
}
};
const visit = async (importPath: string) => {
if (active.includes(importPath)) {
diagnostics.push(diagnostic(
"import-cycle",
`Lock import cycle: ${[...active, importPath].join(" -> ")}`,
importPath,
));
return;
}
if (visited.has(importPath)) return;
active.push(importPath);
let source: string;
try {
source = await readSource(importPath);
} catch (cause) {
diagnostics.push(diagnostic(
"import-read-failed",
`Could not read lock import ${importPath}: ${cause instanceof Error ? cause.message : String(cause)}`,
importPath,
));
active.pop();
return;
}
const parsed = parseQuixosLockDocument(source, importPath);
if (!parsed.ok) {
diagnostics.push(...parsed.diagnostics);
active.pop();
return;
}
if (parsed.document.kind !== "fragment") {
diagnostics.push(diagnostic(
"imported-root-lock",
`Imported file ${importPath} must begin with "quixos-lock fragment"`,
importPath,
));
active.pop();
return;
}
visited.add(importPath);
sourceFiles.push(importPath);
addResources(parsed.document, importPath);
for (const nested of parsed.document.imports) await visit(nested);
active.pop();
};
addResources(root.document, rootFileName);
for (const importPath of root.document.imports) await visit(importPath);
if (diagnostics.length) return { ok: false, diagnostics };
const lock: QuixosRepositoryLock = {
formatVersion: 1,
quixos: root.document.quixos,
resources,
sourceFiles,
};
return { ok: true, lock, diagnostics: [] };
};
export const loadQuixosLock = async (fileName: string): Promise<QuixosLockParseResult> => {
const absoluteRoot = path.resolve(fileName);
const repositoryRoot = path.dirname(absoluteRoot);
const rootName = path.basename(absoluteRoot);
const rootSource = await readFile(absoluteRoot, "utf8");
return await resolveQuixosLock(rootSource, async (relativePath) => {
let absoluteImport = repositoryRoot;
const segments = relativePath.split("/");
for (const [index, segment] of segments.entries()) {
absoluteImport = path.join(absoluteImport, segment);
const metadata = await lstat(absoluteImport);
if (metadata.isSymbolicLink()) {
throw new Error("imports must not traverse symbolic links");
}
const final = index === segments.length - 1;
if ((!final && !metadata.isDirectory()) || (final && !metadata.isFile())) {
throw new Error("imports must be ordinary files beneath ordinary directories");
}
}
return await readFile(absoluteImport, "utf8");
}, rootName);
};
+463
View File
@@ -0,0 +1,463 @@
import {
BaseErrorListener,
CharStream,
CommonTokenStream,
type ATNSimulator,
type RecognitionException,
type Recognizer,
type Token,
} from "antlr4ng";
import { QuixosLockLexer } from "./generated/QuixosLockLexer.js";
import {
QuixosLockParser,
type QuixosSourceBlockContext,
type SourceBlockContext,
} from "./generated/QuixosLockParser.js";
import type {
GitSource,
LockedResource,
QuixosLockDocument,
QuixosRepositoryLock,
QuixosSource,
} from "./types.js";
export type QuixosLockDiagnostic = {
phase: "syntax" | "validation" | "resolution";
code: string;
message: string;
fileName: string;
line: number;
column: number;
path?: string;
};
export type QuixosLockParseResult =
| { ok: true; lock: QuixosRepositoryLock; diagnostics: [] }
| { ok: false; diagnostics: QuixosLockDiagnostic[] };
export type QuixosLockDocumentParseResult =
| { ok: true; document: QuixosLockDocument; diagnostics: [] }
| { ok: false; diagnostics: QuixosLockDiagnostic[] };
class SyntaxErrorListener extends BaseErrorListener {
constructor(
private readonly fileName: string,
private readonly diagnostics: QuixosLockDiagnostic[],
) {
super();
}
override syntaxError<S extends Token, T extends ATNSimulator>(
_recognizer: Recognizer<T>,
_offendingSymbol: S | null,
line: number,
column: number,
message: string,
_error: RecognitionException | null,
): void {
this.diagnostics.push({
phase: "syntax",
code: "syntax-error",
message,
fileName: this.fileName,
line,
column,
});
}
}
const stringValue = (context: { getText(): string }): string =>
JSON.parse(context.getText()) as string;
const lowerSource = (context: SourceBlockContext): GitSource => ({
resolver: "git",
repository: stringValue(context.stringLiteral(0)!),
commit: stringValue(context.stringLiteral(1)!).toLowerCase(),
});
const lowerQuixosSource = (context: QuixosSourceBlockContext): QuixosSource => {
const source = {
resolver: "git" as const,
repository: stringValue(context.stringLiteral(0)!),
commit: stringValue(context.stringLiteral(context.stringLiteral().length - 1)!).toLowerCase(),
};
const policyContext = context.sourcePolicy();
if (!policyContext) return source;
const policy = policyContext.PINNED()
? "pinned"
: policyContext.TRACK_RELEASE()
? "track-release"
: "track-development";
return {
...source,
policy,
ref: stringValue(context.stringLiteral(1)!),
};
};
const issue = (
diagnostics: QuixosLockDiagnostic[],
fileName: string,
code: string,
message: string,
path?: string,
line = 1,
column = 0,
) => diagnostics.push({
phase: "validation",
code,
message,
fileName,
line,
column,
path,
});
const validateImport = (
importPath: string,
index: number,
fileName: string,
diagnostics: QuixosLockDiagnostic[],
line: number,
column: number,
) => {
const path = `imports[${index}]`;
if (
!importPath
|| importPath.startsWith("/")
|| importPath.includes("\\")
|| importPath.split("/").some((segment) => !segment || segment === "." || segment === "..")
|| /^[A-Za-z][A-Za-z0-9+.-]*:/.test(importPath)
) {
issue(
diagnostics,
fileName,
"invalid-import-path",
`${path} must be a normalized relative path beneath the workspace repository root`,
path,
line,
column,
);
}
};
const validateSource = (
source: GitSource,
path: string,
fileName: string,
diagnostics: QuixosLockDiagnostic[],
) => {
if (!/^([0-9a-f]{40}|[0-9a-f]{64})$/.test(source.commit)) {
issue(
diagnostics,
fileName,
"invalid-git-commit",
`${path}.commit must be a full 40- or 64-character Git object ID`,
`${path}.commit`,
);
}
let repository: URL;
try {
repository = new URL(source.repository);
} catch {
issue(
diagnostics,
fileName,
"invalid-git-repository",
`${path}.repository must be an absolute Git URL`,
`${path}.repository`,
);
return;
}
if (!["https:", "ssh:"].includes(repository.protocol)) {
issue(
diagnostics,
fileName,
"unsupported-git-transport",
`${path}.repository must use https:// or ssh://`,
`${path}.repository`,
);
}
if (repository.search || repository.hash) {
issue(
diagnostics,
fileName,
"decorated-git-repository",
`${path}.repository must be a base repository URL without a query or fragment`,
`${path}.repository`,
);
}
if (repository.password || (repository.protocol === "https:" && repository.username)) {
issue(
diagnostics,
fileName,
"embedded-git-credential",
`${path}.repository must not embed credentials`,
`${path}.repository`,
);
}
};
const validateQuixosSource = (
source: QuixosSource,
fileName: string,
diagnostics: QuixosLockDiagnostic[],
) => {
validateSource(source, "quixos", fileName, diagnostics);
if (!source.policy) return;
const forbiddenRefCharacters = new Set("~^:?*[\\");
const invalidRef = !source.ref
|| [...source.ref].some((character) => {
const code = character.charCodeAt(0);
return code <= 0x20 || code === 0x7f || forbiddenRefCharacters.has(character);
})
|| source.ref.startsWith("/")
|| source.ref.endsWith("/")
|| source.ref.endsWith(".")
|| source.ref.includes("..")
|| source.ref.includes("@{")
|| source.ref.includes("//");
if (invalidRef) {
issue(
diagnostics,
fileName,
"invalid-quixos-ref",
"quixos.ref must be a non-empty Git ref without whitespace or Git ref metacharacters",
"quixos.ref",
);
}
if (source.policy === "pinned") {
if (!/^([0-9a-f]{40}|[0-9a-f]{64})$/.test(source.ref)) {
issue(
diagnostics,
fileName,
"pinned-quixos-ref-not-commit",
"A pinned Quixos source must use an exact commit as its ref",
"quixos.ref",
);
} else if (source.ref.toLowerCase() !== source.commit) {
issue(
diagnostics,
fileName,
"pinned-quixos-commit-mismatch",
"A pinned Quixos source ref and resolved commit must be identical",
"quixos.commit",
);
}
}
};
export const parseQuixosLockDocument = (
source: string,
fileName = "<memory>",
): QuixosLockDocumentParseResult => {
const diagnostics: QuixosLockDiagnostic[] = [];
const listener = new SyntaxErrorListener(fileName, diagnostics);
const lexer = new QuixosLockLexer(CharStream.fromString(source));
lexer.removeErrorListeners();
lexer.addErrorListener(listener);
const parser = new QuixosLockParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(listener);
const tree = parser.document();
if (diagnostics.length > 0) return { ok: false, diagnostics };
const formatVersion = Number.parseInt(tree.INTEGER().getText(), 10);
if (formatVersion !== 1) {
issue(
diagnostics,
fileName,
"unsupported-lock-version",
`Unsupported Quixos lock format version ${formatVersion}`,
"formatVersion",
);
}
const fragment = Boolean(tree.FRAGMENT());
const quixosEntries = tree.quixosEntry();
let quixos: QuixosSource | undefined;
if (fragment && quixosEntries.length) {
issue(
diagnostics,
fileName,
"fragment-has-quixos-source",
"A lock fragment inherits the root document's Quixos source and must not redeclare it",
"quixos",
);
} else if (!fragment && quixosEntries.length !== 1) {
issue(
diagnostics,
fileName,
"root-quixos-source-count",
"A root lock document must contain exactly one Quixos source",
"quixos",
);
} else if (!fragment) {
quixos = lowerQuixosSource(quixosEntries[0]!.quixosSourceBlock());
validateQuixosSource(quixos, fileName, diagnostics);
}
const imports = tree.importEntry().map((context, index) => {
const importPath = stringValue(context.stringLiteral());
validateImport(
importPath,
index,
fileName,
diagnostics,
context.start?.line ?? 1,
context.start?.column ?? 0,
);
return importPath;
});
const repeatedImports = new Set<string>();
imports.forEach((importPath, index) => {
if (repeatedImports.has(importPath)) {
issue(
diagnostics,
fileName,
"duplicate-import",
`Duplicate lock import ${importPath}`,
`imports[${index}]`,
);
}
repeatedImports.add(importPath);
});
const resources: LockedResource[] = [];
const bindings = new Set<string>();
for (const [index, context] of tree.resourceEntry().entries()) {
const kind = context.resourceKind().INTERFACE() ? "interface" : "package";
const binding = context.identifier().getText();
const key = `${kind}\0${binding}`;
if (bindings.has(key)) {
issue(
diagnostics,
fileName,
"duplicate-resource-binding",
`Duplicate ${kind} binding ${binding}`,
`resources[${index}].binding`,
);
}
bindings.add(key);
const resource = { kind, binding, source: lowerSource(context.sourceBlock()) } as LockedResource;
validateSource(resource.source, `resources[${index}].source`, fileName, diagnostics);
resources.push(resource);
}
return diagnostics.length > 0
? { ok: false, diagnostics }
: {
ok: true,
document: fragment
? { kind: "fragment", formatVersion: 1, imports, resources }
: { kind: "root", formatVersion: 1, quixos: quixos!, imports, resources },
diagnostics: [],
};
};
export const parseQuixosLock = (
source: string,
fileName = "<memory>",
): QuixosLockParseResult => {
const parsed = parseQuixosLockDocument(source, fileName);
if (!parsed.ok) return parsed;
if (parsed.document.kind === "fragment") {
return {
ok: false,
diagnostics: [{
phase: "validation",
code: "expected-root-lock",
message: "Expected a root Quixos lock, found a lock fragment",
fileName,
line: 1,
column: 0,
}],
};
}
if (parsed.document.imports.length) {
return {
ok: false,
diagnostics: [{
phase: "resolution",
code: "imports-require-file-resolution",
message: "This lock has imports and must be loaded from its repository rather than parsed as an isolated string",
fileName,
line: 1,
column: 0,
}],
};
}
return {
ok: true,
lock: {
formatVersion: 1,
quixos: parsed.document.quixos,
resources: parsed.document.resources,
},
diagnostics: [],
};
};
const quoted = (value: string) => JSON.stringify(value);
const sourceLines = (source: GitSource, indentation: string): string[] => [
`${indentation}repository ${quoted(source.repository)};`,
`${indentation}commit ${quoted(source.commit.toLowerCase())};`,
];
const quixosSourceLines = (source: QuixosSource, indentation: string): string[] => [
`${indentation}repository ${quoted(source.repository)};`,
...(source.policy
? [
`${indentation}policy ${source.policy};`,
`${indentation}ref ${quoted(source.ref)};`,
]
: []),
`${indentation}commit ${quoted(source.commit.toLowerCase())};`,
];
export const formatQuixosLock = (lock: QuixosRepositoryLock): string => {
const lines = [
"quixos-lock version 1 {",
" quixos source {",
...quixosSourceLines(lock.quixos, " "),
" }",
];
for (const resource of lock.resources) {
lines.push(
"",
` ${resource.kind} ${resource.binding} source {`,
...sourceLines(resource.source, " "),
" }",
);
}
lines.push("}", "");
return lines.join("\n");
};
export const formatQuixosLockDocument = (document: QuixosLockDocument): string => {
const lines = [
`quixos-lock${document.kind === "fragment" ? " fragment" : ""} version 1 {`,
];
if (document.kind === "root") {
lines.push(
" quixos source {",
...quixosSourceLines(document.quixos, " "),
" }",
);
}
for (const importPath of document.imports) {
if (lines.length > 1) lines.push("");
lines.push(` import ${quoted(importPath)};`);
}
for (const resource of document.resources) {
if (lines.length > 1) lines.push("");
lines.push(
` ${resource.kind} ${resource.binding} source {`,
...sourceLines(resource.source, " "),
" }",
);
}
lines.push("}", "");
return lines.join("\n");
};
+71
View File
@@ -0,0 +1,71 @@
export type GitSource = {
resolver: "git";
repository: string;
commit: string;
};
export type QuixosSourcePolicy = "pinned" | "track-release" | "track-development";
// Resource repositories only need the exact Quixos commit they were authored
// against. A workspace root additionally declares how a runtime may advance
// that exact baseline.
export type QuixosSource = GitSource & ({
policy: QuixosSourcePolicy;
ref: string;
} | {
policy?: undefined;
ref?: undefined;
});
export type LockedResourceKind = "interface" | "package";
export type LockedResource = {
kind: LockedResourceKind;
binding: string;
source: GitSource;
};
export type QuixosLockRootDocument = {
kind: "root";
formatVersion: 1;
quixos: QuixosSource;
imports: string[];
resources: LockedResource[];
};
export type QuixosLockFragmentDocument = {
kind: "fragment";
formatVersion: 1;
imports: string[];
resources: LockedResource[];
};
export type QuixosLockDocument =
| QuixosLockRootDocument
| QuixosLockFragmentDocument;
export type QuixosRepositoryLock = {
formatVersion: 1;
quixos: QuixosSource;
resources: LockedResource[];
sourceFiles?: string[];
};
export type NixGitInput = {
type: "git";
url: string;
ref: string;
rev: string;
};
export const RETENTION_TAG_PREFIX = "refs/tags/quixos-reachability/";
export const retentionTagForCommit = (commit: string): string =>
`${RETENTION_TAG_PREFIX}${commit.toLowerCase()}`;
export const nixGitInput = (source: GitSource): NixGitInput => ({
type: "git",
url: source.repository,
ref: retentionTagForCommit(source.commit),
rev: source.commit.toLowerCase(),
});
+30
View File
@@ -0,0 +1,30 @@
import assert from "node:assert/strict";
import test from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/parser.js";
import { generateTypeScriptBindings, type BindingSchema } from "../src/bindings/index.js";
const source = { repository: "https://example.test/p.git", commit: "1".repeat(40) };
const schemaFor = (body: string): BindingSchema => {
const compiled = compileCapabilityResourceSource(`external atom Thing id "thing"; package Demo id "demo" revision "demo@1" { ${body} }`, { source });
assert.equal(compiled.ok, true, JSON.stringify(compiled.diagnostics));
if (!compiled.ok || compiled.resource.kind !== "package") throw new Error("expected package");
return { format: "quixos-bindings", version: 1, interfaces: [], packages: [compiled.resource.revision] };
};
test("generator uses exact IDs, restricted ports, nominal references, and lossless scalar types", () => {
const schema = schemaFor('operation run id "run-id" : list<int64> -> optional<atom-ref<Thing>> mode call receiver atom Thing requires { state payload id "payload-id" : bytes [read]; };');
const generated = generateTypeScriptBindings(schema, "demo@1");
assert.match(generated, /Array<bigint>/);
assert.match(generated, /Promise<Uint8Array>/);
assert.match(generated, /QxObjectRef<"atom:thing">/);
assert.doesNotMatch(generated, /"set":/);
assert.match(generated, /"run-id": bindQxHandler/);
assert.equal(generateTypeScriptBindings(schema, "demo@1"), generated);
});
test("missing external types and constructor signatures fail generation", () => {
const schema = schemaFor('function run id "run" : unit -> message "example.Payload";');
assert.throws(() => generateTypeScriptBindings(schema, "demo@1"), /Missing TypeScript message binding/);
assert.match(generateTypeScriptBindings(schema, "demo@1", { messages: { "example.Payload": { module: "./payload.js", export: "payload" } } }), /BindingValue<typeof message0>/);
const constructor = schemaFor('function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing; };');
assert.throws(() => generateTypeScriptBindings(constructor, "demo@1"), /explicit input contract/);
const typed = schemaFor('function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing input string; };');
assert.match(generateTypeScriptBindings(typed, "demo@1"), /construct.*input: string/);
});
+50
View File
@@ -0,0 +1,50 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { snapshotRepository, checkWorkspaceCandidate } from "../src/capability-language/candidate-check.js";
const execFile = promisify(callback);
test("candidate snapshots include dirty and new files without changing Git history or index", async (context) => {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-candidate-test-"));
context.after(() => fs.rm(directory, { recursive: true, force: true }));
const root = path.join(directory, "source");
await fs.mkdir(root);
const git = (...args: string[]) => execFile("git", ["-C", root, ...args]);
await git("init");
await fs.writeFile(path.join(root, "tracked"), "original");
await fs.writeFile(path.join(root, ".gitignore"), "ignored\n");
await git("add", ".");
const index = await fs.readFile(path.join(root, ".git/index"));
await fs.writeFile(path.join(root, "tracked"), "edited");
await fs.writeFile(path.join(root, "new"), "new content");
await fs.writeFile(path.join(root, "ignored"), "not source");
const snapshot = await snapshotRepository(root, path.join(directory, "snapshot"));
assert.equal(await fs.readFile(path.join(snapshot.directory, "tracked"), "utf8"), "edited");
assert.equal(await fs.readFile(path.join(snapshot.directory, "new"), "utf8"), "new content");
await assert.rejects(fs.access(path.join(snapshot.directory, "ignored")));
assert.deepEqual(await fs.readFile(path.join(root, ".git/index")), index);
await assert.rejects(git("rev-parse", "--verify", "HEAD"), "no commit was created");
await fs.symlink("tracked", path.join(root, "link"));
await assert.rejects(snapshotRepository(root, path.join(directory, "rejected")), /regular file/);
});
test("candidate check produces explicitly non-activation evidence and never overwrites a report", async (context) => {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-check-test-"));
context.after(() => fs.rm(directory, { recursive: true, force: true }));
const root = path.join(directory, "source"), output = path.join(directory, "check");
await fs.mkdir(root);
await execFile("git", ["-C", root, "init"]);
await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`);
await fs.writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { atom Subject id "atom:subject"; }`);
const result = await checkWorkspaceCandidate({root, output});
assert.equal(result.candidateOnly, true);
assert.equal(result.activationEvidence, false);
assert.deepEqual(result.blockers, []);
const report = await fs.readFile(path.join(output, "report.json"));
await assert.rejects(checkWorkspaceCandidate({root, output}), /EEXIST/);
assert.deepEqual(await fs.readFile(path.join(output, "report.json")), report);
});
+184
View File
@@ -0,0 +1,184 @@
import assert from "node:assert/strict";
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
compileCapabilityResourceRepository,
compileWorkspaceRepository,
} from "../src/capability-language/index.js";
const quixosCommit = "1".repeat(40);
const namedCommit = "2".repeat(40);
const packageCommit = "3".repeat(40);
const lock = (resources = "") => `quixos-lock version 1 {
quixos source {
repository "https://example.test/quixos.git";
commit "${quixosCommit}";
}
${resources}
}
`;
test("workspace assembly resolves resource-owned dependencies recursively", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-"));
context.after(() => rm(directory, { recursive: true, force: true }));
const root = path.join(directory, "root");
const named = path.join(directory, "named");
const runtime = path.join(directory, "runtime");
await Promise.all([mkdir(root), mkdir(named), mkdir(runtime)]);
await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source {
repository "https://example.test/package-runtime.git";
commit "${packageCommit}";
}`));
await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" {
atom Subject id "atom:subject";
import package Runtime;
}
`);
await writeFile(path.join(named, "quixos.lock"), lock());
await writeFile(path.join(named, "interface.qx"), `interface Named id "interface:named" revision "interface:named@1" {
value name id "member:named:name" : string {
get id "operation:named:name:get";
}
}
`);
await writeFile(path.join(runtime, "quixos.lock"), lock(`interface Named source {
repository "https://example.test/interface-named.git";
commit "${namedCommit}";
}`));
await writeFile(path.join(runtime, "package.qx"), `import interface Named;
package Runtime id "package:runtime" revision "package:runtime@1" {
function describe id "export:runtime:describe" : interface-ref<Named> -> string;
}
`);
const directories = new Map([
[`interface\0https://example.test/interface-named.git\0${namedCommit}`, named],
[`package\0https://example.test/package-runtime.git\0${packageCommit}`, runtime],
]);
const result = await compileWorkspaceRepository({
rootDirectory: root,
resolveResource: async (source, kind) => {
const resolved = directories.get(`${kind}\0${source.repository}\0${source.commit}`);
if (!resolved) throw new Error(`Unexpected source ${source.repository}`);
return { directory: resolved };
},
});
assert.equal(result.resources.length, 2);
assert.deepEqual(
result.workspace.interfaceImports.map((entry) => entry.revisionId),
["interface:named@1"],
);
assert.deepEqual(
result.workspace.packageImports.map((entry) => entry.revisionId),
["package:runtime@1"],
);
assert.equal(result.directResources.size, 1);
const resource = await compileCapabilityResourceRepository({
rootDirectory: runtime,
kind: "package",
source: {
resolver: "git",
repository: "https://example.test/package-runtime.git",
commit: packageCommit,
},
resolveResource: async (source, kind) => {
const resolved = directories.get(`${kind}\0${source.repository}\0${source.commit}`);
if (!resolved) throw new Error(`Unexpected source ${source.repository}`);
return { directory: resolved };
},
});
assert.equal(resource.resource.kind, "package");
assert.equal(resource.resources.length, 2);
assert.equal(resource.directResources.size, 1);
});
test("resource repositories cannot own workspace Quixos selection policy", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-resource-policy-"));
context.after(() => rm(directory, { recursive: true, force: true }));
await writeFile(path.join(directory, "quixos.lock"), `quixos-lock version 1 {
quixos source {
repository "https://example.test/quixos.git";
policy track-development;
ref "dev/alice/main";
commit "${quixosCommit}";
}
}`);
await writeFile(path.join(directory, "package.qx"), `package Runtime id "package:runtime" revision "package:runtime@1" { }\n`);
await assert.rejects(
compileCapabilityResourceRepository({
rootDirectory: directory,
kind: "package",
source: {
resolver: "git",
repository: "https://example.test/package-runtime.git",
commit: packageCommit,
},
resolveResource: async () => ({ directory }),
}),
/resource locks may only declare their exact authored-against commit/,
);
});
test("resource manifests cannot hide lock dependencies", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-mismatch-"));
context.after(() => rm(directory, { recursive: true, force: true }));
const root = path.join(directory, "root");
const runtime = path.join(directory, "runtime");
await Promise.all([mkdir(root), mkdir(runtime)]);
await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source {
repository "https://example.test/package-runtime.git";
commit "${packageCommit}";
}`));
await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" {
atom Subject id "atom:subject";
import package Runtime;
}
`);
await writeFile(path.join(runtime, "quixos.lock"), lock());
await writeFile(path.join(runtime, "package.qx"), `import interface Hidden;
package Runtime id "package:runtime" revision "package:runtime@1" { }
`);
await assert.rejects(
compileWorkspaceRepository({
rootDirectory: root,
resolveResource: async () => ({ directory: runtime }),
}),
/No resolved interface is available for lock binding Hidden/,
);
});
test("workspace assembly must satisfy nominal external interfaces", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-external-"));
context.after(() => rm(directory, { recursive: true, force: true }));
const root = path.join(directory, "root");
const runtime = path.join(directory, "runtime");
await Promise.all([mkdir(root), mkdir(runtime)]);
await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source {
repository "https://example.test/package-runtime.git";
commit "${packageCommit}";
}`));
await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" {
atom Subject id "atom:subject";
import package Runtime;
}
`);
await writeFile(path.join(runtime, "quixos.lock"), lock());
await writeFile(path.join(runtime, "package.qx"), `external interface Named revision "interface:named@1";
package Runtime id "package:runtime" revision "package:runtime@1" { }
`);
await assert.rejects(
compileWorkspaceRepository({
rootDirectory: root,
resolveResource: async () => ({ directory: runtime }),
}),
/requires external interface Named \(interface:named@1\)/,
);
});
+30
View File
@@ -0,0 +1,30 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { resolve } from "node:path";
import { test } from "node:test";
const cli = resolve(process.cwd(), "dist/src/capability-language/cli.js");
const runResourceCheck = (source: string) => spawnSync(
process.execPath,
[cli, "--resource", "--check", "-"],
{ input: source, encoding: "utf8" },
);
test("capability CLI checks a standalone interface resource", () => {
const result = runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" {
value temperature id "member:weather:temperature" : double {
get id "operation:weather:temperature:get";
}
}\n`);
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stdout, "");
});
test("capability CLI reports invalid standalone interface members", () => {
const result = runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" {
value temperature : definitely-not-a-type;
}\n`);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /syntax-error/);
});
+302
View File
@@ -0,0 +1,302 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { test } from "node:test";
import {
compileCapabilityResourceSource,
compileCapabilitySource,
} from "../src/capability-language/index.js";
import {
capabilityFixtureSource,
capabilityResourceSources,
compileCapabilityFixture,
fixtureId,
} from "./fixtures/capability-model.js";
test("RPC record fields retain declared reference targets and reject duplicate fields", () => {
const compile = (fields: string) => compileCapabilityResourceSource(`
external atom Board id "atom:board";
package Runtime id "package:runtime" revision "package:runtime@1" {
function move id "export:move" : record { ${fields} } -> unit;
}`, {source: {repository: "https://example.test/runtime.git", commit: "a".repeat(40)}});
const result = compile("board: atom-ref<Board>; position: record { x: double; y: double; }; note: optional<string>;");
assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
assert.equal(compile("board: atom-ref<Board>; board: string;").ok, false);
});
const compileWebStudioFixture = (packageTransform = (source: string) => source) => {
const fixture = (name: string) => readFileSync(
resolve(process.cwd(), "test/fixtures", name),
"utf8",
);
const react = compileCapabilityResourceSource(
fixture("react-component.interface.qx"),
{
source: {
repository: "https://repos.quixos.org/org-quixos-web-studio/interface-react-component.git",
commit: "2".repeat(40),
},
},
);
if (!react.ok || react.resource.kind !== "interface") {
throw new Error("ReactComponent fixture did not compile");
}
const named = compileCapabilityResourceSource(
fixture("named.interface.qx"),
{
source: {
repository: "https://repos.quixos.org/quixos-test/interface-named.git",
commit: "5".repeat(40),
},
},
);
if (!named.ok || named.resource.kind !== "interface") {
throw new Error("Named fixture did not compile");
}
const has = compileCapabilityResourceSource(
fixture("has-react-component.interface.qx"),
{
source: {
repository: "https://repos.quixos.org/org-quixos-web-studio/interface-has-react-component.git",
commit: "3".repeat(40),
},
environment: {
interfaces: new Map([["ReactComponent", react.resource.revision]]),
interfaceClosure: [react.resource.revision],
},
},
);
if (!has.ok || has.resource.kind !== "interface") {
throw new Error("HasReactComponent fixture did not compile");
}
const component = compileCapabilityResourceSource(
packageTransform(fixture("component-runtime.package.qx")),
{
source: {
repository: "https://repos.quixos.org/quixos-test/package-component-runtime.git",
commit: "4".repeat(40),
},
environment: {
interfaces: new Map([["Named", named.resource.revision]]),
interfaceClosure: [named.resource.revision],
},
},
);
if (!component.ok || component.resource.kind !== "package") {
throw new Error("ComponentRuntime fixture did not compile");
}
return compileCapabilitySource(
fixture("web-studio.capabilities.qx"),
"web-studio.capabilities.qx",
{
interfaces: new Map([
["Named", named.resource.revision],
["ReactComponent", react.resource.revision],
["HasReactComponent", has.resource.revision],
]),
packages: new Map([["ComponentRuntime", component.resource.revision]]),
interfaceClosure: [named.resource.revision, react.resource.revision, has.resource.revision],
packageClosure: [component.resource.revision],
},
);
};
test("ANTLR parses and validates a complete capability workspace", () => {
const result = compileCapabilityFixture();
assert.equal(result.ok, true);
if (!result.ok) return;
assert.equal(result.workspace.workspaceId, fixtureId.workspace);
assert.equal(result.workspace.id, fixtureId.workspaceRevision);
assert.equal(result.workspace.interfaceImports.length, 3);
assert.equal(result.workspace.conformances.length, 4);
assert.equal("id" in result.workspace.conformances[0]!, false);
});
test("conformances preserve authored migration lineage IDs", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace(
"conform Project as Named {",
'conform Project as Named id "conformance:project:named" {',
),
});
assert.equal(result.ok, true);
if (!result.ok) return;
assert.equal(result.workspace.conformances[0]!.id, "conformance:project:named");
});
test("the v1 language has no implicit relationship materialization rule", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace(
/\n}\s*$/,
'\n materialize ProjectOwner.owner if absent with Person;\n}\n',
),
});
assert.equal(result.ok, false);
if (result.ok) return;
assert.ok(result.diagnostics.some((entry) => entry.phase === "syntax"));
});
test("forward declarations make source order irrelevant", () => {
const source = capabilityFixtureSource
.replace(/ atom Project[^\n]*\n/, "")
.replace(/ atom Person[^\n]*\n/, "")
.replace(
/\n}\s*$/,
'\n atom Project id "atom:project";\n atom Person id "atom:person";\n}\n',
);
assert.equal(compileCapabilityFixture({ workspace: source }).ok, true);
});
test("interfaces can declare ordinary call operations", () => {
const workspace = capabilityFixtureSource.replace(
"bind summary.get to package TodoRuntime.summaryGet",
"bind summarize.call to package TodoRuntime.summaryGet",
);
const summary = capabilityResourceSources.summary.replace(
" value summary id \"member:summary:summary\" : string {\n get id \"operation:summary:summary:get\";\n }",
" operation summarize id \"member:summary:summarize\" : string -> string {\n call id \"operation:summary:summarize\";\n }",
);
const todo = capabilityResourceSources.todo.replace(
"operation summaryGet id \"export:todo-runtime:summary-get\" : unit -> string",
"operation summaryGet id \"export:todo-runtime:summary-get\" : string -> string",
);
const result = compileCapabilityFixture({ workspace, summary, todo });
assert.equal(result.ok, true);
if (!result.ok) return;
const member = result.workspace.interfaceImports
.find((entry) => entry.displayName === "Summary")?.members[0];
assert.equal(member?.kind, "operation");
});
test("state defaults accept recursive JSON values", () => {
const source = capabilityFixtureSource.replace(
' policy optimistic-register default "Untitled project";',
` policy optimistic-register default "Untitled project";
shared state ProjectMetadata id "slot:project:metadata" on Project : message "example.Metadata"
policy optimistic-register default {"labels":["compiler","runtime"],"score":1.5,"enabled":true,"extra":null};`,
);
const result = compileCapabilityFixture({ workspace: source });
assert.equal(result.ok, true);
if (!result.ok) return;
const metadata = result.workspace.sharedAttachments.find(
(entry) => entry.id === "slot:project:metadata",
);
assert.equal(metadata?.kind, "state");
if (metadata?.kind !== "state") return;
assert.deepEqual(metadata.defaultValue, {
labels: ["compiler", "runtime"],
score: 1.5,
enabled: true,
extra: null,
});
});
test("syntax errors retain source locations", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace("atom Project", "atom Project ???"),
});
assert.equal(result.ok, false);
if (result.ok) return;
assert.ok(result.diagnostics.some((entry) =>
entry.phase === "syntax" && entry.line > 0
));
});
test("unknown authoring names are lowering errors", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace(
"to state ProjectTitle.read",
"to state NotAState.read",
),
});
assert.equal(result.ok, false);
if (result.ok) return;
assert.ok(result.diagnostics.some((entry) =>
entry.phase === "lowering" &&
entry.code === "unknown-symbol" &&
entry.message.includes("NotAState")
));
});
test("well-formed but invalid programs report semantic paths", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace(
"bind name.get to state ProjectTitle.read",
"bind name.get to state ProjectTitle.write",
),
});
assert.equal(result.ok, false);
if (result.ok) return;
const diagnostic = result.diagnostics.find(
(entry) => entry.code === "invalid-state-binding",
);
assert.ok(diagnostic);
assert.equal(diagnostic.phase, "validation");
assert.ok(diagnostic.path?.includes("operationBindings"));
});
test("Web Studio sidecars declare lazy materialization and checked cross-object ports", () => {
const result = compileWebStudioFixture();
assert.equal(result.ok, true);
if (!result.ok) return;
const host = result.workspace.conformances.find((entry) =>
entry.atomId === "atom:project" &&
entry.interfaceRevisionId === "interface:org.quixos.web-studio.has-react-component@1"
);
assert.deepEqual(host?.relationshipMaterializations, [{
memberId: "member:org.quixos.web-studio.has-react-component:component",
constructorAtomId: "atom:project-component",
edgeTypeId: "edge:project:component",
constructedProjectionId: "projection:component:subject",
}]);
const component = result.workspace.conformances.find((entry) =>
entry.atomId === "atom:project-component"
);
const binding = component?.operationBindings[0]?.binding;
assert.equal(binding?.kind, "package");
if (binding?.kind !== "package") return;
assert.deepEqual(binding.dependencies[0]?.binding, {
kind: "interface",
interfaceRevisionId: "interface:named@1",
via: {
edgeTypeId: "edge:project:component",
projectionId: "projection:component:subject",
},
});
});
test("relationship materializers require a constructor from the host atom", () => {
const result = compileWebStudioFixture((source) => source.replace(
"constructs ProjectComponent : atom-ref<Project>;",
"constructs ProjectComponent : unit;",
));
assert.equal(result.ok, false);
if (result.ok) return;
assert.ok(result.diagnostics.some((entry) =>
entry.code === "invalid-relationship-materialization" &&
entry.message.includes("must accept")
));
});
test("callable interface ports require a full source dependency, not a nominal reference", () => {
const result = compileCapabilityResourceSource(`
external interface Named revision "interface:named@1";
package Runtime id "package:runtime" revision "package:runtime@1" {
function readName id "export:runtime:read-name" : unit -> string requires {
interface named id "port:runtime:named" : Named;
};
}
`, {
source: {
repository: "https://repos.quixos.org/quixos-test/package-runtime.git",
commit: "6".repeat(40),
},
});
assert.equal(result.ok, false);
if (result.ok) return;
assert.ok(result.diagnostics.some((entry) =>
entry.code === "nominal-interface-port" && entry.message.includes("import interface Named")
));
});
+278
View File
@@ -0,0 +1,278 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import {
compileWorkspaceRevision,
computeCapabilityClosure,
resolveOperationPlan,
validateWorkspaceRevision,
valueType,
type CapabilityValidationIssueCode,
type WorkspaceRevision,
} from "../src/capability-model/index.js";
import {
fixtureId,
makeValidCapabilityWorkspace,
} from "./fixtures/capability-model.js";
test("ordinary state rejects managed references even under list/optional wrappers", () => {
const workspace = makeValidCapabilityWorkspace();
const state = [...workspace.sharedAttachments, ...workspace.conformances.flatMap((entry) => entry.privateAttachments)].find((entry) => entry.kind === "state")!;
if (state.kind !== "state") throw new Error("fixture missing state");
state.valueType = {kind: "list", value: {kind: "optional", value: {kind: "object-ref", expectation: {kind: "atom", atomId: state.attachedTo}}}};
assert.ok(validateWorkspaceRevision(workspace).some((entry) => entry.message.includes("graph relationships")));
});
const expectIssue = (
workspace: WorkspaceRevision,
code: CapabilityValidationIssueCode,
) => {
const issues = validateWorkspaceRevision(workspace);
assert.ok(
issues.some((entry) => entry.code === code),
`Expected ${code}, got:\n${issues
.map((entry) => `${entry.code} ${entry.path}: ${entry.message}`)
.join("\n")}`,
);
assert.equal(compileWorkspaceRevision(workspace).ok, false);
};
test("constructor dependency input contracts match the selected constructor", () => {
const workspace = makeValidCapabilityWorkspace();
const port = workspace.packageImports.flatMap((pkg) => pkg.exports).flatMap((entry) => entry.dependencyPorts)
.find((port) => port.requirement.kind === "constructor")!;
assert.equal(port.requirement.kind, "constructor");
if (port.requirement.kind !== "constructor") return;
port.requirement.inputType = valueType.unit;
assert.deepEqual(validateWorkspaceRevision(workspace), []);
port.requirement.inputType = valueType.string;
expectIssue(workspace, "invalid-dependency-binding");
});
const conformance = (
workspace: WorkspaceRevision,
identity: Pick<(typeof workspace.conformances)[number], "atomId" | "interfaceRevisionId">,
) => {
const result = workspace.conformances.find((entry) =>
entry.atomId === identity.atomId &&
entry.interfaceRevisionId === identity.interfaceRevisionId
);
assert.ok(
result,
`Missing fixture conformance ${identity.atomId} as ${identity.interfaceRevisionId}`,
);
return result;
};
test("the representative v1 workspace compiles to native and package plans", () => {
const workspace = makeValidCapabilityWorkspace();
assert.deepEqual(validateWorkspaceRevision(workspace), []);
const compiled = compileWorkspaceRevision(workspace);
assert.equal(compiled.ok, true);
if (!compiled.ok) return;
const title = resolveOperationPlan(
compiled.plan,
fixtureId.project,
fixtureId.namedV1,
fixtureId.namedGet,
);
assert.equal(title?.kind, "state");
if (title?.kind === "state") {
assert.equal(title.binding.slotId, fixtureId.projectTitle);
assert.equal(title.binding.primitive, "read");
assert.deepEqual(title.attachment.owner, { kind: "workspace" });
}
const owner = resolveOperationPlan(
compiled.plan,
fixtureId.project,
fixtureId.ownedV1,
fixtureId.ownerResolve,
);
assert.equal(owner?.kind, "edge");
if (owner?.kind === "edge") {
assert.equal(owner.binding.edgeTypeId, fixtureId.projectOwner);
assert.deepEqual(owner.attachment.owner, {
kind: "conformance",
...fixtureId.projectOwnedConformance,
});
}
const summary = resolveOperationPlan(
compiled.plan,
fixtureId.project,
fixtureId.summaryV1,
fixtureId.summaryGet,
);
assert.equal(summary?.kind, "package");
if (summary?.kind === "package") {
assert.equal(summary.packageRevision.revisionId, fixtureId.todoRuntimeV1);
assert.equal(summary.packageExport.id, fixtureId.summaryGetExport);
assert.deepEqual(
summary.dependencies.map((entry) => [entry.port.id, entry.binding.kind]),
[
[fixtureId.titlePort, "state"],
[fixtureId.namedPort, "interface"],
[fixtureId.personPort, "constructor"],
],
);
}
});
test("the closure is an exact tree-shaking boundary", () => {
const result = compileWorkspaceRevision(makeValidCapabilityWorkspace());
assert.equal(result.ok, true);
if (!result.ok) return;
const closure = computeCapabilityClosure(result.plan, [
{ atomId: fixtureId.project, interfaceRevisionId: fixtureId.summaryV1 },
]);
assert.deepEqual(closure.conformances, [
fixtureId.projectNamedConformance,
fixtureId.projectSummaryConformance,
]);
assert.deepEqual(closure.packageRevisionIds, [fixtureId.todoRuntimeV1]);
assert.deepEqual(closure.attachmentIds, [fixtureId.projectTitle]);
assert.deepEqual(closure.constructorAtomIds, [fixtureId.person]);
});
test("compiled plans are snapshots, not mutable authoring state", () => {
const workspace = makeValidCapabilityWorkspace();
const result = compileWorkspaceRevision(workspace);
assert.equal(result.ok, true);
if (!result.ok) return;
conformance(workspace, fixtureId.projectNamedConformance)
.operationBindings[0]!.binding = {
kind: "state",
slotId: fixtureId.personName,
primitive: "read",
};
const resolved = resolveOperationPlan(
result.plan,
fixtureId.project,
fixtureId.namedV1,
fixtureId.namedGet,
);
assert.equal(resolved?.kind, "state");
if (resolved?.kind === "state") {
assert.equal(resolved.binding.slotId, fixtureId.projectTitle);
}
});
test("a conformance binds every operation exactly once", () => {
const missing = makeValidCapabilityWorkspace();
conformance(missing, fixtureId.projectNamedConformance).operationBindings.pop();
expectIssue(missing, "missing-operation-binding");
const duplicate = makeValidCapabilityWorkspace();
const bindings = conformance(
duplicate,
fixtureId.projectNamedConformance,
).operationBindings;
bindings.push(structuredClone(bindings[0]!));
expectIssue(duplicate, "duplicate-operation-binding");
});
test("private attachments are visible only to their owning conformance", () => {
const workspace = makeValidCapabilityWorkspace();
const binding = conformance(
workspace,
fixtureId.projectNamedConformance,
).operationBindings[0]!.binding;
assert.equal(binding.kind, "state");
if (binding.kind !== "state") return;
binding.slotId = fixtureId.personName;
expectIssue(workspace, "private-attachment-access");
});
test("related-object dependency views cannot traverse another conformance's private edge", () => {
const workspace = makeValidCapabilityWorkspace();
const packageBinding = conformance(
workspace,
fixtureId.projectSummaryConformance,
).operationBindings[0]!.binding;
assert.equal(packageBinding.kind, "package");
if (packageBinding.kind !== "package") return;
const named = packageBinding.dependencies.find(
(dependency) => dependency.portId === fixtureId.namedPort,
);
assert.ok(named && named.binding.kind === "interface");
named.binding.via = {
edgeTypeId: fixtureId.projectOwner,
projectionId: fixtureId.projectOwnerProjection,
};
expectIssue(workspace, "private-attachment-access");
const owner = conformance(workspace, fixtureId.projectOwnedConformance);
const edge = owner.privateAttachments.find((entry) => entry.kind === "edge" && entry.id === fixtureId.projectOwner);
assert.ok(edge?.kind === "edge");
edge.endpoints.find((endpoint) => endpoint.projectionId === fixtureId.projectOwnerProjection)!.publicTraversal = true;
assert.equal(validateWorkspaceRevision(workspace).some((entry) => entry.code === "private-attachment-access"), false, "Only an explicitly exported read-only traversal crosses ownership");
});
test("native state and edge providers must match operation shape", () => {
const stateWorkspace = makeValidCapabilityWorkspace();
const state = conformance(
stateWorkspace,
fixtureId.projectNamedConformance,
).operationBindings[0]!.binding;
assert.equal(state.kind, "state");
if (state.kind === "state") state.primitive = "write";
expectIssue(stateWorkspace, "invalid-state-binding");
const edgeWorkspace = makeValidCapabilityWorkspace();
const edge = conformance(
edgeWorkspace,
fixtureId.projectOwnedConformance,
).operationBindings[0]!.binding;
assert.equal(edge.kind, "edge");
if (edge.kind === "edge") edge.primitive = "connect";
expectIssue(edgeWorkspace, "invalid-edge-binding");
});
test("package dependencies are complete, exact, and explicitly injected", () => {
const missing = makeValidCapabilityWorkspace();
const packageBinding = conformance(
missing,
fixtureId.projectSummaryConformance,
).operationBindings[0]!.binding;
assert.equal(packageBinding.kind, "package");
if (packageBinding.kind === "package") packageBinding.dependencies.pop();
expectIssue(missing, "invalid-dependency-binding");
const wrongType = makeValidCapabilityWorkspace();
const summaryExport = wrongType.packageImports[0]!.exports.find(
(entry) => entry.id === fixtureId.summaryGetExport,
);
assert.ok(summaryExport);
summaryExport.dependencyPorts[0]!.requirement = {
kind: "state",
valueType: valueType.int32,
primitives: ["read"],
};
expectIssue(wrongType, "invalid-dependency-binding");
});
test("package receiver requirements cannot depend on themselves", () => {
const workspace = makeValidCapabilityWorkspace();
const summaryExport = workspace.packageImports[0]!.exports.find(
(entry) => entry.id === fixtureId.summaryGetExport,
);
assert.ok(summaryExport && summaryExport.kind === "operation");
summaryExport.receiverRequirement = {
kind: "all-interfaces",
interfaceRevisionIds: [fixtureId.summaryV1],
};
summaryExport.dependencyPorts = [];
const packageBinding = conformance(
workspace,
fixtureId.projectSummaryConformance,
).operationBindings[0]!.binding;
assert.equal(packageBinding.kind, "package");
if (packageBinding.kind === "package") packageBinding.dependencies = [];
expectIssue(workspace, "cyclic-conformance-requirement");
});
test("constructors are statically checked", () => {
const badConstructor = makeValidCapabilityWorkspace();
badConstructor.constructors[0]!.exportId = fixtureId.summaryGetExport;
expectIssue(badConstructor, "invalid-constructor");
});
+98
View File
@@ -0,0 +1,98 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { capabilityId as id, valueType, validateWorkspaceRevision } from "../src/capability-model/index.js";
import { contentDigest, planEvolution, runtimeContracts, storageContracts } from "../src/capability-model/evolution.js";
import { capabilityFixtureSource, capabilityResourceSources, compileCapabilityFixture, makeValidCapabilityWorkspace } from "./fixtures/capability-model.js";
test("QX carries stable conformance IDs and implementation semantic majors", () => {
const result = compileCapabilityFixture({
workspace: capabilityFixtureSource.replace("conform Project as Named {", 'conform Project as Named id "conformance:project:named" semantic-major 3 {'),
todo: capabilityResourceSources.todo.replace('revision "package:todo-runtime@1" {', 'revision "package:todo-runtime@1" semantic-major 2 {'),
});
assert.ok(result.ok, JSON.stringify(result));
assert.equal(result.workspace.conformances[0]!.id, "conformance:project:named");
assert.equal(result.workspace.conformances[0]!.semanticMajor, 3);
assert.equal(result.workspace.packageImports[0]!.semanticMajor, 2);
});
test("invalid majors and duplicate owner identities are rejected", () => {
const workspace = makeValidCapabilityWorkspace();
workspace.packageImports[0]!.semanticMajor = 0;
workspace.conformances[0]!.semanticMajor = 1.5;
workspace.conformances[0]!.id = id.conformance("same");
workspace.conformances[1]!.id = id.conformance("same");
const issues = validateWorkspaceRevision(workspace);
assert.equal(issues.filter((entry) => entry.code === "invalid-semantic-major").length, 2);
assert.ok(issues.some((entry) => entry.code === "duplicate-conformance-id"));
});
test("hashing is canonical and rejects values outside the persisted JSON contract", () => {
assert.equal(contentDigest({ z: 1, a: { y: 3, b: 2 } }), contentDigest({ a: { b: 2, y: 3 }, z: 1 }));
assert.notEqual(contentDigest([1, 2]), contentDigest([2, 1]));
assert.throws(() => contentDigest(NaN));
assert.throws(() => contentDigest(new Date()));
});
test("a workspace root change and display names do not restart package runtimes", () => {
const before = makeValidCapabilityWorkspace();
const after = structuredClone(before);
after.id = id.workspaceRevision("next");
after.sourceRootCommit = "f".repeat(40);
after.atoms[0]!.displayName = "Renamed";
after.sharedAttachments[0]!.displayName = "Renamed storage";
after.conformances.reverse();
after.interfaceImports.reverse();
const report = planEvolution(before, after, { allowLegacy: true });
assert.deepEqual(report.runtimeActions.map((entry) => entry.action), ["keep"]);
assert.deepEqual(report.storageChanges, []);
assert.deepEqual(report.packageChecks, []);
});
test("consumer changes preserve unrelated resource owners", () => {
const before = makeValidCapabilityWorkspace();
before.packageImports.push({ packageId: id.package("package:resource-owner"), revisionId: id.packageRevision("package:resource-owner@1"),
displayName: "ResourceOwner", source: { repository: "https://example.org/owner.git", commit: "a".repeat(40) },
exports: [{ kind: "function", id: id.packageExport("export:owner:ping"), displayName: "ping", inputType: valueType.unit, outputType: valueType.unit, dependencyPorts: [] }] });
const after = structuredClone(before);
after.packageImports[0]!.source.commit = "e".repeat(40);
const actions = planEvolution(before, after, { allowLegacy: true }).runtimeActions;
assert.equal(actions.find((entry) => entry.groupId === "package:resource-owner")!.action, "keep");
assert.equal(actions.find((entry) => entry.groupId === "package:todo-runtime")!.action, "replace");
});
test("storage defaults and ownership changes invalidate consumers without requiring a major", () => {
const before = makeValidCapabilityWorkspace();
const after = structuredClone(before);
const slot = after.sharedAttachments[0]!;
assert.equal(slot.kind, "state");
if (slot.kind === "state") slot.defaultValue = "Different default";
const report = planEvolution(before, after, { allowLegacy: true });
assert.equal(report.storageChanges.length, 1);
assert.equal(report.runtimeActions[0]!.action, "replace");
assert.deepEqual(report.reviews, []);
assert.notDeepEqual(storageContracts(before), storageContracts(after));
});
test("semantic review receipts cover exact consumer/provider contracts", () => {
const before = makeValidCapabilityWorkspace();
before.conformances[0]!.id = id.conformance("conformance:project:named");
const after = structuredClone(before);
after.conformances[0]!.semanticMajor = 2;
const report = planEvolution(before, after, { allowLegacy: true });
assert.equal(report.reviews.length, 1);
assert.equal(report.reviews[0]!.accepted, false);
const receipt = { requirementDigest: report.reviews[0]!.requirementDigest, decision: "accepted-unchanged" as const,
rationale: "Reviewed the semantic change against summary behavior", agentId: "workspace-agent" };
assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, true);
after.packageImports[0]!.source.commit = "f".repeat(40);
assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, false);
});
test("evolution enrollment is explicit and never erases legacy ownership", () => {
const workspace = makeValidCapabilityWorkspace();
const report = planEvolution(workspace, workspace);
assert.ok(report.blockers.some((entry) => entry.includes("workspace-shared")));
assert.ok(report.blockers.some((entry) => entry.includes("authored ID")));
assert.equal(runtimeContracts(workspace).length, 1);
assert.throws(() => planEvolution(workspace, { ...workspace, workspaceId: id.workspace("other") }), /different workspace/);
});
+194
View File
@@ -0,0 +1,194 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import {
capabilityId,
type WorkspaceRevision,
} from "../../src/capability-model/index.js";
import {
compileCapabilityResourceSource,
compileCapabilitySource,
type CapabilityImportEnvironment,
} from "../../src/capability-language/index.js";
import type {
InterfaceRevision,
PackageRevision,
} from "../../src/capability-model/index.js";
export const fixtureId = {
workspace: capabilityId.workspace("workspace:todo"),
workspaceRevision: capabilityId.workspaceRevision("workspace:todo@1"),
project: capabilityId.atom("atom:project"),
person: capabilityId.atom("atom:person"),
namedV1: capabilityId.interfaceRevision("interface:named@1"),
ownedV1: capabilityId.interfaceRevision("interface:owned@1"),
summaryV1: capabilityId.interfaceRevision("interface:summary@1"),
namedGet: capabilityId.operation("operation:named:name:get"),
namedSet: capabilityId.operation("operation:named:name:set"),
ownerResolve: capabilityId.operation("operation:owned:owner:resolve"),
summaryGet: capabilityId.operation("operation:summary:summary:get"),
projectTitle: capabilityId.slot("slot:project:title"),
personName: capabilityId.slot("slot:person:name"),
projectOwner: capabilityId.edgeType("edge:project:owner"),
projectOwnerProjection: capabilityId.edgeProjection(
"projection:project-owner:owner",
),
projectNamedConformance: {
atomId: capabilityId.atom("atom:project"),
interfaceRevisionId: capabilityId.interfaceRevision("interface:named@1"),
},
personNamedConformance: {
atomId: capabilityId.atom("atom:person"),
interfaceRevisionId: capabilityId.interfaceRevision("interface:named@1"),
},
projectOwnedConformance: {
atomId: capabilityId.atom("atom:project"),
interfaceRevisionId: capabilityId.interfaceRevision("interface:owned@1"),
},
projectSummaryConformance: {
atomId: capabilityId.atom("atom:project"),
interfaceRevisionId: capabilityId.interfaceRevision("interface:summary@1"),
},
todoRuntimeV1: capabilityId.packageRevision("package:todo-runtime@1"),
summaryGetExport: capabilityId.packageExport(
"export:todo-runtime:summary-get",
),
createPersonExport: capabilityId.packageExport(
"export:todo-runtime:create-person",
),
titlePort: capabilityId.dependencyPort("port:summary:title"),
namedPort: capabilityId.dependencyPort("port:summary:named"),
personPort: capabilityId.dependencyPort("port:summary:person"),
} as const;
export const capabilityFixturePath = resolve(
process.cwd(),
"test/fixtures/todo.capabilities.qx",
);
export const capabilityFixtureSource = readFileSync(
capabilityFixturePath,
"utf8",
);
const resourceSource = (name: string) => readFileSync(
resolve(process.cwd(), "test/fixtures", name),
"utf8",
);
export const capabilityResourceSources = {
named: resourceSource("named.interface.qx"),
owned: resourceSource("owned.interface.qx"),
summary: resourceSource("summary.interface.qx"),
todo: resourceSource("todo.package.qx"),
} as const;
const source = (repository: string, commit: string) => ({ repository, commit });
const interfaceRevision = (
resource: { kind: "interface"; revision: InterfaceRevision } |
{ kind: "package"; revision: PackageRevision },
) => {
if (resource.kind !== "interface") throw new Error("Expected interface fixture");
return resource.revision;
};
const packageRevision = (
resource: { kind: "interface"; revision: InterfaceRevision } |
{ kind: "package"; revision: PackageRevision },
) => {
if (resource.kind !== "package") throw new Error("Expected package fixture");
return resource.revision;
};
export const compileCapabilityFixture = (overrides: Partial<{
workspace: string;
named: string;
owned: string;
summary: string;
todo: string;
}> = {}) => {
const named = compileCapabilityResourceSource(
overrides.named ?? capabilityResourceSources.named,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-named.git",
"2".repeat(40),
),
fileName: "named.interface.qx",
},
);
if (!named.ok) return named;
const namedRevision = interfaceRevision(named.resource);
const namedEnvironment: CapabilityImportEnvironment = {
interfaces: new Map([["Named", namedRevision]]),
interfaceClosure: [namedRevision],
};
const owned = compileCapabilityResourceSource(
overrides.owned ?? capabilityResourceSources.owned,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-owned.git",
"3".repeat(40),
),
fileName: "owned.interface.qx",
environment: namedEnvironment,
},
);
if (!owned.ok) return owned;
const ownedRevision = interfaceRevision(owned.resource);
const summary = compileCapabilityResourceSource(
overrides.summary ?? capabilityResourceSources.summary,
{
source: source(
"https://repos.quixos.org/quixos-todo/interface-summary.git",
"4".repeat(40),
),
fileName: "summary.interface.qx",
},
);
if (!summary.ok) return summary;
const summaryRevision = interfaceRevision(summary.resource);
const todo = compileCapabilityResourceSource(
overrides.todo ?? capabilityResourceSources.todo,
{
source: source(
"https://repos.quixos.org/quixos-todo/package-todo-runtime.git",
"5".repeat(40),
),
fileName: "todo.package.qx",
environment: namedEnvironment,
},
);
if (!todo.ok) return todo;
const todoRevision = packageRevision(todo.resource);
return compileCapabilitySource(
overrides.workspace ?? capabilityFixtureSource,
capabilityFixturePath,
{
interfaces: new Map([
["Named", namedRevision],
["Owned", ownedRevision],
["Summary", summaryRevision],
]),
packages: new Map([["TodoRuntime", todoRevision]]),
interfaceClosure: [
namedRevision,
ownedRevision,
summaryRevision,
],
packageClosure: [todoRevision],
},
);
};
export const makeValidCapabilityWorkspace = (): WorkspaceRevision => {
const result = compileCapabilityFixture();
if (!result.ok) {
throw new Error(
result.diagnostics
.map((entry) => `${entry.phase}/${entry.code}: ${entry.message}`)
.join("\n"),
);
}
return structuredClone(result.workspace);
};
+10
View File
@@ -0,0 +1,10 @@
import interface Named;
external atom Project id "atom:project";
external atom ProjectComponent id "atom:project-component";
package ComponentRuntime id "package:component-runtime" revision "package:component-runtime@1" {
constructor createComponent id "export:component-runtime:create-component" constructs ProjectComponent : atom-ref<Project>;
operation propsGet id "export:component-runtime:props-get" : unit -> string mode call receiver atom ProjectComponent requires {
interface project id "port:component:project" : Named;
};
}
+7
View File
@@ -0,0 +1,7 @@
import interface ReactComponent;
interface HasReactComponent id "interface:org.quixos.web-studio.has-react-component" revision "interface:org.quixos.web-studio.has-react-component@1" {
relation component id "member:org.quixos.web-studio.has-react-component:component" : optional-one interface ReactComponent {
resolve id "operation:org.quixos.web-studio.has-react-component:component:resolve";
}
}
+7
View File
@@ -0,0 +1,7 @@
interface Named id "interface:named" revision "interface:named@1" {
value name id "member:named:name" : string {
get id "operation:named:name:get";
set id "operation:named:name:set";
watch start id "operation:named:name:watch-start" stop id "operation:named:name:watch-stop";
}
}
+10
View File
@@ -0,0 +1,10 @@
import interface Named;
interface Owned id "interface:owned" revision "interface:owned@1" {
relation owner id "member:owned:owner" : exactly-one interface Named {
resolve id "operation:owned:owner:resolve";
connect id "operation:owned:owner:connect";
disconnect id "operation:owned:owner:disconnect";
watch start id "operation:owned:owner:watch-start" stop id "operation:owned:owner:watch-stop";
}
}
+5
View File
@@ -0,0 +1,5 @@
interface ReactComponent id "interface:org.quixos.web-studio.react-component" revision "interface:org.quixos.web-studio.react-component@1" {
value props id "member:org.quixos.web-studio.react-component:props" : string {
get id "operation:org.quixos.web-studio.react-component:props:get";
}
}
+5
View File
@@ -0,0 +1,5 @@
interface Summary id "interface:summary" revision "interface:summary@1" {
value summary id "member:summary:summary" : string {
get id "operation:summary:summary:get";
}
}
+50
View File
@@ -0,0 +1,50 @@
workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "1111111111111111111111111111111111111111" {
atom Project id "atom:project" doc "A project containing work.";
atom Person id "atom:person";
import interface Named;
import interface Owned;
import interface Summary;
import package TodoRuntime;
shared state ProjectTitle id "slot:project:title" on Project : string
policy optimistic-register default "Untitled project";
conform Project as Named {
bind name.get to state ProjectTitle.read;
bind name.set to state ProjectTitle.write;
bind name.watch-start to state ProjectTitle.watch-start;
bind name.watch-stop to state ProjectTitle.watch-stop;
}
conform Person as Named {
private state PersonName id "slot:person:name" on Person : string
policy optimistic-register default "Anonymous";
bind name.get to state PersonName.read;
bind name.set to state PersonName.write;
bind name.watch-start to state PersonName.watch-start;
bind name.watch-stop to state PersonName.watch-stop;
}
conform Project as Owned {
private edge ProjectOwner id "edge:project:owner" {
atom Project projection owner id "projection:project-owner:owner" exactly-one;
interface Named projection ownedProjects id "projection:project-owner:owned-projects" many;
}
bind owner.resolve to edge ProjectOwner.owner.resolve;
bind owner.connect to edge ProjectOwner.owner.connect;
bind owner.disconnect to edge ProjectOwner.owner.disconnect;
bind owner.watch-start to edge ProjectOwner.owner.watch-start;
bind owner.watch-stop to edge ProjectOwner.owner.watch-stop;
}
conform Project as Summary {
bind summary.get to package TodoRuntime.summaryGet with {
title to state ProjectTitle;
named to interface Named;
person to constructor Person;
};
}
constructor Person to TodoRuntime.createPerson;
}
+12
View File
@@ -0,0 +1,12 @@
import interface Named;
external atom Person id "atom:person";
package TodoRuntime id "package:todo-runtime" revision "package:todo-runtime@1" {
operation summaryGet id "export:todo-runtime:summary-get" : unit -> string
mode call receiver interfaces [Named] requires {
state title id "port:summary:title" : string [read];
interface named id "port:summary:named" : Named;
constructor person id "port:summary:person" : Person;
};
constructor createPerson id "export:todo-runtime:create-person" constructs Person : unit;
}
+8
View File
@@ -0,0 +1,8 @@
external atom Thing id "atom:example:thing";
package TypedExample id "package:typed-example" revision "package:typed-example@1" {
constructor createThing id "export:typed-example:create" constructs Thing : unit;
operation payloadGet id "export:typed-example:payload" : unit -> bytes mode call receiver atom Thing requires {
state payload id "port:typed-example:payload" : bytes [read];
};
}
+6
View File
@@ -0,0 +1,6 @@
quixos-lock version 1 {
quixos source {
repository "https://example.test/quixos.git";
commit "1111111111111111111111111111111111111111";
}
}
+35
View File
@@ -0,0 +1,35 @@
workspace WebStudioFixture id "workspace:web-studio-fixture" revision "workspace:web-studio-fixture@1" commit "1111111111111111111111111111111111111111" {
atom Project id "atom:project";
atom ProjectComponent id "atom:project-component";
import interface Named;
import interface ReactComponent;
import interface HasReactComponent;
import package ComponentRuntime;
shared state ProjectName id "slot:project:name" on Project : string
policy optimistic-register default "Untitled project";
shared edge ProjectComponentEdge id "edge:project:component" {
atom ProjectComponent projection subject id "projection:component:subject" exactly-one;
atom Project projection component id "projection:project:component" optional-one;
}
conform Project as Named {
bind name.get to state ProjectName.read;
bind name.set to state ProjectName.write;
bind name.watch-start to state ProjectName.watch-start;
bind name.watch-stop to state ProjectName.watch-stop;
}
conform Project as HasReactComponent {
bind component.resolve to edge ProjectComponentEdge.component.resolve;
materialize component if absent using constructor ProjectComponent via edge ProjectComponentEdge.subject;
}
conform ProjectComponent as ReactComponent {
bind props.get to package ComponentRuntime.propsGet with {
project to interface Named via edge ProjectComponentEdge.subject;
};
}
constructor ProjectComponent to ComponentRuntime.createComponent;
}
+36
View File
@@ -0,0 +1,36 @@
import test from "node:test";
import assert from "node:assert/strict";
import { contentDigest, validateMigrationCatalog, selectMigrationPath, type MigrationCatalog } from "../src/capability-model/index.js";
const before = { fields: ["name"] }, after = { fields: ["first", "last"] };
const from = contentDigest(before), to = contentDigest(after);
const catalog = (): MigrationCatalog => ({ schemaVersion: 1, contracts: { [from]: before, [to]: after }, migrations: [{
id: "split-name", scopeId: "person-name", from, to,
implementation: { exportId: "migrate-name", file: "src/migrate-name.ts", digest: contentDigest("implementation") }, predecessors: [],
ports: [{ name: "source", view: "old", access: ["read"], contractDigest: from }, { name: "target", view: "new", access: ["write"], contractDigest: to }],
}] });
test("published migrations retain exact old contracts and explicit local bindings", () => {
const value = validateMigrationCatalog(catalog(), new Set(["migrate-name"]));
const selection = { scopeId: "person-name", from, to, path: ["split-name"], bindings: { source: "old-slot", target: "new-slot" } };
const result = selectMigrationPath(value, selection);
assert.equal(result[0].alreadyApplied, false);
assert.equal(selectMigrationPath(value, selection, new Map([["split-name", result[0].digest]]))[0].alreadyApplied, true);
const changed = catalog(); changed.migrations[0].implementation.digest = contentDigest("new code");
assert.throws(() => selectMigrationPath(changed, selection, new Map([["split-name", result[0].digest]])), /different code/);
assert.throws(() => selectMigrationPath(value, { ...selection, path: [] }), /does not cover/);
assert.throws(() => selectMigrationPath(value, { ...selection, bindings: {} }), /Missing local/);
});
test("old migration views cannot be writable and contract hashes are verified", () => {
const value = catalog(); value.migrations[0].ports[0].access = ["write"];
assert.throws(() => validateMigrationCatalog(value), /read-only/);
const corrupt = catalog(); corrupt.contracts[from] = {};
assert.throws(() => validateMigrationCatalog(corrupt), /digest mismatch/);
});
test("migration history rejects cycles and non-boolean compatibility promises", () => {
const cyclic = catalog();
cyclic.migrations[0].predecessors = ["split-name"];
assert.throws(() => validateMigrationCatalog(cyclic), /Cyclic/);
const misleading = catalog();
(misleading.migrations[0] as unknown as {preservesOldReaders: string}).preservesOldReaders = "false";
assert.throws(() => validateMigrationCatalog(misleading), /must be booleans/);
});
+59
View File
@@ -0,0 +1,59 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import {promisify} from "node:util";
import {execFile as callback} from "node:child_process";
import {planPinUpgrades, applyPinUpgrades, type UpgradeEffects} from "../src/capability-language/pin-upgrades.js";
const execFile = promisify(callback);
test("pin upgrades publish children before parent locks and resume without republishing completed nodes", async (context) => {
const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-test-"));
context.after(() => fs.rm(workbench, {recursive: true, force: true}));
const from = "a".repeat(40), to = "b".repeat(40), framework = "c".repeat(40);
const sources = [{kind: "workspace" as const, directory: "root", source: {repository: "https://example.test/workspace.git", commit: from}},
{kind: "interface" as const, directory: "resources/Named", source: {repository: "https://example.test/named.git", commit: from}}];
const lock = `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${framework}"; }`;
for (const node of sources) {
const directory = path.join(workbench, node.directory);
await fs.mkdir(directory, {recursive: true});
await execFile("git", ["-C", directory, "init"]);
await execFile("git", ["-C", directory, "remote", "add", "origin", node.source.repository]);
await fs.writeFile(path.join(directory, ".gitignore"), ".quixos/\n");
await fs.writeFile(path.join(directory, "quixos.lock"), lock + (node.kind === "workspace" ? ` interface Named source { repository "${sources[1].source.repository}"; commit "${from}"; }` : "") + " }");
}
await fs.writeFile(path.join(workbench, "resources/Named/interface.qx"), 'interface Named id "interface:named" revision "interface:named@1" { value name id "member:name" : string { get id "op:get"; } }');
await fs.writeFile(path.join(workbench, "root/workspace.qx"), `workspace W id "workspace:w" revision "workspace:w@1" commit "${from}" { import interface Named; atom A id "atom:a"; }`);
const snapshotMap = path.join(workbench, "snapshots.json");
await fs.writeFile(snapshotMap, JSON.stringify({resources: [{kind: "interface", repository: sources[1].source.repository, commit: to, directory: path.join(workbench, "resources/Named")}]}));
const oldMap = process.env.QUIXOS_SNAPSHOT_MAP;
process.env.QUIXOS_SNAPSHOT_MAP = snapshotMap;
context.after(() => {if (oldMap === undefined) delete process.env.QUIXOS_SNAPSHOT_MAP; else process.env.QUIXOS_SNAPSHOT_MAP = oldMap;});
const plan = await planPinUpgrades(workbench, {nodes: sources});
await fs.mkdir(path.join(workbench, ".quixos"), {recursive: true});
await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({resources: [{...sources[1], source: {resolver: "git", ...sources[1].source}}]}));
assert.deepEqual(plan.nodes.map((node) => node.directory), ["resources/Named", "root"]);
let fail = true;
const published: string[] = [];
const effects: UpgradeEffects = {
check: async (node) => {if (node.kind === "workspace" && fail) throw new Error("refactor required");},
snapshot: async () => to,
publish: async (root) => {published.push(path.relative(workbench, root));},
};
await assert.rejects(() => applyPinUpgrades(plan, undefined, effects), /refactor required/);
assert.deepEqual(published, ["resources/Named"]);
assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(to));
const id = (await fs.readdir(path.join(workbench, ".quixos/upgrades"))).find((name) => name.endsWith(".json"))!.slice(0, -5);
fail = false;
await fs.appendFile(path.join(workbench, "root/workspace.qx"), "\n// explicit refactor\n");
await assert.rejects(() => applyPinUpgrades(plan, id, effects), /accept-edits/);
const result = await applyPinUpgrades(plan, id, effects, {acceptEdits: true});
assert.equal(result.activated, false);
assert.deepEqual(published, ["resources/Named", "root"]);
const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"));
assert.equal(graph.resources.length, 1);
assert.equal(graph.resources[0].source.commit, to);
const next = await planPinUpgrades(workbench, {nodes: [sources[0], {...sources[1], source: graph.resources[0].source}]});
assert.deepEqual(next.nodes.find((node) => node.kind === "workspace")!.dependencies, ["resources/Named"]);
});
+84
View File
@@ -0,0 +1,84 @@
import assert from "node:assert/strict";
import test from "node:test";
import { mkdtemp, writeFile, readFile, rm, symlink } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { parseQx, formatQx, lintQx, applySourceEdits, addWorkspaceImport, walkSyntax,
resolveQxSources, readQxSource, compileCapabilitySource, scaffoldAtom, compileWorkspaceRepository } from "../src/capability-language/index.js";
const workspace = `workspace Test id "w" revision "w@1" commit "${"1".repeat(40)}" {\n// keep 🐈 comment\n}\n`;
test("lint reports invalid and redundant imports without resolving repositories", () => {
const source = workspace.replace("}\n", 'import "a.qx"; import "a.qx"; import "../bad.qx";\n}\n');
assert.deepEqual(lintQx(source).map((entry) => [entry.code, entry.severity]), [
["duplicate-source-import", "warning"], ["invalid-source-import", "error"],
]);
});
test("lossless tokens, UTF-16 ranges, and safe source edits", () => {
const text = workspace.replace("}\n", 'atom Cat id "🐈";\n}\n');
const parsed = parseQx(text);
assert.deepEqual(parsed.diagnostics, []);
assert.equal(parsed.tokens.map((token) => token.text).join(""), text);
const atom = [...walkSyntax(parsed.root)].find((node) => node.kind === "atomDecl")!;
assert.equal(text.slice(atom.start, atom.end), 'atom Cat id "🐈";');
assert.equal(applySourceEdits(text, [{ ...atom, text: 'atom Dog id "dog";' }]), text.replace('atom Cat id "🐈";', 'atom Dog id "dog";'));
assert.throws(() => applySourceEdits(text, [{ start: 0, end: 4, text: "" }, { start: 3, end: 7, text: "" }]), /overlapping/);
assert.throws(() => applySourceEdits(text, [{ start: -1, end: 0, text: "" }]), /Invalid/);
});
test("formatting preserves comments and strings, is idempotent, and rejects invalid source", () => {
const text = workspace.replace("}\n", 'atom Cat id "{ cat }"; /* multiline\n unchanged */\n}\n');
const formatted = formatQx(text);
assert.match(formatted, / atom Cat id "\{ cat \}"; \/\* multiline\n unchanged \*\//);
assert.equal(formatQx(formatted), formatted);
assert.deepEqual(parseQx(formatted).diagnostics, []);
assert.throws(() => formatQx("workspace {"), /syntax errors/);
});
test("imports preserve root text, deduplicate diamonds, reject cycles, and compile together", async () => {
const root = addWorkspaceImport(addWorkspaceImport(workspace, "a.qx"), "b.qx");
assert.equal(addWorkspaceImport(root, "a.qx"), root);
assert.match(root, /keep 🐈 comment/);
const files: Record<string, string> = { "workspace.qx": root,
"a.qx": 'fragment { import "shared.qx"; atom A id "a"; }',
"b.qx": 'fragment { import "shared.qx"; atom B id "b"; }',
"shared.qx": 'fragment { atom Shared id "shared"; }' };
const result = await resolveQxSources(async (name) => files[name]!);
assert.equal(result.sourceFiles.length, 4);
const compiled = compileCapabilitySource(result.source);
assert.equal(compiled.ok, true, JSON.stringify(compiled.diagnostics));
if (compiled.ok) assert.equal(compiled.workspace.atoms.length, 3);
const unresolved = compileCapabilitySource(root);
assert.equal(unresolved.ok, false);
assert.match(JSON.stringify(unresolved.diagnostics), /unresolved-source-import/);
files["shared.qx"] = 'fragment { import "a.qx"; }';
await assert.rejects(resolveQxSources(async (name) => files[name]!), /cycle/);
assert.throws(() => addWorkspaceImport(workspace, "../x.qx"), /Invalid/);
});
test("repository scaffolding validates before writing and refuses duplicates and symlinks", async (t) => {
const root = await mkdtemp(path.join(os.tmpdir(), "qx-scaffold-"));
t.after(() => rm(root, { recursive: true, force: true }));
await writeFile(path.join(root, "workspace.qx"), workspace);
await writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/q.git"; commit "${"1".repeat(40)}"; } }`);
const resolveResource = async (): Promise<never> => { throw new Error("unexpected resource"); };
const options = { root, name: "Cat", id: "cat", write: false, resolveResource };
await scaffoldAtom(options);
assert.equal(await readFile(path.join(root, "workspace.qx"), "utf8"), workspace);
await assert.rejects(readFile(path.join(root, "Cat.qx")), /ENOENT/);
await scaffoldAtom({ ...options, write: true });
const compiled = await compileWorkspaceRepository({ rootDirectory: root, resolveResource });
assert.equal(compiled.workspace.atoms[0]!.id, "cat");
await assert.rejects(scaffoldAtom({ ...options, write: true }), /already exists/);
await assert.rejects(scaffoldAtom({ ...options, name: "Other", write: true }), /Duplicate|duplicate/);
await assert.rejects(readFile(path.join(root, "Other.qx")), /ENOENT/);
await symlink(path.join(root, "Cat.qx"), path.join(root, "link.qx"));
await assert.rejects(readQxSource(root, "link.qx"), /ordinary files/);
});
test("lowering diagnostics map to the imported file", async () => {
const files: Record<string, string> = { "workspace.qx": addWorkspaceImport(workspace, "bad.qx"),
"bad.qx": 'fragment {\n conform Missing as Nope {}\n}' };
const sources = await resolveQxSources(async (name) => files[name]!);
const compiled = compileCapabilitySource(sources.source);
assert.equal(compiled.ok, false);
const diagnostic = compiled.diagnostics[0]!;
const position = sources.originalPosition(diagnostic.line, diagnostic.column);
assert.equal(position.fileName, "bad.qx");
assert.equal(position.line, 2);
});
+277
View File
@@ -0,0 +1,277 @@
import assert from "node:assert/strict";
import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
formatQuixosLock,
formatQuixosLockDocument,
loadQuixosLock,
nixGitInput,
parseQuixosLock,
parseQuixosLockDocument,
resolveQuixosLock,
retentionTagForCommit,
} from "../src/resource-lock/index.js";
const quixosCommit = "1".repeat(40);
const namedCommit = "2".repeat(40);
const packageCommit = "3".repeat(64);
const fixture = `quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
policy track-development;
ref "dev/alice/main";
commit "${quixosCommit}";
}
interface Named source {
repository "https://repos.example/alice/interface-named.git";
commit "${namedCommit}";
}
package TodoRuntime source {
repository "ssh://git@repos.example/alice/package-todo-runtime.git";
commit "${packageCommit}";
}
}
`;
test("parses and canonically formats a Git-only repository lock", () => {
const parsed = parseQuixosLock(fixture, "quixos.lock");
assert.equal(parsed.ok, true);
if (!parsed.ok) return;
assert.equal(parsed.lock.formatVersion, 1);
assert.equal(parsed.lock.quixos.commit, quixosCommit);
assert.equal(parsed.lock.quixos.policy, "track-development");
assert.equal(parsed.lock.quixos.ref, "dev/alice/main");
assert.deepEqual(
parsed.lock.resources.map(({ kind, binding }) => ({ kind, binding })),
[
{ kind: "interface", binding: "Named" },
{ kind: "package", binding: "TodoRuntime" },
],
);
assert.deepEqual(parseQuixosLock(formatQuixosLock(parsed.lock)), {
ok: true,
lock: parsed.lock,
diagnostics: [],
});
});
test("validates pinned Quixos selections without imposing policy on resource locks", () => {
const mismatch = parseQuixosLockDocument(`quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
policy pinned;
ref "${namedCommit}";
commit "${quixosCommit}";
}
}`);
assert.equal(mismatch.ok, false);
if (!mismatch.ok) {
assert.equal(mismatch.diagnostics[0]?.code, "pinned-quixos-commit-mismatch");
}
const resourceBaseline = parseQuixosLockDocument(`quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
commit "${quixosCommit}";
}
}`);
assert.equal(resourceBaseline.ok, true);
});
test("derives immutable retention and Nix inputs without storing extra identity", () => {
const source = {
resolver: "git" as const,
repository: "https://repos.example/alice/interface-named.git",
commit: namedCommit,
};
const ref = `refs/tags/quixos-reachability/${namedCommit}`;
assert.equal(retentionTagForCommit(namedCommit.toUpperCase()), ref);
assert.deepEqual(nixGitInput(source), {
type: "git",
url: source.repository,
ref,
rev: namedCommit,
});
});
test("rejects mutable revisions, embedded credentials, and duplicate bindings", () => {
const parsed = parseQuixosLock(`quixos-lock version 1 {
quixos source {
repository "https://user:secret@example/quixos.git";
commit "main";
}
interface Named source {
repository "file:///tmp/named";
commit "${namedCommit}";
}
interface Named source {
repository "https://repos.example/named.git?ref=main";
commit "${namedCommit}";
}
}`);
assert.equal(parsed.ok, false);
if (parsed.ok) return;
assert.deepEqual(
new Set(parsed.diagnostics.map((entry) => entry.code)),
new Set([
"invalid-git-commit",
"embedded-git-credential",
"unsupported-git-transport",
"decorated-git-repository",
"duplicate-resource-binding",
]),
);
});
test("resolves root-relative lock fragments into one deterministic resource closure", async () => {
const root = `quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
commit "${quixosCommit}";
}
import "locks/web-studio.lock";
import "locks/domain.lock";
package RootRuntime source {
repository "https://repos.example/alice/package-root.git";
commit "${packageCommit}";
}
}`;
const sources = new Map([
["locks/web-studio.lock", `quixos-lock fragment version 1 {
import "locks/shared.lock";
interface Placeable source {
repository "https://repos.example/alice/interface-placeable.git";
commit "${namedCommit}";
}
}`],
["locks/shared.lock", `quixos-lock fragment version 1 {
interface Named source {
repository "https://repos.example/alice/interface-named.git";
commit "${namedCommit}";
}
}`],
["locks/domain.lock", `quixos-lock fragment version 1 {
package TodoRuntime source {
repository "https://repos.example/alice/package-todo.git";
commit "${packageCommit}";
}
}`],
]);
const result = await resolveQuixosLock(root, async (relativePath) => {
const source = sources.get(relativePath);
if (!source) throw new Error("missing fixture");
return source;
});
assert.equal(result.ok, true);
if (!result.ok) return;
assert.deepEqual(result.lock.sourceFiles, [
"quixos.lock",
"locks/web-studio.lock",
"locks/shared.lock",
"locks/domain.lock",
]);
assert.deepEqual(result.lock.resources.map(({ kind, binding }) => ({ kind, binding })), [
{ kind: "package", binding: "RootRuntime" },
{ kind: "interface", binding: "Placeable" },
{ kind: "interface", binding: "Named" },
{ kind: "package", binding: "TodoRuntime" },
]);
});
test("lock fragments format canonically and cannot redeclare the Quixos source", () => {
const document = {
kind: "fragment" as const,
formatVersion: 1 as const,
imports: ["locks/shared.lock"],
resources: [{
kind: "interface" as const,
binding: "Named",
source: {
resolver: "git" as const,
repository: "https://repos.example/alice/interface-named.git",
commit: namedCommit,
},
}],
};
assert.deepEqual(parseQuixosLockDocument(formatQuixosLockDocument(document)), {
ok: true,
document,
diagnostics: [],
});
const invalid = parseQuixosLockDocument(`quixos-lock fragment version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
commit "${quixosCommit}";
}
}`, "bad.lock");
assert.equal(invalid.ok, false);
if (!invalid.ok) assert.equal(invalid.diagnostics[0]?.code, "fragment-has-quixos-source");
});
test("lock imports reject traversal, cycles, root documents, and cross-file binding collisions", async () => {
const invalidPath = parseQuixosLockDocument(`quixos-lock fragment version 1 {
import "../outside.lock";
}`, "bad-path.lock");
assert.equal(invalidPath.ok, false);
if (!invalidPath.ok) assert.equal(invalidPath.diagnostics[0]?.code, "invalid-import-path");
const root = `quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
commit "${quixosCommit}";
}
import "a.lock";
import "root-again.lock";
interface Named source {
repository "https://repos.example/alice/interface-named.git";
commit "${namedCommit}";
}
}`;
const sources = new Map([
["a.lock", `quixos-lock fragment version 1 {
import "b.lock";
interface Named source {
repository "https://repos.example/alice/interface-named-copy.git";
commit "${namedCommit}";
}
}`],
["b.lock", `quixos-lock fragment version 1 { import "a.lock"; }`],
["root-again.lock", root],
]);
const result = await resolveQuixosLock(root, async (relativePath) => sources.get(relativePath) ?? "");
assert.equal(result.ok, false);
if (!result.ok) {
assert.deepEqual(new Set(result.diagnostics.map(({ code }) => code)), new Set([
"duplicate-resource-binding",
"import-cycle",
"imported-root-lock",
]));
}
});
test("file loading rejects a symlink in any import path component", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-lock-test-"));
context.after(() => rm(directory, { recursive: true, force: true }));
const outside = path.join(directory, "outside");
await mkdir(outside);
await writeFile(path.join(outside, "fragment.lock"), "quixos-lock fragment version 1 {}\n");
await symlink(outside, path.join(directory, "linked"), "dir");
await writeFile(path.join(directory, "quixos.lock"), `quixos-lock version 1 {
quixos source {
repository "https://gitea.example/quixos/quixos.git";
commit "${quixosCommit}";
}
import "linked/fragment.lock";
}`);
const result = await loadQuixosLock(path.join(directory, "quixos.lock"));
assert.equal(result.ok, false);
if (!result.ok) {
assert.equal(result.diagnostics[0]?.code, "import-read-failed");
assert.match(result.diagnostics[0]?.message ?? "", /symbolic links/);
}
});
+47
View File
@@ -0,0 +1,47 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import {execFile as callback} from "node:child_process";
import {promisify} from "node:util";
import {scaffoldRecipe} from "../src/capability-language/scaffold-recipes.js";
import {planStructure, applyStructure} from "../src/capability-language/structural-plan.js";
import {contentDigest} from "../src/capability-model/evolution.js";
const execFile = promisify(callback);
test("package/function/migration scaffolds register implementations and refresh code digests without overwriting code", async (context) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-recipes-"));
context.after(() => fs.rm(root, {recursive: true, force: true}));
await execFile("git", ["-C", root, "init"]);
await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n");
const source = {repository: "https://example.test/chess.git", commit: "a".repeat(40)};
const base = {source, directory: "packages/Chess"};
const apply = async (command: Parameters<typeof scaffoldRecipe>[1], input: Parameters<typeof scaffoldRecipe>[2]) => applyStructure(await planStructure(root, await scaffoldRecipe(root, command, input)));
await apply("package", {...base, name: "Chess", id: "package:chess", revision: "package:chess@1", tools: {quixos: source, protocol: source, helpers: source, sdk: source}});
await apply("function", {...base, name: "play", id: "export:play"});
const filename = path.join(root, base.directory, "src/impl/play.ts");
const edited = 'import type {Implementation} from "../generated-bindings.js";\nexport const handler: Implementation["play"] = async () => null;\n';
await fs.writeFile(filename, edited);
const old = {version: 1}, next = {version: 2}, from = contentDigest(old), to = contentDigest(next);
await apply("migration", {...base, name: "upgrade", id: "export:upgrade", migration: {id: "upgrade-v2", scopeId: "board", from, to, predecessors: [], ports: [], contracts: {[from]: old, [to]: next}}});
const migrationFile = path.join(root, base.directory, "src/migrations/upgrade.ts");
await fs.appendFile(migrationFile, "\n// authored migration change\n");
await apply("refresh", base);
assert.equal(await fs.readFile(filename, "utf8"), edited);
const catalog = JSON.parse(await fs.readFile(path.join(root, base.directory, "quixos.migrations.json"), "utf8"));
assert.equal(catalog.migrations[0].implementation.digest, contentDigest(await fs.readFile(migrationFile, "utf8")));
const bindings = await fs.readFile(path.join(root, base.directory, "src/generated-bindings.ts"), "utf8");
assert.match(bindings, /export:play/);
assert.match(await fs.readFile(path.join(root, base.directory, "src/migrate.ts"), "utf8"), /export:upgrade/);
if (process.env.QX_SCAFFOLD_TEST_SDK) {
const sdk = await fs.realpath(process.env.QX_SCAFFOLD_TEST_SDK);
const packageRoot = path.join(root, base.directory);
await fs.mkdir(path.join(packageRoot, "node_modules/@quixos"), {recursive: true});
await fs.symlink(sdk, path.join(packageRoot, "node_modules/@quixos/camino-package-runtime"));
await fs.symlink(path.join(sdk, "node_modules/@types"), path.join(packageRoot, "node_modules/@types"));
await execFile(path.join(sdk, "node_modules/.bin/tsc"), ["--noEmit"], {cwd: packageRoot});
await execFile("nix-instantiate", ["--parse", path.join(packageRoot, "flake.nix")]);
}
await assert.rejects(() => apply("function", {...base, name: "play", id: "export:play"}), /unique/);
});
+42
View File
@@ -0,0 +1,42 @@
import test from "node:test";
import assert from "node:assert/strict";
import { editStructure, scaffoldResourceSource } from "../src/capability-language/structural-edits.js";
test("package scaffolding and function edits preserve surrounding source and use exact selectors", () => {
const source = `// 🧭 resource comment\n${scaffoldResourceSource("package", "Chess", "package:chess", "package:chess@1")}`;
const functionSource = 'function evaluate id "export:evaluate" : string -> string;';
const appended = editStructure(source, {operation: "append", parent: {kind: "packageResourceDecl", id: "package:chess"}, source: functionSource});
assert.ok(appended.startsWith("// 🧭 resource comment\n"));
assert.ok(appended.includes(functionSource));
const replaced = editStructure(appended, {operation: "replace", target: {kind: "packageFunctionExport", id: "export:evaluate"}, source: 'function evaluate id "export:evaluate" : unit -> string;'});
assert.ok(replaced.includes(": unit -> string;"));
assert.ok(!editStructure(replaced, {operation: "remove", target: {kind: "packageFunctionExport", id: "export:evaluate"}}).includes("evaluate"));
assert.throws(() => editStructure(source, {operation: "remove", target: {kind: "packageResourceDecl", id: "package:chess@1"}}), /exactly once/);
assert.throws(() => editStructure(source, {operation: "append", parent: {kind: "packageResourceDecl"}, source: "not valid QX"}), /Invalid structural/);
});
test("dependency scaffolding validates exact sources and preserves unrelated lock comments", () => {
const source = `// 🧭 lock\nquixos-lock version 1 {\n quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; }\n // retained comment\n}\n`;
const dependency = {operation: "dependency" as const, kind: "package" as const, name: "Chess", source: {repository: "https://example.test/chess.git", commit: "b".repeat(40)}};
const appended = editStructure(source, dependency);
assert.ok(appended.includes("// retained comment"));
assert.ok(appended.startsWith("// 🧭 lock\n"));
assert.throws(() => editStructure(source, {...dependency, source: {...dependency.source, commit: "main"}}), /Invalid dependency/);
const changed = editStructure(appended, {...dependency, source: {...dependency.source, commit: "c".repeat(40)}});
assert.ok(!changed.includes("b".repeat(40)));
assert.ok(!editStructure(changed, {...dependency, source: null}).includes("package Chess"));
});
test("conformance enrollment, major edits, imports, and private attachment removal preserve valid structure", () => {
const source = `fragment { conform Game as Playable { private state Board id "slot:board" on Game : string policy optimistic-register; } }`;
const selector = {kind: "conformanceDecl", names: ["Game", "Playable"]};
const enrolled = editStructure(source, {operation: "conformance-id", target: selector, id: "conformance:playable"});
assert.ok(enrolled.includes('as Playable id "conformance:playable"'));
const major = editStructure(enrolled, {operation: "semantic-major", target: {kind: "conformanceDecl", id: "conformance:playable"}, major: 2});
assert.ok(major.includes("semantic-major 2"));
assert.throws(() => editStructure(major, {operation: "conformance-id", target: selector, id: "different"}), /Cannot change/);
const removed = editStructure(major, {operation: "remove", target: {kind: "stateDecl", id: "slot:board"}});
assert.ok(!removed.includes("private"));
const imported = editStructure(removed, {operation: "import", kind: "interface", name: "Playable"});
assert.equal(editStructure(imported, {operation: "import", kind: "interface", name: "Playable"}), imported);
});
+37
View File
@@ -0,0 +1,37 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { execFile as callback } from "node:child_process";
import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "../src/capability-language/structural-plan.js";
const execFile = promisify(callback);
test("structural plans validate the graph, journal originals, and reject stale edits", async (context) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structural-test-"));
context.after(() => fs.rm(root, {recursive: true, force: true}));
await execFile("git", ["-C", root, "init"]);
await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n");
await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`);
const before = `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { atom Subject id "atom:subject"; }`;
await fs.writeFile(path.join(root, "workspace.qx"), before);
const request: StructuralRequest = {kind: "workspace", files: [{file: "workspace.qx", edits: [{operation: "append", parent: {kind: "workspaceDecl"}, source: 'atom Game id "atom:game";'}]}]};
const plan = await planStructure(root, request);
assert.equal(await fs.readFile(path.join(root, "workspace.qx"), "utf8"), before);
await fs.writeFile(path.join(root, "workspace.qx"), `${before}\n// newer edit`);
await assert.rejects(() => applyStructure(plan), /Stale scaffold/);
await fs.writeFile(path.join(root, "workspace.qx"), before);
const applied = await applyStructure(plan);
assert.equal(applied.phase, "complete");
assert.ok((await fs.readFile(path.join(root, "workspace.qx"), "utf8")).includes("atom Game"));
const journal = JSON.parse(await fs.readFile(applied.journalPath, "utf8"));
assert.equal(journal.changes[0].before, before);
assert.equal((await resumeStructure(root, applied.id)).phase, "complete");
// Simulate a process interrupted after one file was written, before recording
// completion. Replay accepts the already-written exact after image.
journal.phase = "prepared";
await fs.writeFile(applied.journalPath, JSON.stringify(journal));
assert.equal((await resumeStructure(root, applied.id)).phase, "complete");
await assert.rejects(() => planStructure(root, request), /Duplicate|duplicate/);
});
+2 -1
View File
@@ -7,5 +7,6 @@
"outDir": "dist", "outDir": "dist",
"rootDir": ".", "rootDir": ".",
"sourceMap": true "sourceMap": true
} },
"include": ["src/**/*.ts"]
} }
+1 -1
View File
@@ -14,6 +14,6 @@
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"types": ["node"] "types": ["node"]
}, },
"include": ["src/**/*.ts"], "include": ["src/**/*.ts", "test/**/*.ts"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": false,
"declaration": false,
"noEmit": false,
"outDir": "dist",
"rootDir": ".",
"sourceMap": true
},
"include": ["src/**/*.ts", "test/**/*.ts"]
}
+188
View File
@@ -0,0 +1,188 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
{ lib, stdenv, nodejs, git, cacert, fetchurl, writeShellScript, writeShellScriptBin }:
{ src, overrideAttrs ? null, ... } @ args:
let
yarnBin = fetchurl {
url = "https://repo.yarnpkg.com/4.18.0/packages/yarnpkg-cli/bin/yarn.js";
hash = "sha512-/Lhxb+fNDuzhQf/Bi5IZOp35IEwbqDGJwoiDUiP8C75kr0c7qw1emSen2utcryuwfrJ4fMkzjKBA6hJfKh8vfg==";
};
cacheFolder = ".yarn/cache";
lockfile = ./yarn.lock;
# Call overrideAttrs on a derivation if a function is provided.
optionalOverride = fn: drv:
if fn == null then drv else drv.overrideAttrs fn;
# Simple stub that provides the global yarn command.
yarn = writeShellScriptBin "yarn" ''
exec '${nodejs}/bin/node' '${yarnBin}' "$@"
'';
# Common attributes between Yarn derivations.
drvCommon = {
# Make sure the build uses the right Node.js version everywhere.
buildInputs = [ nodejs yarn ];
# All dependencies should already be cached.
yarn_enable_network = "0";
# Tell node-gyp to use the provided Node.js headers for native code builds.
npm_config_nodedir = nodejs;
};
# Comman variables that we set in a Nix build, but not in a Nix shell.
buildVars = ''
# Make Yarn produce friendlier logging for automated builds.
export CI=1
# Tell node-pre-gyp to never fetch binaries / always build from source.
export npm_config_build_from_source=true
'';
# Create derivations for fetching dependencies.
cacheDrvs = let
in lib.mapAttrs (locator: { filename, hash }: stdenv.mkDerivation {
name = lib.strings.sanitizeDerivationName locator;
buildInputs = [ yarn git cacert ];
buildCommand = ''
cd '${src}'
${buildVars}
HOME="$TMP" yarn_enable_global_cache=false yarn_cache_folder="$TMP" \
yarn nixify fetch ${lib.escapeShellArg locator}
# Because we change the cache dir, Yarn may generate a different name.
mv "$TMP/$(sed 's/-[^-]*\.[^-]*$//' <<< "$outputFilename")"-* $out
'';
outputFilename = filename;
outputHash = hash;
}) cacheEntries;
# Create a shell snippet to copy dependencies from a list of derivations.
mkCacheBuilderForDrvs = drvs:
writeShellScript "collect-yarn-cache" (lib.concatMapStrings (drv: ''
cp --reflink=auto ${drv} '${drv.outputFilename}'
'') drvs);
# Main project derivation.
project = stdenv.mkDerivation (drvCommon // {
inherit src;
name = "@quixos/quixos-protocol";
configurePhase = ''
${buildVars}
# Copy over the Yarn cache.
rm -fr '${cacheFolder}'
mkdir -p '${cacheFolder}'
pushd '${cacheFolder}' > /dev/null
source ${mkCacheBuilderForDrvs (lib.attrValues cacheDrvs)}
popd > /dev/null
# Yarn may need a writable home directory.
export yarn_global_folder="$TMP"
# Ensure global cache is disabled. Cache must be part of our output.
touch .yarnrc.yml
sed -i -e '/^enableGlobalCache/d' .yarnrc.yml
echo 'enableGlobalCache: false' >> .yarnrc.yml
# Some node-gyp calls may call out to npm, which could fail due to an
# read-only home dir.
export HOME="$TMP"
# running preConfigure after the cache is populated allows for
# preConfigure to contain substituteInPlace for dependencies as well as the
# main project. This is necessary for native bindings that maybe have
# hardcoded values.
runHook preConfigure
# Run normal Yarn install to complete dependency installation.
yarn install --immutable --immutable-cache
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
runHook postBuild
'';
installPhase = ''
runHook preInstall
# Move the package contents to the output directory.
if grep -q '"workspaces"' package.json; then
# We can't use `yarn pack` in a workspace setup, because it only
# packages the outer workspace.
mkdir -p "$out/libexec"
mv $PWD "$out/libexec/$name"
else
# - If the package.json has a `files` field, only files matching those patterns are copied
# - Otherwise all files are copied.
yarn pack --out package.tgz
mkdir -p "$out/libexec/$name"
tar xzf package.tgz --directory "$out/libexec/$name" --strip-components=1
cp --reflink=auto .yarnrc* "$out/libexec/$name"
cp --reflink=auto ${lockfile} "$out/libexec/$name/yarn.lock"
cp --reflink=auto --recursive .yarn "$out/libexec/$name"
# Copy the Yarn linker output into the package.
cp --reflink=auto --recursive node_modules "$out/libexec/$name"
fi
cd "$out/libexec/$name"
# Invoke a plugin internal command to setup binaries.
mkdir -p "$out/bin"
yarn nixify install-bin $out/bin
# A package with node_modules doesn't need the cache
yarn cache clean
runHook postInstall
'';
passthru = {
inherit nodejs;
yarn-freestanding = yarn;
yarn = writeShellScriptBin "yarn" ''
exec '${yarn}/bin/yarn' --cwd '${overriddenProject}/libexec/${overriddenProject.name}' "$@"
'';
};
});
overriddenProject = optionalOverride overrideAttrs project;
cacheEntries = {
"@bufbuild/protobuf@npm:2.14.1" = { filename = "@bufbuild-protobuf-npm-2.14.1-78e9ea56a2-3f913aca03.zip"; hash = "sha512-P5E6ygPfhBnEsh2ADqVNf2yDalND5l1vaGJ57DVmxJB3KZOO8vlKXifZLo0/StzW4CcRi6Kbp2TSHhxjfN3HAg=="; };
"@bufbuild/protoc-gen-es@npm:2.14.1" = { filename = "@bufbuild-protoc-gen-es-npm-2.14.1-230b1181a2-79f9fad1d5.zip"; hash = "sha512-efn60dVmXaptZUmyh4oPY/6/rbGHuFNAg/vrI4Ydwizz6mmmYKEvD45Ps8AtJO6ALxcYRBVt5C+0sXEuOgZ1aQ=="; };
"@bufbuild/protoplugin@npm:2.14.1" = { filename = "@bufbuild-protoplugin-npm-2.14.1-ca1a20a987-6a727aa5a8.zip"; hash = "sha512-anJ6pahI4FA13tkPUOE65Sx3NXutalhHalbU/3ANyBPKxmTgzDC1IBNU5cFY6//rANyDUxbWaqLDhKLp2qXf4w=="; };
"@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; };
"@typescript/typescript-linux-arm64@npm:7.0.2" = { filename = "@typescript-typescript-linux-arm64-npm-7.0.2-9cacfc53a0-10c0.zip"; hash = "sha512-s4c7nGT/lQIw6x0IfrL0b3ECy/D0g7UoDLdiIWJoM6EGXk5N/dmXdxUdA8Cr3hV/3nO2BYKTQ6Ai8/zGhiSQ3A=="; };
"@typescript/typescript-linux-x64@npm:7.0.2" = { filename = "@typescript-typescript-linux-x64-npm-7.0.2-8dd7ff8d24-10c0.zip"; hash = "sha512-k9EUK5CZ3OOPnyslp8SAAonHJpnpPmlaulppI/NmFyCzNs3UN/TLPUkyQia3KIxgbyTzKoXI5L5fl8Uj9bJ7Cg=="; };
"@typescript/vfs@npm:1.6.4" = { filename = "@typescript-vfs-npm-1.6.4-78935c9d3e-acb9de42f2.zip"; hash = "sha512-rLneQvI/2j9149eQC6EG7zI6L0588OSpTbtFfibTU8pjuzUZPtGjL8hzPzrlkJlhKykLBr1iIWlIYb65v7YvYg=="; };
"antlr-ng@npm:1.0.10" = { filename = "antlr-ng-npm-1.0.10-840fe729e4-0893eda0fd.zip"; hash = "sha512-CJPtoP3M4dMpwYykUaxo7zdA+pOnISq5cq66bDNyLJvnL1dqN5cakOTnJSu6241VnAiH8yWPr+pZtJlsb+9P6g=="; };
"antlr4ng@npm:3.0.15" = { filename = "antlr4ng-npm-3.0.15-58a4d495ba-75421480b9.zip"; hash = "sha512-dUIUgLmWAcCuF0HkFHuF4k6egiNuw1TFH75QsthdcTeJRAW0dIk5xH5yCAC9GcChkAa49o+14Bg2xxf6vaaLXA=="; };
"antlr4ng@npm:3.0.16" = { filename = "antlr4ng-npm-3.0.16-c4e3e7474c-01b51d1d73.zip"; hash = "sha512-AbUdHXPKEvIt/Pcjuj12n0ewLDJhi8cp470iczT6FUoKQLFrDj0fJF+ldWv067uU3rKWoj1z8FLckL5qLEd9EQ=="; };
"base64-js@npm:1.5.1" = { filename = "base64-js-npm-1.5.1-b2f7275641-f23823513b.zip"; hash = "sha512-8jgjUTtjFzoAEDD65PLavig7manTJK3jrT0UjiGBNGdvHuhWjId8157BxTFY3PLSulJ6l8YGYYkoupndkwECvw=="; };
"commander@npm:13.1.0" = { filename = "commander-npm-13.1.0-bdbbfaaf9d-7b8c5544bb.zip"; hash = "sha512-e4xVRLunBPvoS3yrLgQ9+FhtXBFKTFtgf4OuUGBwiUDtC1vVg4z4zidTnN4mXBy9Wc48jGsBftPuyJQ+OkFRZA=="; };
"debug@npm:4.4.3" = { filename = "debug-npm-4.4.3-0105c6123a-d79136ec6c.zip"; hash = "sha512-15E27GyD7L79D2pVk9pqnJHsTX3cS1TIg9bnHsmsy19noaXpbQCjKBlrW1yG02XpjYo6cIVqrxa057GYXmf1pg=="; };
"fast-printf@npm:1.6.10" = { filename = "fast-printf-npm-1.6.10-c05cca9b81-630cccbef8.zip"; hash = "sha512-YwzMvvg0mm7q2plYyTkenRS1vZUn3SjZ5+nylbAGxkDdyo/CRwz/VepcbJLKUwSIu62/fCT0hSrYw02RVdqddQ=="; };
"he@npm:1.2.0" = { filename = "he-npm-1.2.0-3b73a2ff07-a27d478bef.zip"; hash = "sha512-on1Hi+/jyBkvAGzdBjmmZ5iXnfpuISXGrFgqGaXr/sYq2D6DguYDYXDYc/RuRTan55W/i5W/fCR/TMCCXMyMFw=="; };
"luxon@npm:3.5.0" = { filename = "luxon-npm-3.5.0-92bb977f7f-335789bba9.zip"; hash = "sha512-M1eJu6lQd9uDHvmYlO2t6yMCOz6yE3obVqzQ0pAIK2kc95MUPWnjC8Bp7JXwtJ82QZ9I6VHGgBTxn/4SBF40lA=="; };
"ms@npm:2.1.3" = { filename = "ms-npm-2.1.3-81ff3cfac1-d924b57e73.zip"; hash = "sha512-2SS1fnMSs7Y60h/Fs9wK9eeNYaH8fPtUV+2vJjJr9ivlMHzIf/toYu8cKzOwIzzbXU8BxMlYzA1mCUi2Wih6SA=="; };
"pako@npm:0.2.9" = { filename = "pako-npm-0.2.9-c88ac0d326-79c1806ebc.zip"; hash = "sha512-ecGAbrzzJbYK5Znk1yJ8LjRte4Kdwg9c8kzvB8k0B53DphxbPIJ4ovehkMSmE+ND6hHlMC2+JS79EXEt9LawQQ=="; };
"stringtemplate4ts@npm:1.0.9" = { filename = "stringtemplate4ts-npm-1.0.9-df48ae4223-c5f46367d0.zip"; hash = "sha512-xfRjZ9A3XBp8ryC4YZCEL0rv+1Q9dosicN/TxeQsTc9JkBwCp7BowI6MREDAKiApCFlOvO2+0PXolHA9I/VMAg=="; };
"tiny-inflate@npm:1.0.3" = { filename = "tiny-inflate-npm-1.0.3-a7419a5c65-fab6875372.zip"; hash = "sha512-+raHU3JU9uxEyaLogASP5w2jVCq6KPc82j50yVyr80KjOTcvKmwDLjIjJPAazMA8omwEuiutmz64zz7pm7p/mw=="; };
"typescript@npm:5.4.5" = { filename = "typescript-npm-5.4.5-8568a42232-2954022ada.zip"; hash = "sha512-KVQCKto0D9PWqeK45TT2XVfJLV85iaJjdUp4q6VJ9+ZSmswZIZE1YKS4FsRtzn30pNKfnxGj3A1CE7t20EMlHg=="; };
"typescript@npm:7.0.2" = { filename = "typescript-npm-7.0.2-0bde2bcec5-3dcee51ec8.zip"; hash = "sha512-Pc7lHsjDMkkjJbzb999ItmZodR8SfmIq59QbbHFusZGEQkpF4U93UPUPNAIyOItp5ih4WRVfBqXOLfdvEssxzw=="; };
"typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c" = { filename = "typescript-patch-6e159bfddb-db2ad2a16c.zip"; hash = "sha512-2yrSoWyoKfUEJ+6x2hVeekXlmO7HsIbYtOi6ROWiNfdY5gbWgcZpkiMNP8O4mVhl5f0LIqLJVIbQsyAPgwcuyQ=="; };
"typescript@patch:typescript@npm%3A7.0.2#optional!builtin<compat/typescript>::version=7.0.2&hash=3bafbf" = { filename = "typescript-patch-4859cbaf66-6b3cdc369c.zip"; hash = "sha512-azzcNpzYKdRuAHNOpkIz7oRBnCglrY/kCJFa3ed6vO1LeiQB9+7VF9VKek9dHBhpdTy5kBjfHCFZx2HZ8zSDpg=="; };
"undici-types@npm:7.18.2" = { filename = "undici-types-npm-7.18.2-3e6d69d829-85a7918911.zip"; hash = "sha512-haeRiRE6I4lZ16ZHNo5PfFVZw6QE69uPxEiBRc6UJvzYIlKoRKMCeY38Djfm+xeP9IHtA7xMr2NMV1fZ70NSHQ=="; };
"unicode-properties@npm:1.4.1" = { filename = "unicode-properties-npm-1.4.1-122054452f-1d140b7945.zip"; hash = "sha512-HRQLeUVmT7DvU96VUXCCHgd7lJ7vN3xuSQWQLwfjOQOScb+ioAXk9MYHSwgNNCC0hsUtyQXhH5JJSaBNH7R//Q=="; };
"unicode-trie@npm:2.0.0" = { filename = "unicode-trie-npm-2.0.0-54e0a4dd52-2422368645.zip"; hash = "sha512-JCI2hkUknzFWQKHJ6VBgRqp3OPycXVnhXCB83W7GYQHDWwufddw6wo/nvhmq8e/ImLvqB0+h6OKV73Nq63kEuw=="; };
};
in overriddenProject
+524
View File
@@ -0,0 +1,524 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 10
cacheKey: 10c0
"@bufbuild/protobuf@npm:2.14.1, @bufbuild/protobuf@npm:^2.12.1":
version: 2.14.1
resolution: "@bufbuild/protobuf@npm:2.14.1"
checksum: 10c0/3f913aca03df8419c4b21d800ea54d7f6c836a5343e65d6f686279ec3566c4907729938ef2f94a5e27d92e8d3f4adcd6e027118ba29ba764d21e1c637cddc702
languageName: node
linkType: hard
"@bufbuild/protoc-gen-es@npm:^2.12.1":
version: 2.14.1
resolution: "@bufbuild/protoc-gen-es@npm:2.14.1"
dependencies:
"@bufbuild/protobuf": "npm:2.14.1"
"@bufbuild/protoplugin": "npm:2.14.1"
peerDependencies:
"@bufbuild/protobuf": 2.14.1
peerDependenciesMeta:
"@bufbuild/protobuf":
optional: true
bin:
protoc-gen-es: bin/protoc-gen-es
checksum: 10c0/79f9fad1d5665daa6d6549b2878a0f63febfadb187b8534083fbeb23861dc22cf3ea69a660a12f0f8e4fb3c02d24ee802f171844156de42fb4b1712e3a067569
languageName: node
linkType: hard
"@bufbuild/protoplugin@npm:2.14.1":
version: 2.14.1
resolution: "@bufbuild/protoplugin@npm:2.14.1"
dependencies:
"@bufbuild/protobuf": "npm:2.14.1"
"@typescript/vfs": "npm:^1.6.2"
typescript: "npm:5.4.5"
checksum: 10c0/6a727aa5a848e05035ded90f50e13ae52c77357bad6a58476a56d4ff700dc813cac664e0cc30b5201354e5c158ebffeb00dc835316d66aa2c384a2e9daa5dfe3
languageName: node
linkType: hard
"@quixos/quixos-protocol@workspace:.":
version: 0.0.0-use.local
resolution: "@quixos/quixos-protocol@workspace:."
dependencies:
"@bufbuild/protobuf": "npm:^2.12.1"
"@bufbuild/protoc-gen-es": "npm:^2.12.1"
"@types/node": "npm:^24"
antlr-ng: "npm:^1.0.10"
antlr4ng: "npm:^3.0.16"
typescript: "npm:^7.0.2"
bin:
quixos-capability-compile: dist/src/capability-language/cli.js
quixos-codegen-ts: dist/src/bindings/cli.js
quixos-descriptor-check: dist/src/descriptor-check.js
quixos-lock-check: dist/src/resource-lock/cli.js
quixos-qx: dist/src/capability-language/tool-cli.js
quixos-resource-compile: dist/src/capability-language/resource-cli.js
quixos-workspace-compile: dist/src/capability-language/workspace-cli.js
languageName: unknown
linkType: soft
"@types/node@npm:^24":
version: 24.13.3
resolution: "@types/node@npm:24.13.3"
dependencies:
undici-types: "npm:~7.18.0"
checksum: 10c0/a5bc08f49b9581dcdca90e02cd77197a3c799840807664942e5cd5161a553d4d2ae8064f7e3294410d748d7d098b5c1848be7fa50c06f29c4193ab16be4e59eb
languageName: node
linkType: hard
"@typescript/typescript-aix-ppc64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-aix-ppc64@npm:7.0.2"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@typescript/typescript-darwin-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-darwin-arm64@npm:7.0.2"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-darwin-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-darwin-x64@npm:7.0.2"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-freebsd-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-freebsd-arm64@npm:7.0.2"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-freebsd-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-freebsd-x64@npm:7.0.2"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-linux-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-arm64@npm:7.0.2"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-linux-arm@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-arm@npm:7.0.2"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@typescript/typescript-linux-loong64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-loong64@npm:7.0.2"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@typescript/typescript-linux-mips64el@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-mips64el@npm:7.0.2"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@typescript/typescript-linux-ppc64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-ppc64@npm:7.0.2"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@typescript/typescript-linux-riscv64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-riscv64@npm:7.0.2"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@typescript/typescript-linux-s390x@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-s390x@npm:7.0.2"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@typescript/typescript-linux-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-linux-x64@npm:7.0.2"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-netbsd-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-netbsd-arm64@npm:7.0.2"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-netbsd-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-netbsd-x64@npm:7.0.2"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-openbsd-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-openbsd-arm64@npm:7.0.2"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-openbsd-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-openbsd-x64@npm:7.0.2"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-sunos-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-sunos-x64@npm:7.0.2"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@typescript/typescript-win32-arm64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-win32-arm64@npm:7.0.2"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@typescript/typescript-win32-x64@npm:7.0.2":
version: 7.0.2
resolution: "@typescript/typescript-win32-x64@npm:7.0.2"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@typescript/vfs@npm:^1.6.2":
version: 1.6.4
resolution: "@typescript/vfs@npm:1.6.4"
dependencies:
debug: "npm:^4.4.3"
peerDependencies:
typescript: "*"
checksum: 10c0/acb9de42f23fda3f75e3d7900ba106ef323a2f4e7cf0e4a94dbb457e26d353ca63bb35193ed1a32fc8733f3ae59099612b290b06bd6221694861beb9bfb62f62
languageName: node
linkType: hard
"antlr-ng@npm:^1.0.10":
version: 1.0.10
resolution: "antlr-ng@npm:1.0.10"
dependencies:
antlr4ng: "npm:3.0.16"
commander: "npm:13.1.0"
stringtemplate4ts: "npm:1.0.9"
unicode-properties: "npm:1.4.1"
bin:
antlr-ng: dist/cli/runner.js
grun: dist/cli/TestRig.js
interpreter: dist/cli/Interpreter.js
testrig: dist/cli/TestRig.js
checksum: 10c0/0893eda0fdcce1d329c18ca451ac68ef3740fa93a7212ab972aeba6c33722c9be72f576a37971a90e4e7252bbadb8d559c0887f3258fafea59b4996c6fef4fea
languageName: node
linkType: hard
"antlr4ng@npm:3.0.15":
version: 3.0.15
resolution: "antlr4ng@npm:3.0.15"
checksum: 10c0/75421480b99601c0ae1741e4147b85e24e9e82236ec354c51fbe50b2d85d7137894405b4748939c47e720800bd19c0a19006b8f68fb5e01836c717fabda68b5c
languageName: node
linkType: hard
"antlr4ng@npm:3.0.16, antlr4ng@npm:^3.0.16":
version: 3.0.16
resolution: "antlr4ng@npm:3.0.16"
checksum: 10c0/01b51d1d73ca12f22dfcf723ba3d769f47b02c32618bc729e3bd227334fa154a0a40b16b0e3d1f245fa5756bf4ebbb94deb296a23d73f052dc90be6a2c477d11
languageName: node
linkType: hard
"base64-js@npm:^1.3.0":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
languageName: node
linkType: hard
"commander@npm:13.1.0":
version: 13.1.0
resolution: "commander@npm:13.1.0"
checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164
languageName: node
linkType: hard
"debug@npm:^4.4.3":
version: 4.4.3
resolution: "debug@npm:4.4.3"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6
languageName: node
linkType: hard
"fast-printf@npm:1.6.10":
version: 1.6.10
resolution: "fast-printf@npm:1.6.10"
checksum: 10c0/630cccbef8349a6eeada9958c9391e9d14b5bd9527dd28d9e7e9f295b006c640ddca8fc2470cff55ea5c6c92ca530488bbadbf7c24f4852ad8c34d9155da9d75
languageName: node
linkType: hard
"he@npm:1.2.0":
version: 1.2.0
resolution: "he@npm:1.2.0"
bin:
he: bin/he
checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17
languageName: node
linkType: hard
"luxon@npm:3.5.0":
version: 3.5.0
resolution: "luxon@npm:3.5.0"
checksum: 10c0/335789bba95077db831ef99894edadeb23023b3eb2137a1b56acd0d290082b691cf793143d69e30bc069ec95f0b49f36419f48e951c68014f19ffe12045e3494
languageName: node
linkType: hard
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
languageName: node
linkType: hard
"pako@npm:^0.2.5":
version: 0.2.9
resolution: "pako@npm:0.2.9"
checksum: 10c0/79c1806ebcf325b60ae599e4d7227c2e346d7b829dc20f5cf24cef07c934079dc3a61c5b3c8278a2f7a190c4a613e343ea11e5302dbe252efd11712df4b6b041
languageName: node
linkType: hard
"stringtemplate4ts@npm:1.0.9":
version: 1.0.9
resolution: "stringtemplate4ts@npm:1.0.9"
dependencies:
antlr4ng: "npm:3.0.15"
fast-printf: "npm:1.6.10"
he: "npm:1.2.0"
luxon: "npm:3.5.0"
checksum: 10c0/c5f46367d0375c1a7caf20b86190842f4aeffb543d768b2270dfd3c5e42c4dcf49901c02a7b068c08e8c4440c02a202908594ebcedbed0f5e894703d23f54c02
languageName: node
linkType: hard
"tiny-inflate@npm:^1.0.0":
version: 1.0.3
resolution: "tiny-inflate@npm:1.0.3"
checksum: 10c0/fab687537254f6ec44c9a2e880048fe70da3542aba28f73cda3e74c95cabf342a339372f2a6c032e322324f01accc03ca26c04ba2bad9b3eb8cf3ee99bba7f9b
languageName: node
linkType: hard
"typescript@npm:5.4.5":
version: 5.4.5
resolution: "typescript@npm:5.4.5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/2954022ada340fd3d6a9e2b8e534f65d57c92d5f3989a263754a78aba549f7e6529acc1921913560a4b816c46dce7df4a4d29f9f11a3dc0d4213bb76d043251e
languageName: node
linkType: hard
"typescript@npm:^7.0.2":
version: 7.0.2
resolution: "typescript@npm:7.0.2"
dependencies:
"@typescript/typescript-aix-ppc64": "npm:7.0.2"
"@typescript/typescript-darwin-arm64": "npm:7.0.2"
"@typescript/typescript-darwin-x64": "npm:7.0.2"
"@typescript/typescript-freebsd-arm64": "npm:7.0.2"
"@typescript/typescript-freebsd-x64": "npm:7.0.2"
"@typescript/typescript-linux-arm": "npm:7.0.2"
"@typescript/typescript-linux-arm64": "npm:7.0.2"
"@typescript/typescript-linux-loong64": "npm:7.0.2"
"@typescript/typescript-linux-mips64el": "npm:7.0.2"
"@typescript/typescript-linux-ppc64": "npm:7.0.2"
"@typescript/typescript-linux-riscv64": "npm:7.0.2"
"@typescript/typescript-linux-s390x": "npm:7.0.2"
"@typescript/typescript-linux-x64": "npm:7.0.2"
"@typescript/typescript-netbsd-arm64": "npm:7.0.2"
"@typescript/typescript-netbsd-x64": "npm:7.0.2"
"@typescript/typescript-openbsd-arm64": "npm:7.0.2"
"@typescript/typescript-openbsd-x64": "npm:7.0.2"
"@typescript/typescript-sunos-x64": "npm:7.0.2"
"@typescript/typescript-win32-arm64": "npm:7.0.2"
"@typescript/typescript-win32-x64": "npm:7.0.2"
dependenciesMeta:
"@typescript/typescript-aix-ppc64":
optional: true
"@typescript/typescript-darwin-arm64":
optional: true
"@typescript/typescript-darwin-x64":
optional: true
"@typescript/typescript-freebsd-arm64":
optional: true
"@typescript/typescript-freebsd-x64":
optional: true
"@typescript/typescript-linux-arm":
optional: true
"@typescript/typescript-linux-arm64":
optional: true
"@typescript/typescript-linux-loong64":
optional: true
"@typescript/typescript-linux-mips64el":
optional: true
"@typescript/typescript-linux-ppc64":
optional: true
"@typescript/typescript-linux-riscv64":
optional: true
"@typescript/typescript-linux-s390x":
optional: true
"@typescript/typescript-linux-x64":
optional: true
"@typescript/typescript-netbsd-arm64":
optional: true
"@typescript/typescript-netbsd-x64":
optional: true
"@typescript/typescript-openbsd-arm64":
optional: true
"@typescript/typescript-openbsd-x64":
optional: true
"@typescript/typescript-sunos-x64":
optional: true
"@typescript/typescript-win32-arm64":
optional: true
"@typescript/typescript-win32-x64":
optional: true
bin:
tsc: bin/tsc
checksum: 10c0/3dcee51ec8c332492325bcdbf7df48b66668751f127e622ae7d41b6c716eb19184424a45e14f7750f50f340232388b69e6287859155f06a5ce2df76f12cb31cf
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>":
version: 5.4.5
resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/db2ad2a16ca829f50427eeb1da155e7a45e598eec7b086d8b4e8ba44e5a235f758e606d681c66992230d3fc3b8995865e5fd0b22a2c95486d0b3200f83072ec9
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A^7.0.2#optional!builtin<compat/typescript>":
version: 7.0.2
resolution: "typescript@patch:typescript@npm%3A7.0.2#optional!builtin<compat/typescript>::version=7.0.2&hash=3bafbf"
dependencies:
"@typescript/typescript-aix-ppc64": "npm:7.0.2"
"@typescript/typescript-darwin-arm64": "npm:7.0.2"
"@typescript/typescript-darwin-x64": "npm:7.0.2"
"@typescript/typescript-freebsd-arm64": "npm:7.0.2"
"@typescript/typescript-freebsd-x64": "npm:7.0.2"
"@typescript/typescript-linux-arm": "npm:7.0.2"
"@typescript/typescript-linux-arm64": "npm:7.0.2"
"@typescript/typescript-linux-loong64": "npm:7.0.2"
"@typescript/typescript-linux-mips64el": "npm:7.0.2"
"@typescript/typescript-linux-ppc64": "npm:7.0.2"
"@typescript/typescript-linux-riscv64": "npm:7.0.2"
"@typescript/typescript-linux-s390x": "npm:7.0.2"
"@typescript/typescript-linux-x64": "npm:7.0.2"
"@typescript/typescript-netbsd-arm64": "npm:7.0.2"
"@typescript/typescript-netbsd-x64": "npm:7.0.2"
"@typescript/typescript-openbsd-arm64": "npm:7.0.2"
"@typescript/typescript-openbsd-x64": "npm:7.0.2"
"@typescript/typescript-sunos-x64": "npm:7.0.2"
"@typescript/typescript-win32-arm64": "npm:7.0.2"
"@typescript/typescript-win32-x64": "npm:7.0.2"
dependenciesMeta:
"@typescript/typescript-aix-ppc64":
optional: true
"@typescript/typescript-darwin-arm64":
optional: true
"@typescript/typescript-darwin-x64":
optional: true
"@typescript/typescript-freebsd-arm64":
optional: true
"@typescript/typescript-freebsd-x64":
optional: true
"@typescript/typescript-linux-arm":
optional: true
"@typescript/typescript-linux-arm64":
optional: true
"@typescript/typescript-linux-loong64":
optional: true
"@typescript/typescript-linux-mips64el":
optional: true
"@typescript/typescript-linux-ppc64":
optional: true
"@typescript/typescript-linux-riscv64":
optional: true
"@typescript/typescript-linux-s390x":
optional: true
"@typescript/typescript-linux-x64":
optional: true
"@typescript/typescript-netbsd-arm64":
optional: true
"@typescript/typescript-netbsd-x64":
optional: true
"@typescript/typescript-openbsd-arm64":
optional: true
"@typescript/typescript-openbsd-x64":
optional: true
"@typescript/typescript-sunos-x64":
optional: true
"@typescript/typescript-win32-arm64":
optional: true
"@typescript/typescript-win32-x64":
optional: true
bin:
tsc: bin/tsc
checksum: 10c0/6b3cdc369cd829d46e00734ea64233ee84419c2825ad8fe408915adde77abced4b7a2401f7eed517d54a7a4f5d1c1869753cb99018df1c2159c761d9f33483a6
languageName: node
linkType: hard
"undici-types@npm:~7.18.0":
version: 7.18.2
resolution: "undici-types@npm:7.18.2"
checksum: 10c0/85a79189113a238959d7a647368e4f7c5559c3a404ebdb8fc4488145ce9426fcd82252a844a302798dfc0e37e6fb178ff481ed03bc4caf634c5757d9ef43521d
languageName: node
linkType: hard
"unicode-properties@npm:1.4.1":
version: 1.4.1
resolution: "unicode-properties@npm:1.4.1"
dependencies:
base64-js: "npm:^1.3.0"
unicode-trie: "npm:^2.0.0"
checksum: 10c0/1d140b7945664fb0ef53de955170821e077b949eef377c6e4905902f07e339039271bfa2a005e4f4c6074b080d3420b486c52dc905e11f924949a04d1fb47ffd
languageName: node
linkType: hard
"unicode-trie@npm:^2.0.0":
version: 2.0.0
resolution: "unicode-trie@npm:2.0.0"
dependencies:
pako: "npm:^0.2.5"
tiny-inflate: "npm:^1.0.0"
checksum: 10c0/2422368645249f315640a1c9e9506046aa7738fc9c5d59e15c207cdd6ec66101c35b0b9f75dc3ac28fe7be19aaf1efc898bbea074fa1e8e295ef736aeb7904bb
languageName: node
linkType: hard