Make local graph execution optional across composed packages

This commit is contained in:
Timothy J. Aveni
2026-09-28 21:05:42 -07:00
parent 1e72713ae2
commit e9c15fe40f
87 changed files with 3703 additions and 3400 deletions
+11 -6
View File
@@ -5,6 +5,7 @@ import type { StructuralRequest } from "./structural-plan.js";
import { parseQx, walkSyntax } from "./source.js";
import { addImplementation } from "./implementation-edit.js";
import { formatQuixosLock } from "../resource-lock/index.js";
import { javascriptTarget } from "../execution-world/native-backends.js";
const json = (value: unknown) => JSON.stringify(value, null, 2) + "\n";
const q = (value: string) => JSON.stringify(value);
const nixString = (value: string) => q(value).replaceAll("${", "\\${");
@@ -31,7 +32,10 @@ export async function executionScaffold(
const create = (file: string, content: string) => files.push({ file: prefix + file, create: content });
if (command === "package") {
if (!spec.tools || !spec.revision) throw new Error("Execution package requires exact tool sources and a revision");
create("package.qx", `package ${spec.name} id ${q(spec.id)} revision ${q(spec.revision)} {\n}\n`);
create(
"package.qx",
`symbol JavaScript id ${q(javascriptTarget)};\npackage ${spec.name} id ${q(spec.id)} revision ${q(spec.revision)} {\n}\n`,
);
create(
"quixos.lock",
formatQuixosLock({ formatVersion: 1, quixos: { resolver: "git", ...spec.tools.quixos }, resources: [] }),
@@ -90,7 +94,7 @@ in camino.assembleCaminoPackage {
pkgs.lib.optional (portable != []) (camino.mkCaminoTypeScriptPackage {
inherit pkgs context dependencies; src = ./.;
entry = "src/server.ts"; entries.browser = "src/browser.ts";
targets = pkgs.lib.unique (pkgs.lib.concatMap (entry: entry.execution.targets) portable);
targets = pkgs.lib.unique (map (target: if target == "target:org.quixos.execution.javascript@1" then "browser" else if target == "server" then "server" else throw "Unsupported TypeScript backend target") (pkgs.lib.concatMap (entry: entry.execution.targets) portable));
}) ++ pkgs.lib.optional (remote != []) (camino.mkCaminoTypeScriptService {
inherit pkgs context dependencies; src = ./.; entry = "src/remote.ts";
}) ++ pkgs.lib.optional (modules != {}) (camino.mkCaminoReactPackage {
@@ -134,15 +138,16 @@ in camino.assembleCaminoPackage {
.filter((token) => !token.trivia && token.start >= execution.start && token.end <= execution.end)
.map((token) => token.text);
const remote = tokens[1] === "remote";
const targets = remote
const declaredTargets = remote
? ["remote"]
: [...walkSyntax(execution)].filter((node) => node.kind === "identifier").map(text);
const targets = declaredTargets.length ? declaredTargets : ["server"];
if (
!targets.length ||
new Set(targets).size !== targets.length ||
targets.some((target) => !(remote ? ["remote"] : ["browser", "server"]).includes(target))
targets.some((target) => !(remote ? ["remote"] : ["JavaScript", "server"]).includes(target))
)
throw new Error("Execution scaffold supports browser/server portable targets and remote service handlers");
throw new Error("This TypeScript scaffold supports the JavaScript target, server, and remote service handlers");
const authored = await fs.readFile(path.join(root, prefix, "package.qx"), "utf8");
const syntax = parseQx(authored);
const packageNode = syntax.root.children.find((node) => node.kind === "packageResourceDecl");
@@ -174,7 +179,7 @@ in camino.assembleCaminoPackage {
`import type { ${remote ? "Remote" : "Portable"}Implementation } from "../gen/${remote ? "remote" : "qx"}.js";\nexport const handler: ${remote ? "Remote" : "Portable"}Implementation[${q(spec.name)}] = async (_context) => { throw new Error(${q("Implement " + spec.name)}); };\n`,
);
for (const target of targets) {
const file = prefix + `src/${target}.ts`;
const file = prefix + `src/${target === "JavaScript" ? "browser" : target}.ts`;
const before = await fs.readFile(path.join(root, file), "utf8");
files.push({
file,
File diff suppressed because one or more lines are too long
@@ -42,120 +42,121 @@ REF=41
FRAGMENT=42
IMPORT=43
EXTERNAL=44
ATOM=45
INTERFACE=46
INTERFACES=47
PACKAGE=48
VALUE=49
FIELD=50
RELATION=51
OPERATION=52
FUNCTION=53
CONSTRUCTOR=54
CONSTRUCTS=55
INPUT=56
CONFORM=57
AS=58
BIND=59
STATIC=60
TO=61
PRIVATE=62
SHARED=63
STATE=64
EDGE=65
PROJECTION=66
WITH=67
USING=68
ACCEPT=69
VIA=70
MATERIALIZE=71
IF=72
ABSENT=73
ON=74
POLICY=75
DEFAULT=76
SOURCE=77
REPOSITORY=78
COMMIT=79
REVISION=80
SEMANTIC_MAJOR=81
ON_DELETE=82
LIFECYCLE=83
JOURNAL=84
SUBSCRIPTION=85
RETAIN_OTHER=86
KEYED=87
PUBLIC_TRAVERSAL=88
ID=89
DOC=90
MODE=91
EMITS=92
RECEIVER=93
REQUIRES=94
ANY=95
GET=96
SET=97
WATCH=98
START=99
STOP=100
READ=101
WRITE=102
RESOLVE=103
CONNECT=104
DISCONNECT=105
CALL=106
WATCH_START=107
WATCH_STOP=108
SUBSCRIBE=109
UNSUBSCRIBE=110
REGISTER=111
CRDT=112
OPTIONAL_ONE=113
EXACTLY_ONE=114
MANY_UNIQUE=115
MANY=116
ORDERED=117
UNIT=118
WATCH_HANDLE=119
MESSAGE=120
ATOM_REF=121
INTERFACE_REF=122
OPTIONAL=123
LIST=124
RECORD=125
BOOL=126
BYTES=127
DOUBLE=128
INT32=129
INT64=130
STRING=131
UINT32=132
UINT64=133
TRUE=134
FALSE=135
NULL=136
ARROW=137
COLON=138
SEMI=139
COMMA=140
DOT=141
LBRACE=142
RBRACE=143
LBRACK=144
RBRACK=145
LPAREN=146
RPAREN=147
LT=148
GT=149
AMP=150
EQUAL=151
INTEGER=152
JSON_NUMBER=153
IDENTIFIER=154
STRING_LITERAL=155
LINE_COMMENT=156
BLOCK_COMMENT=157
WS=158
SYMBOL=45
ATOM=46
INTERFACE=47
INTERFACES=48
PACKAGE=49
VALUE=50
FIELD=51
RELATION=52
OPERATION=53
FUNCTION=54
CONSTRUCTOR=55
CONSTRUCTS=56
INPUT=57
CONFORM=58
AS=59
BIND=60
STATIC=61
TO=62
PRIVATE=63
SHARED=64
STATE=65
EDGE=66
PROJECTION=67
WITH=68
USING=69
ACCEPT=70
VIA=71
MATERIALIZE=72
IF=73
ABSENT=74
ON=75
POLICY=76
DEFAULT=77
SOURCE=78
REPOSITORY=79
COMMIT=80
REVISION=81
SEMANTIC_MAJOR=82
ON_DELETE=83
LIFECYCLE=84
JOURNAL=85
SUBSCRIPTION=86
RETAIN_OTHER=87
KEYED=88
PUBLIC_TRAVERSAL=89
ID=90
DOC=91
MODE=92
EMITS=93
RECEIVER=94
REQUIRES=95
ANY=96
GET=97
SET=98
WATCH=99
START=100
STOP=101
READ=102
WRITE=103
RESOLVE=104
CONNECT=105
DISCONNECT=106
CALL=107
WATCH_START=108
WATCH_STOP=109
SUBSCRIBE=110
UNSUBSCRIBE=111
REGISTER=112
CRDT=113
OPTIONAL_ONE=114
EXACTLY_ONE=115
MANY_UNIQUE=116
MANY=117
ORDERED=118
UNIT=119
WATCH_HANDLE=120
MESSAGE=121
ATOM_REF=122
INTERFACE_REF=123
OPTIONAL=124
LIST=125
RECORD=126
BOOL=127
BYTES=128
DOUBLE=129
INT32=130
INT64=131
STRING=132
UINT32=133
UINT64=134
TRUE=135
FALSE=136
NULL=137
ARROW=138
COLON=139
SEMI=140
COMMA=141
DOT=142
LBRACE=143
RBRACE=144
LBRACK=145
RBRACK=146
LPAREN=147
RPAREN=148
LT=149
GT=150
AMP=151
EQUAL=152
INTEGER=153
JSON_NUMBER=154
IDENTIFIER=155
STRING_LITERAL=156
LINE_COMMENT=157
BLOCK_COMMENT=158
WS=159
'edit'=1
'module'=2
'contract'=3
@@ -200,110 +201,111 @@ WS=158
'fragment'=42
'import'=43
'external'=44
'atom'=45
'interface'=46
'interfaces'=47
'package'=48
'value'=49
'field'=50
'relation'=51
'operation'=52
'function'=53
'constructor'=54
'constructs'=55
'input'=56
'conform'=57
'as'=58
'bind'=59
'static'=60
'to'=61
'private'=62
'shared'=63
'state'=64
'edge'=65
'projection'=66
'with'=67
'using'=68
'accept'=69
'via'=70
'materialize'=71
'if'=72
'absent'=73
'on'=74
'policy'=75
'default'=76
'source'=77
'repository'=78
'commit'=79
'revision'=80
'semantic-major'=81
'on-delete'=82
'lifecycle'=83
'journal'=84
'subscription'=85
'retain-other'=86
'keyed'=87
'public-traversal'=88
'id'=89
'doc'=90
'mode'=91
'emits'=92
'receiver'=93
'requires'=94
'any'=95
'get'=96
'set'=97
'watch'=98
'start'=99
'stop'=100
'read'=101
'write'=102
'resolve'=103
'connect'=104
'disconnect'=105
'call'=106
'watch-start'=107
'watch-stop'=108
'subscribe'=109
'unsubscribe'=110
'register'=111
'crdt'=112
'optional-one'=113
'exactly-one'=114
'many-unique'=115
'many'=116
'ordered'=117
'unit'=118
'watch-handle'=119
'message'=120
'atom-ref'=121
'interface-ref'=122
'optional'=123
'list'=124
'record'=125
'bool'=126
'bytes'=127
'double'=128
'int32'=129
'int64'=130
'string'=131
'uint32'=132
'uint64'=133
'true'=134
'false'=135
'null'=136
'->'=137
':'=138
';'=139
','=140
'.'=141
'{'=142
'}'=143
'['=144
']'=145
'('=146
')'=147
'<'=148
'>'=149
'&'=150
'='=151
'symbol'=45
'atom'=46
'interface'=47
'interfaces'=48
'package'=49
'value'=50
'field'=51
'relation'=52
'operation'=53
'function'=54
'constructor'=55
'constructs'=56
'input'=57
'conform'=58
'as'=59
'bind'=60
'static'=61
'to'=62
'private'=63
'shared'=64
'state'=65
'edge'=66
'projection'=67
'with'=68
'using'=69
'accept'=70
'via'=71
'materialize'=72
'if'=73
'absent'=74
'on'=75
'policy'=76
'default'=77
'source'=78
'repository'=79
'commit'=80
'revision'=81
'semantic-major'=82
'on-delete'=83
'lifecycle'=84
'journal'=85
'subscription'=86
'retain-other'=87
'keyed'=88
'public-traversal'=89
'id'=90
'doc'=91
'mode'=92
'emits'=93
'receiver'=94
'requires'=95
'any'=96
'get'=97
'set'=98
'watch'=99
'start'=100
'stop'=101
'read'=102
'write'=103
'resolve'=104
'connect'=105
'disconnect'=106
'call'=107
'watch-start'=108
'watch-stop'=109
'subscribe'=110
'unsubscribe'=111
'register'=112
'crdt'=113
'optional-one'=114
'exactly-one'=115
'many-unique'=116
'many'=117
'ordered'=118
'unit'=119
'watch-handle'=120
'message'=121
'atom-ref'=122
'interface-ref'=123
'optional'=124
'list'=125
'record'=126
'bool'=127
'bytes'=128
'double'=129
'int32'=130
'int64'=131
'string'=132
'uint32'=133
'uint64'=134
'true'=135
'false'=136
'null'=137
'->'=138
':'=139
';'=140
','=141
'.'=142
'{'=143
'}'=144
'['=145
']'=146
'('=147
')'=148
'<'=149
'>'=150
'&'=151
'='=152
File diff suppressed because one or more lines are too long
@@ -42,120 +42,121 @@ REF=41
FRAGMENT=42
IMPORT=43
EXTERNAL=44
ATOM=45
INTERFACE=46
INTERFACES=47
PACKAGE=48
VALUE=49
FIELD=50
RELATION=51
OPERATION=52
FUNCTION=53
CONSTRUCTOR=54
CONSTRUCTS=55
INPUT=56
CONFORM=57
AS=58
BIND=59
STATIC=60
TO=61
PRIVATE=62
SHARED=63
STATE=64
EDGE=65
PROJECTION=66
WITH=67
USING=68
ACCEPT=69
VIA=70
MATERIALIZE=71
IF=72
ABSENT=73
ON=74
POLICY=75
DEFAULT=76
SOURCE=77
REPOSITORY=78
COMMIT=79
REVISION=80
SEMANTIC_MAJOR=81
ON_DELETE=82
LIFECYCLE=83
JOURNAL=84
SUBSCRIPTION=85
RETAIN_OTHER=86
KEYED=87
PUBLIC_TRAVERSAL=88
ID=89
DOC=90
MODE=91
EMITS=92
RECEIVER=93
REQUIRES=94
ANY=95
GET=96
SET=97
WATCH=98
START=99
STOP=100
READ=101
WRITE=102
RESOLVE=103
CONNECT=104
DISCONNECT=105
CALL=106
WATCH_START=107
WATCH_STOP=108
SUBSCRIBE=109
UNSUBSCRIBE=110
REGISTER=111
CRDT=112
OPTIONAL_ONE=113
EXACTLY_ONE=114
MANY_UNIQUE=115
MANY=116
ORDERED=117
UNIT=118
WATCH_HANDLE=119
MESSAGE=120
ATOM_REF=121
INTERFACE_REF=122
OPTIONAL=123
LIST=124
RECORD=125
BOOL=126
BYTES=127
DOUBLE=128
INT32=129
INT64=130
STRING=131
UINT32=132
UINT64=133
TRUE=134
FALSE=135
NULL=136
ARROW=137
COLON=138
SEMI=139
COMMA=140
DOT=141
LBRACE=142
RBRACE=143
LBRACK=144
RBRACK=145
LPAREN=146
RPAREN=147
LT=148
GT=149
AMP=150
EQUAL=151
INTEGER=152
JSON_NUMBER=153
IDENTIFIER=154
STRING_LITERAL=155
LINE_COMMENT=156
BLOCK_COMMENT=157
WS=158
SYMBOL=45
ATOM=46
INTERFACE=47
INTERFACES=48
PACKAGE=49
VALUE=50
FIELD=51
RELATION=52
OPERATION=53
FUNCTION=54
CONSTRUCTOR=55
CONSTRUCTS=56
INPUT=57
CONFORM=58
AS=59
BIND=60
STATIC=61
TO=62
PRIVATE=63
SHARED=64
STATE=65
EDGE=66
PROJECTION=67
WITH=68
USING=69
ACCEPT=70
VIA=71
MATERIALIZE=72
IF=73
ABSENT=74
ON=75
POLICY=76
DEFAULT=77
SOURCE=78
REPOSITORY=79
COMMIT=80
REVISION=81
SEMANTIC_MAJOR=82
ON_DELETE=83
LIFECYCLE=84
JOURNAL=85
SUBSCRIPTION=86
RETAIN_OTHER=87
KEYED=88
PUBLIC_TRAVERSAL=89
ID=90
DOC=91
MODE=92
EMITS=93
RECEIVER=94
REQUIRES=95
ANY=96
GET=97
SET=98
WATCH=99
START=100
STOP=101
READ=102
WRITE=103
RESOLVE=104
CONNECT=105
DISCONNECT=106
CALL=107
WATCH_START=108
WATCH_STOP=109
SUBSCRIBE=110
UNSUBSCRIBE=111
REGISTER=112
CRDT=113
OPTIONAL_ONE=114
EXACTLY_ONE=115
MANY_UNIQUE=116
MANY=117
ORDERED=118
UNIT=119
WATCH_HANDLE=120
MESSAGE=121
ATOM_REF=122
INTERFACE_REF=123
OPTIONAL=124
LIST=125
RECORD=126
BOOL=127
BYTES=128
DOUBLE=129
INT32=130
INT64=131
STRING=132
UINT32=133
UINT64=134
TRUE=135
FALSE=136
NULL=137
ARROW=138
COLON=139
SEMI=140
COMMA=141
DOT=142
LBRACE=143
RBRACE=144
LBRACK=145
RBRACK=146
LPAREN=147
RPAREN=148
LT=149
GT=150
AMP=151
EQUAL=152
INTEGER=153
JSON_NUMBER=154
IDENTIFIER=155
STRING_LITERAL=156
LINE_COMMENT=157
BLOCK_COMMENT=158
WS=159
'edit'=1
'module'=2
'contract'=3
@@ -200,110 +201,111 @@ WS=158
'fragment'=42
'import'=43
'external'=44
'atom'=45
'interface'=46
'interfaces'=47
'package'=48
'value'=49
'field'=50
'relation'=51
'operation'=52
'function'=53
'constructor'=54
'constructs'=55
'input'=56
'conform'=57
'as'=58
'bind'=59
'static'=60
'to'=61
'private'=62
'shared'=63
'state'=64
'edge'=65
'projection'=66
'with'=67
'using'=68
'accept'=69
'via'=70
'materialize'=71
'if'=72
'absent'=73
'on'=74
'policy'=75
'default'=76
'source'=77
'repository'=78
'commit'=79
'revision'=80
'semantic-major'=81
'on-delete'=82
'lifecycle'=83
'journal'=84
'subscription'=85
'retain-other'=86
'keyed'=87
'public-traversal'=88
'id'=89
'doc'=90
'mode'=91
'emits'=92
'receiver'=93
'requires'=94
'any'=95
'get'=96
'set'=97
'watch'=98
'start'=99
'stop'=100
'read'=101
'write'=102
'resolve'=103
'connect'=104
'disconnect'=105
'call'=106
'watch-start'=107
'watch-stop'=108
'subscribe'=109
'unsubscribe'=110
'register'=111
'crdt'=112
'optional-one'=113
'exactly-one'=114
'many-unique'=115
'many'=116
'ordered'=117
'unit'=118
'watch-handle'=119
'message'=120
'atom-ref'=121
'interface-ref'=122
'optional'=123
'list'=124
'record'=125
'bool'=126
'bytes'=127
'double'=128
'int32'=129
'int64'=130
'string'=131
'uint32'=132
'uint64'=133
'true'=134
'false'=135
'null'=136
'->'=137
':'=138
';'=139
','=140
'.'=141
'{'=142
'}'=143
'['=144
']'=145
'('=146
')'=147
'<'=148
'>'=149
'&'=150
'='=151
'symbol'=45
'atom'=46
'interface'=47
'interfaces'=48
'package'=49
'value'=50
'field'=51
'relation'=52
'operation'=53
'function'=54
'constructor'=55
'constructs'=56
'input'=57
'conform'=58
'as'=59
'bind'=60
'static'=61
'to'=62
'private'=63
'shared'=64
'state'=65
'edge'=66
'projection'=67
'with'=68
'using'=69
'accept'=70
'via'=71
'materialize'=72
'if'=73
'absent'=74
'on'=75
'policy'=76
'default'=77
'source'=78
'repository'=79
'commit'=80
'revision'=81
'semantic-major'=82
'on-delete'=83
'lifecycle'=84
'journal'=85
'subscription'=86
'retain-other'=87
'keyed'=88
'public-traversal'=89
'id'=90
'doc'=91
'mode'=92
'emits'=93
'receiver'=94
'requires'=95
'any'=96
'get'=97
'set'=98
'watch'=99
'start'=100
'stop'=101
'read'=102
'write'=103
'resolve'=104
'connect'=105
'disconnect'=106
'call'=107
'watch-start'=108
'watch-stop'=109
'subscribe'=110
'unsubscribe'=111
'register'=112
'crdt'=113
'optional-one'=114
'exactly-one'=115
'many-unique'=116
'many'=117
'ordered'=118
'unit'=119
'watch-handle'=120
'message'=121
'atom-ref'=122
'interface-ref'=123
'optional'=124
'list'=125
'record'=126
'bool'=127
'bytes'=128
'double'=129
'int32'=130
'int64'=131
'string'=132
'uint32'=133
'uint64'=134
'true'=135
'false'=136
'null'=137
'->'=138
':'=139
';'=140
','=141
'.'=142
'{'=143
'}'=144
'['=145
']'=146
'('=147
')'=148
'<'=149
'>'=150
'&'=151
'='=152
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -10,6 +10,7 @@ import { WorkspaceItemContext } from "./QuixosCapabilityParser.js";
import { ResourceImportDeclContext } from "./QuixosCapabilityParser.js";
import { ExternalAtomDeclContext } from "./QuixosCapabilityParser.js";
import { ExternalInterfaceDeclContext } from "./QuixosCapabilityParser.js";
import { SymbolDeclContext } from "./QuixosCapabilityParser.js";
import { ResourcePreambleContext } from "./QuixosCapabilityParser.js";
import { AtomDeclContext } from "./QuixosCapabilityParser.js";
import { InterfaceResourceDeclContext } from "./QuixosCapabilityParser.js";
@@ -145,6 +146,12 @@ export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Re
* @return the visitor result
*/
visitExternalInterfaceDecl?: (ctx: ExternalInterfaceDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.symbolDecl`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSymbolDecl?: (ctx: SymbolDeclContext) => Result;
/**
* Visit a parse tree produced by `QuixosCapabilityParser.resourcePreamble`.
* @param ctx the parse tree
+60 -16
View File
@@ -205,6 +205,7 @@ interface LoweringState {
fileName: string;
diagnostics: CapabilitySourceDiagnostic[];
atoms: Map<string, AtomId>;
symbols: Map<string, string>;
interfaces: Map<string, InterfaceSymbol>;
packages: Map<string, PackageSymbol>;
attachments: Map<string, AttachmentSymbol>;
@@ -387,14 +388,33 @@ const lowerSelectedOperations = (
return { operations };
};
const lowerExecution = (context: ExecutionClauseContext | null): { execution?: ExecutionContract } => {
const lowerExecution = (
state: LoweringState,
context: ExecutionClauseContext | null,
implementation = false,
): { execution?: ExecutionContract } => {
if (!context) return {};
if (!implementation && context.identifierList())
loweringIssue(
state,
context,
"interface-execution-targets",
"Interfaces declare effects; execution targets belong to package implementations",
);
const targets = context
.identifierList()
?.identifier()
.map((node) => {
const name = identifier(node);
if (name === "server") return name;
return requireSymbol(state, state.symbols, name, node, "nominal execution target") ?? `unresolved:${name}`;
}) ?? ["server"];
return {
execution: context.REMOTE()
? { kind: "remote" }
: {
kind: context.SNAPSHOT() ? "snapshot" : "transaction",
targets: context.identifierList()!.identifier().map(identifier),
targets: implementation ? targets : ["server"],
},
};
};
@@ -508,17 +528,17 @@ const lowerValueMember = (state: LoweringState, context: ValueMemberContext): In
if (operation.GET()) {
operations.push({
...valueOperation("get", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
});
} else if (operation.EDIT()) {
operations.push({
...valueOperation("edit", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
});
} else if (operation.SET()) {
operations.push({
...valueOperation("set", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
});
} else {
operations.push(
@@ -611,7 +631,7 @@ const lowerRelationshipMember = (
);
}
if (operation.executionClause())
Object.assign(operations[operations.length - 1]!, lowerExecution(operation.executionClause()));
Object.assign(operations[operations.length - 1]!, lowerExecution(state, operation.executionClause()));
}
return {
kind: "relationship",
@@ -653,7 +673,7 @@ const lowerOperationMember = (state: LoweringState, context: OperationMemberCont
outputType,
mode: "call",
...(context.STATIC() ? { scope: "class" as const } : {}),
...lowerExecution(context.executionClause()),
...lowerExecution(state, context.executionClause()),
},
],
};
@@ -814,18 +834,24 @@ const lowerInterfaceTemplate = (
const id = capabilityId.operation(stringValue(operation.stringLiteral(0)));
if (operation.GET())
return [
{ ...signature("get", id, valueType.unit, type), ...lowerExecution(operation.executionClause()) },
{
...signature("get", id, valueType.unit, type),
...lowerExecution(state, operation.executionClause()),
},
];
if (operation.EDIT())
return [
{
...signature("edit", id, { kind: "change", value: type }, valueType.unit),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
},
];
if (operation.SET())
return [
{ ...signature("set", id, type, valueType.unit), ...lowerExecution(operation.executionClause()) },
{
...signature("set", id, type, valueType.unit),
...lowerExecution(state, operation.executionClause()),
},
];
return [
signature("watch-start", id, valueType.unit, valueType.watchHandle, "watch-start", type),
@@ -859,7 +885,7 @@ const lowerInterfaceTemplate = (
outputType,
),
...(operation.STATIC() ? { scope: "class" as const } : {}),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
},
],
};
@@ -907,7 +933,10 @@ const lowerInterfaceTemplate = (
const id = capabilityId.operation(stringValue(operation.stringLiteral(0)));
if (operation.RESOLVE())
return [
{ ...signature("resolve", id, valueType.unit, resolved), ...lowerExecution(operation.executionClause()) },
{
...signature("resolve", id, valueType.unit, resolved),
...lowerExecution(state, operation.executionClause()),
},
];
if (operation.CONNECT() || operation.DISCONNECT())
return [
@@ -918,7 +947,7 @@ const lowerInterfaceTemplate = (
operation.CONNECT() ? keyedTarget : targetType,
valueType.unit,
),
...lowerExecution(operation.executionClause()),
...lowerExecution(state, operation.executionClause()),
},
];
return [
@@ -1196,7 +1225,7 @@ const lowerPackageOperation = (
inputType: lowerValueType(state, context.valueType(0)),
outputType: lowerValueType(state, context.valueType(1)),
mode: text(context.operationMode()) as InterfaceOperationMode,
...lowerExecution(context.executionClause()),
...lowerExecution(state, context.executionClause(), true),
eventType: event ? lowerValueType(state, event.valueType()) : undefined,
receiverRequirement: lowerReceiver(state, context.receiverRequirement()),
dependencyPorts: ports,
@@ -1213,7 +1242,7 @@ const lowerPackageFunction = (
const alias = identifier(context.identifier());
const entry: PackageExport = {
kind: "function",
...lowerExecution(context.executionClause()),
...lowerExecution(state, context.executionClause(), true),
id: capabilityId.packageExport(stringValue(context.stringLiteral())),
displayName: alias,
inputType: lowerValueType(state, context.valueType(0)),
@@ -1355,7 +1384,7 @@ const lowerGenericPackageExport = (
displayName: identifier(context.identifier()),
parameters,
kind: operation ? "operation" : "function",
...lowerExecution(context.executionClause()),
...lowerExecution(state, context.executionClause(), true),
inputType: state.types.expression(context.valueType(0)!),
outputType: state.types.expression(context.valueType(1)!),
dependencyPorts,
@@ -3092,6 +3121,7 @@ const newLoweringState = (fileName: string, diagnostics: CapabilitySourceDiagnos
atoms,
types: new GenericSourceTypes(atoms),
interfaces: new Map(),
symbols: new Map(),
packages: new Map(),
attachments: new Map(),
};
@@ -3240,6 +3270,20 @@ const compileCapabilityResourceSourceInternal = (
const externalAtoms = externalAtomsFrom(state, preamble.atoms);
const imports = registerImports(state, preamble.imports, environment);
const externalInterfaces = externalInterfacesFrom(state, preamble.interfaces);
for (const declaration of (interfaceContext ?? packageContext)!
.resourcePreamble()
.flatMap((entry) => entry.symbolDecl() ?? [])) {
const name = identifier(declaration.identifier());
const identity = stringValue(declaration.stringLiteral());
if (name === "server" || identity === "server" || !identity.trim())
loweringIssue(
state,
declaration,
"invalid-symbol",
"Nominal symbols require a nonempty identity distinct from the server executor",
);
declareSymbol(state, state.symbols, name, identity, declaration, "symbol");
}
state.types.declareAliases(preamble.aliases);
let resource: CapabilityResource;