Add factory creation ports and owner initialization contracts
This commit is contained in:
@@ -222,6 +222,7 @@ dependencyPort
|
||||
| STATE identifier ID stringLiteral COLON valueType primitiveList SEMI
|
||||
| EDGE identifier ID stringLiteral COLON cardinality targetConstraint primitiveList SEMI
|
||||
| INTERFACE identifier ID stringLiteral COLON identifier typeArguments? selectedOperations? SEMI
|
||||
| CREATION identifier ID stringLiteral COLON identifier INPUT valueType SEMI
|
||||
| CONSTRUCTOR identifier ID stringLiteral COLON identifier (INPUT valueType)? SEMI
|
||||
| QUERY identifier ID stringLiteral COLON identifier DOT identifier SEMI
|
||||
;
|
||||
@@ -255,7 +256,7 @@ stateDecl
|
||||
;
|
||||
|
||||
storagePolicy
|
||||
: OPTIMISTIC_REGISTER
|
||||
: REGISTER
|
||||
| CRDT LPAREN valueType RPAREN
|
||||
;
|
||||
|
||||
@@ -277,9 +278,23 @@ conformanceItem
|
||||
: PRIVATE attachmentDecl
|
||||
| operationBindingDecl
|
||||
| stateFieldBindingDecl
|
||||
| initializationDecl
|
||||
| relationshipMaterializationDecl
|
||||
;
|
||||
|
||||
initializationDecl
|
||||
: INITIALIZE LBRACE initializationField* RBRACE SEMI
|
||||
;
|
||||
|
||||
initializationField
|
||||
: identifier TO STATE identifier SEMI
|
||||
| identifier TO EDGE identifier DOT identifier SEMI
|
||||
;
|
||||
|
||||
creationOwnerMapping
|
||||
: interfaceType FROM INPUT (DOT identifier)* SEMI
|
||||
;
|
||||
|
||||
stateFieldBindingDecl
|
||||
: BIND identifier TO STATE identifier SEMI
|
||||
;
|
||||
@@ -341,6 +356,7 @@ 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 typeArguments? (VIA EDGE identifier DOT identifier)? SEMI
|
||||
| identifier TO CREATION identifier WITH LBRACE creationOwnerMapping* RBRACE SEMI
|
||||
| identifier TO CONSTRUCTOR identifier SEMI
|
||||
| identifier TO WORKSPACE? QUERY identifier DOT identifier (VIA EDGE identifier DOT identifier)? SEMI
|
||||
;
|
||||
@@ -433,6 +449,8 @@ stringLiteral
|
||||
: STRING_LITERAL
|
||||
;
|
||||
|
||||
CREATION: 'creation';
|
||||
INITIALIZE: 'initialize';
|
||||
WORKSPACE: 'workspace';
|
||||
EXECUTION: 'execution';
|
||||
SNAPSHOT: 'snapshot';
|
||||
@@ -528,7 +546,7 @@ WATCH_START: 'watch-start';
|
||||
WATCH_STOP: 'watch-stop';
|
||||
SUBSCRIBE: 'subscribe';
|
||||
UNSUBSCRIBE: 'unsubscribe';
|
||||
OPTIMISTIC_REGISTER: 'optimistic-register';
|
||||
REGISTER: 'register';
|
||||
CRDT: 'crdt';
|
||||
OPTIONAL_ONE: 'optional-one';
|
||||
EXACTLY_ONE: 'exactly-one';
|
||||
|
||||
@@ -115,6 +115,8 @@ export const genericImplementationType = (
|
||||
}
|
||||
case "query":
|
||||
return queryPort(requirement);
|
||||
case "creation":
|
||||
throw new Error("Creation ports require the portable transaction context generator (LF-09)");
|
||||
case "constructor":
|
||||
return object([
|
||||
[
|
||||
|
||||
@@ -249,6 +249,8 @@ export const generateTypeScriptBindings = (
|
||||
},
|
||||
};
|
||||
}
|
||||
case "creation":
|
||||
throw new Error("Creation ports require the portable transaction context generator (LF-09)");
|
||||
case "constructor": {
|
||||
const input = requirement.inputType;
|
||||
if (!input)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,279 +1,283 @@
|
||||
WORKSPACE=1
|
||||
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
|
||||
'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
|
||||
CREATION=1
|
||||
INITIALIZE=2
|
||||
WORKSPACE=3
|
||||
EXECUTION=4
|
||||
SNAPSHOT=5
|
||||
TRANSACTION=6
|
||||
REMOTE=7
|
||||
TARGETS=8
|
||||
PORTABLE=9
|
||||
REPLICA=10
|
||||
LOCAL=11
|
||||
FROM=12
|
||||
RESULT_BYTES=13
|
||||
RPC_CALLS=14
|
||||
DEADLINE_MS=15
|
||||
QUERY=16
|
||||
REFLECTION=17
|
||||
SPECIALIZE=18
|
||||
ROOT=19
|
||||
DOCUMENT=20
|
||||
FRAGMENTS=21
|
||||
VIEW=22
|
||||
MAX=23
|
||||
ALLOW=24
|
||||
POLL=25
|
||||
QUERYABLE=26
|
||||
RPC=27
|
||||
QUERY_REASON=28
|
||||
TYPE=29
|
||||
OBJECT=30
|
||||
STORABLE=31
|
||||
IMPLEMENTS=32
|
||||
REF=33
|
||||
FRAGMENT=34
|
||||
IMPORT=35
|
||||
EXTERNAL=36
|
||||
ATOM=37
|
||||
INTERFACE=38
|
||||
INTERFACES=39
|
||||
PACKAGE=40
|
||||
VALUE=41
|
||||
RELATION=42
|
||||
OPERATION=43
|
||||
FUNCTION=44
|
||||
CONSTRUCTOR=45
|
||||
CONSTRUCTS=46
|
||||
INPUT=47
|
||||
CONFORM=48
|
||||
AS=49
|
||||
BIND=50
|
||||
STATIC=51
|
||||
TO=52
|
||||
PRIVATE=53
|
||||
SHARED=54
|
||||
STATE=55
|
||||
EDGE=56
|
||||
PROJECTION=57
|
||||
WITH=58
|
||||
USING=59
|
||||
VIA=60
|
||||
MATERIALIZE=61
|
||||
IF=62
|
||||
ABSENT=63
|
||||
ON=64
|
||||
POLICY=65
|
||||
DEFAULT=66
|
||||
SOURCE=67
|
||||
REPOSITORY=68
|
||||
COMMIT=69
|
||||
REVISION=70
|
||||
SEMANTIC_MAJOR=71
|
||||
ON_DELETE=72
|
||||
RETAIN_OTHER=73
|
||||
KEYED=74
|
||||
PUBLIC_TRAVERSAL=75
|
||||
ID=76
|
||||
DOC=77
|
||||
MODE=78
|
||||
EMITS=79
|
||||
RECEIVER=80
|
||||
REQUIRES=81
|
||||
ANY=82
|
||||
GET=83
|
||||
SET=84
|
||||
WATCH=85
|
||||
START=86
|
||||
STOP=87
|
||||
READ=88
|
||||
WRITE=89
|
||||
RESOLVE=90
|
||||
CONNECT=91
|
||||
DISCONNECT=92
|
||||
CALL=93
|
||||
WATCH_START=94
|
||||
WATCH_STOP=95
|
||||
SUBSCRIBE=96
|
||||
UNSUBSCRIBE=97
|
||||
REGISTER=98
|
||||
CRDT=99
|
||||
OPTIONAL_ONE=100
|
||||
EXACTLY_ONE=101
|
||||
MANY_UNIQUE=102
|
||||
MANY=103
|
||||
ORDERED=104
|
||||
UNIT=105
|
||||
WATCH_HANDLE=106
|
||||
MESSAGE=107
|
||||
ATOM_REF=108
|
||||
INTERFACE_REF=109
|
||||
OPTIONAL=110
|
||||
LIST=111
|
||||
RECORD=112
|
||||
BOOL=113
|
||||
BYTES=114
|
||||
DOUBLE=115
|
||||
INT32=116
|
||||
INT64=117
|
||||
STRING=118
|
||||
UINT32=119
|
||||
UINT64=120
|
||||
TRUE=121
|
||||
FALSE=122
|
||||
NULL=123
|
||||
ARROW=124
|
||||
COLON=125
|
||||
SEMI=126
|
||||
COMMA=127
|
||||
DOT=128
|
||||
LBRACE=129
|
||||
RBRACE=130
|
||||
LBRACK=131
|
||||
RBRACK=132
|
||||
LPAREN=133
|
||||
RPAREN=134
|
||||
LT=135
|
||||
GT=136
|
||||
AMP=137
|
||||
EQUAL=138
|
||||
INTEGER=139
|
||||
JSON_NUMBER=140
|
||||
IDENTIFIER=141
|
||||
STRING_LITERAL=142
|
||||
LINE_COMMENT=143
|
||||
BLOCK_COMMENT=144
|
||||
WS=145
|
||||
'creation'=1
|
||||
'initialize'=2
|
||||
'workspace'=3
|
||||
'execution'=4
|
||||
'snapshot'=5
|
||||
'transaction'=6
|
||||
'remote'=7
|
||||
'targets'=8
|
||||
'portable'=9
|
||||
'replica'=10
|
||||
'local'=11
|
||||
'from'=12
|
||||
'result-bytes'=13
|
||||
'rpc-calls'=14
|
||||
'deadline-ms'=15
|
||||
'query'=16
|
||||
'reflection'=17
|
||||
'specialize'=18
|
||||
'root'=19
|
||||
'document'=20
|
||||
'fragments'=21
|
||||
'view'=22
|
||||
'max'=23
|
||||
'allow'=24
|
||||
'poll'=25
|
||||
'queryable'=26
|
||||
'rpc'=27
|
||||
'query-reason'=28
|
||||
'type'=29
|
||||
'object'=30
|
||||
'storable'=31
|
||||
'implements'=32
|
||||
'ref'=33
|
||||
'fragment'=34
|
||||
'import'=35
|
||||
'external'=36
|
||||
'atom'=37
|
||||
'interface'=38
|
||||
'interfaces'=39
|
||||
'package'=40
|
||||
'value'=41
|
||||
'relation'=42
|
||||
'operation'=43
|
||||
'function'=44
|
||||
'constructor'=45
|
||||
'constructs'=46
|
||||
'input'=47
|
||||
'conform'=48
|
||||
'as'=49
|
||||
'bind'=50
|
||||
'static'=51
|
||||
'to'=52
|
||||
'private'=53
|
||||
'shared'=54
|
||||
'state'=55
|
||||
'edge'=56
|
||||
'projection'=57
|
||||
'with'=58
|
||||
'using'=59
|
||||
'via'=60
|
||||
'materialize'=61
|
||||
'if'=62
|
||||
'absent'=63
|
||||
'on'=64
|
||||
'policy'=65
|
||||
'default'=66
|
||||
'source'=67
|
||||
'repository'=68
|
||||
'commit'=69
|
||||
'revision'=70
|
||||
'semantic-major'=71
|
||||
'on-delete'=72
|
||||
'retain-other'=73
|
||||
'keyed'=74
|
||||
'public-traversal'=75
|
||||
'id'=76
|
||||
'doc'=77
|
||||
'mode'=78
|
||||
'emits'=79
|
||||
'receiver'=80
|
||||
'requires'=81
|
||||
'any'=82
|
||||
'get'=83
|
||||
'set'=84
|
||||
'watch'=85
|
||||
'start'=86
|
||||
'stop'=87
|
||||
'read'=88
|
||||
'write'=89
|
||||
'resolve'=90
|
||||
'connect'=91
|
||||
'disconnect'=92
|
||||
'call'=93
|
||||
'watch-start'=94
|
||||
'watch-stop'=95
|
||||
'subscribe'=96
|
||||
'unsubscribe'=97
|
||||
'register'=98
|
||||
'crdt'=99
|
||||
'optional-one'=100
|
||||
'exactly-one'=101
|
||||
'many-unique'=102
|
||||
'many'=103
|
||||
'ordered'=104
|
||||
'unit'=105
|
||||
'watch-handle'=106
|
||||
'message'=107
|
||||
'atom-ref'=108
|
||||
'interface-ref'=109
|
||||
'optional'=110
|
||||
'list'=111
|
||||
'record'=112
|
||||
'bool'=113
|
||||
'bytes'=114
|
||||
'double'=115
|
||||
'int32'=116
|
||||
'int64'=117
|
||||
'string'=118
|
||||
'uint32'=119
|
||||
'uint64'=120
|
||||
'true'=121
|
||||
'false'=122
|
||||
'null'=123
|
||||
'->'=124
|
||||
':'=125
|
||||
';'=126
|
||||
','=127
|
||||
'.'=128
|
||||
'{'=129
|
||||
'}'=130
|
||||
'['=131
|
||||
']'=132
|
||||
'('=133
|
||||
')'=134
|
||||
'<'=135
|
||||
'>'=136
|
||||
'&'=137
|
||||
'='=138
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,279 +1,283 @@
|
||||
WORKSPACE=1
|
||||
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
|
||||
'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
|
||||
CREATION=1
|
||||
INITIALIZE=2
|
||||
WORKSPACE=3
|
||||
EXECUTION=4
|
||||
SNAPSHOT=5
|
||||
TRANSACTION=6
|
||||
REMOTE=7
|
||||
TARGETS=8
|
||||
PORTABLE=9
|
||||
REPLICA=10
|
||||
LOCAL=11
|
||||
FROM=12
|
||||
RESULT_BYTES=13
|
||||
RPC_CALLS=14
|
||||
DEADLINE_MS=15
|
||||
QUERY=16
|
||||
REFLECTION=17
|
||||
SPECIALIZE=18
|
||||
ROOT=19
|
||||
DOCUMENT=20
|
||||
FRAGMENTS=21
|
||||
VIEW=22
|
||||
MAX=23
|
||||
ALLOW=24
|
||||
POLL=25
|
||||
QUERYABLE=26
|
||||
RPC=27
|
||||
QUERY_REASON=28
|
||||
TYPE=29
|
||||
OBJECT=30
|
||||
STORABLE=31
|
||||
IMPLEMENTS=32
|
||||
REF=33
|
||||
FRAGMENT=34
|
||||
IMPORT=35
|
||||
EXTERNAL=36
|
||||
ATOM=37
|
||||
INTERFACE=38
|
||||
INTERFACES=39
|
||||
PACKAGE=40
|
||||
VALUE=41
|
||||
RELATION=42
|
||||
OPERATION=43
|
||||
FUNCTION=44
|
||||
CONSTRUCTOR=45
|
||||
CONSTRUCTS=46
|
||||
INPUT=47
|
||||
CONFORM=48
|
||||
AS=49
|
||||
BIND=50
|
||||
STATIC=51
|
||||
TO=52
|
||||
PRIVATE=53
|
||||
SHARED=54
|
||||
STATE=55
|
||||
EDGE=56
|
||||
PROJECTION=57
|
||||
WITH=58
|
||||
USING=59
|
||||
VIA=60
|
||||
MATERIALIZE=61
|
||||
IF=62
|
||||
ABSENT=63
|
||||
ON=64
|
||||
POLICY=65
|
||||
DEFAULT=66
|
||||
SOURCE=67
|
||||
REPOSITORY=68
|
||||
COMMIT=69
|
||||
REVISION=70
|
||||
SEMANTIC_MAJOR=71
|
||||
ON_DELETE=72
|
||||
RETAIN_OTHER=73
|
||||
KEYED=74
|
||||
PUBLIC_TRAVERSAL=75
|
||||
ID=76
|
||||
DOC=77
|
||||
MODE=78
|
||||
EMITS=79
|
||||
RECEIVER=80
|
||||
REQUIRES=81
|
||||
ANY=82
|
||||
GET=83
|
||||
SET=84
|
||||
WATCH=85
|
||||
START=86
|
||||
STOP=87
|
||||
READ=88
|
||||
WRITE=89
|
||||
RESOLVE=90
|
||||
CONNECT=91
|
||||
DISCONNECT=92
|
||||
CALL=93
|
||||
WATCH_START=94
|
||||
WATCH_STOP=95
|
||||
SUBSCRIBE=96
|
||||
UNSUBSCRIBE=97
|
||||
REGISTER=98
|
||||
CRDT=99
|
||||
OPTIONAL_ONE=100
|
||||
EXACTLY_ONE=101
|
||||
MANY_UNIQUE=102
|
||||
MANY=103
|
||||
ORDERED=104
|
||||
UNIT=105
|
||||
WATCH_HANDLE=106
|
||||
MESSAGE=107
|
||||
ATOM_REF=108
|
||||
INTERFACE_REF=109
|
||||
OPTIONAL=110
|
||||
LIST=111
|
||||
RECORD=112
|
||||
BOOL=113
|
||||
BYTES=114
|
||||
DOUBLE=115
|
||||
INT32=116
|
||||
INT64=117
|
||||
STRING=118
|
||||
UINT32=119
|
||||
UINT64=120
|
||||
TRUE=121
|
||||
FALSE=122
|
||||
NULL=123
|
||||
ARROW=124
|
||||
COLON=125
|
||||
SEMI=126
|
||||
COMMA=127
|
||||
DOT=128
|
||||
LBRACE=129
|
||||
RBRACE=130
|
||||
LBRACK=131
|
||||
RBRACK=132
|
||||
LPAREN=133
|
||||
RPAREN=134
|
||||
LT=135
|
||||
GT=136
|
||||
AMP=137
|
||||
EQUAL=138
|
||||
INTEGER=139
|
||||
JSON_NUMBER=140
|
||||
IDENTIFIER=141
|
||||
STRING_LITERAL=142
|
||||
LINE_COMMENT=143
|
||||
BLOCK_COMMENT=144
|
||||
WS=145
|
||||
'creation'=1
|
||||
'initialize'=2
|
||||
'workspace'=3
|
||||
'execution'=4
|
||||
'snapshot'=5
|
||||
'transaction'=6
|
||||
'remote'=7
|
||||
'targets'=8
|
||||
'portable'=9
|
||||
'replica'=10
|
||||
'local'=11
|
||||
'from'=12
|
||||
'result-bytes'=13
|
||||
'rpc-calls'=14
|
||||
'deadline-ms'=15
|
||||
'query'=16
|
||||
'reflection'=17
|
||||
'specialize'=18
|
||||
'root'=19
|
||||
'document'=20
|
||||
'fragments'=21
|
||||
'view'=22
|
||||
'max'=23
|
||||
'allow'=24
|
||||
'poll'=25
|
||||
'queryable'=26
|
||||
'rpc'=27
|
||||
'query-reason'=28
|
||||
'type'=29
|
||||
'object'=30
|
||||
'storable'=31
|
||||
'implements'=32
|
||||
'ref'=33
|
||||
'fragment'=34
|
||||
'import'=35
|
||||
'external'=36
|
||||
'atom'=37
|
||||
'interface'=38
|
||||
'interfaces'=39
|
||||
'package'=40
|
||||
'value'=41
|
||||
'relation'=42
|
||||
'operation'=43
|
||||
'function'=44
|
||||
'constructor'=45
|
||||
'constructs'=46
|
||||
'input'=47
|
||||
'conform'=48
|
||||
'as'=49
|
||||
'bind'=50
|
||||
'static'=51
|
||||
'to'=52
|
||||
'private'=53
|
||||
'shared'=54
|
||||
'state'=55
|
||||
'edge'=56
|
||||
'projection'=57
|
||||
'with'=58
|
||||
'using'=59
|
||||
'via'=60
|
||||
'materialize'=61
|
||||
'if'=62
|
||||
'absent'=63
|
||||
'on'=64
|
||||
'policy'=65
|
||||
'default'=66
|
||||
'source'=67
|
||||
'repository'=68
|
||||
'commit'=69
|
||||
'revision'=70
|
||||
'semantic-major'=71
|
||||
'on-delete'=72
|
||||
'retain-other'=73
|
||||
'keyed'=74
|
||||
'public-traversal'=75
|
||||
'id'=76
|
||||
'doc'=77
|
||||
'mode'=78
|
||||
'emits'=79
|
||||
'receiver'=80
|
||||
'requires'=81
|
||||
'any'=82
|
||||
'get'=83
|
||||
'set'=84
|
||||
'watch'=85
|
||||
'start'=86
|
||||
'stop'=87
|
||||
'read'=88
|
||||
'write'=89
|
||||
'resolve'=90
|
||||
'connect'=91
|
||||
'disconnect'=92
|
||||
'call'=93
|
||||
'watch-start'=94
|
||||
'watch-stop'=95
|
||||
'subscribe'=96
|
||||
'unsubscribe'=97
|
||||
'register'=98
|
||||
'crdt'=99
|
||||
'optional-one'=100
|
||||
'exactly-one'=101
|
||||
'many-unique'=102
|
||||
'many'=103
|
||||
'ordered'=104
|
||||
'unit'=105
|
||||
'watch-handle'=106
|
||||
'message'=107
|
||||
'atom-ref'=108
|
||||
'interface-ref'=109
|
||||
'optional'=110
|
||||
'list'=111
|
||||
'record'=112
|
||||
'bool'=113
|
||||
'bytes'=114
|
||||
'double'=115
|
||||
'int32'=116
|
||||
'int64'=117
|
||||
'string'=118
|
||||
'uint32'=119
|
||||
'uint64'=120
|
||||
'true'=121
|
||||
'false'=122
|
||||
'null'=123
|
||||
'->'=124
|
||||
':'=125
|
||||
';'=126
|
||||
','=127
|
||||
'.'=128
|
||||
'{'=129
|
||||
'}'=130
|
||||
'['=131
|
||||
']'=132
|
||||
'('=133
|
||||
')'=134
|
||||
'<'=135
|
||||
'>'=136
|
||||
'&'=137
|
||||
'='=138
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,9 @@ import { EdgeDeclContext } from "./QuixosCapabilityParser.js";
|
||||
import { EdgeEndpointContext } from "./QuixosCapabilityParser.js";
|
||||
import { ConformanceDeclContext } from "./QuixosCapabilityParser.js";
|
||||
import { ConformanceItemContext } from "./QuixosCapabilityParser.js";
|
||||
import { InitializationDeclContext } from "./QuixosCapabilityParser.js";
|
||||
import { InitializationFieldContext } from "./QuixosCapabilityParser.js";
|
||||
import { CreationOwnerMappingContext } from "./QuixosCapabilityParser.js";
|
||||
import { StateFieldBindingDeclContext } from "./QuixosCapabilityParser.js";
|
||||
import { RelationshipMaterializationDeclContext } from "./QuixosCapabilityParser.js";
|
||||
import { OperationBindingDeclContext } from "./QuixosCapabilityParser.js";
|
||||
@@ -390,6 +393,24 @@ export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Re
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitConformanceItem?: (ctx: ConformanceItemContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.initializationDecl`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitInitializationDecl?: (ctx: InitializationDeclContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.initializationField`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitInitializationField?: (ctx: InitializationFieldContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.creationOwnerMapping`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitCreationOwnerMapping?: (ctx: CreationOwnerMappingContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `QuixosCapabilityParser.stateFieldBindingDecl`.
|
||||
* @param ctx the parse tree
|
||||
|
||||
@@ -1009,6 +1009,14 @@ const lowerDependencyPort = (state: LoweringState, context: DependencyPortContex
|
||||
: undefined;
|
||||
}
|
||||
const atomId = requireSymbol(state, state.atoms, targetName, context, "atom");
|
||||
if (context.CREATION())
|
||||
return atomId
|
||||
? {
|
||||
id,
|
||||
displayName: name,
|
||||
requirement: { kind: "creation", atomId, inputType: lowerValueType(state, context.valueType()) },
|
||||
}
|
||||
: undefined;
|
||||
return atomId
|
||||
? {
|
||||
id,
|
||||
@@ -1229,7 +1237,11 @@ const lowerGenericPackageExport = (
|
||||
throw new GenericTypeError("missing-constructor-input", id, "Constructor ports require an explicit input type");
|
||||
return {
|
||||
...base,
|
||||
requirement: { kind: "constructor", target, inputType: state.types.expression(port.valueType()!) },
|
||||
requirement: {
|
||||
kind: port.CREATION() ? "creation" : "constructor",
|
||||
target,
|
||||
inputType: state.types.expression(port.valueType()!),
|
||||
},
|
||||
};
|
||||
});
|
||||
const operation = "receiverRequirement" in context ? context : undefined;
|
||||
@@ -1567,8 +1579,8 @@ const lowerState = (state: LoweringState, context: StateDeclContext): StateSlotD
|
||||
attachedTo: atomId,
|
||||
displayName: identifier(context.identifier(0)),
|
||||
valueType: lowerValueType(state, context.valueType()),
|
||||
storagePolicy: policy.OPTIMISTIC_REGISTER()
|
||||
? { kind: "optimistic-register" }
|
||||
storagePolicy: policy.REGISTER()
|
||||
? { kind: "register" }
|
||||
: {
|
||||
kind: "crdt-document",
|
||||
updateType: lowerValueType(state, policy.valueType()),
|
||||
@@ -1774,6 +1786,24 @@ const lowerBoundDependencies = (
|
||||
}
|
||||
const atomName = identifier(entry.identifier(1));
|
||||
const atomId = requireSymbol(state, state.atoms, atomName, entry, "atom");
|
||||
if (entry.CREATION())
|
||||
return atomId
|
||||
? [
|
||||
{
|
||||
portId,
|
||||
binding: {
|
||||
kind: "creation",
|
||||
atomId,
|
||||
owners: entry.creationOwnerMapping().map((owner) => ({
|
||||
interfaceRevisionId: new TypeSubstitution(state.types.environment()).application(
|
||||
state.types.interface(owner.interfaceType()),
|
||||
),
|
||||
inputPath: owner.identifier().map(identifier),
|
||||
})),
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
return atomId ? [{ portId, binding: { kind: "constructor", atomId } }] : [];
|
||||
});
|
||||
};
|
||||
@@ -2025,8 +2055,43 @@ const lowerConformance = (
|
||||
const lowered = lowerRelationshipMaterialization(state, interfaceName, materialization);
|
||||
return lowered ? [lowered] : [];
|
||||
});
|
||||
const initializers = context
|
||||
.conformanceItem()
|
||||
.flatMap((item) => (item.initializationDecl() ? [item.initializationDecl()!] : []));
|
||||
if (initializers.length > 1)
|
||||
loweringIssue(state, context, "duplicate-initialization", "Declare one initialization mapping per owner");
|
||||
const initialization = initializers.flatMap((initializer) =>
|
||||
initializer.initializationField().flatMap((field): import("../capability-model/types.js").InitializationField[] => {
|
||||
const input = identifier(field.identifier(0));
|
||||
const attachment = requireSymbol(
|
||||
state,
|
||||
state.attachments,
|
||||
identifier(field.identifier(1)),
|
||||
field,
|
||||
"initialization attachment",
|
||||
);
|
||||
if (!attachment) return [];
|
||||
if (field.STATE() && attachment.attachment.kind === "state")
|
||||
return [{ input, binding: { kind: "state", slotId: attachment.attachment.id } }];
|
||||
if (field.EDGE() && attachment.attachment.kind === "edge") {
|
||||
const projectionId = requireSymbol(
|
||||
state,
|
||||
attachment.projections,
|
||||
identifier(field.identifier(2)),
|
||||
field,
|
||||
"initialization projection",
|
||||
);
|
||||
return projectionId
|
||||
? [{ input, binding: { kind: "edge", edgeTypeId: attachment.attachment.id, projectionId } }]
|
||||
: [];
|
||||
}
|
||||
loweringIssue(state, field, "invalid-initialization", "Initialization attachment kind does not match");
|
||||
return [];
|
||||
}),
|
||||
);
|
||||
state.interfaces.set(interfaceName, originalSymbol);
|
||||
return {
|
||||
...(initializers.length ? { initialization } : {}),
|
||||
atomId,
|
||||
interfaceRevisionId: interfaceSymbol.revisionId,
|
||||
...(context.stringLiteral() ? { id: capabilityId.conformance(stringValue(context.stringLiteral()!)) } : {}),
|
||||
@@ -2562,7 +2627,7 @@ const compileCapabilityResourceSourceInternal = (
|
||||
types.push(requirement.valueType);
|
||||
}
|
||||
if (requirement.kind === "edge") types.push({ kind: "object-ref", expectation: requirement.target });
|
||||
if (requirement.kind === "constructor")
|
||||
if (requirement.kind === "constructor" || requirement.kind === "creation")
|
||||
types.push(requirement.inputType, { kind: "object-ref", expectation: requirement.target });
|
||||
if (requirement.kind === "interface") requires.push(requirement.application);
|
||||
}
|
||||
|
||||
@@ -149,6 +149,10 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[
|
||||
}
|
||||
if (binding.kind === "state") parent.dependencies.add(`attachment:${binding.slotId}`);
|
||||
if (binding.kind === "edge") parent.dependencies.add(`attachment:${binding.edgeTypeId}`);
|
||||
if (binding.kind === "creation") {
|
||||
for (const owner of binding.owners)
|
||||
parent.dependencies.add(conformanceKey(binding.atomId, owner.interfaceRevisionId));
|
||||
}
|
||||
if (binding.kind === "constructor") {
|
||||
parent.dependencies.add(`constructor:${binding.atomId}`);
|
||||
const ctor = workspace.constructors.find((entry) => entry.atomId === binding.atomId);
|
||||
@@ -210,6 +214,9 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[
|
||||
atomId: conformance.atomId,
|
||||
interfaceRevisionId: conformance.interfaceRevisionId,
|
||||
operations: sorted(conformance.operationBindings, (entry) => entry.operationId),
|
||||
...(conformance.initialization
|
||||
? { initialization: sorted(conformance.initialization, (entry) => entry.input) }
|
||||
: {}),
|
||||
materializations: sorted(conformance.relationshipMaterializations, (entry) => entry.memberId),
|
||||
});
|
||||
parent.dependencies.add(`interface:${conformance.interfaceRevisionId}`);
|
||||
|
||||
@@ -89,6 +89,8 @@ export function validateExecutionContracts(workspace: WorkspaceRevision): Capabi
|
||||
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 === "creation") {
|
||||
if (entry.execution.kind !== "transaction") fail(portPath, "Object creation requires transaction execution");
|
||||
} else if (requirement.kind !== "interface")
|
||||
fail(portPath, `Portable execution does not support ${requirement.kind} dependencies`);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
|
||||
application: InterfaceApplicationExpression;
|
||||
operations?: import("./types.js").OperationId[];
|
||||
}
|
||||
| { kind: "creation"; target: ObjectTypeExpression; inputType: ValueTypeExpression }
|
||||
| { kind: "constructor"; target: ObjectTypeExpression; inputType: ValueTypeExpression };
|
||||
};
|
||||
|
||||
@@ -101,6 +102,7 @@ export const specializePackageExport = (
|
||||
...(requirement.operations ? { operations: requirement.operations } : {}),
|
||||
},
|
||||
};
|
||||
case "creation":
|
||||
case "constructor": {
|
||||
const target = substitution.object(requirement.target);
|
||||
if (target.kind !== "atom")
|
||||
@@ -112,7 +114,7 @@ export const specializePackageExport = (
|
||||
return {
|
||||
...port,
|
||||
requirement: {
|
||||
kind: "constructor",
|
||||
kind: requirement.kind,
|
||||
atomId: target.atomId,
|
||||
inputType: substitution.value(requirement.inputType),
|
||||
},
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import type {
|
||||
BoundDependency,
|
||||
Conformance,
|
||||
DependencyPort,
|
||||
InitializationField,
|
||||
ValueType,
|
||||
WorkspaceRevision,
|
||||
} from "./types.js";
|
||||
import type { CapabilityValidationIssue } from "./validation.js";
|
||||
import { cardinalityValueType, valueTypesEqual } from "./validation.js";
|
||||
|
||||
/** Creation grants seed inputs on new objects, never general private-state authority. */
|
||||
export function validateInitialization(workspace: WorkspaceRevision): CapabilityValidationIssue[] {
|
||||
const issues: CapabilityValidationIssue[] = [];
|
||||
const fail = (path: string, message: string) => issues.push({ code: "invalid-initialization", path, message });
|
||||
const ownerKey = (owner: Conformance) => `${owner.atomId}/${owner.interfaceRevisionId}`;
|
||||
const shapes = new Map<string, ValueType>();
|
||||
const fieldType = (owner: Conformance, field: InitializationField): ValueType | undefined => {
|
||||
const binding = field.binding;
|
||||
const attachment = owner.privateAttachments.find(
|
||||
(a) => a.kind === binding.kind && a.id === (binding.kind === "state" ? binding.slotId : binding.edgeTypeId),
|
||||
);
|
||||
if (!attachment) {
|
||||
fail(ownerKey(owner), `Initialization input ${field.input} must name an attachment owned by this conformance`);
|
||||
return;
|
||||
}
|
||||
if (attachment.kind === "state") return attachment.valueType;
|
||||
if (binding.kind !== "edge") return;
|
||||
const index = attachment.endpoints.findIndex((e) => e.projectionId === binding.projectionId);
|
||||
const endpoint = attachment.endpoints[index];
|
||||
const target = attachment.endpoints[1 - index];
|
||||
if (!endpoint || !target || endpoint.constraint.kind !== "atom" || endpoint.constraint.atomId !== owner.atomId) {
|
||||
fail(ownerKey(owner), `Initialization input ${field.input} must name an edge projection on the newborn atom`);
|
||||
return;
|
||||
}
|
||||
if (endpoint.cardinality === "many" || endpoint.cardinality === "many-unique") {
|
||||
const fields: Record<string, ValueType> = {
|
||||
key: { kind: "scalar", name: "string" },
|
||||
target: cardinalityValueType(target.constraint, "exactly-one"),
|
||||
};
|
||||
if (endpoint.keyType)
|
||||
fields.mapKey = { kind: "scalar", name: endpoint.keyType === "boolean" ? "bool" : endpoint.keyType };
|
||||
return { kind: "list", value: { kind: "record", fields } };
|
||||
}
|
||||
return cardinalityValueType(target.constraint, endpoint.cardinality);
|
||||
};
|
||||
for (const owner of workspace.conformances) {
|
||||
if (!owner.initialization) continue;
|
||||
const fields: Record<string, ValueType> = Object.create(null);
|
||||
const destinations = new Set<string>();
|
||||
for (const field of owner.initialization) {
|
||||
const path = `${ownerKey(owner)}/${field.input}`;
|
||||
if (!field.input || Object.hasOwn(fields, field.input))
|
||||
fail(path, "Initialization input names must be nonempty and distinct");
|
||||
const destination = JSON.stringify(field.binding);
|
||||
if (destinations.has(destination)) fail(path, "An attachment may have only one initialization input");
|
||||
destinations.add(destination);
|
||||
const type = fieldType(owner, field);
|
||||
if (type) fields[field.input] = type;
|
||||
}
|
||||
shapes.set(ownerKey(owner), { kind: "record", fields });
|
||||
}
|
||||
const checkBindings = (ports: DependencyPort[], dependencies: BoundDependency[], path: string) => {
|
||||
for (const dependency of dependencies) {
|
||||
if (dependency.binding.kind !== "creation") continue;
|
||||
const binding = dependency.binding;
|
||||
const requirement = ports.find((p) => p.id === dependency.portId)?.requirement;
|
||||
if (requirement?.kind !== "creation") continue; // General binding validation reports the kind mismatch.
|
||||
const portPath = `${path}/${dependency.portId}`;
|
||||
if (binding.atomId !== requirement.atomId) fail(portPath, `Creation port requires atom ${requirement.atomId}`);
|
||||
const initialized = new Set<string>();
|
||||
const supplied = new Set<string>();
|
||||
for (const mapping of binding.owners) {
|
||||
const key = `${binding.atomId}/${mapping.interfaceRevisionId}`;
|
||||
if (supplied.has(key)) fail(portPath, `Duplicate initialization owner ${key}`);
|
||||
supplied.add(key);
|
||||
const owner = workspace.conformances.find((o) => ownerKey(o) === key);
|
||||
const shape = shapes.get(key);
|
||||
if (!owner || !shape) {
|
||||
fail(portPath, `Owner ${key} has no declared initialization mapping`);
|
||||
continue;
|
||||
}
|
||||
let seed: ValueType | undefined = requirement.inputType;
|
||||
for (const component of mapping.inputPath) seed = seed?.kind === "record" ? seed.fields[component] : undefined;
|
||||
if (!seed || !valueTypesEqual(seed, shape))
|
||||
fail(
|
||||
portPath,
|
||||
`Seed input${mapping.inputPath.map((p) => `.${p}`).join("")} does not match initialization inputs of ${key}`,
|
||||
);
|
||||
for (const field of owner.initialization!)
|
||||
if (field.binding.kind === "state") initialized.add(field.binding.slotId);
|
||||
}
|
||||
const states = [
|
||||
...workspace.sharedAttachments,
|
||||
...workspace.conformances.flatMap((o) => o.privateAttachments),
|
||||
].filter((a) => a.kind === "state" && a.attachedTo === binding.atomId);
|
||||
for (const state of states)
|
||||
if (state.kind === "state" && state.defaultValue === undefined && !initialized.has(state.id))
|
||||
fail(portPath, `Required state ${state.id} has no default or declared seed mapping`);
|
||||
}
|
||||
};
|
||||
const exports = new Map(
|
||||
workspace.packageImports.flatMap((p) => p.exports.map((e) => [`${p.revisionId}/${e.id}`, e] as const)),
|
||||
);
|
||||
for (const owner of workspace.conformances)
|
||||
for (const operation of owner.operationBindings) {
|
||||
if (operation.binding.kind !== "package") continue;
|
||||
const entry = exports.get(`${operation.binding.packageRevisionId}/${operation.binding.exportId}`);
|
||||
if (entry)
|
||||
checkBindings(
|
||||
entry.dependencyPorts,
|
||||
operation.binding.dependencies,
|
||||
`${ownerKey(owner)}/${operation.operationId}`,
|
||||
);
|
||||
}
|
||||
for (const constructor of workspace.constructors) {
|
||||
const entry = exports.get(`${constructor.packageRevisionId}/${constructor.exportId}`);
|
||||
if (entry) checkBindings(entry.dependencyPorts, constructor.dependencies, `constructor/${constructor.atomId}`);
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
@@ -181,7 +181,7 @@ export interface InterfaceRevision {
|
||||
argumentRequirements?: { target: ObjectExpectation; required: InterfaceRevisionId }[];
|
||||
}
|
||||
|
||||
export type StoragePolicy = { kind: "optimistic-register" } | { kind: "crdt-document"; updateType: ValueType };
|
||||
export type StoragePolicy = { kind: "register" } | { kind: "crdt-document"; updateType: ValueType };
|
||||
|
||||
export interface StateSlotDefinition {
|
||||
kind: "state";
|
||||
@@ -246,6 +246,7 @@ export type DependencyPortRequirement =
|
||||
/** Exact public operation identities, not implementation export names. */
|
||||
operations?: OperationId[];
|
||||
}
|
||||
| { kind: "creation"; atomId: AtomId; inputType: ValueType }
|
||||
| { kind: "constructor"; atomId: AtomId; inputType?: ValueType };
|
||||
|
||||
export interface DependencyPort {
|
||||
@@ -320,6 +321,12 @@ export type DependencyBinding =
|
||||
interfaceRevisionId: InterfaceRevisionId;
|
||||
via?: EdgeTraversal;
|
||||
}
|
||||
| {
|
||||
kind: "creation";
|
||||
atomId: AtomId;
|
||||
owners: { interfaceRevisionId: InterfaceRevisionId; inputPath: string[] }[];
|
||||
via?: never;
|
||||
}
|
||||
| { kind: "constructor"; atomId: AtomId };
|
||||
|
||||
export interface BoundDependency {
|
||||
@@ -376,7 +383,13 @@ export interface OperationBinding {
|
||||
queryReason?: string;
|
||||
}
|
||||
|
||||
export interface InitializationField {
|
||||
input: string;
|
||||
binding: { kind: "state"; slotId: SlotId } | { kind: "edge"; edgeTypeId: EdgeTypeId; projectionId: EdgeProjectionId };
|
||||
}
|
||||
|
||||
export interface Conformance {
|
||||
initialization?: InitializationField[];
|
||||
/** Absent only for legacy assemblies awaiting explicit ownership enrollment. */
|
||||
id?: ConformanceId;
|
||||
semanticMajor?: number;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { validateInitialization } from "./initialization.js";
|
||||
import { validateExecutionContracts } from "./execution.js";
|
||||
import { resolveForwardedBinding, type ConcreteBinding } from "./forwarding.js";
|
||||
import type {
|
||||
@@ -42,6 +43,7 @@ import { specializePackageExport } from "./generic-packages.js";
|
||||
import { isDeepStrictEqual } from "node:util";
|
||||
|
||||
export type CapabilityValidationIssueCode =
|
||||
| "invalid-initialization"
|
||||
| "invalid-execution-contract"
|
||||
| "invalid-query-contract"
|
||||
| "query-provider-reason-required"
|
||||
@@ -1043,6 +1045,7 @@ const validatePackages = (
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "creation":
|
||||
case "constructor":
|
||||
if (port.requirement.inputType)
|
||||
validateValueType(issues, port.requirement.inputType, `${portPath}.requirement.inputType`, indexes);
|
||||
@@ -1794,14 +1797,14 @@ const validateConformances = (
|
||||
});
|
||||
if (
|
||||
operation.scope === "class" &&
|
||||
(binding.dependencies.some((entry) => entry.binding.kind !== "constructor") ||
|
||||
packageExport.dependencyPorts.some((entry) => entry.requirement.kind !== "constructor"))
|
||||
(binding.dependencies.some((entry) => !["constructor", "creation"].includes(entry.binding.kind)) ||
|
||||
packageExport.dependencyPorts.some((entry) => !["constructor", "creation"].includes(entry.requirement.kind)))
|
||||
) {
|
||||
issue(
|
||||
issues,
|
||||
"invalid-package-binding",
|
||||
bindingPath,
|
||||
"Class functions may inject constructors, not instance-dependent ports",
|
||||
"Class functions require receiverless dependencies such as creation ports",
|
||||
);
|
||||
}
|
||||
validateBoundDependencies(issues, {
|
||||
@@ -2084,7 +2087,7 @@ export const validateWorkspaceRevision = (workspace: WorkspaceRevision): Capabil
|
||||
validatePackages(workspace, issues, indexes);
|
||||
validateConformances(workspace, issues, indexes);
|
||||
validateConstructors(workspace, issues, indexes);
|
||||
issues.push(...validateExecutionContracts(workspace));
|
||||
issues.push(...validateExecutionContracts(workspace), ...validateInitialization(workspace));
|
||||
return issues;
|
||||
};
|
||||
|
||||
@@ -2327,6 +2330,11 @@ export const computeCapabilityClosure = (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "creation": {
|
||||
for (const owner of dependency.binding.owners)
|
||||
queued.push({ atomId: dependency.binding.atomId, interfaceRevisionId: owner.interfaceRevisionId });
|
||||
break;
|
||||
}
|
||||
case "constructor": {
|
||||
constructors.add(dependency.binding.atomId);
|
||||
const constructor = plan.constructors.get(dependency.binding.atomId);
|
||||
|
||||
@@ -148,10 +148,10 @@ test("interfaces can declare ordinary call operations", () => {
|
||||
|
||||
test("state defaults accept recursive JSON values", () => {
|
||||
const source = capabilityFixtureSource.replace(
|
||||
/policy optimistic-register default "Untitled project";/,
|
||||
` policy optimistic-register default "Untitled project";
|
||||
/policy register default "Untitled project";/,
|
||||
` policy 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};`,
|
||||
policy register default {"labels":["compiler","runtime"],"score":1.5,"enabled":true,"extra":null};`,
|
||||
);
|
||||
const result = compileCapabilityFixture({ workspace: source });
|
||||
assert.equal(result.ok, true);
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import {
|
||||
compileCapabilityResourceSource,
|
||||
compileCapabilitySource,
|
||||
formatQx,
|
||||
} from "../src/capability-language/index.js";
|
||||
import type { InterfaceRevision } from "../src/capability-model/types.js";
|
||||
const source = { repository: "https://example.test/factories.git", commit: "a".repeat(40) };
|
||||
const seed = "record { data: record { title: string; }; tags: record { tags: list<string>; }; }";
|
||||
const iface = (text: string): InterfaceRevision => {
|
||||
const result = compileCapabilityResourceSource(text, { source });
|
||||
assert.ok(result.ok && result.resource.kind === "interface", JSON.stringify(result.diagnostics));
|
||||
return result.resource.revision;
|
||||
};
|
||||
const data = iface(`interface Data id "data" revision "data@1" {
|
||||
value title id "title" : string { get id "title:get" execution snapshot targets [browser, server]; }
|
||||
}`);
|
||||
const tagged = iface(`interface Tagged id "tagged" revision "tagged@1" {
|
||||
value tags id "tags" : list<string> { get id "tags:get" execution snapshot targets [browser, server]; }
|
||||
}`);
|
||||
const factory = iface(`external atom Note id "note";
|
||||
interface Factory id "factory" revision "factory@1" {
|
||||
static operation create id "create" : ${seed} -> atom-ref<Note> { call id "create:call" execution transaction targets [browser, server]; }
|
||||
static operation importNote id "import" : ${seed} -> atom-ref<Note> { call id "import:call" execution transaction targets [browser, server]; }
|
||||
}`);
|
||||
const runtime = compileCapabilityResourceSource(
|
||||
`external atom Note id "note";
|
||||
package Runtime id "runtime" revision "runtime@1" {
|
||||
function createNote id "create-note" : ${seed} -> atom-ref<Note> execution transaction targets [browser, server] requires {
|
||||
creation note id "new-note" : Note input ${seed};
|
||||
};
|
||||
function importNote id "import-note" : ${seed} -> atom-ref<Note> execution transaction targets [browser, server] requires {
|
||||
creation note id "new-note" : Note input ${seed};
|
||||
};
|
||||
}`,
|
||||
{ source },
|
||||
);
|
||||
assert.ok(runtime.ok && runtime.resource.kind === "package", JSON.stringify(runtime.diagnostics));
|
||||
const packageRevision = runtime.resource.revision;
|
||||
const text = `workspace Notes id "notes" revision "notes@1" commit "${"a".repeat(40)}" {
|
||||
import interface Data; import interface Tagged; import interface Factory; import package Runtime;
|
||||
atom Note id "note";
|
||||
conform Note as Data id "owner:data" {
|
||||
private state Title id "title-state" on Note : string policy register;
|
||||
initialize { title to state Title; };
|
||||
bind title.get to state Title.read;
|
||||
}
|
||||
conform Note as Tagged id "owner:tags" {
|
||||
private state Tags id "tags-state" on Note : list<string> policy register;
|
||||
initialize { tags to state Tags; };
|
||||
bind tags.get to state Tags.read;
|
||||
}
|
||||
conform Note as Factory id "owner:factory" {
|
||||
bind create.call to package Runtime.createNote with {
|
||||
note to creation Note with { Data from input.data; Tagged from input.tags; };
|
||||
};
|
||||
bind importNote.call to package Runtime.importNote with {
|
||||
note to creation Note with { Data from input.data; Tagged from input.tags; };
|
||||
};
|
||||
}
|
||||
}`;
|
||||
const compile = (textOverride = text) =>
|
||||
compileCapabilitySource(formatQx(textOverride), "notes.qx", {
|
||||
interfaces: new Map([
|
||||
["Data", data],
|
||||
["Tagged", tagged],
|
||||
["Factory", factory],
|
||||
]),
|
||||
interfaceClosure: [data, tagged, factory],
|
||||
packages: new Map([["Runtime", packageRevision]]),
|
||||
packageClosure: [packageRevision],
|
||||
});
|
||||
test("two factories initialize two independent state owners without constructors", () => {
|
||||
const result = compile();
|
||||
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
||||
assert.deepEqual(result.workspace.constructors, []);
|
||||
assert.equal(result.workspace.conformances.filter((c) => c.initialization).length, 2);
|
||||
const owners = result.workspace.conformances[2]!.operationBindings.map((b) =>
|
||||
b.binding.kind === "package" ? b.binding.dependencies[0]!.binding : undefined,
|
||||
);
|
||||
for (const binding of owners) {
|
||||
assert.ok(binding?.kind === "creation");
|
||||
assert.deepEqual(binding.owners, [
|
||||
{ interfaceRevisionId: "data@1", inputPath: ["data"] },
|
||||
{ interfaceRevisionId: "tagged@1", inputPath: ["tags"] },
|
||||
]);
|
||||
}
|
||||
});
|
||||
test("creation rejects missing seeds, wrong input shapes, and private initialization laundering", () => {
|
||||
for (const [edit, message] of [
|
||||
[text.replaceAll("Tagged from input.tags;", ""), "Required state tags-state"],
|
||||
[text.replaceAll("Data from input.data;", "Data from input.tags;"), "does not match"],
|
||||
[text.replace("title to state Title;", "title to state Tags;"), "owned by this conformance"],
|
||||
[
|
||||
text.replaceAll("Data from input.data;", "Data from input.data; Data from input.data;"),
|
||||
"Duplicate initialization owner",
|
||||
],
|
||||
]) {
|
||||
const result = compile(edit!);
|
||||
assert.equal(result.ok, false);
|
||||
assert.match(JSON.stringify(result.diagnostics), new RegExp(message!));
|
||||
}
|
||||
});
|
||||
|
||||
test("defaults supply unmapped state and seeds cannot invoke an undeclared owner", () => {
|
||||
const withDefault = text
|
||||
.replace("on Note : list<string> policy register;", "on Note : list<string> policy register default [];")
|
||||
.replaceAll("Tagged from input.tags;", "");
|
||||
const result = compile(withDefault);
|
||||
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
||||
const missing = compile(text.replace("initialize { tags to state Tags; };", ""));
|
||||
assert.equal(missing.ok, false);
|
||||
assert.match(JSON.stringify(missing.diagnostics), /no declared initialization mapping/);
|
||||
});
|
||||
|
||||
test("creation ports cannot be used in snapshot execution", () => {
|
||||
const result = compileCapabilityResourceSource(
|
||||
`external atom Note id "note";
|
||||
package P id "p" revision "p@1" {
|
||||
function createNote id "create" : unit -> atom-ref<Note> execution snapshot targets [browser, server] requires {
|
||||
creation note id "note" : Note input record { title:string; };
|
||||
};
|
||||
}`,
|
||||
{ source },
|
||||
);
|
||||
assert.equal(result.ok, false);
|
||||
assert.match(JSON.stringify(result.diagnostics), /creation requires transaction/);
|
||||
});
|
||||
|
||||
test("the authored storage policy is register, without an old spelling alias", () => {
|
||||
const result = compileCapabilitySource(text.replaceAll("policy register", "policy optimistic-register"), "notes.qx");
|
||||
assert.equal(result.ok, false);
|
||||
assert.ok(result.diagnostics.some((d) => d.phase === "syntax"));
|
||||
});
|
||||
|
||||
test("initialization contracts participate in executable source identity", async () => {
|
||||
const { runtimeContracts } = await import("../src/capability-model/index.js");
|
||||
const result = compile();
|
||||
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
||||
const before = runtimeContracts(result.workspace);
|
||||
const changed = structuredClone(result.workspace);
|
||||
changed.conformances[0]!.initialization![0]!.input = "renamed";
|
||||
assert.notDeepEqual(runtimeContracts(changed), before);
|
||||
});
|
||||
|
||||
test("to-many initialization requires stable entry keys and typed targets", async () => {
|
||||
const { validateInitialization } = await import("../src/capability-model/initialization.js");
|
||||
const { capabilityId } = await import("../src/capability-model/index.js");
|
||||
const result = compile();
|
||||
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
||||
const workspace = result.workspace;
|
||||
const owner = workspace.conformances[1]!;
|
||||
const state = owner.privateAttachments[0]!;
|
||||
assert.equal(state.kind, "state");
|
||||
if (state.kind !== "state") return;
|
||||
state.defaultValue = [];
|
||||
const edgeId = capabilityId.edgeType("children"),
|
||||
projectionId = capabilityId.edgeProjection("children:parent");
|
||||
owner.privateAttachments.push({
|
||||
kind: "edge",
|
||||
id: edgeId,
|
||||
displayName: "Children",
|
||||
endpoints: [
|
||||
{
|
||||
projectionId,
|
||||
displayName: "children",
|
||||
constraint: { kind: "atom", atomId: owner.atomId },
|
||||
cardinality: "many-unique",
|
||||
ordered: false,
|
||||
},
|
||||
{
|
||||
projectionId: capabilityId.edgeProjection("children:child"),
|
||||
displayName: "parent",
|
||||
constraint: { kind: "atom", atomId: owner.atomId },
|
||||
cardinality: "optional-one",
|
||||
ordered: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
owner.initialization = [{ input: "children", binding: { kind: "edge", edgeTypeId: edgeId, projectionId } }];
|
||||
const entryType = {
|
||||
kind: "record" as const,
|
||||
fields: {
|
||||
key: { kind: "scalar" as const, name: "string" as const },
|
||||
target: { kind: "object-ref" as const, expectation: { kind: "atom" as const, atomId: owner.atomId } },
|
||||
},
|
||||
};
|
||||
for (const entry of workspace.packageImports[0]!.exports) {
|
||||
const port = entry.dependencyPorts[0]!.requirement;
|
||||
assert.ok(port.kind === "creation" && port.inputType.kind === "record");
|
||||
port.inputType.fields.tags = { kind: "record", fields: { children: { kind: "list", value: entryType } } };
|
||||
}
|
||||
assert.deepEqual(validateInitialization(workspace), []);
|
||||
Reflect.deleteProperty(entryType.fields, "key");
|
||||
assert.ok(validateInitialization(workspace).some((i) => i.message.includes("does not match")));
|
||||
});
|
||||
Vendored
+3
-3
@@ -98,8 +98,8 @@ export async function queryWorkspaceFixture(options: { live?: boolean } = {}) {
|
||||
);
|
||||
const implementation = (atom: string) => `conform ${atom} as TaskFacts id "${atom}-facts" {
|
||||
private state Title${atom} id "${atom}:title" on ${atom} : string policy crdt(string) default "Untitled";
|
||||
private state Done${atom} id "${atom}:done" on ${atom} : bool policy optimistic-register default false;
|
||||
private state Rank${atom} id "${atom}:rank" on ${atom} : int64 policy optimistic-register default 0;
|
||||
private state Done${atom} id "${atom}:done" on ${atom} : bool policy register default false;
|
||||
private state Rank${atom} id "${atom}:rank" on ${atom} : int64 policy register default 0;
|
||||
private edge Assignee${atom} id "${atom}:assignee" {
|
||||
atom ${atom} projection assignee id "${atom}:assignee:forward" optional-one;
|
||||
interface PersonFacts projection tasks id "${atom}:assignee:inverse" many;
|
||||
@@ -114,7 +114,7 @@ export async function queryWorkspaceFixture(options: { live?: boolean } = {}) {
|
||||
import interface PersonFacts; import interface TaskFacts; import interface Collection; import package Queries;
|
||||
atom Task id "Task"; atom Reminder id "Reminder"; atom Person id "Person"; atom Tasks id "Tasks";
|
||||
conform Person as PersonFacts id "person-facts" {
|
||||
private state Name id "person:name" on Person : string policy optimistic-register default "Nobody";
|
||||
private state Name id "person:name" on Person : string policy register default "Nobody";
|
||||
bind name to state Name;
|
||||
}
|
||||
${implementation("Task")} ${implementation("Reminder")}
|
||||
|
||||
Vendored
+2
-2
@@ -8,7 +8,7 @@ workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "111111111
|
||||
import package TodoRuntime;
|
||||
|
||||
shared state ProjectTitle id "slot:project:title" on Project : string
|
||||
policy optimistic-register default "Untitled project";
|
||||
policy register default "Untitled project";
|
||||
|
||||
conform Project as Named {
|
||||
bind name.get to state ProjectTitle.read;
|
||||
@@ -19,7 +19,7 @@ workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "111111111
|
||||
|
||||
conform Person as Named {
|
||||
private state PersonName id "slot:person:name" on Person : string
|
||||
policy optimistic-register default "Anonymous";
|
||||
policy 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;
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ workspace WebStudioFixture id "workspace:web-studio-fixture" revision "workspace
|
||||
import package ComponentRuntime;
|
||||
|
||||
shared state ProjectName id "slot:project:name" on Project : string
|
||||
policy optimistic-register default "Untitled project";
|
||||
policy 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;
|
||||
|
||||
@@ -36,7 +36,7 @@ function fixture(transform = (s: string) => s, generic = false) {
|
||||
${forward("Relay", "Alias")}
|
||||
${forward("Alias", generic ? "Owner<string>" : "Owner")}
|
||||
conform Item as Owner${generic ? "<string>" : ""} id "owner-conformance" {
|
||||
private state Title id "title" on Item : string policy optimistic-register default "";
|
||||
private state Title id "title" on Item : string policy register default "";
|
||||
bind title to state Title;
|
||||
}
|
||||
}`;
|
||||
|
||||
@@ -272,7 +272,7 @@ test("generic conformances bind state against specialized signatures", () => {
|
||||
atom Document id "document";
|
||||
import interface Reader;
|
||||
conform Document as Reader<string> id "reader-conformance" {
|
||||
private state Values id "values-slot" on Document : list<optional<string>> policy optimistic-register;
|
||||
private state Values id "values-slot" on Document : list<optional<string>> policy register;
|
||||
bind values.get to state Values.read;
|
||||
bind values.watch-start to state Values.watch-start;
|
||||
bind values.watch-stop to state Values.watch-stop;
|
||||
@@ -328,7 +328,7 @@ test("package receivers and injected dependencies select exact applications", ()
|
||||
import interface Reader; import interface Summary; import package P;
|
||||
atom Note id "note";
|
||||
conform Note as Reader<string> id "reader-conformance" {
|
||||
private state Values id "values" on Note : list<optional<string>> policy optimistic-register;
|
||||
private state Values id "values" on Note : list<optional<string>> policy register;
|
||||
bind values.get to state Values.read;
|
||||
bind values.watch-start to state Values.watch-start;
|
||||
bind values.watch-stop to state Values.watch-stop;
|
||||
|
||||
@@ -82,7 +82,7 @@ test("dependency scaffolding validates exact sources and preserves unrelated loc
|
||||
});
|
||||
|
||||
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 source = `fragment { conform Game as Playable { private state Board id "slot:board" on Game : string policy 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"'));
|
||||
|
||||
Reference in New Issue
Block a user