Add local-first execution contracts to the capability language
This commit is contained in:
@@ -131,7 +131,12 @@ export const genericImplementationType = (
|
||||
};
|
||||
const operations = (contract.template?.members ?? contract.members).flatMap((member) =>
|
||||
member.operations
|
||||
.filter((op) => op.mode === "call" && op.scope !== "class")
|
||||
.filter(
|
||||
(op) =>
|
||||
op.mode === "call" &&
|
||||
op.scope !== "class" &&
|
||||
(!requirement.operations || requirement.operations.includes(op.id)),
|
||||
)
|
||||
.map((op) => ({ ...op, name: `${member.displayName}.${op.displayName}` })),
|
||||
);
|
||||
const methods: [string, string][] = [
|
||||
|
||||
@@ -213,7 +213,12 @@ export const generateTypeScriptBindings = (
|
||||
// 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" && operation.scope !== "class")
|
||||
.filter(
|
||||
(operation) =>
|
||||
operation.mode === "call" &&
|
||||
operation.scope !== "class" &&
|
||||
(!requirement.operations || requirement.operations.includes(operation.id)),
|
||||
)
|
||||
.map((operation) => ({ ...operation, name: `${member.displayName}.${operation.displayName}` })),
|
||||
);
|
||||
const methods: [string, string][] = [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,261 +1,279 @@
|
||||
WORKSPACE=1
|
||||
RESULT_BYTES=2
|
||||
RPC_CALLS=3
|
||||
DEADLINE_MS=4
|
||||
QUERY=5
|
||||
REFLECTION=6
|
||||
SPECIALIZE=7
|
||||
ROOT=8
|
||||
DOCUMENT=9
|
||||
FRAGMENTS=10
|
||||
VIEW=11
|
||||
MAX=12
|
||||
ALLOW=13
|
||||
POLL=14
|
||||
QUERYABLE=15
|
||||
RPC=16
|
||||
QUERY_REASON=17
|
||||
TYPE=18
|
||||
OBJECT=19
|
||||
STORABLE=20
|
||||
IMPLEMENTS=21
|
||||
REF=22
|
||||
FRAGMENT=23
|
||||
IMPORT=24
|
||||
EXTERNAL=25
|
||||
ATOM=26
|
||||
INTERFACE=27
|
||||
INTERFACES=28
|
||||
PACKAGE=29
|
||||
VALUE=30
|
||||
RELATION=31
|
||||
OPERATION=32
|
||||
FUNCTION=33
|
||||
CONSTRUCTOR=34
|
||||
CONSTRUCTS=35
|
||||
INPUT=36
|
||||
CONFORM=37
|
||||
AS=38
|
||||
BIND=39
|
||||
STATIC=40
|
||||
TO=41
|
||||
PRIVATE=42
|
||||
SHARED=43
|
||||
STATE=44
|
||||
EDGE=45
|
||||
PROJECTION=46
|
||||
WITH=47
|
||||
USING=48
|
||||
VIA=49
|
||||
MATERIALIZE=50
|
||||
IF=51
|
||||
ABSENT=52
|
||||
ON=53
|
||||
POLICY=54
|
||||
DEFAULT=55
|
||||
SOURCE=56
|
||||
REPOSITORY=57
|
||||
COMMIT=58
|
||||
REVISION=59
|
||||
SEMANTIC_MAJOR=60
|
||||
ON_DELETE=61
|
||||
RETAIN_OTHER=62
|
||||
KEYED=63
|
||||
PUBLIC_TRAVERSAL=64
|
||||
ID=65
|
||||
DOC=66
|
||||
MODE=67
|
||||
EMITS=68
|
||||
RECEIVER=69
|
||||
REQUIRES=70
|
||||
ANY=71
|
||||
GET=72
|
||||
SET=73
|
||||
WATCH=74
|
||||
START=75
|
||||
STOP=76
|
||||
READ=77
|
||||
WRITE=78
|
||||
RESOLVE=79
|
||||
CONNECT=80
|
||||
DISCONNECT=81
|
||||
CALL=82
|
||||
WATCH_START=83
|
||||
WATCH_STOP=84
|
||||
SUBSCRIBE=85
|
||||
UNSUBSCRIBE=86
|
||||
OPTIMISTIC_REGISTER=87
|
||||
CRDT=88
|
||||
OPTIONAL_ONE=89
|
||||
EXACTLY_ONE=90
|
||||
MANY_UNIQUE=91
|
||||
MANY=92
|
||||
ORDERED=93
|
||||
UNIT=94
|
||||
WATCH_HANDLE=95
|
||||
MESSAGE=96
|
||||
ATOM_REF=97
|
||||
INTERFACE_REF=98
|
||||
OPTIONAL=99
|
||||
LIST=100
|
||||
RECORD=101
|
||||
BOOL=102
|
||||
BYTES=103
|
||||
DOUBLE=104
|
||||
INT32=105
|
||||
INT64=106
|
||||
STRING=107
|
||||
UINT32=108
|
||||
UINT64=109
|
||||
TRUE=110
|
||||
FALSE=111
|
||||
NULL=112
|
||||
ARROW=113
|
||||
COLON=114
|
||||
SEMI=115
|
||||
COMMA=116
|
||||
DOT=117
|
||||
LBRACE=118
|
||||
RBRACE=119
|
||||
LBRACK=120
|
||||
RBRACK=121
|
||||
LPAREN=122
|
||||
RPAREN=123
|
||||
LT=124
|
||||
GT=125
|
||||
AMP=126
|
||||
EQUAL=127
|
||||
INTEGER=128
|
||||
JSON_NUMBER=129
|
||||
IDENTIFIER=130
|
||||
STRING_LITERAL=131
|
||||
LINE_COMMENT=132
|
||||
BLOCK_COMMENT=133
|
||||
WS=134
|
||||
EXECUTION=2
|
||||
SNAPSHOT=3
|
||||
TRANSACTION=4
|
||||
REMOTE=5
|
||||
TARGETS=6
|
||||
PORTABLE=7
|
||||
REPLICA=8
|
||||
LOCAL=9
|
||||
FROM=10
|
||||
RESULT_BYTES=11
|
||||
RPC_CALLS=12
|
||||
DEADLINE_MS=13
|
||||
QUERY=14
|
||||
REFLECTION=15
|
||||
SPECIALIZE=16
|
||||
ROOT=17
|
||||
DOCUMENT=18
|
||||
FRAGMENTS=19
|
||||
VIEW=20
|
||||
MAX=21
|
||||
ALLOW=22
|
||||
POLL=23
|
||||
QUERYABLE=24
|
||||
RPC=25
|
||||
QUERY_REASON=26
|
||||
TYPE=27
|
||||
OBJECT=28
|
||||
STORABLE=29
|
||||
IMPLEMENTS=30
|
||||
REF=31
|
||||
FRAGMENT=32
|
||||
IMPORT=33
|
||||
EXTERNAL=34
|
||||
ATOM=35
|
||||
INTERFACE=36
|
||||
INTERFACES=37
|
||||
PACKAGE=38
|
||||
VALUE=39
|
||||
RELATION=40
|
||||
OPERATION=41
|
||||
FUNCTION=42
|
||||
CONSTRUCTOR=43
|
||||
CONSTRUCTS=44
|
||||
INPUT=45
|
||||
CONFORM=46
|
||||
AS=47
|
||||
BIND=48
|
||||
STATIC=49
|
||||
TO=50
|
||||
PRIVATE=51
|
||||
SHARED=52
|
||||
STATE=53
|
||||
EDGE=54
|
||||
PROJECTION=55
|
||||
WITH=56
|
||||
USING=57
|
||||
VIA=58
|
||||
MATERIALIZE=59
|
||||
IF=60
|
||||
ABSENT=61
|
||||
ON=62
|
||||
POLICY=63
|
||||
DEFAULT=64
|
||||
SOURCE=65
|
||||
REPOSITORY=66
|
||||
COMMIT=67
|
||||
REVISION=68
|
||||
SEMANTIC_MAJOR=69
|
||||
ON_DELETE=70
|
||||
RETAIN_OTHER=71
|
||||
KEYED=72
|
||||
PUBLIC_TRAVERSAL=73
|
||||
ID=74
|
||||
DOC=75
|
||||
MODE=76
|
||||
EMITS=77
|
||||
RECEIVER=78
|
||||
REQUIRES=79
|
||||
ANY=80
|
||||
GET=81
|
||||
SET=82
|
||||
WATCH=83
|
||||
START=84
|
||||
STOP=85
|
||||
READ=86
|
||||
WRITE=87
|
||||
RESOLVE=88
|
||||
CONNECT=89
|
||||
DISCONNECT=90
|
||||
CALL=91
|
||||
WATCH_START=92
|
||||
WATCH_STOP=93
|
||||
SUBSCRIBE=94
|
||||
UNSUBSCRIBE=95
|
||||
OPTIMISTIC_REGISTER=96
|
||||
CRDT=97
|
||||
OPTIONAL_ONE=98
|
||||
EXACTLY_ONE=99
|
||||
MANY_UNIQUE=100
|
||||
MANY=101
|
||||
ORDERED=102
|
||||
UNIT=103
|
||||
WATCH_HANDLE=104
|
||||
MESSAGE=105
|
||||
ATOM_REF=106
|
||||
INTERFACE_REF=107
|
||||
OPTIONAL=108
|
||||
LIST=109
|
||||
RECORD=110
|
||||
BOOL=111
|
||||
BYTES=112
|
||||
DOUBLE=113
|
||||
INT32=114
|
||||
INT64=115
|
||||
STRING=116
|
||||
UINT32=117
|
||||
UINT64=118
|
||||
TRUE=119
|
||||
FALSE=120
|
||||
NULL=121
|
||||
ARROW=122
|
||||
COLON=123
|
||||
SEMI=124
|
||||
COMMA=125
|
||||
DOT=126
|
||||
LBRACE=127
|
||||
RBRACE=128
|
||||
LBRACK=129
|
||||
RBRACK=130
|
||||
LPAREN=131
|
||||
RPAREN=132
|
||||
LT=133
|
||||
GT=134
|
||||
AMP=135
|
||||
EQUAL=136
|
||||
INTEGER=137
|
||||
JSON_NUMBER=138
|
||||
IDENTIFIER=139
|
||||
STRING_LITERAL=140
|
||||
LINE_COMMENT=141
|
||||
BLOCK_COMMENT=142
|
||||
WS=143
|
||||
'workspace'=1
|
||||
'result-bytes'=2
|
||||
'rpc-calls'=3
|
||||
'deadline-ms'=4
|
||||
'query'=5
|
||||
'reflection'=6
|
||||
'specialize'=7
|
||||
'root'=8
|
||||
'document'=9
|
||||
'fragments'=10
|
||||
'view'=11
|
||||
'max'=12
|
||||
'allow'=13
|
||||
'poll'=14
|
||||
'queryable'=15
|
||||
'rpc'=16
|
||||
'query-reason'=17
|
||||
'type'=18
|
||||
'object'=19
|
||||
'storable'=20
|
||||
'implements'=21
|
||||
'ref'=22
|
||||
'fragment'=23
|
||||
'import'=24
|
||||
'external'=25
|
||||
'atom'=26
|
||||
'interface'=27
|
||||
'interfaces'=28
|
||||
'package'=29
|
||||
'value'=30
|
||||
'relation'=31
|
||||
'operation'=32
|
||||
'function'=33
|
||||
'constructor'=34
|
||||
'constructs'=35
|
||||
'input'=36
|
||||
'conform'=37
|
||||
'as'=38
|
||||
'bind'=39
|
||||
'static'=40
|
||||
'to'=41
|
||||
'private'=42
|
||||
'shared'=43
|
||||
'state'=44
|
||||
'edge'=45
|
||||
'projection'=46
|
||||
'with'=47
|
||||
'using'=48
|
||||
'via'=49
|
||||
'materialize'=50
|
||||
'if'=51
|
||||
'absent'=52
|
||||
'on'=53
|
||||
'policy'=54
|
||||
'default'=55
|
||||
'source'=56
|
||||
'repository'=57
|
||||
'commit'=58
|
||||
'revision'=59
|
||||
'semantic-major'=60
|
||||
'on-delete'=61
|
||||
'retain-other'=62
|
||||
'keyed'=63
|
||||
'public-traversal'=64
|
||||
'id'=65
|
||||
'doc'=66
|
||||
'mode'=67
|
||||
'emits'=68
|
||||
'receiver'=69
|
||||
'requires'=70
|
||||
'any'=71
|
||||
'get'=72
|
||||
'set'=73
|
||||
'watch'=74
|
||||
'start'=75
|
||||
'stop'=76
|
||||
'read'=77
|
||||
'write'=78
|
||||
'resolve'=79
|
||||
'connect'=80
|
||||
'disconnect'=81
|
||||
'call'=82
|
||||
'watch-start'=83
|
||||
'watch-stop'=84
|
||||
'subscribe'=85
|
||||
'unsubscribe'=86
|
||||
'optimistic-register'=87
|
||||
'crdt'=88
|
||||
'optional-one'=89
|
||||
'exactly-one'=90
|
||||
'many-unique'=91
|
||||
'many'=92
|
||||
'ordered'=93
|
||||
'unit'=94
|
||||
'watch-handle'=95
|
||||
'message'=96
|
||||
'atom-ref'=97
|
||||
'interface-ref'=98
|
||||
'optional'=99
|
||||
'list'=100
|
||||
'record'=101
|
||||
'bool'=102
|
||||
'bytes'=103
|
||||
'double'=104
|
||||
'int32'=105
|
||||
'int64'=106
|
||||
'string'=107
|
||||
'uint32'=108
|
||||
'uint64'=109
|
||||
'true'=110
|
||||
'false'=111
|
||||
'null'=112
|
||||
'->'=113
|
||||
':'=114
|
||||
';'=115
|
||||
','=116
|
||||
'.'=117
|
||||
'{'=118
|
||||
'}'=119
|
||||
'['=120
|
||||
']'=121
|
||||
'('=122
|
||||
')'=123
|
||||
'<'=124
|
||||
'>'=125
|
||||
'&'=126
|
||||
'='=127
|
||||
'execution'=2
|
||||
'snapshot'=3
|
||||
'transaction'=4
|
||||
'remote'=5
|
||||
'targets'=6
|
||||
'portable'=7
|
||||
'replica'=8
|
||||
'local'=9
|
||||
'from'=10
|
||||
'result-bytes'=11
|
||||
'rpc-calls'=12
|
||||
'deadline-ms'=13
|
||||
'query'=14
|
||||
'reflection'=15
|
||||
'specialize'=16
|
||||
'root'=17
|
||||
'document'=18
|
||||
'fragments'=19
|
||||
'view'=20
|
||||
'max'=21
|
||||
'allow'=22
|
||||
'poll'=23
|
||||
'queryable'=24
|
||||
'rpc'=25
|
||||
'query-reason'=26
|
||||
'type'=27
|
||||
'object'=28
|
||||
'storable'=29
|
||||
'implements'=30
|
||||
'ref'=31
|
||||
'fragment'=32
|
||||
'import'=33
|
||||
'external'=34
|
||||
'atom'=35
|
||||
'interface'=36
|
||||
'interfaces'=37
|
||||
'package'=38
|
||||
'value'=39
|
||||
'relation'=40
|
||||
'operation'=41
|
||||
'function'=42
|
||||
'constructor'=43
|
||||
'constructs'=44
|
||||
'input'=45
|
||||
'conform'=46
|
||||
'as'=47
|
||||
'bind'=48
|
||||
'static'=49
|
||||
'to'=50
|
||||
'private'=51
|
||||
'shared'=52
|
||||
'state'=53
|
||||
'edge'=54
|
||||
'projection'=55
|
||||
'with'=56
|
||||
'using'=57
|
||||
'via'=58
|
||||
'materialize'=59
|
||||
'if'=60
|
||||
'absent'=61
|
||||
'on'=62
|
||||
'policy'=63
|
||||
'default'=64
|
||||
'source'=65
|
||||
'repository'=66
|
||||
'commit'=67
|
||||
'revision'=68
|
||||
'semantic-major'=69
|
||||
'on-delete'=70
|
||||
'retain-other'=71
|
||||
'keyed'=72
|
||||
'public-traversal'=73
|
||||
'id'=74
|
||||
'doc'=75
|
||||
'mode'=76
|
||||
'emits'=77
|
||||
'receiver'=78
|
||||
'requires'=79
|
||||
'any'=80
|
||||
'get'=81
|
||||
'set'=82
|
||||
'watch'=83
|
||||
'start'=84
|
||||
'stop'=85
|
||||
'read'=86
|
||||
'write'=87
|
||||
'resolve'=88
|
||||
'connect'=89
|
||||
'disconnect'=90
|
||||
'call'=91
|
||||
'watch-start'=92
|
||||
'watch-stop'=93
|
||||
'subscribe'=94
|
||||
'unsubscribe'=95
|
||||
'optimistic-register'=96
|
||||
'crdt'=97
|
||||
'optional-one'=98
|
||||
'exactly-one'=99
|
||||
'many-unique'=100
|
||||
'many'=101
|
||||
'ordered'=102
|
||||
'unit'=103
|
||||
'watch-handle'=104
|
||||
'message'=105
|
||||
'atom-ref'=106
|
||||
'interface-ref'=107
|
||||
'optional'=108
|
||||
'list'=109
|
||||
'record'=110
|
||||
'bool'=111
|
||||
'bytes'=112
|
||||
'double'=113
|
||||
'int32'=114
|
||||
'int64'=115
|
||||
'string'=116
|
||||
'uint32'=117
|
||||
'uint64'=118
|
||||
'true'=119
|
||||
'false'=120
|
||||
'null'=121
|
||||
'->'=122
|
||||
':'=123
|
||||
';'=124
|
||||
','=125
|
||||
'.'=126
|
||||
'{'=127
|
||||
'}'=128
|
||||
'['=129
|
||||
']'=130
|
||||
'('=131
|
||||
')'=132
|
||||
'<'=133
|
||||
'>'=134
|
||||
'&'=135
|
||||
'='=136
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,261 +1,279 @@
|
||||
WORKSPACE=1
|
||||
RESULT_BYTES=2
|
||||
RPC_CALLS=3
|
||||
DEADLINE_MS=4
|
||||
QUERY=5
|
||||
REFLECTION=6
|
||||
SPECIALIZE=7
|
||||
ROOT=8
|
||||
DOCUMENT=9
|
||||
FRAGMENTS=10
|
||||
VIEW=11
|
||||
MAX=12
|
||||
ALLOW=13
|
||||
POLL=14
|
||||
QUERYABLE=15
|
||||
RPC=16
|
||||
QUERY_REASON=17
|
||||
TYPE=18
|
||||
OBJECT=19
|
||||
STORABLE=20
|
||||
IMPLEMENTS=21
|
||||
REF=22
|
||||
FRAGMENT=23
|
||||
IMPORT=24
|
||||
EXTERNAL=25
|
||||
ATOM=26
|
||||
INTERFACE=27
|
||||
INTERFACES=28
|
||||
PACKAGE=29
|
||||
VALUE=30
|
||||
RELATION=31
|
||||
OPERATION=32
|
||||
FUNCTION=33
|
||||
CONSTRUCTOR=34
|
||||
CONSTRUCTS=35
|
||||
INPUT=36
|
||||
CONFORM=37
|
||||
AS=38
|
||||
BIND=39
|
||||
STATIC=40
|
||||
TO=41
|
||||
PRIVATE=42
|
||||
SHARED=43
|
||||
STATE=44
|
||||
EDGE=45
|
||||
PROJECTION=46
|
||||
WITH=47
|
||||
USING=48
|
||||
VIA=49
|
||||
MATERIALIZE=50
|
||||
IF=51
|
||||
ABSENT=52
|
||||
ON=53
|
||||
POLICY=54
|
||||
DEFAULT=55
|
||||
SOURCE=56
|
||||
REPOSITORY=57
|
||||
COMMIT=58
|
||||
REVISION=59
|
||||
SEMANTIC_MAJOR=60
|
||||
ON_DELETE=61
|
||||
RETAIN_OTHER=62
|
||||
KEYED=63
|
||||
PUBLIC_TRAVERSAL=64
|
||||
ID=65
|
||||
DOC=66
|
||||
MODE=67
|
||||
EMITS=68
|
||||
RECEIVER=69
|
||||
REQUIRES=70
|
||||
ANY=71
|
||||
GET=72
|
||||
SET=73
|
||||
WATCH=74
|
||||
START=75
|
||||
STOP=76
|
||||
READ=77
|
||||
WRITE=78
|
||||
RESOLVE=79
|
||||
CONNECT=80
|
||||
DISCONNECT=81
|
||||
CALL=82
|
||||
WATCH_START=83
|
||||
WATCH_STOP=84
|
||||
SUBSCRIBE=85
|
||||
UNSUBSCRIBE=86
|
||||
OPTIMISTIC_REGISTER=87
|
||||
CRDT=88
|
||||
OPTIONAL_ONE=89
|
||||
EXACTLY_ONE=90
|
||||
MANY_UNIQUE=91
|
||||
MANY=92
|
||||
ORDERED=93
|
||||
UNIT=94
|
||||
WATCH_HANDLE=95
|
||||
MESSAGE=96
|
||||
ATOM_REF=97
|
||||
INTERFACE_REF=98
|
||||
OPTIONAL=99
|
||||
LIST=100
|
||||
RECORD=101
|
||||
BOOL=102
|
||||
BYTES=103
|
||||
DOUBLE=104
|
||||
INT32=105
|
||||
INT64=106
|
||||
STRING=107
|
||||
UINT32=108
|
||||
UINT64=109
|
||||
TRUE=110
|
||||
FALSE=111
|
||||
NULL=112
|
||||
ARROW=113
|
||||
COLON=114
|
||||
SEMI=115
|
||||
COMMA=116
|
||||
DOT=117
|
||||
LBRACE=118
|
||||
RBRACE=119
|
||||
LBRACK=120
|
||||
RBRACK=121
|
||||
LPAREN=122
|
||||
RPAREN=123
|
||||
LT=124
|
||||
GT=125
|
||||
AMP=126
|
||||
EQUAL=127
|
||||
INTEGER=128
|
||||
JSON_NUMBER=129
|
||||
IDENTIFIER=130
|
||||
STRING_LITERAL=131
|
||||
LINE_COMMENT=132
|
||||
BLOCK_COMMENT=133
|
||||
WS=134
|
||||
EXECUTION=2
|
||||
SNAPSHOT=3
|
||||
TRANSACTION=4
|
||||
REMOTE=5
|
||||
TARGETS=6
|
||||
PORTABLE=7
|
||||
REPLICA=8
|
||||
LOCAL=9
|
||||
FROM=10
|
||||
RESULT_BYTES=11
|
||||
RPC_CALLS=12
|
||||
DEADLINE_MS=13
|
||||
QUERY=14
|
||||
REFLECTION=15
|
||||
SPECIALIZE=16
|
||||
ROOT=17
|
||||
DOCUMENT=18
|
||||
FRAGMENTS=19
|
||||
VIEW=20
|
||||
MAX=21
|
||||
ALLOW=22
|
||||
POLL=23
|
||||
QUERYABLE=24
|
||||
RPC=25
|
||||
QUERY_REASON=26
|
||||
TYPE=27
|
||||
OBJECT=28
|
||||
STORABLE=29
|
||||
IMPLEMENTS=30
|
||||
REF=31
|
||||
FRAGMENT=32
|
||||
IMPORT=33
|
||||
EXTERNAL=34
|
||||
ATOM=35
|
||||
INTERFACE=36
|
||||
INTERFACES=37
|
||||
PACKAGE=38
|
||||
VALUE=39
|
||||
RELATION=40
|
||||
OPERATION=41
|
||||
FUNCTION=42
|
||||
CONSTRUCTOR=43
|
||||
CONSTRUCTS=44
|
||||
INPUT=45
|
||||
CONFORM=46
|
||||
AS=47
|
||||
BIND=48
|
||||
STATIC=49
|
||||
TO=50
|
||||
PRIVATE=51
|
||||
SHARED=52
|
||||
STATE=53
|
||||
EDGE=54
|
||||
PROJECTION=55
|
||||
WITH=56
|
||||
USING=57
|
||||
VIA=58
|
||||
MATERIALIZE=59
|
||||
IF=60
|
||||
ABSENT=61
|
||||
ON=62
|
||||
POLICY=63
|
||||
DEFAULT=64
|
||||
SOURCE=65
|
||||
REPOSITORY=66
|
||||
COMMIT=67
|
||||
REVISION=68
|
||||
SEMANTIC_MAJOR=69
|
||||
ON_DELETE=70
|
||||
RETAIN_OTHER=71
|
||||
KEYED=72
|
||||
PUBLIC_TRAVERSAL=73
|
||||
ID=74
|
||||
DOC=75
|
||||
MODE=76
|
||||
EMITS=77
|
||||
RECEIVER=78
|
||||
REQUIRES=79
|
||||
ANY=80
|
||||
GET=81
|
||||
SET=82
|
||||
WATCH=83
|
||||
START=84
|
||||
STOP=85
|
||||
READ=86
|
||||
WRITE=87
|
||||
RESOLVE=88
|
||||
CONNECT=89
|
||||
DISCONNECT=90
|
||||
CALL=91
|
||||
WATCH_START=92
|
||||
WATCH_STOP=93
|
||||
SUBSCRIBE=94
|
||||
UNSUBSCRIBE=95
|
||||
OPTIMISTIC_REGISTER=96
|
||||
CRDT=97
|
||||
OPTIONAL_ONE=98
|
||||
EXACTLY_ONE=99
|
||||
MANY_UNIQUE=100
|
||||
MANY=101
|
||||
ORDERED=102
|
||||
UNIT=103
|
||||
WATCH_HANDLE=104
|
||||
MESSAGE=105
|
||||
ATOM_REF=106
|
||||
INTERFACE_REF=107
|
||||
OPTIONAL=108
|
||||
LIST=109
|
||||
RECORD=110
|
||||
BOOL=111
|
||||
BYTES=112
|
||||
DOUBLE=113
|
||||
INT32=114
|
||||
INT64=115
|
||||
STRING=116
|
||||
UINT32=117
|
||||
UINT64=118
|
||||
TRUE=119
|
||||
FALSE=120
|
||||
NULL=121
|
||||
ARROW=122
|
||||
COLON=123
|
||||
SEMI=124
|
||||
COMMA=125
|
||||
DOT=126
|
||||
LBRACE=127
|
||||
RBRACE=128
|
||||
LBRACK=129
|
||||
RBRACK=130
|
||||
LPAREN=131
|
||||
RPAREN=132
|
||||
LT=133
|
||||
GT=134
|
||||
AMP=135
|
||||
EQUAL=136
|
||||
INTEGER=137
|
||||
JSON_NUMBER=138
|
||||
IDENTIFIER=139
|
||||
STRING_LITERAL=140
|
||||
LINE_COMMENT=141
|
||||
BLOCK_COMMENT=142
|
||||
WS=143
|
||||
'workspace'=1
|
||||
'result-bytes'=2
|
||||
'rpc-calls'=3
|
||||
'deadline-ms'=4
|
||||
'query'=5
|
||||
'reflection'=6
|
||||
'specialize'=7
|
||||
'root'=8
|
||||
'document'=9
|
||||
'fragments'=10
|
||||
'view'=11
|
||||
'max'=12
|
||||
'allow'=13
|
||||
'poll'=14
|
||||
'queryable'=15
|
||||
'rpc'=16
|
||||
'query-reason'=17
|
||||
'type'=18
|
||||
'object'=19
|
||||
'storable'=20
|
||||
'implements'=21
|
||||
'ref'=22
|
||||
'fragment'=23
|
||||
'import'=24
|
||||
'external'=25
|
||||
'atom'=26
|
||||
'interface'=27
|
||||
'interfaces'=28
|
||||
'package'=29
|
||||
'value'=30
|
||||
'relation'=31
|
||||
'operation'=32
|
||||
'function'=33
|
||||
'constructor'=34
|
||||
'constructs'=35
|
||||
'input'=36
|
||||
'conform'=37
|
||||
'as'=38
|
||||
'bind'=39
|
||||
'static'=40
|
||||
'to'=41
|
||||
'private'=42
|
||||
'shared'=43
|
||||
'state'=44
|
||||
'edge'=45
|
||||
'projection'=46
|
||||
'with'=47
|
||||
'using'=48
|
||||
'via'=49
|
||||
'materialize'=50
|
||||
'if'=51
|
||||
'absent'=52
|
||||
'on'=53
|
||||
'policy'=54
|
||||
'default'=55
|
||||
'source'=56
|
||||
'repository'=57
|
||||
'commit'=58
|
||||
'revision'=59
|
||||
'semantic-major'=60
|
||||
'on-delete'=61
|
||||
'retain-other'=62
|
||||
'keyed'=63
|
||||
'public-traversal'=64
|
||||
'id'=65
|
||||
'doc'=66
|
||||
'mode'=67
|
||||
'emits'=68
|
||||
'receiver'=69
|
||||
'requires'=70
|
||||
'any'=71
|
||||
'get'=72
|
||||
'set'=73
|
||||
'watch'=74
|
||||
'start'=75
|
||||
'stop'=76
|
||||
'read'=77
|
||||
'write'=78
|
||||
'resolve'=79
|
||||
'connect'=80
|
||||
'disconnect'=81
|
||||
'call'=82
|
||||
'watch-start'=83
|
||||
'watch-stop'=84
|
||||
'subscribe'=85
|
||||
'unsubscribe'=86
|
||||
'optimistic-register'=87
|
||||
'crdt'=88
|
||||
'optional-one'=89
|
||||
'exactly-one'=90
|
||||
'many-unique'=91
|
||||
'many'=92
|
||||
'ordered'=93
|
||||
'unit'=94
|
||||
'watch-handle'=95
|
||||
'message'=96
|
||||
'atom-ref'=97
|
||||
'interface-ref'=98
|
||||
'optional'=99
|
||||
'list'=100
|
||||
'record'=101
|
||||
'bool'=102
|
||||
'bytes'=103
|
||||
'double'=104
|
||||
'int32'=105
|
||||
'int64'=106
|
||||
'string'=107
|
||||
'uint32'=108
|
||||
'uint64'=109
|
||||
'true'=110
|
||||
'false'=111
|
||||
'null'=112
|
||||
'->'=113
|
||||
':'=114
|
||||
';'=115
|
||||
','=116
|
||||
'.'=117
|
||||
'{'=118
|
||||
'}'=119
|
||||
'['=120
|
||||
']'=121
|
||||
'('=122
|
||||
')'=123
|
||||
'<'=124
|
||||
'>'=125
|
||||
'&'=126
|
||||
'='=127
|
||||
'execution'=2
|
||||
'snapshot'=3
|
||||
'transaction'=4
|
||||
'remote'=5
|
||||
'targets'=6
|
||||
'portable'=7
|
||||
'replica'=8
|
||||
'local'=9
|
||||
'from'=10
|
||||
'result-bytes'=11
|
||||
'rpc-calls'=12
|
||||
'deadline-ms'=13
|
||||
'query'=14
|
||||
'reflection'=15
|
||||
'specialize'=16
|
||||
'root'=17
|
||||
'document'=18
|
||||
'fragments'=19
|
||||
'view'=20
|
||||
'max'=21
|
||||
'allow'=22
|
||||
'poll'=23
|
||||
'queryable'=24
|
||||
'rpc'=25
|
||||
'query-reason'=26
|
||||
'type'=27
|
||||
'object'=28
|
||||
'storable'=29
|
||||
'implements'=30
|
||||
'ref'=31
|
||||
'fragment'=32
|
||||
'import'=33
|
||||
'external'=34
|
||||
'atom'=35
|
||||
'interface'=36
|
||||
'interfaces'=37
|
||||
'package'=38
|
||||
'value'=39
|
||||
'relation'=40
|
||||
'operation'=41
|
||||
'function'=42
|
||||
'constructor'=43
|
||||
'constructs'=44
|
||||
'input'=45
|
||||
'conform'=46
|
||||
'as'=47
|
||||
'bind'=48
|
||||
'static'=49
|
||||
'to'=50
|
||||
'private'=51
|
||||
'shared'=52
|
||||
'state'=53
|
||||
'edge'=54
|
||||
'projection'=55
|
||||
'with'=56
|
||||
'using'=57
|
||||
'via'=58
|
||||
'materialize'=59
|
||||
'if'=60
|
||||
'absent'=61
|
||||
'on'=62
|
||||
'policy'=63
|
||||
'default'=64
|
||||
'source'=65
|
||||
'repository'=66
|
||||
'commit'=67
|
||||
'revision'=68
|
||||
'semantic-major'=69
|
||||
'on-delete'=70
|
||||
'retain-other'=71
|
||||
'keyed'=72
|
||||
'public-traversal'=73
|
||||
'id'=74
|
||||
'doc'=75
|
||||
'mode'=76
|
||||
'emits'=77
|
||||
'receiver'=78
|
||||
'requires'=79
|
||||
'any'=80
|
||||
'get'=81
|
||||
'set'=82
|
||||
'watch'=83
|
||||
'start'=84
|
||||
'stop'=85
|
||||
'read'=86
|
||||
'write'=87
|
||||
'resolve'=88
|
||||
'connect'=89
|
||||
'disconnect'=90
|
||||
'call'=91
|
||||
'watch-start'=92
|
||||
'watch-stop'=93
|
||||
'subscribe'=94
|
||||
'unsubscribe'=95
|
||||
'optimistic-register'=96
|
||||
'crdt'=97
|
||||
'optional-one'=98
|
||||
'exactly-one'=99
|
||||
'many-unique'=100
|
||||
'many'=101
|
||||
'ordered'=102
|
||||
'unit'=103
|
||||
'watch-handle'=104
|
||||
'message'=105
|
||||
'atom-ref'=106
|
||||
'interface-ref'=107
|
||||
'optional'=108
|
||||
'list'=109
|
||||
'record'=110
|
||||
'bool'=111
|
||||
'bytes'=112
|
||||
'double'=113
|
||||
'int32'=114
|
||||
'int64'=115
|
||||
'string'=116
|
||||
'uint32'=117
|
||||
'uint64'=118
|
||||
'true'=119
|
||||
'false'=120
|
||||
'null'=121
|
||||
'->'=122
|
||||
':'=123
|
||||
';'=124
|
||||
','=125
|
||||
'.'=126
|
||||
'{'=127
|
||||
'}'=128
|
||||
'['=129
|
||||
']'=130
|
||||
'('=131
|
||||
')'=132
|
||||
'<'=133
|
||||
'>'=134
|
||||
'&'=135
|
||||
'='=136
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,8 @@ import { PackageQuerySpecializationContext } from "./QuixosCapabilityParser.js";
|
||||
import { QueryClauseContext } from "./QuixosCapabilityParser.js";
|
||||
import { PackageOperationExportContext } from "./QuixosCapabilityParser.js";
|
||||
import { PackageFunctionExportContext } from "./QuixosCapabilityParser.js";
|
||||
import { ExecutionClauseContext } from "./QuixosCapabilityParser.js";
|
||||
import { SelectedOperationsContext } from "./QuixosCapabilityParser.js";
|
||||
import { PackageConstructorExportContext } from "./QuixosCapabilityParser.js";
|
||||
import { EventClauseContext } from "./QuixosCapabilityParser.js";
|
||||
import { OperationModeContext } from "./QuixosCapabilityParser.js";
|
||||
@@ -274,6 +276,18 @@ export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Re
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitPackageFunctionExport?: (ctx: PackageFunctionExportContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.executionClause`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitExecutionClause?: (ctx: ExecutionClauseContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.selectedOperations`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitSelectedOperations?: (ctx: SelectedOperationsContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.packageConstructorExport`.
|
||||
* @param ctx the parse tree
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
cardinalityValueType,
|
||||
compileWorkspaceRevision,
|
||||
valueType,
|
||||
type ExecutionContract,
|
||||
type AtomId,
|
||||
type BoundDependency,
|
||||
type CompiledWorkspaceRevision,
|
||||
@@ -57,6 +58,7 @@ import { specializeQueryTemplate } from "../query/templates.js";
|
||||
import { QuixosCapabilityLexer } from "./generated/QuixosCapabilityLexer.js";
|
||||
import {
|
||||
QuixosCapabilityParser,
|
||||
type ExecutionClauseContext,
|
||||
type AttachmentDeclContext,
|
||||
type ConformanceDeclContext,
|
||||
type DependencyBindingBlockContext,
|
||||
@@ -361,6 +363,38 @@ const lowerValueType = (state: LoweringState, context: ValueTypeContext | null):
|
||||
return valueType.unit;
|
||||
};
|
||||
|
||||
const lowerSelectedOperations = (
|
||||
state: LoweringState,
|
||||
context: DependencyPortContext,
|
||||
target: InterfaceSymbol | undefined,
|
||||
): { operations?: InterfaceOperation["id"][] } => {
|
||||
const selected = context.selectedOperations();
|
||||
if (!selected) return {};
|
||||
const operations = selected.memberOperationRef().flatMap((reference) => {
|
||||
const name = identifier(reference.identifier());
|
||||
const member = target && requireSymbol(state, target.members, name, reference, "interface member");
|
||||
const operation =
|
||||
member &&
|
||||
requireSymbol(state, member.operations, text(reference.operationName()), reference, "interface operation");
|
||||
return operation ? [operation] : [];
|
||||
});
|
||||
if (new Set(operations).size !== operations.length)
|
||||
loweringIssue(state, context, "duplicate-operation-selection", "Interface operation selection contains duplicates");
|
||||
return { operations };
|
||||
};
|
||||
|
||||
const lowerExecution = (context: ExecutionClauseContext | null): { execution?: ExecutionContract } => {
|
||||
if (!context) return {};
|
||||
return {
|
||||
execution: context.REMOTE()
|
||||
? { kind: "remote" }
|
||||
: {
|
||||
kind: context.SNAPSHOT() ? "snapshot" : "transaction",
|
||||
targets: context.identifierList()!.identifier().map(identifier),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const valueOperation = (displayName: string, id: InterfaceOperation["id"], value: ValueType): InterfaceOperation => {
|
||||
switch (displayName) {
|
||||
case "get":
|
||||
@@ -456,13 +490,15 @@ const lowerValueMember = (state: LoweringState, context: ValueMemberContext): In
|
||||
const operations: InterfaceOperation[] = [];
|
||||
for (const operation of context.valueMemberOperation()) {
|
||||
if (operation.GET()) {
|
||||
operations.push(
|
||||
valueOperation("get", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
|
||||
);
|
||||
operations.push({
|
||||
...valueOperation("get", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
|
||||
...lowerExecution(operation.executionClause()),
|
||||
});
|
||||
} else if (operation.SET()) {
|
||||
operations.push(
|
||||
valueOperation("set", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
|
||||
);
|
||||
operations.push({
|
||||
...valueOperation("set", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
|
||||
...lowerExecution(operation.executionClause()),
|
||||
});
|
||||
} else {
|
||||
operations.push(
|
||||
valueOperation("watch-start", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType),
|
||||
@@ -475,7 +511,11 @@ const lowerValueMember = (state: LoweringState, context: ValueMemberContext): In
|
||||
...(context.queryReadContract()
|
||||
? {
|
||||
queryRead: {
|
||||
execution: context.queryReadContract()!.RPC() ? ("rpc-permitted" as const) : ("native" as const),
|
||||
execution: context.queryReadContract()!.RPC()
|
||||
? ("rpc-permitted" as const)
|
||||
: context.queryReadContract()!.PORTABLE()
|
||||
? ("portable" as const)
|
||||
: ("native" as const),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
@@ -540,6 +580,8 @@ const lowerRelationshipMember = (
|
||||
),
|
||||
);
|
||||
}
|
||||
if (operation.executionClause())
|
||||
Object.assign(operations[operations.length - 1]!, lowerExecution(operation.executionClause()));
|
||||
}
|
||||
return {
|
||||
kind: "relationship",
|
||||
@@ -547,7 +589,11 @@ const lowerRelationshipMember = (
|
||||
...(context.queryReadContract()
|
||||
? {
|
||||
queryRead: {
|
||||
execution: context.queryReadContract()!.RPC() ? ("rpc-permitted" as const) : ("native" as const),
|
||||
execution: context.queryReadContract()!.RPC()
|
||||
? ("rpc-permitted" as const)
|
||||
: context.queryReadContract()!.PORTABLE()
|
||||
? ("portable" as const)
|
||||
: ("native" as const),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
@@ -577,6 +623,7 @@ const lowerOperationMember = (state: LoweringState, context: OperationMemberCont
|
||||
outputType,
|
||||
mode: "call",
|
||||
...(context.STATIC() ? { scope: "class" as const } : {}),
|
||||
...lowerExecution(context.executionClause()),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -699,7 +746,11 @@ const lowerInterfaceTemplate = (
|
||||
...(value.queryReadContract()
|
||||
? {
|
||||
queryRead: {
|
||||
execution: value.queryReadContract()!.RPC() ? ("rpc-permitted" as const) : ("native" as const),
|
||||
execution: value.queryReadContract()!.RPC()
|
||||
? ("rpc-permitted" as const)
|
||||
: value.queryReadContract()!.PORTABLE()
|
||||
? ("portable" as const)
|
||||
: ("native" as const),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
@@ -708,8 +759,14 @@ const lowerInterfaceTemplate = (
|
||||
valueType: type,
|
||||
operations: value.valueMemberOperation().flatMap((operation) => {
|
||||
const id = capabilityId.operation(stringValue(operation.stringLiteral(0)));
|
||||
if (operation.GET()) return [signature("get", id, valueType.unit, type)];
|
||||
if (operation.SET()) return [signature("set", id, type, valueType.unit)];
|
||||
if (operation.GET())
|
||||
return [
|
||||
{ ...signature("get", id, valueType.unit, type), ...lowerExecution(operation.executionClause()) },
|
||||
];
|
||||
if (operation.SET())
|
||||
return [
|
||||
{ ...signature("set", id, type, valueType.unit), ...lowerExecution(operation.executionClause()) },
|
||||
];
|
||||
return [
|
||||
signature("watch-start", id, valueType.unit, valueType.watchHandle, "watch-start", type),
|
||||
signature(
|
||||
@@ -742,6 +799,7 @@ const lowerInterfaceTemplate = (
|
||||
outputType,
|
||||
),
|
||||
...(operation.STATIC() ? { scope: "class" as const } : {}),
|
||||
...lowerExecution(operation.executionClause()),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -762,7 +820,11 @@ const lowerInterfaceTemplate = (
|
||||
...(relationship.queryReadContract()
|
||||
? {
|
||||
queryRead: {
|
||||
execution: relationship.queryReadContract()!.RPC() ? ("rpc-permitted" as const) : ("native" as const),
|
||||
execution: relationship.queryReadContract()!.RPC()
|
||||
? ("rpc-permitted" as const)
|
||||
: relationship.queryReadContract()!.PORTABLE()
|
||||
? ("portable" as const)
|
||||
: ("native" as const),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
@@ -773,9 +835,17 @@ const lowerInterfaceTemplate = (
|
||||
ordered: Boolean(relationship.ORDERED()),
|
||||
operations: relationship.relationshipOperation().flatMap((operation) => {
|
||||
const id = capabilityId.operation(stringValue(operation.stringLiteral(0)));
|
||||
if (operation.RESOLVE()) return [signature("resolve", id, valueType.unit, resolved)];
|
||||
if (operation.RESOLVE())
|
||||
return [
|
||||
{ ...signature("resolve", id, valueType.unit, resolved), ...lowerExecution(operation.executionClause()) },
|
||||
];
|
||||
if (operation.CONNECT() || operation.DISCONNECT())
|
||||
return [signature(operation.CONNECT() ? "connect" : "disconnect", id, targetType, valueType.unit)];
|
||||
return [
|
||||
{
|
||||
...signature(operation.CONNECT() ? "connect" : "disconnect", id, targetType, valueType.unit),
|
||||
...lowerExecution(operation.executionClause()),
|
||||
},
|
||||
];
|
||||
return [
|
||||
signature("watch-start", id, valueType.unit, valueType.watchHandle, "watch-start", resolved),
|
||||
signature(
|
||||
@@ -933,6 +1003,7 @@ const lowerDependencyPort = (state: LoweringState, context: DependencyPortContex
|
||||
requirement: {
|
||||
kind: "interface",
|
||||
interfaceRevisionId: target.revisionId,
|
||||
...lowerSelectedOperations(state, context, target),
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
@@ -1036,6 +1107,7 @@ const lowerPackageOperation = (
|
||||
inputType: lowerValueType(state, context.valueType(0)),
|
||||
outputType: lowerValueType(state, context.valueType(1)),
|
||||
mode: text(context.operationMode()) as InterfaceOperationMode,
|
||||
...lowerExecution(context.executionClause()),
|
||||
eventType: event ? lowerValueType(state, event.valueType()) : undefined,
|
||||
receiverRequirement: lowerReceiver(state, context.receiverRequirement()),
|
||||
dependencyPorts: ports,
|
||||
@@ -1052,6 +1124,7 @@ const lowerPackageFunction = (
|
||||
const alias = identifier(context.identifier());
|
||||
const entry: PackageExport = {
|
||||
kind: "function",
|
||||
...lowerExecution(context.executionClause()),
|
||||
id: capabilityId.packageExport(stringValue(context.stringLiteral())),
|
||||
displayName: alias,
|
||||
inputType: lowerValueType(state, context.valueType(0)),
|
||||
@@ -1143,6 +1216,7 @@ const lowerGenericPackageExport = (
|
||||
requirement: {
|
||||
kind: "interface",
|
||||
application: state.types.interfaceByName(identifier(port.identifier(1)), port.typeArguments()),
|
||||
...lowerSelectedOperations(state, port, state.interfaces.get(identifier(port.identifier(1)))),
|
||||
},
|
||||
};
|
||||
const name = identifier(port.identifier(1)),
|
||||
@@ -1186,6 +1260,7 @@ const lowerGenericPackageExport = (
|
||||
displayName: identifier(context.identifier()),
|
||||
parameters,
|
||||
kind: operation ? "operation" : "function",
|
||||
...lowerExecution(context.executionClause()),
|
||||
inputType: state.types.expression(context.valueType(0)!),
|
||||
outputType: state.types.expression(context.valueType(1)!),
|
||||
dependencyPorts,
|
||||
@@ -1275,7 +1350,22 @@ const lowerPackage = (
|
||||
};
|
||||
const assigned = new Set<string>();
|
||||
for (const clause of query.queryClause()) {
|
||||
if (clause.FRAGMENTS()) declaration.fragments.push(stringValue(clause.stringLiteral()!));
|
||||
if (clause.REPLICA() || clause.LOCAL()) {
|
||||
if (declaration.placement)
|
||||
loweringIssue(state, clause, "duplicate-query-placement", "Declare query placement once");
|
||||
else if (clause.REPLICA()) declaration.placement = { kind: "replica" };
|
||||
else {
|
||||
const name = identifier(clause.identifier(0));
|
||||
const sourceQueryId = requireSymbol(
|
||||
state,
|
||||
state.packages.get(alias)!.queries!,
|
||||
name,
|
||||
clause,
|
||||
"replica query",
|
||||
);
|
||||
if (sourceQueryId) declaration.placement = { kind: "local", sourceQueryId };
|
||||
}
|
||||
} else if (clause.FRAGMENTS()) declaration.fragments.push(stringValue(clause.stringLiteral()!));
|
||||
else if (clause.VIEW()) {
|
||||
const name = identifier(clause.identifier(0));
|
||||
if (parameters.length) {
|
||||
@@ -1952,7 +2042,7 @@ const interfaceSymbolFor = (revision: InterfaceRevision): InterfaceSymbol => ({
|
||||
revisionId: revision.revisionId,
|
||||
contractAvailable: true,
|
||||
members: new Map(
|
||||
revision.members.map((member) => [
|
||||
(revision.template?.members ?? revision.members).map((member) => [
|
||||
member.displayName,
|
||||
{
|
||||
memberId: member.id,
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import type { ExecutionContract, InterfaceOperation, WorkspaceRevision } from "./types.js";
|
||||
import type { CapabilityValidationIssue } from "./validation.js";
|
||||
import { resolveForwardedBinding } from "./forwarding.js";
|
||||
|
||||
/** Validate declared effects and targets; this is not a JavaScript purity proof. */
|
||||
export function validateExecutionContracts(workspace: WorkspaceRevision): CapabilityValidationIssue[] {
|
||||
const issues: CapabilityValidationIssue[] = [];
|
||||
const fail = (path: string, message: string) => issues.push({ code: "invalid-execution-contract", path, message });
|
||||
const interfaces = new Map(workspace.interfaceImports.map((i) => [i.revisionId, i]));
|
||||
const packages = new Map(workspace.packageImports.map((p) => [p.revisionId, p]));
|
||||
const operations = (id: InterfaceOperation["id"], revision: WorkspaceRevision["interfaceImports"][number]) =>
|
||||
(revision.template?.members ?? revision.members).flatMap((m) => m.operations).find((o) => o.id === id);
|
||||
const check = (contract: ExecutionContract | undefined, path: string) => {
|
||||
if (!contract || contract.kind === "remote") return;
|
||||
if (
|
||||
!["snapshot", "transaction"].includes(contract.kind) ||
|
||||
!Array.isArray(contract.targets) ||
|
||||
!contract.targets.length ||
|
||||
contract.targets.some((t) => typeof t !== "string" || !t.trim()) ||
|
||||
new Set(contract.targets).size !== contract.targets.length
|
||||
)
|
||||
fail(path, "Portable execution requires a nonempty set of distinct target names");
|
||||
};
|
||||
const supports = (
|
||||
requested: ExecutionContract | undefined,
|
||||
provided: ExecutionContract | undefined,
|
||||
path: string,
|
||||
) => {
|
||||
if (!requested || requested.kind === "remote") return;
|
||||
if (!provided || provided.kind === "remote") {
|
||||
fail(path, "Portable execution depends on a remote-only operation");
|
||||
return;
|
||||
}
|
||||
if (requested.kind === "snapshot" && provided.kind !== "snapshot")
|
||||
fail(path, "Snapshot execution cannot perform a transaction");
|
||||
for (const target of requested.targets ?? [])
|
||||
if (!provided.targets?.includes(target)) fail(path, `Dependency does not provide execution target ${target}`);
|
||||
};
|
||||
for (const revision of workspace.interfaceImports)
|
||||
for (const member of revision.template?.members ?? revision.members) {
|
||||
for (const operation of member.operations) {
|
||||
check(operation.execution, `${revision.revisionId}/${operation.id}`);
|
||||
if (operation.execution && operation.execution.kind !== "remote" && operation.mode !== "call")
|
||||
fail(
|
||||
`${revision.revisionId}/${operation.id}`,
|
||||
"Watch lifecycle operations are remote; portable reads use snapshots",
|
||||
);
|
||||
}
|
||||
if (member.kind !== "operation" && member.queryRead?.execution === "portable") {
|
||||
const getter = member.operations.find((o) => o.displayName === (member.kind === "value" ? "get" : "resolve"));
|
||||
if (getter?.execution?.kind !== "snapshot")
|
||||
fail(`${revision.revisionId}/${member.id}`, "Queryable portable members require a snapshot getter");
|
||||
}
|
||||
}
|
||||
for (const revision of workspace.packageImports) {
|
||||
for (const entry of [...revision.exports, ...(revision.genericExports ?? [])]) {
|
||||
const path = `${revision.revisionId}/${entry.id}`;
|
||||
check(entry.execution, path);
|
||||
for (const port of entry.dependencyPorts) {
|
||||
const requirement = port.requirement;
|
||||
const portPath = `${path}/${port.id}`;
|
||||
if (requirement.kind === "interface") {
|
||||
const contract = interfaces.get(
|
||||
"interfaceRevisionId" in requirement
|
||||
? requirement.interfaceRevisionId
|
||||
: requirement.application.definitionId,
|
||||
);
|
||||
const selected = requirement.operations;
|
||||
if (selected && (!selected.length || new Set(selected).size !== selected.length))
|
||||
fail(portPath, "Selected interface operations must be nonempty and distinct");
|
||||
for (const id of selected ?? [])
|
||||
if (contract && !operations(id, contract)) fail(portPath, `Unknown selected operation ${id}`);
|
||||
if (entry.execution && entry.execution.kind !== "remote") {
|
||||
if (!selected) fail(portPath, "Portable interface ports must select their required operations");
|
||||
for (const id of selected ?? [])
|
||||
if (contract) supports(entry.execution, operations(id, contract)?.execution, portPath);
|
||||
}
|
||||
}
|
||||
if (!entry.execution || entry.execution.kind === "remote") continue;
|
||||
if (requirement.kind === "state" || requirement.kind === "edge") {
|
||||
const read = requirement.kind === "state" ? "read" : "resolve";
|
||||
for (const primitive of requirement.primitives) {
|
||||
if (primitive.startsWith("watch"))
|
||||
fail(portPath, "Portable operations cannot acquire watch lifecycle primitives");
|
||||
else if (entry.execution.kind === "snapshot" && primitive !== read)
|
||||
fail(portPath, "Snapshot operations cannot acquire write primitives");
|
||||
}
|
||||
} else if (requirement.kind === "query") {
|
||||
const query = packages.get(requirement.packageRevisionId)?.queries?.find((q) => q.id === requirement.queryId);
|
||||
if (query?.placement?.kind !== "local")
|
||||
fail(portPath, "Portable operations require a local query dependency");
|
||||
} else if (requirement.kind !== "interface")
|
||||
fail(portPath, `Portable execution does not support ${requirement.kind} dependencies`);
|
||||
}
|
||||
}
|
||||
for (const query of revision.queries ?? [])
|
||||
if (query.placement?.kind === "local") {
|
||||
const sourceQueryId = query.placement.sourceQueryId;
|
||||
const source = revision.queries?.find((q) => q.id === sourceQueryId);
|
||||
if (source?.placement?.kind !== "replica")
|
||||
fail(`${revision.revisionId}/${query.id}`, "Local queries must name a replica query in the same package");
|
||||
}
|
||||
}
|
||||
for (const conformance of workspace.conformances) {
|
||||
const revision = interfaces.get(conformance.interfaceRevisionId);
|
||||
if (!revision) continue;
|
||||
for (const entry of conformance.operationBindings) {
|
||||
const operation = operations(entry.operationId, revision);
|
||||
if (!operation?.execution || operation.execution.kind === "remote") continue;
|
||||
const path = `${conformance.atomId}/${revision.revisionId}/${entry.operationId}`;
|
||||
let binding;
|
||||
try {
|
||||
binding = resolveForwardedBinding(
|
||||
workspace,
|
||||
conformance.atomId,
|
||||
revision.revisionId,
|
||||
entry.operationId,
|
||||
).binding;
|
||||
} catch {
|
||||
continue;
|
||||
} // Ordinary binding validation reports unresolved references and cycles.
|
||||
if (entry.binding.kind === "interface") {
|
||||
const target = interfaces.get(entry.binding.interfaceRevisionId);
|
||||
if (target) supports(operation.execution, operations(entry.binding.operationId, target)?.execution, path);
|
||||
}
|
||||
if (binding.kind === "package") {
|
||||
const provider = packages.get(binding.packageRevisionId)?.exports.find((e) => e.id === binding.exportId);
|
||||
supports(operation.execution, provider?.execution, path);
|
||||
} else {
|
||||
const read = binding.kind === "state" ? "read" : "resolve";
|
||||
if (binding.primitive.startsWith("watch"))
|
||||
fail(path, "Portable operations cannot bind watch lifecycle primitives");
|
||||
else if (operation.execution.kind === "snapshot" && binding.primitive !== read)
|
||||
fail(path, "Snapshot operations cannot bind write primitives");
|
||||
}
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
@@ -34,11 +34,16 @@ export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
|
||||
cardinality: Extract<DependencyPort["requirement"], { kind: "edge" }>["cardinality"];
|
||||
primitives: Extract<DependencyPort["requirement"], { kind: "edge" }>["primitives"];
|
||||
}
|
||||
| { kind: "interface"; application: InterfaceApplicationExpression }
|
||||
| {
|
||||
kind: "interface";
|
||||
application: InterfaceApplicationExpression;
|
||||
operations?: import("./types.js").OperationId[];
|
||||
}
|
||||
| { kind: "constructor"; target: ObjectTypeExpression; inputType: ValueTypeExpression };
|
||||
};
|
||||
|
||||
export interface GenericPackageExport {
|
||||
execution?: import("./types.js").ExecutionContract;
|
||||
id: PackageExportId;
|
||||
displayName: string;
|
||||
parameters: TypeParameter[];
|
||||
@@ -90,7 +95,11 @@ export const specializePackageExport = (
|
||||
case "interface":
|
||||
return {
|
||||
...port,
|
||||
requirement: { kind: "interface", interfaceRevisionId: substitution.application(requirement.application) },
|
||||
requirement: {
|
||||
kind: "interface",
|
||||
interfaceRevisionId: substitution.application(requirement.application),
|
||||
...(requirement.operations ? { operations: requirement.operations } : {}),
|
||||
},
|
||||
};
|
||||
case "constructor": {
|
||||
const target = substitution.object(requirement.target);
|
||||
@@ -112,6 +121,7 @@ export const specializePackageExport = (
|
||||
}
|
||||
});
|
||||
const common = {
|
||||
...(definition.execution ? { execution: structuredClone(definition.execution) } : {}),
|
||||
id: capabilityId.packageExport(`export-application:sha256:${digest}`),
|
||||
displayName: `${definition.displayName}$${digest}`,
|
||||
inputType: substitution.value(definition.inputType),
|
||||
|
||||
@@ -104,7 +104,11 @@ export interface AtomDefinition {
|
||||
|
||||
export type InterfaceOperationMode = "call" | "watch-start" | "watch-stop" | "subscribe" | "unsubscribe";
|
||||
|
||||
/** Omission has remote semantics; availability never strengthens an imported contract. */
|
||||
export type ExecutionContract = { kind: "remote" } | { kind: "snapshot" | "transaction"; targets: string[] };
|
||||
|
||||
export interface InterfaceOperation<Type = ValueType> {
|
||||
execution?: ExecutionContract;
|
||||
id: OperationId;
|
||||
displayName: string;
|
||||
inputType: Type;
|
||||
@@ -116,7 +120,7 @@ export interface InterfaceOperation<Type = ValueType> {
|
||||
eventType?: Type;
|
||||
}
|
||||
|
||||
export type QueryReadContract = { execution: "native" | "rpc-permitted" };
|
||||
export type QueryReadContract = { execution: "native" | "rpc-permitted" | "portable" };
|
||||
|
||||
interface InterfaceMemberBase<Type = ValueType> {
|
||||
id: MemberId;
|
||||
@@ -239,6 +243,8 @@ export type DependencyPortRequirement =
|
||||
| {
|
||||
kind: "interface";
|
||||
interfaceRevisionId: InterfaceRevisionId;
|
||||
/** Exact public operation identities, not implementation export names. */
|
||||
operations?: OperationId[];
|
||||
}
|
||||
| { kind: "constructor"; atomId: AtomId; inputType?: ValueType };
|
||||
|
||||
@@ -249,6 +255,7 @@ export interface DependencyPort {
|
||||
}
|
||||
|
||||
interface PackageExportBase {
|
||||
execution?: ExecutionContract;
|
||||
id: PackageExportId;
|
||||
displayName: string;
|
||||
inputType: ValueType;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { validateExecutionContracts } from "./execution.js";
|
||||
import { resolveForwardedBinding, type ConcreteBinding } from "./forwarding.js";
|
||||
import type {
|
||||
AtomConstructorBinding,
|
||||
@@ -41,6 +42,7 @@ import { specializePackageExport } from "./generic-packages.js";
|
||||
import { isDeepStrictEqual } from "node:util";
|
||||
|
||||
export type CapabilityValidationIssueCode =
|
||||
| "invalid-execution-contract"
|
||||
| "invalid-query-contract"
|
||||
| "query-provider-reason-required"
|
||||
| "invalid-semantic-major"
|
||||
@@ -521,7 +523,7 @@ const collectIdentityIndexes = (
|
||||
if (member.kind !== "operation" && member.queryRead) {
|
||||
const contract = member.queryRead.execution;
|
||||
const getter = member.kind === "value" ? "get" : "resolve";
|
||||
if (contract !== "native" && contract !== "rpc-permitted")
|
||||
if (contract !== "native" && contract !== "rpc-permitted" && contract !== "portable")
|
||||
issue(issues, "invalid-query-contract", memberPath, "Unknown query execution contract");
|
||||
if (member.operations.filter((operation) => operation.displayName === getter).length !== 1)
|
||||
issue(issues, "invalid-query-contract", memberPath, `Queryable members require exactly one ${getter}`);
|
||||
@@ -1590,7 +1592,7 @@ const validateConformances = (
|
||||
bindingPath,
|
||||
"This queryable contract requires a native read binding; a justification cannot weaken it",
|
||||
);
|
||||
else if (!native && !entry.queryReason?.trim())
|
||||
else if (!native && member.queryRead.execution === "rpc-permitted" && !entry.queryReason?.trim())
|
||||
issue(
|
||||
issues,
|
||||
"query-provider-reason-required",
|
||||
@@ -2082,6 +2084,7 @@ export const validateWorkspaceRevision = (workspace: WorkspaceRevision): Capabil
|
||||
validatePackages(workspace, issues, indexes);
|
||||
validateConformances(workspace, issues, indexes);
|
||||
validateConstructors(workspace, issues, indexes);
|
||||
issues.push(...validateExecutionContracts(workspace));
|
||||
return issues;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -128,6 +128,23 @@ export async function compileQuery(
|
||||
const member = contract.members.find((entry) => entry.displayName === name);
|
||||
if (!member || member.kind === "operation" || !member.queryRead)
|
||||
return fail("QUERY_FIELD_NOT_QUERYABLE", `${contract.displayName}.${name} is not queryable`, node);
|
||||
if (declaration.placement?.kind === "local") {
|
||||
const getter = member.operations.find(
|
||||
(operation) => operation.displayName === (member.kind === "value" ? "get" : "resolve"),
|
||||
);
|
||||
if (getter?.execution?.kind !== "snapshot" || !getter.execution.targets.includes("browser"))
|
||||
fail(
|
||||
"QUERY_LOCAL_DEPENDENCY",
|
||||
`Local query requires a browser snapshot getter for ${contract.displayName}.${name}; split the remote field or move the query to the server`,
|
||||
node,
|
||||
);
|
||||
if (member.queryRead.execution === "rpc-permitted")
|
||||
fail(
|
||||
"QUERY_LOCAL_DEPENDENCY",
|
||||
`Local query cannot include remote-compute field ${contract.displayName}.${name}`,
|
||||
node,
|
||||
);
|
||||
}
|
||||
const key = `${id}\0${member.id}`;
|
||||
const effect = effects.get(key) ?? {
|
||||
interfaceRevisionId: id,
|
||||
|
||||
+2
-2
@@ -82,12 +82,12 @@ export function linkQueries(workspace: WorkspaceRevision): LinkedQuery[] {
|
||||
getter.id,
|
||||
).binding;
|
||||
if (binding.kind === "package") {
|
||||
if (effect.execution !== "rpc-permitted")
|
||||
if (effect.execution === "native")
|
||||
throw new QueryCompileError(
|
||||
"QUERY_NATIVE_BINDING_REQUIRED",
|
||||
`Native query field ${effect.memberId} binds package code`,
|
||||
);
|
||||
if (!provider.queryReason?.trim())
|
||||
if (effect.execution === "rpc-permitted" && !provider.queryReason?.trim())
|
||||
throw new QueryCompileError(
|
||||
"QUERY_RPC_PROVIDER_REASON",
|
||||
`Package query field ${effect.memberId} needs query-reason`,
|
||||
|
||||
@@ -46,6 +46,15 @@ export const queryRuntimePlan = (linked: LinkedQuery, workspace: WorkspaceRevisi
|
||||
InstalledQuerySchema,
|
||||
create(InstalledQuerySchema, {
|
||||
id: linked.id,
|
||||
placement: linked.checked.declaration.placement && {
|
||||
kind:
|
||||
linked.checked.declaration.placement.kind === "replica"
|
||||
? { case: "replica", value: true }
|
||||
: {
|
||||
case: "localSourceQueryId",
|
||||
value: `${linked.packageRevisionId}:${linked.checked.declaration.placement.sourceQueryId}`,
|
||||
},
|
||||
},
|
||||
definitionDigest: linked.checked.definitionDigest,
|
||||
bindingDigest: linked.bindingDigest,
|
||||
root: {
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ export const workspaceQueryRootId: InterfaceRevisionId = capabilityId.interfaceR
|
||||
"qx-internal-workspace-query-root",
|
||||
);
|
||||
export const workspaceObjectsMemberId: MemberId = capabilityId.member("qx-internal-workspace-objects");
|
||||
export function queryRootSchema(root: QueryRoot, interfaces: readonly InterfaceRevision[]) {
|
||||
export function queryRootSchema(root: QueryRoot, interfaces: readonly InterfaceRevision[], local = false) {
|
||||
if (interfaces.some((i) => i.revisionId === workspaceQueryRootId))
|
||||
throw new QueryCompileError("QUERY_RESERVED_ID", "The workspace query root identity is compiler-owned");
|
||||
if (root.kind === "object") return { interface: root.interface, interfaces };
|
||||
@@ -27,7 +27,7 @@ export function queryRootSchema(root: QueryRoot, interfaces: readonly InterfaceR
|
||||
{
|
||||
kind: "relationship",
|
||||
id: workspaceObjectsMemberId,
|
||||
displayName: "objects",
|
||||
displayName: local ? "availableObjects" : "objects",
|
||||
target: { kind: "interface", interfaceRevisionId: root.interface },
|
||||
cardinality: "many-unique",
|
||||
ordered: false,
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ export function querySchema(
|
||||
interfaces: readonly InterfaceRevision[],
|
||||
document?: DocumentNode,
|
||||
) {
|
||||
const root = queryRootSchema(declaration.root, interfaces);
|
||||
const root = queryRootSchema(declaration.root, interfaces, declaration.placement?.kind === "local");
|
||||
const contracts = new Map(root.interfaces.map((entry) => [entry.revisionId, entry]));
|
||||
const objects = new Map<InterfaceRevisionId, GraphQLObjectType>();
|
||||
const orders = new Map<InterfaceRevisionId, GraphQLInputObjectType>();
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ export type QueryRoot<Interface = InterfaceRevisionId> =
|
||||
| { kind: "object"; interface: Interface }
|
||||
| { kind: "workspace-interface"; interface: Interface };
|
||||
export interface QueryDeclaration {
|
||||
placement?: { kind: "replica" } | { kind: "local"; sourceQueryId: string };
|
||||
id: string;
|
||||
displayName: string;
|
||||
root: QueryRoot;
|
||||
@@ -64,7 +65,7 @@ export interface QueryFieldEffect {
|
||||
interfaceRevisionId: InterfaceRevisionId;
|
||||
memberId: MemberId;
|
||||
uses: QueryUse[];
|
||||
execution: "native" | "rpc-permitted";
|
||||
execution: "native" | "rpc-permitted" | "portable";
|
||||
}
|
||||
export interface QuerySourceLocation {
|
||||
file: string;
|
||||
|
||||
Reference in New Issue
Block a user