Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9fb42dce4 | |||
| 42af87f8bd | |||
| 9867bf4552 | |||
| 483bc68a94 | |||
| 1e25f391e7 | |||
| 4e17693d82 | |||
| ce793cc54f | |||
| fd6fee133d | |||
| f5ced64533 | |||
| eabf721e94 | |||
| 0c46850973 | |||
| c4d42a0ac5 | |||
| fd6160ec67 | |||
| fd039f095b | |||
| f4987093f6 | |||
| 4d52789807 | |||
| 60550bb760 | |||
| eaccec410c | |||
| c33dfc2817 | |||
| 27373d801b | |||
| f852c8f0aa | |||
| 3c2e4a7e85 | |||
| ee4d8867a3 | |||
| ffac727009 | |||
| e6b34535af | |||
| cd35e03b2d | |||
| a9134843bc | |||
| ea153c3cd7 | |||
| ca0381d280 | |||
| 5587511713 | |||
| 5dd3948784 | |||
| 7dba68cff5 | |||
| 2ad29f8ddb |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git",
|
||||
"sourceCommit": "1a3ecab531f39491ae0ce3c2a12de0d264440ed7",
|
||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
|
||||
"sourceCommit": "61cb9aa5a580c2846d426464228c7d1082b7021a",
|
||||
"sourcePath": "quixos-protocol",
|
||||
"exportName": "quixos-protocol",
|
||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
+24
@@ -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"
|
||||
@@ -1,8 +1,138 @@
|
||||
# Quixos Protocol
|
||||
# Quixos protocol and capability compiler
|
||||
|
||||
Shared protobuf definitions for Camino, camino-orch, package descriptors, and
|
||||
future package runtime RPC.
|
||||
This package owns the shared protobuf APIs for Camino, `quixos-orch`, package
|
||||
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
|
||||
local TypeScript bindings from these protos; the generated bindings here are
|
||||
available for later consolidation.
|
||||
## Capability language
|
||||
|
||||
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.
|
||||
|
||||
@@ -10,47 +10,130 @@
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
quixos-protocol = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "quixos-protocol";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
npmDeps = pkgs.importNpmLock { npmRoot = ./.; };
|
||||
npmConfigHook = pkgs.importNpmLock.npmConfigHook;
|
||||
nativeBuildInputs = [
|
||||
pkgs.importNpmLock.npmConfigHook
|
||||
pkgs.nodejs_24
|
||||
nodejs = pkgs.nodejs_24;
|
||||
quixos-protocol = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) {
|
||||
src = pkgs.lib.cleanSource ./.;
|
||||
overrideAttrs = old: {
|
||||
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [
|
||||
pkgs.diffutils
|
||||
pkgs.esbuild
|
||||
pkgs.protobuf
|
||||
pkgs.git
|
||||
];
|
||||
buildPhase = ''
|
||||
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}"
|
||||
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
|
||||
'';
|
||||
doCheck = true;
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
yarn test
|
||||
runHook postCheck
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out"
|
||||
cp --reflink=auto --recursive grammar "$out/grammar"
|
||||
cp --reflink=auto --recursive proto "$out/proto"
|
||||
cp --reflink=auto --recursive dist "$out/dist"
|
||||
cp package.json "$out/package.json"
|
||||
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
|
||||
#!/bin/sh
|
||||
export PATH="${pkgs.protobuf}/bin:\$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
|
||||
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
|
||||
'';
|
||||
};
|
||||
};
|
||||
in {
|
||||
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 {
|
||||
packages = [
|
||||
pkgs.nodejs_24
|
||||
nodejs
|
||||
pkgs.protobuf
|
||||
pkgs.typescript
|
||||
quixos-protocol.yarn-freestanding
|
||||
quixos-protocol
|
||||
];
|
||||
shellHook = ''
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
Generated
-142
@@ -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
@@ -2,24 +2,40 @@
|
||||
"name": "@quixos/quixos-protocol",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.18.0",
|
||||
"type": "module",
|
||||
"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": {
|
||||
"./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/*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run 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",
|
||||
"typecheck": "npm run generate && tsc --noEmit"
|
||||
"build": "yarn generate && tsc -p tsconfig.build.json",
|
||||
"generate": "yarn generate:proto && yarn generate:parser",
|
||||
"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": {
|
||||
"@bufbuild/protobuf": "^2.12.1",
|
||||
"@bufbuild/protoc-gen-es": "^2.12.1"
|
||||
"@bufbuild/protoc-gen-es": "^2.12.1",
|
||||
"antlr4ng": "^3.0.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24",
|
||||
"typescript": "^5.9.3"
|
||||
"antlr-ng": "^1.0.10",
|
||||
"typescript": "^7.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
+86
-117
@@ -3,74 +3,47 @@ syntax = "proto3";
|
||||
package camino;
|
||||
|
||||
import "camino/schema.proto";
|
||||
import "quixos/refs.proto";
|
||||
|
||||
service CaminoService {
|
||||
rpc RegisterSchema(RegisterSchemaRequest) returns (RegisterSchemaResponse);
|
||||
rpc ListClasses(ListClassesRequest) returns (ListClassesResponse);
|
||||
rpc InstallPersistencePlan(InstallPersistencePlanRequest) returns (InstallPersistencePlanResponse);
|
||||
rpc GetPersistencePlan(GetPersistencePlanRequest) returns (GetPersistencePlanResponse);
|
||||
rpc CreateObject(CreateObjectRequest) returns (CreateObjectResponse);
|
||||
rpc GetObject(GetObjectRequest) returns (GetObjectResponse);
|
||||
rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse);
|
||||
rpc SetField(SetFieldRequest) returns (SetFieldResponse);
|
||||
rpc AddEdge(AddEdgeRequest) returns (AddEdgeResponse);
|
||||
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
|
||||
rpc RemoveEdge(RemoveEdgeRequest) returns (RemoveEdgeResponse);
|
||||
rpc ReadState(ReadStateRequest) returns (ReadStateResponse);
|
||||
rpc WriteState(WriteStateRequest) returns (WriteStateResponse);
|
||||
rpc ConnectEdge(ConnectEdgeRequest) returns (ConnectEdgeResponse);
|
||||
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 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 ObjectValue {
|
||||
map<string, Value> fields = 1;
|
||||
}
|
||||
|
||||
message ListValue {
|
||||
repeated Value values = 1;
|
||||
}
|
||||
|
||||
message RefValue {
|
||||
string object_id = 1;
|
||||
}
|
||||
|
||||
message ObjectValue { map<string, Value> fields = 1; }
|
||||
message ListValue { repeated Value values = 1; }
|
||||
message RefValue { string object_id = 1; }
|
||||
message CrdtValue {
|
||||
string type = 1;
|
||||
string encoding = 2;
|
||||
bytes payload = 3;
|
||||
}
|
||||
|
||||
message FieldValueSource {
|
||||
message StateValueSource {
|
||||
string object_id = 1;
|
||||
string field_name = 2;
|
||||
string field_type = 3;
|
||||
string field_storage = 4;
|
||||
string conflict_strategy = 5;
|
||||
uint64 revision = 6;
|
||||
string slot_id = 2;
|
||||
string value_type_json = 3;
|
||||
string storage_policy_json = 4;
|
||||
uint64 revision = 5;
|
||||
// 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 {
|
||||
FieldValueSource field = 1;
|
||||
}
|
||||
message ValueSource { StateValueSource state = 1; }
|
||||
|
||||
message Value {
|
||||
oneof kind {
|
||||
@@ -88,84 +61,82 @@ message Value {
|
||||
ValueSource source = 11;
|
||||
}
|
||||
|
||||
message InstallPersistencePlanRequest { PersistencePlan plan = 1; }
|
||||
message InstallPersistencePlanResponse { PersistencePlan plan = 1; }
|
||||
message GetPersistencePlanRequest {}
|
||||
message GetPersistencePlanResponse { PersistencePlan plan = 1; }
|
||||
|
||||
message CreateObjectRequest {
|
||||
string class_id = 1;
|
||||
map<string, Value> fields = 2;
|
||||
string atom_id = 1;
|
||||
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 {
|
||||
CaminoObject object = 1;
|
||||
}
|
||||
|
||||
message GetObjectRequest {
|
||||
message ReadStateRequest {
|
||||
string object_id = 1;
|
||||
string slot_id = 2;
|
||||
}
|
||||
|
||||
message GetObjectResponse {
|
||||
CaminoObject object = 1;
|
||||
}
|
||||
|
||||
message ListObjectsRequest {
|
||||
string class_id = 1;
|
||||
}
|
||||
|
||||
message ListObjectsResponse {
|
||||
repeated CaminoObject objects = 1;
|
||||
}
|
||||
|
||||
message SetFieldRequest {
|
||||
message ReadStateResponse { Value value = 1; }
|
||||
message WriteStateRequest {
|
||||
string object_id = 1;
|
||||
string field_name = 2;
|
||||
string slot_id = 2;
|
||||
Value value = 3;
|
||||
string client_mutation_id = 4;
|
||||
}
|
||||
|
||||
message SetFieldResponse {
|
||||
CaminoObject object = 1;
|
||||
message WriteStateResponse {
|
||||
Value value = 1;
|
||||
CaminoObject object = 2;
|
||||
}
|
||||
|
||||
message AddEdgeRequest {
|
||||
string from_object_id = 1;
|
||||
string source_field = 2;
|
||||
string to_object_id = 3;
|
||||
map<string, Value> fields = 4;
|
||||
message ConnectEdgeRequest {
|
||||
string edge_type_id = 1;
|
||||
string projection_id = 2;
|
||||
string object_id = 3;
|
||||
string target_object_id = 4;
|
||||
optional int32 ordinal = 5;
|
||||
optional int32 target_ordinal = 6;
|
||||
}
|
||||
|
||||
message AddEdgeResponse {
|
||||
CaminoEdge edge = 1;
|
||||
}
|
||||
|
||||
message ListEdgesRequest {
|
||||
message ConnectEdgeResponse { CaminoEdge edge = 1; }
|
||||
message ResolveEdgeRequest {
|
||||
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 {
|
||||
repeated CaminoEdge edges = 1;
|
||||
}
|
||||
|
||||
message RemoveEdgeRequest {
|
||||
message CollectionEntry {
|
||||
// Existing entry identity to preserve; empty allocates a new canonical edge.
|
||||
string edge_id = 1;
|
||||
string target_object_id = 2;
|
||||
Value key = 3;
|
||||
}
|
||||
|
||||
message RemoveEdgeResponse {
|
||||
string edge_id = 1;
|
||||
message ReadCollectionResponse {
|
||||
uint64 revision = 1;
|
||||
repeated CollectionEntry entries = 2;
|
||||
}
|
||||
|
||||
message ListOpsRequest {
|
||||
message ReplaceCollectionRequest {
|
||||
string object_id = 1;
|
||||
string edge_type_id = 2;
|
||||
string projection_id = 3;
|
||||
uint64 expected_revision = 4;
|
||||
repeated CollectionEntry entries = 5;
|
||||
}
|
||||
|
||||
message ListOpsResponse {
|
||||
repeated CaminoOp ops = 1;
|
||||
}
|
||||
|
||||
message ListOpsRequest { string object_id = 1; }
|
||||
message ListOpsResponse { repeated CaminoOp ops = 1; }
|
||||
message WatchObjectRequest {
|
||||
string object_id = 1;
|
||||
string after_op_id = 2;
|
||||
bool include_snapshot = 3;
|
||||
// Managed runtimes must watch only their injected attachments.
|
||||
repeated string attachment_ids = 4;
|
||||
}
|
||||
|
||||
message WatchObjectEvent {
|
||||
string object_id = 1;
|
||||
CaminoObject snapshot = 2;
|
||||
@@ -174,27 +145,26 @@ message WatchObjectEvent {
|
||||
|
||||
message CaminoObject {
|
||||
string id = 1;
|
||||
string class_id = 2;
|
||||
uint32 schema_version = 3;
|
||||
string atom_id = 2;
|
||||
string workspace_revision_id = 3;
|
||||
string created_at = 4;
|
||||
string updated_at = 5;
|
||||
map<string, Value> fields = 6;
|
||||
map<string, uint64> field_revisions = 7;
|
||||
map<string, string> field_conflicts = 8;
|
||||
map<string, Value> state = 6;
|
||||
map<string, uint64> state_revisions = 7;
|
||||
}
|
||||
|
||||
message CaminoEdge {
|
||||
string id = 1;
|
||||
string edge_type_id = 2;
|
||||
string from_object_id = 3;
|
||||
string to_object_id = 4;
|
||||
string source_field = 5;
|
||||
string cardinality = 6;
|
||||
string inverse = 7;
|
||||
map<string, Value> fields = 8;
|
||||
optional int32 ordinal = 9;
|
||||
string created_at = 10;
|
||||
string updated_at = 11;
|
||||
string first_object_id = 3;
|
||||
string second_object_id = 4;
|
||||
string first_projection_id = 5;
|
||||
string second_projection_id = 6;
|
||||
optional int32 first_ordinal = 7;
|
||||
optional int32 second_ordinal = 8;
|
||||
string created_at = 9;
|
||||
string first_key_json = 10;
|
||||
string second_key_json = 11;
|
||||
}
|
||||
|
||||
message CaminoOp {
|
||||
@@ -203,7 +173,6 @@ message CaminoOp {
|
||||
string op_kind = 3;
|
||||
Value payload = 4;
|
||||
string actor = 5;
|
||||
quixos.FunctionRef function_ref = 6;
|
||||
string created_at = 7;
|
||||
string client_mutation_id = 8;
|
||||
string created_at = 6;
|
||||
string client_mutation_id = 7;
|
||||
}
|
||||
|
||||
@@ -1,71 +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;
|
||||
bool display_label = 51025;
|
||||
}
|
||||
|
||||
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
-166
@@ -2,190 +2,72 @@ syntax = "proto3";
|
||||
|
||||
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 {
|
||||
CARDINALITY_UNSPECIFIED = 0;
|
||||
OPTIONAL_ONE = 1;
|
||||
EXACTLY_ONE = 2;
|
||||
MANY = 3;
|
||||
MANY_UNIQUE = 4;
|
||||
MANY_ORDERED = 5;
|
||||
}
|
||||
|
||||
enum DocRefStrength {
|
||||
DOC_REF_STRENGTH_UNSPECIFIED = 0;
|
||||
SOFT_REF = 1;
|
||||
PROJECTED_EDGE = 2;
|
||||
message AtomDefinition {
|
||||
string atom_id = 1;
|
||||
string display_name = 2;
|
||||
}
|
||||
|
||||
enum FieldStorageKind {
|
||||
FIELD_STORAGE_KIND_UNSPECIFIED = 0;
|
||||
STORED = 1;
|
||||
DERIVED = 2;
|
||||
LAZY = 3;
|
||||
EXTERNAL = 4;
|
||||
STATIC_FINAL = 5;
|
||||
message AtomConformance {
|
||||
string atom_id = 1;
|
||||
string interface_revision_id = 2;
|
||||
string conformance_id = 3;
|
||||
}
|
||||
|
||||
message Ref {
|
||||
string object_id = 1;
|
||||
message StateAttachment {
|
||||
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 WatchStartResult {
|
||||
string watch_id = 1;
|
||||
message EndpointConstraint {
|
||||
oneof kind {
|
||||
string atom_id = 1;
|
||||
string interface_revision_id = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message WatchStopRequest {
|
||||
string watch_id = 1;
|
||||
message EdgeEndpoint {
|
||||
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 CrdtText {
|
||||
bytes payload = 1;
|
||||
message EdgeAttachment {
|
||||
string edge_type_id = 1;
|
||||
string display_name = 2;
|
||||
EdgeEndpoint first = 3;
|
||||
EdgeEndpoint second = 4;
|
||||
string owner_conformance_id = 5;
|
||||
}
|
||||
|
||||
message CrdtRichText {
|
||||
bytes payload = 1;
|
||||
}
|
||||
|
||||
message CrdtJson {
|
||||
bytes payload = 1;
|
||||
}
|
||||
|
||||
message ClassSchema {
|
||||
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;
|
||||
bool is_display_label = 10;
|
||||
}
|
||||
|
||||
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;
|
||||
// Camino consumes this persistence-only projection of a checked workspace.
|
||||
// Interfaces, operation bindings, packages, and constructors stay in orch.
|
||||
message PersistencePlan {
|
||||
string workspace_id = 1;
|
||||
string workspace_revision_id = 2;
|
||||
repeated AtomDefinition atoms = 3;
|
||||
repeated AtomConformance conformances = 4;
|
||||
repeated StateAttachment states = 5;
|
||||
repeated EdgeAttachment edges = 6;
|
||||
}
|
||||
|
||||
+55
-48
@@ -8,35 +8,56 @@ import "quixos/refs.proto";
|
||||
import "quixos/runtime.proto";
|
||||
|
||||
service OrchestratorRuntime {
|
||||
rpc InvokeFunction(InvokeFunctionRequest) returns (InvokeFunctionResponse);
|
||||
rpc WatchFunction(WatchFunctionRequest) returns (stream WatchFunctionEvent);
|
||||
rpc InvokeCapability(InvokeCapabilityRequest) returns (InvokeCapabilityResponse);
|
||||
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 ListPackageRuntimes(ListPackageRuntimesRequest) returns (ListPackageRuntimesResponse);
|
||||
rpc ListActivations(ListActivationsRequest) returns (ListActivationsResponse);
|
||||
rpc CloseActivation(CloseActivationRequest) returns (CloseActivationResponse);
|
||||
}
|
||||
|
||||
message InvokeFunctionRequest {
|
||||
quixos.FunctionRef function = 1;
|
||||
string object_id = 2;
|
||||
map<string, camino.Value> input = 3;
|
||||
message ConstructObjectRequest {
|
||||
string atom_id = 1;
|
||||
map<string, camino.Value> input = 2;
|
||||
}
|
||||
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;
|
||||
Activation activation = 2;
|
||||
bool ok = 3;
|
||||
camino.Value result = 4;
|
||||
string error = 5;
|
||||
repeated quixos.runtime.DerivedDependency dependencies = 6;
|
||||
}
|
||||
|
||||
message WatchFunctionRequest {
|
||||
quixos.FunctionRef function = 1;
|
||||
message WatchCapabilityRequest {
|
||||
quixos.CapabilityRef capability = 1;
|
||||
string object_id = 2;
|
||||
map<string, camino.Value> input = 3;
|
||||
}
|
||||
|
||||
message WatchFunctionEvent {
|
||||
message WatchCapabilityEvent {
|
||||
string invocation_id = 1;
|
||||
Activation activation = 2;
|
||||
string watch_id = 3;
|
||||
@@ -46,36 +67,24 @@ message WatchFunctionEvent {
|
||||
bool initial = 7;
|
||||
}
|
||||
|
||||
message GetWorkspaceRequest {}
|
||||
message GetWorkspaceResponse {
|
||||
string workspace_id = 1;
|
||||
string workspace_revision_id = 2;
|
||||
string source_root_commit = 3;
|
||||
}
|
||||
message ListActivationsRequest {}
|
||||
|
||||
message ListPackageDescriptorsRequest {}
|
||||
|
||||
message ListPackageDescriptorsResponse {
|
||||
repeated quixos.PackageDescriptor descriptors = 1;
|
||||
}
|
||||
|
||||
message ListPackageDescriptorsResponse { repeated quixos.PackageDescriptor descriptors = 1; }
|
||||
message ListPackageRuntimesRequest {}
|
||||
|
||||
message ListPackageRuntimesResponse {
|
||||
repeated PackageRuntimeStatus runtimes = 1;
|
||||
}
|
||||
|
||||
message ListActivationsResponse {
|
||||
repeated Activation activations = 1;
|
||||
}
|
||||
|
||||
message CloseActivationRequest {
|
||||
string activation_id = 1;
|
||||
string reason = 2;
|
||||
}
|
||||
|
||||
message CloseActivationResponse {
|
||||
Activation activation = 1;
|
||||
}
|
||||
message ListPackageRuntimesResponse { repeated PackageRuntimeStatus runtimes = 1; }
|
||||
message ListActivationsResponse { repeated Activation activations = 1; }
|
||||
message CloseActivationRequest { string activation_id = 1; string reason = 2; }
|
||||
message CloseActivationResponse { Activation activation = 1; }
|
||||
|
||||
message Activation {
|
||||
string activation_id = 1;
|
||||
quixos.FunctionRef function = 2;
|
||||
quixos.PackageExportRef export = 2;
|
||||
string object_id = 3;
|
||||
string state = 4;
|
||||
uint32 demand = 5;
|
||||
@@ -88,17 +97,15 @@ message Activation {
|
||||
|
||||
message PackageRuntimeStatus {
|
||||
string runtime_key = 1;
|
||||
string package_namespace = 2;
|
||||
string package_name = 3;
|
||||
uint32 descriptor_version = 4;
|
||||
string source_repo = 5;
|
||||
string server_installable = 6;
|
||||
string source_path = 7;
|
||||
string server_path = 8;
|
||||
uint32 pid = 9;
|
||||
string state = 10;
|
||||
string started_at = 11;
|
||||
string last_handshake_at = 12;
|
||||
string runtime_protocol_version = 13;
|
||||
uint32 advertised_function_count = 14;
|
||||
string package_revision_id = 2;
|
||||
string source_repository = 3;
|
||||
string source_commit = 4;
|
||||
string build_target = 5;
|
||||
string server_path = 6;
|
||||
uint32 pid = 7;
|
||||
string state = 8;
|
||||
string started_at = 9;
|
||||
string last_handshake_at = 10;
|
||||
string runtime_protocol_version = 11;
|
||||
uint32 advertised_export_count = 12;
|
||||
}
|
||||
|
||||
@@ -2,70 +2,14 @@ syntax = "proto3";
|
||||
|
||||
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 {
|
||||
string package_namespace = 1;
|
||||
string package_name = 2;
|
||||
uint32 descriptor_version = 3;
|
||||
uint32 interface_compatible_back_to = 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;
|
||||
string package_id = 1;
|
||||
string package_revision_id = 2;
|
||||
string runtime_protocol_version = 3;
|
||||
repeated RuntimeExport exports = 4;
|
||||
}
|
||||
|
||||
enum FunctionCapabilityKind {
|
||||
FUNCTION_CAPABILITY_KIND_UNSPECIFIED = 0;
|
||||
METHOD = 1;
|
||||
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;
|
||||
message RuntimeExport {
|
||||
string export_id = 1;
|
||||
string runtime_symbol = 2;
|
||||
}
|
||||
|
||||
+26
-6
@@ -2,10 +2,30 @@ syntax = "proto3";
|
||||
|
||||
package quixos;
|
||||
|
||||
message FunctionRef {
|
||||
string package_namespace = 1;
|
||||
string package_name = 2;
|
||||
string symbol = 3;
|
||||
string version_ref = 4;
|
||||
string operation = 5;
|
||||
message CapabilityRef {
|
||||
string interface_revision_id = 1;
|
||||
string operation_id = 2;
|
||||
}
|
||||
|
||||
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
@@ -9,44 +9,68 @@ service PackageRuntime {
|
||||
rpc Handshake(HandshakeRequest) returns (HandshakeResponse);
|
||||
rpc Invoke(InvokeRequest) returns (InvokeResponse);
|
||||
rpc Watch(WatchRequest) returns (stream WatchEvent);
|
||||
rpc GetInvocationStatus(InvocationControlRequest) returns (InvocationStatus);
|
||||
rpc CancelInvocation(InvocationControlRequest) returns (InvocationStatus);
|
||||
}
|
||||
|
||||
message HandshakeRequest {
|
||||
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 {
|
||||
string package_namespace = 1;
|
||||
string package_name = 2;
|
||||
string runtime_protocol_version = 3;
|
||||
repeated quixos.FunctionRef functions = 4;
|
||||
message InvocationContext {
|
||||
string workspace_epoch = 1;
|
||||
string instance_id = 2;
|
||||
string binding_digest = 3;
|
||||
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 {
|
||||
string invocation_id = 1;
|
||||
quixos.FunctionRef function = 2;
|
||||
quixos.PackageExportRef export = 2;
|
||||
string object_id = 3;
|
||||
map<string, camino.Value> input = 4;
|
||||
repeated quixos.InjectedDependency dependencies = 5;
|
||||
InvocationContext context = 6;
|
||||
}
|
||||
|
||||
message InvokeResponse {
|
||||
bool ok = 1;
|
||||
camino.Value result = 2;
|
||||
string error = 3;
|
||||
repeated DerivedDependency dependencies = 4;
|
||||
}
|
||||
|
||||
message WatchRequest {
|
||||
string invocation_id = 1;
|
||||
quixos.FunctionRef function = 2;
|
||||
quixos.PackageExportRef export = 2;
|
||||
string object_id = 3;
|
||||
map<string, camino.Value> input = 4;
|
||||
repeated quixos.InjectedDependency dependencies = 5;
|
||||
InvocationContext context = 6;
|
||||
}
|
||||
|
||||
message DerivedDependency {
|
||||
string kind = 1;
|
||||
string object_id = 2;
|
||||
string field_name = 3;
|
||||
string source_field = 4;
|
||||
string attachment_id = 3;
|
||||
string projection_id = 4;
|
||||
}
|
||||
|
||||
message WatchEvent {
|
||||
|
||||
@@ -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; });
|
||||
@@ -0,0 +1,132 @@
|
||||
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`);
|
||||
});
|
||||
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(operations.map((operation) => [operation.name, `(${params(operation.inputType)}) => Promise<${type(operation.outputType)}>`])),
|
||||
spec: { kind: "interface", id: entry.id, operations: Object.fromEntries(operations.map((operation) => [operation.name, operation])) } };
|
||||
}
|
||||
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", "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`;
|
||||
};
|
||||
@@ -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,
|
||||
])),
|
||||
};
|
||||
};
|
||||
@@ -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 }); }
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
@@ -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";
|
||||
@@ -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
@@ -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"));}
|
||||
};
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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};
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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));
|
||||
@@ -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}`);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 1–100 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);
|
||||
});
|
||||
@@ -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; });
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./types.js";
|
||||
export * from "./validation.js";
|
||||
export * from "./evolution.js";
|
||||
export * from "./migrations.js";
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -3,23 +3,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fromBinary, toJsonString } from "@bufbuild/protobuf";
|
||||
import type { PackageDescriptor } from "./gen/quixos/package_pb.js";
|
||||
import {
|
||||
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(":");
|
||||
import { PackageDescriptorSchema } from "./gen/quixos/package_pb.js";
|
||||
|
||||
const protoPaths = () => {
|
||||
const candidates = [
|
||||
@@ -76,87 +60,25 @@ export const validatePackageDescriptor = (
|
||||
descriptor: PackageDescriptor,
|
||||
): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (!descriptor.packageNamespace) {
|
||||
errors.push("packageNamespace is required");
|
||||
if (!descriptor.packageId) {
|
||||
errors.push("packageId is required");
|
||||
}
|
||||
if (!descriptor.packageName) {
|
||||
errors.push("packageName is required");
|
||||
if (!descriptor.packageRevisionId) {
|
||||
errors.push("packageRevisionId is required");
|
||||
}
|
||||
if (descriptor.descriptorVersion === 0) {
|
||||
errors.push("descriptorVersion must be greater than zero");
|
||||
if (!descriptor.runtimeProtocolVersion) {
|
||||
errors.push("runtimeProtocolVersion is required");
|
||||
}
|
||||
if (
|
||||
descriptor.interfaceCompatibleBackTo !== 0 &&
|
||||
descriptor.interfaceCompatibleBackTo > descriptor.descriptorVersion
|
||||
) {
|
||||
errors.push("interfaceCompatibleBackTo must not exceed descriptorVersion");
|
||||
}
|
||||
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;
|
||||
const seenExports = new Set<string>();
|
||||
for (const [index, entry] of descriptor.exports.entries()) {
|
||||
if (!entry.exportId) errors.push(`exports[${index}].exportId is required`);
|
||||
if (!entry.runtimeSymbol) {
|
||||
errors.push(`exports[${index}].runtimeSymbol is required`);
|
||||
}
|
||||
if (!fn.packageNamespace) {
|
||||
fn.packageNamespace = descriptor.packageNamespace;
|
||||
}
|
||||
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`);
|
||||
if (seenExports.has(entry.exportId)) {
|
||||
errors.push(`duplicate export: ${entry.exportId}`);
|
||||
}
|
||||
seenExports.add(entry.exportId);
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
+445
-260
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
|
||||
+180
-718
File diff suppressed because it is too large
Load Diff
+233
-78
@@ -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)
|
||||
/* eslint-disable */
|
||||
|
||||
import type { GenFile, GenMessage, GenService } 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 type { PackageDescriptor } 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 type { DerivedDependency } 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.
|
||||
*/
|
||||
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;
|
||||
@@ -38,19 +126,27 @@ export type InvokeFunctionRequest = Message<"quixos.orch.InvokeFunctionRequest">
|
||||
* @generated from field: map<string, camino.Value> input = 3;
|
||||
*/
|
||||
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.
|
||||
* Use `create(InvokeFunctionRequestSchema)` to create a new message.
|
||||
* Describes the message quixos.orch.InvokeCapabilityRequest.
|
||||
* Use `create(InvokeCapabilityRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const InvokeFunctionRequestSchema: GenMessage<InvokeFunctionRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 0);
|
||||
export const InvokeCapabilityRequestSchema: GenMessage<InvokeCapabilityRequest> = /*@__PURE__*/
|
||||
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;
|
||||
*/
|
||||
@@ -75,23 +171,28 @@ export type InvokeFunctionResponse = Message<"quixos.orch.InvokeFunctionResponse
|
||||
* @generated from field: string error = 5;
|
||||
*/
|
||||
error: string;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 6;
|
||||
*/
|
||||
dependencies: DerivedDependency[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message quixos.orch.InvokeFunctionResponse.
|
||||
* Use `create(InvokeFunctionResponseSchema)` to create a new message.
|
||||
* Describes the message quixos.orch.InvokeCapabilityResponse.
|
||||
* Use `create(InvokeCapabilityResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const InvokeFunctionResponseSchema: GenMessage<InvokeFunctionResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 1);
|
||||
export const InvokeCapabilityResponseSchema: GenMessage<InvokeCapabilityResponse> = /*@__PURE__*/
|
||||
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;
|
||||
@@ -105,16 +206,16 @@ export type WatchFunctionRequest = Message<"quixos.orch.WatchFunctionRequest"> &
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message quixos.orch.WatchFunctionRequest.
|
||||
* Use `create(WatchFunctionRequestSchema)` to create a new message.
|
||||
* Describes the message quixos.orch.WatchCapabilityRequest.
|
||||
* Use `create(WatchCapabilityRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const WatchFunctionRequestSchema: GenMessage<WatchFunctionRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 2);
|
||||
export const WatchCapabilityRequestSchema: GenMessage<WatchCapabilityRequest> = /*@__PURE__*/
|
||||
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;
|
||||
*/
|
||||
@@ -152,11 +253,51 @@ export type WatchFunctionEvent = Message<"quixos.orch.WatchFunctionEvent"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message quixos.orch.WatchFunctionEvent.
|
||||
* Use `create(WatchFunctionEventSchema)` to create a new message.
|
||||
* Describes the message quixos.orch.WatchCapabilityEvent.
|
||||
* Use `create(WatchCapabilityEventSchema)` to create a new message.
|
||||
*/
|
||||
export const WatchFunctionEventSchema: GenMessage<WatchFunctionEvent> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 3);
|
||||
export const WatchCapabilityEventSchema: GenMessage<WatchCapabilityEvent> = /*@__PURE__*/
|
||||
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
|
||||
@@ -169,7 +310,7 @@ export type ListActivationsRequest = Message<"quixos.orch.ListActivationsRequest
|
||||
* Use `create(ListActivationsRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const ListActivationsRequestSchema: GenMessage<ListActivationsRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 4);
|
||||
messageDesc(file_quixos_orch, 10);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const ListPackageDescriptorsRequestSchema: GenMessage<ListPackageDescriptorsRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 5);
|
||||
messageDesc(file_quixos_orch, 11);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const ListPackageDescriptorsResponseSchema: GenMessage<ListPackageDescriptorsResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 6);
|
||||
messageDesc(file_quixos_orch, 12);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const ListPackageRuntimesRequestSchema: GenMessage<ListPackageRuntimesRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 7);
|
||||
messageDesc(file_quixos_orch, 13);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const ListPackageRuntimesResponseSchema: GenMessage<ListPackageRuntimesResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 8);
|
||||
messageDesc(file_quixos_orch, 14);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const ListActivationsResponseSchema: GenMessage<ListActivationsResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 9);
|
||||
messageDesc(file_quixos_orch, 15);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const CloseActivationRequestSchema: GenMessage<CloseActivationRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 10);
|
||||
messageDesc(file_quixos_orch, 16);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const CloseActivationResponseSchema: GenMessage<CloseActivationResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 11);
|
||||
messageDesc(file_quixos_orch, 17);
|
||||
|
||||
/**
|
||||
* @generated from message quixos.orch.Activation
|
||||
@@ -297,9 +438,9 @@ export type Activation = Message<"quixos.orch.Activation"> & {
|
||||
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;
|
||||
@@ -347,7 +488,7 @@ export type Activation = Message<"quixos.orch.Activation"> & {
|
||||
* Use `create(ActivationSchema)` to create a new message.
|
||||
*/
|
||||
export const ActivationSchema: GenMessage<Activation> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 12);
|
||||
messageDesc(file_quixos_orch, 18);
|
||||
|
||||
/**
|
||||
* @generated from message quixos.orch.PackageRuntimeStatus
|
||||
@@ -359,69 +500,59 @@ export type PackageRuntimeStatus = Message<"quixos.orch.PackageRuntimeStatus"> &
|
||||
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;
|
||||
*/
|
||||
serverInstallable: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string source_path = 7;
|
||||
*/
|
||||
sourcePath: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string server_path = 8;
|
||||
* @generated from field: string server_path = 6;
|
||||
*/
|
||||
serverPath: string;
|
||||
|
||||
/**
|
||||
* @generated from field: uint32 pid = 9;
|
||||
* @generated from field: uint32 pid = 7;
|
||||
*/
|
||||
pid: number;
|
||||
|
||||
/**
|
||||
* @generated from field: string state = 10;
|
||||
* @generated from field: string state = 8;
|
||||
*/
|
||||
state: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string started_at = 11;
|
||||
* @generated from field: string started_at = 9;
|
||||
*/
|
||||
startedAt: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string last_handshake_at = 12;
|
||||
* @generated from field: string last_handshake_at = 10;
|
||||
*/
|
||||
lastHandshakeAt: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string runtime_protocol_version = 13;
|
||||
* @generated from field: string runtime_protocol_version = 11;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
export const PackageRuntimeStatusSchema: GenMessage<PackageRuntimeStatus> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_orch, 13);
|
||||
messageDesc(file_quixos_orch, 19);
|
||||
|
||||
/**
|
||||
* @generated from service quixos.orch.OrchestratorRuntime
|
||||
*/
|
||||
export const OrchestratorRuntime: GenService<{
|
||||
/**
|
||||
* @generated from rpc quixos.orch.OrchestratorRuntime.InvokeFunction
|
||||
* @generated from rpc quixos.orch.OrchestratorRuntime.InvokeCapability
|
||||
*/
|
||||
invokeFunction: {
|
||||
invokeCapability: {
|
||||
methodKind: "unary";
|
||||
input: typeof InvokeFunctionRequestSchema;
|
||||
output: typeof InvokeFunctionResponseSchema;
|
||||
input: typeof InvokeCapabilityRequestSchema;
|
||||
output: typeof InvokeCapabilityResponseSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc quixos.orch.OrchestratorRuntime.WatchFunction
|
||||
* @generated from rpc quixos.orch.OrchestratorRuntime.WatchCapability
|
||||
*/
|
||||
watchFunction: {
|
||||
watchCapability: {
|
||||
methodKind: "server_streaming";
|
||||
input: typeof WatchFunctionRequestSchema;
|
||||
output: typeof WatchFunctionEventSchema;
|
||||
input: typeof WatchCapabilityRequestSchema;
|
||||
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
|
||||
|
||||
+33
-283
@@ -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)
|
||||
/* eslint-disable */
|
||||
|
||||
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||
import { enumDesc, 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 { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import type { Message } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* Describes the file quixos/package.proto.
|
||||
*/
|
||||
export const file_quixos_package: GenFile = /*@__PURE__*/
|
||||
fileDesc("ChRxdWl4b3MvcGFja2FnZS5wcm90bxIGcXVpeG9zIm4KClBhY2thZ2VSZWYSGQoRcGFja2FnZV9uYW1lc3BhY2UYASABKAkSFAoMcGFja2FnZV9uYW1lGAIgASgJEhoKEmRlc2NyaXB0b3JfdmVyc2lvbhgDIAEoDRITCgt2ZXJzaW9uX3JlZhgEIAEoCSKgBAoRUGFja2FnZURlc2NyaXB0b3ISGQoRcGFja2FnZV9uYW1lc3BhY2UYASABKAkSFAoMcGFja2FnZV9uYW1lGAIgASgJEhoKEmRlc2NyaXB0b3JfdmVyc2lvbhgDIAEoDRIkChxpbnRlcmZhY2VfY29tcGF0aWJsZV9iYWNrX3RvGAQgASgNEiEKGXJ1bm5lcl9jb21wYXRpYmxlX2JhY2tfdG8YBSABKA0SEwoLc291cmNlX3JlcG8YBiABKAkSEgoKc291cmNlX3JlZhgHIAEoCRIbChNyZXNvbHZlZF9zb3VyY2VfcmVmGAggASgJEhoKEnNlcnZlcl9pbnN0YWxsYWJsZRgJIAEoCRIgChhydW50aW1lX3Byb3RvY29sX3ZlcnNpb24YCiABKAkSKgoNY2xhc3NfZXhwb3J0cxgLIAMoCzITLnF1aXhvcy5DbGFzc0V4cG9ydBIwChBmdW5jdGlvbl9leHBvcnRzGAwgAygLMhYucXVpeG9zLkZ1bmN0aW9uRXhwb3J0EjIKEWNvbXBvbmVudF9leHBvcnRzGA0gAygLMhcucXVpeG9zLkNvbXBvbmVudEV4cG9ydBIuCg9zaWRlY2FyX2V4cG9ydHMYDiADKAsyFS5xdWl4b3MuU2lkZWNhckV4cG9ydBIvCgxkZXBlbmRlbmNpZXMYDyADKAsyGS5xdWl4b3MuUGFja2FnZURlcGVuZGVuY3kiRwoLQ2xhc3NFeHBvcnQSIwoIY2xhc3NfaWQYASABKAsyES5jYW1pbm8uU3ltYm9sUmVmEhMKC3NvdXJjZV9maWxlGAIgASgJItoBCg5GdW5jdGlvbkV4cG9ydBIlCghmdW5jdGlvbhgBIAEoCzITLnF1aXhvcy5GdW5jdGlvblJlZhISCgppbnB1dF90eXBlGAIgASgJEhMKC291dHB1dF90eXBlGAMgASgJEhkKEWludGVyZmFjZV92ZXJzaW9uGAQgASgNEiQKHGludGVyZmFjZV9jb21wYXRpYmxlX2JhY2tfdG8YBSABKA0SNwoPY2FwYWJpbGl0eV9raW5kGAYgASgOMh4ucXVpeG9zLkZ1bmN0aW9uQ2FwYWJpbGl0eUtpbmQiYwoPQ29tcG9uZW50RXhwb3J0Eg4KBnN5bWJvbBgBIAEoCRISCgpwcm9wc190eXBlGAIgASgJEiwKEXN1cHBvcnRlZF9jbGFzc2VzGAMgAygLMhEuY2FtaW5vLlN5bWJvbFJlZiI5Cg1TaWRlY2FyRXhwb3J0Eg4KBnN5bWJvbBgBIAEoCRIYChBzaWRlX2VmZmVjdF9tb2RlGAIgASgJIpUBChFQYWNrYWdlRGVwZW5kZW5jeRIjCgdwYWNrYWdlGAEgASgLMhIucXVpeG9zLlBhY2thZ2VSZWYSJAocaW50ZXJmYWNlX2NvbXBhdGlibGVfYmFja190bxgCIAEoDRIZChFyZWZhY3Rvcl9yZXF1aXJlZBgDIAEoCBIaChJjb21wYXRpYmlsaXR5X25vdGUYBCABKAkqggEKFkZ1bmN0aW9uQ2FwYWJpbGl0eUtpbmQSKAokRlVOQ1RJT05fQ0FQQUJJTElUWV9LSU5EX1VOU1BFQ0lGSUVEEAASCgoGTUVUSE9EEAESEgoORklFTERfUkVTT0xWRVIQAhINCglNSUdSQVRJT04QAxIPCgtDT05TVFJVQ1RPUhAEYgZwcm90bzM", [file_camino_schema, file_quixos_refs]);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
fileDesc("ChRxdWl4b3MvcGFja2FnZS5wcm90bxIGcXVpeG9zIo4BChFQYWNrYWdlRGVzY3JpcHRvchISCgpwYWNrYWdlX2lkGAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAMgASgJEiYKB2V4cG9ydHMYBCADKAsyFS5xdWl4b3MuUnVudGltZUV4cG9ydCI6Cg1SdW50aW1lRXhwb3J0EhEKCWV4cG9ydF9pZBgBIAEoCRIWCg5ydW50aW1lX3N5bWJvbBgCIAEoCWIGcHJvdG8z");
|
||||
|
||||
/**
|
||||
* @generated from 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;
|
||||
*/
|
||||
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;
|
||||
* @generated from field: string runtime_protocol_version = 3;
|
||||
*/
|
||||
runtimeProtocolVersion: string;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated quixos.ClassExport class_exports = 11;
|
||||
* @generated from field: repeated quixos.RuntimeExport exports = 4;
|
||||
*/
|
||||
classExports: ClassExport[];
|
||||
|
||||
/**
|
||||
* @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[];
|
||||
exports: RuntimeExport[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -133,186 +42,27 @@ export type PackageDescriptor = Message<"quixos.PackageDescriptor"> & {
|
||||
* Use `create(PackageDescriptorSchema)` to create a new message.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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
@@ -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)
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -10,42 +10,125 @@ import type { Message } from "@bufbuild/protobuf";
|
||||
* Describes the file quixos/refs.proto.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
operationId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message quixos.FunctionRef.
|
||||
* Use `create(FunctionRefSchema)` to create a new message.
|
||||
* Describes the message quixos.CapabilityRef.
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @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
@@ -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)
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegen
|
||||
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import type { Value } 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 type { Message } from "@bufbuild/protobuf";
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { Message } from "@bufbuild/protobuf";
|
||||
* Describes the file quixos/runtime.proto.
|
||||
*/
|
||||
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
|
||||
@@ -24,6 +24,11 @@ export type HandshakeRequest = Message<"quixos.runtime.HandshakeRequest"> & {
|
||||
* @generated from field: string orch_protocol_version = 1;
|
||||
*/
|
||||
orchProtocolVersion: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string nonce = 2;
|
||||
*/
|
||||
nonce: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,24 +43,34 @@ export const HandshakeRequestSchema: GenMessage<HandshakeRequest> = /*@__PURE__*
|
||||
*/
|
||||
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;
|
||||
*/
|
||||
packageName: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string runtime_protocol_version = 3;
|
||||
* @generated from field: string runtime_protocol_version = 2;
|
||||
*/
|
||||
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__*/
|
||||
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
|
||||
*/
|
||||
@@ -75,9 +175,9 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
|
||||
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;
|
||||
@@ -88,6 +188,16 @@ export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
|
||||
* @generated from field: map<string, camino.Value> input = 4;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
export const InvokeRequestSchema: GenMessage<InvokeRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_runtime, 2);
|
||||
messageDesc(file_quixos_runtime, 5);
|
||||
|
||||
/**
|
||||
* @generated from message quixos.runtime.InvokeResponse
|
||||
@@ -115,6 +225,11 @@ export type InvokeResponse = Message<"quixos.runtime.InvokeResponse"> & {
|
||||
* @generated from field: string error = 3;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
export const InvokeResponseSchema: GenMessage<InvokeResponse> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_runtime, 3);
|
||||
messageDesc(file_quixos_runtime, 6);
|
||||
|
||||
/**
|
||||
* @generated from message quixos.runtime.WatchRequest
|
||||
@@ -134,9 +249,9 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
|
||||
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;
|
||||
@@ -147,6 +262,16 @@ export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
|
||||
* @generated from field: map<string, camino.Value> input = 4;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
export const WatchRequestSchema: GenMessage<WatchRequest> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_runtime, 4);
|
||||
messageDesc(file_quixos_runtime, 7);
|
||||
|
||||
/**
|
||||
* @generated from message quixos.runtime.DerivedDependency
|
||||
@@ -171,14 +296,14 @@ export type DerivedDependency = Message<"quixos.runtime.DerivedDependency"> & {
|
||||
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.
|
||||
*/
|
||||
export const DerivedDependencySchema: GenMessage<DerivedDependency> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_runtime, 5);
|
||||
messageDesc(file_quixos_runtime, 8);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const WatchEventSchema: GenMessage<WatchEvent> = /*@__PURE__*/
|
||||
messageDesc(file_quixos_runtime, 6);
|
||||
messageDesc(file_quixos_runtime, 9);
|
||||
|
||||
/**
|
||||
* @generated from service quixos.runtime.PackageRuntime
|
||||
@@ -253,6 +378,22 @@ export const PackageRuntime: GenService<{
|
||||
input: typeof WatchRequestSchema;
|
||||
output: typeof WatchEventSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc quixos.runtime.PackageRuntime.GetInvocationStatus
|
||||
*/
|
||||
getInvocationStatus: {
|
||||
methodKind: "unary";
|
||||
input: typeof InvocationControlRequestSchema;
|
||||
output: typeof InvocationStatusSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc quixos.runtime.PackageRuntime.CancelInvocation
|
||||
*/
|
||||
cancelInvocation: {
|
||||
methodKind: "unary";
|
||||
input: typeof InvocationControlRequestSchema;
|
||||
output: typeof InvocationStatusSchema;
|
||||
},
|
||||
}> = /*@__PURE__*/
|
||||
serviceDesc(file_quixos_runtime, 0);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./loader.js";
|
||||
export * from "./parser.js";
|
||||
export * from "./types.js";
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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");
|
||||
};
|
||||
@@ -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(),
|
||||
});
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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\)/,
|
||||
);
|
||||
});
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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")
|
||||
));
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
@@ -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/);
|
||||
});
|
||||
Vendored
+194
@@ -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
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
Vendored
+7
@@ -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";
|
||||
}
|
||||
}
|
||||
Vendored
+10
@@ -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
@@ -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";
|
||||
}
|
||||
}
|
||||
Vendored
+5
@@ -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";
|
||||
}
|
||||
}
|
||||
Vendored
+50
@@ -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;
|
||||
}
|
||||
Vendored
+12
@@ -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
@@ -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
@@ -0,0 +1,6 @@
|
||||
quixos-lock version 1 {
|
||||
quixos source {
|
||||
repository "https://example.test/quixos.git";
|
||||
commit "1111111111111111111111111111111111111111";
|
||||
}
|
||||
}
|
||||
+35
@@ -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;
|
||||
}
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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"]);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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/);
|
||||
}
|
||||
});
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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
@@ -7,5 +7,6 @@
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,6 +14,6 @@
|
||||
"verbatimModuleSyntax": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": false,
|
||||
"declaration": false,
|
||||
"noEmit": false,
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user