Implement main-thread Camino sessions and synchronous CRDT editing
This commit is contained in:
@@ -189,18 +189,22 @@ fragmentVariable : identifier TO identifier SEMI;
|
|||||||
|
|
||||||
packageOperationExport
|
packageOperationExport
|
||||||
: OPERATION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType
|
: OPERATION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType
|
||||||
MODE operationMode executionClause? eventClause? RECEIVER receiverRequirement dependencyBlock? acceptanceClause? SEMI
|
MODE operationMode executionClause? eventClause? RECEIVER receiverRequirement dependencyBlock? preparationClause? acceptanceClause? SEMI
|
||||||
;
|
;
|
||||||
|
|
||||||
packageFunctionExport
|
packageFunctionExport
|
||||||
: FUNCTION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType
|
: FUNCTION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType
|
||||||
executionClause? dependencyBlock? acceptanceClause? SEMI
|
executionClause? dependencyBlock? preparationClause? acceptanceClause? SEMI
|
||||||
;
|
;
|
||||||
|
|
||||||
acceptanceClause
|
acceptanceClause
|
||||||
: ACCEPT USING identifier
|
: ACCEPT USING identifier
|
||||||
;
|
;
|
||||||
|
|
||||||
|
preparationClause
|
||||||
|
: PREPARES CREATION identifier USING stringLiteral
|
||||||
|
;
|
||||||
|
|
||||||
executionClause
|
executionClause
|
||||||
: EXECUTION REMOTE
|
: EXECUTION REMOTE
|
||||||
| EXECUTION (SNAPSHOT | TRANSACTION) TARGETS LBRACK identifierList RBRACK
|
| EXECUTION (SNAPSHOT | TRANSACTION) TARGETS LBRACK identifierList RBRACK
|
||||||
@@ -418,6 +422,7 @@ valueType
|
|||||||
| OPTIONAL LT valueType GT
|
| OPTIONAL LT valueType GT
|
||||||
| LIST LT valueType GT
|
| LIST LT valueType GT
|
||||||
| RECORD LBRACE recordField* RBRACE
|
| RECORD LBRACE recordField* RBRACE
|
||||||
|
| VARIANT ID stringLiteral LBRACE variantCase+ RBRACE
|
||||||
| identifier typeArguments?
|
| identifier typeArguments?
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -425,6 +430,10 @@ recordField
|
|||||||
: identifier COLON valueType SEMI
|
: identifier COLON valueType SEMI
|
||||||
;
|
;
|
||||||
|
|
||||||
|
variantCase
|
||||||
|
: identifier ID stringLiteral COLON valueType SEMI
|
||||||
|
;
|
||||||
|
|
||||||
scalarType
|
scalarType
|
||||||
: BOOL
|
: BOOL
|
||||||
| BYTES
|
| BYTES
|
||||||
@@ -517,6 +526,8 @@ PRESENTATION: 'presentation';
|
|||||||
SUBJECT: 'subject';
|
SUBJECT: 'subject';
|
||||||
FOR: 'for';
|
FOR: 'for';
|
||||||
CREATION: 'creation';
|
CREATION: 'creation';
|
||||||
|
VARIANT: 'variant';
|
||||||
|
PREPARES: 'prepares';
|
||||||
INITIALIZE: 'initialize';
|
INITIALIZE: 'initialize';
|
||||||
WORKSPACE: 'workspace';
|
WORKSPACE: 'workspace';
|
||||||
EXECUTION: 'execution';
|
EXECUTION: 'execution';
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export const generateClientContracts = (
|
|||||||
throw new Error("Host contracts require closed interface applications, not generic definitions");
|
throw new Error("Host contracts require closed interface applications, not generic definitions");
|
||||||
const type = (value: ValueType): string => {
|
const type = (value: ValueType): string => {
|
||||||
switch (value.kind) {
|
switch (value.kind) {
|
||||||
|
case "variant":
|
||||||
|
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||||
case "builtin":
|
case "builtin":
|
||||||
return value.name === "unit" ? "undefined" : "string";
|
return value.name === "unit" ? "undefined" : "string";
|
||||||
case "scalar":
|
case "scalar":
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export const genericImplementationType = (
|
|||||||
const value = (type: ValueTypeExpression, scope: Scope, depth = 0): string => {
|
const value = (type: ValueTypeExpression, scope: Scope, depth = 0): string => {
|
||||||
if (depth > 128) throw new Error("Generic type expansion exceeds depth limit");
|
if (depth > 128) throw new Error("Generic type expansion exceeds depth limit");
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant":
|
||||||
|
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||||
case "parameter": {
|
case "parameter": {
|
||||||
const bound = scope.arguments.get(type.parameterId);
|
const bound = scope.arguments.get(type.parameterId);
|
||||||
if (bound) return bound;
|
if (bound) return bound;
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ export const generateTypeScriptBindings = (
|
|||||||
const messages = new Map<string, string>();
|
const messages = new Map<string, string>();
|
||||||
const type = (value: ValueType): string => {
|
const type = (value: ValueType): string => {
|
||||||
switch (value.kind) {
|
switch (value.kind) {
|
||||||
|
case "variant":
|
||||||
|
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||||
case "builtin":
|
case "builtin":
|
||||||
return value.name === "unit" ? "null" : "QxWatchHandle";
|
return value.name === "unit" ? "null" : "QxWatchHandle";
|
||||||
case "scalar":
|
case "scalar":
|
||||||
@@ -340,6 +342,7 @@ export const generateTypeScriptBindings = (
|
|||||||
// A used port still fails above if its own required codec is missing.
|
// A used port still fails above if its own required codec is missing.
|
||||||
const hasCodec = (value: ValueType): boolean => {
|
const hasCodec = (value: ValueType): boolean => {
|
||||||
if (value.kind === "message") return !!options.messages?.[value.descriptorId];
|
if (value.kind === "message") return !!options.messages?.[value.descriptorId];
|
||||||
|
if (value.kind === "variant") return false;
|
||||||
if (value.kind === "record") return Object.values(value.fields).every(hasCodec);
|
if (value.kind === "record") return Object.values(value.fields).every(hasCodec);
|
||||||
if (value.kind === "list" || value.kind === "optional") return hasCodec(value.value);
|
if (value.kind === "list" || value.kind === "optional") return hasCodec(value.value);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ const expectation = (e: ObjectExpectation) => ({
|
|||||||
/** The protocol IR and replica wire schema intentionally share structure, not reference identity. */
|
/** The protocol IR and replica wire schema intentionally share structure, not reference identity. */
|
||||||
export function portableValueSchema(type: ValueType): unknown {
|
export function portableValueSchema(type: ValueType): unknown {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant":
|
||||||
|
return {
|
||||||
|
kind: "variant",
|
||||||
|
id: type.id,
|
||||||
|
cases: Object.fromEntries(
|
||||||
|
Object.entries(type.cases).map(([label, entry]) => [
|
||||||
|
label,
|
||||||
|
{ id: entry.id, value: portableValueSchema(entry.value) },
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
};
|
||||||
case "change":
|
case "change":
|
||||||
return {
|
return {
|
||||||
kind: "record",
|
kind: "record",
|
||||||
@@ -65,6 +76,10 @@ export function portableValueSchema(type: ValueType): unknown {
|
|||||||
export function tsType(type: ValueType): string {
|
export function tsType(type: ValueType): string {
|
||||||
portableValueSchema(type);
|
portableValueSchema(type);
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant":
|
||||||
|
return `(${Object.entries(type.cases)
|
||||||
|
.map(([label, entry]) => `{ readonly kind: ${q(label)}; readonly value: ${tsType(entry.value)} }`)
|
||||||
|
.join(" | ")})`;
|
||||||
case "change":
|
case "change":
|
||||||
return `PreparedChange<${tsType(type.value)}>`;
|
return `PreparedChange<${tsType(type.value)}>`;
|
||||||
case "scalar":
|
case "scalar":
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export function generateReactBindings(
|
|||||||
const declarations: string[] = [];
|
const declarations: string[] = [];
|
||||||
const type = (value: ValueType): string => {
|
const type = (value: ValueType): string => {
|
||||||
switch (value.kind) {
|
switch (value.kind) {
|
||||||
|
case "variant":
|
||||||
|
throw new Error("Variants require the Camino portable value runtime; legacy RPC bindings are unsupported");
|
||||||
case "builtin":
|
case "builtin":
|
||||||
if (value.name !== "unit") throw new Error(`Unsupported React props builtin ${value.name}`);
|
if (value.name !== "unit") throw new Error(`Unsupported React props builtin ${value.name}`);
|
||||||
return "null";
|
return "null";
|
||||||
@@ -85,6 +87,7 @@ export function generateReactBindings(
|
|||||||
const browserValue = (value: ValueType, seen: Set<string>): boolean => {
|
const browserValue = (value: ValueType, seen: Set<string>): boolean => {
|
||||||
if (value.kind === "message") return false;
|
if (value.kind === "message") return false;
|
||||||
if (value.kind === "builtin") return value.name === "unit";
|
if (value.kind === "builtin") return value.name === "unit";
|
||||||
|
if (value.kind === "variant") return false;
|
||||||
if (value.kind === "record") return Object.values(value.fields).every((field) => browserValue(field, seen));
|
if (value.kind === "record") return Object.values(value.fields).every((field) => browserValue(field, seen));
|
||||||
if (value.kind === "list" || value.kind === "optional") return browserValue(value.value, seen);
|
if (value.kind === "list" || value.kind === "optional") return browserValue(value.value, seen);
|
||||||
if (value.kind === "object-ref" && value.expectation.kind === "interface") {
|
if (value.kind === "object-ref" && value.expectation.kind === "interface") {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,152 +5,154 @@ PRESENTATION=4
|
|||||||
SUBJECT=5
|
SUBJECT=5
|
||||||
FOR=6
|
FOR=6
|
||||||
CREATION=7
|
CREATION=7
|
||||||
INITIALIZE=8
|
VARIANT=8
|
||||||
WORKSPACE=9
|
PREPARES=9
|
||||||
EXECUTION=10
|
INITIALIZE=10
|
||||||
SNAPSHOT=11
|
WORKSPACE=11
|
||||||
TRANSACTION=12
|
EXECUTION=12
|
||||||
REMOTE=13
|
SNAPSHOT=13
|
||||||
TARGETS=14
|
TRANSACTION=14
|
||||||
PORTABLE=15
|
REMOTE=15
|
||||||
REPLICA=16
|
TARGETS=16
|
||||||
LOCAL=17
|
PORTABLE=17
|
||||||
FROM=18
|
REPLICA=18
|
||||||
RESULT_BYTES=19
|
LOCAL=19
|
||||||
RPC_CALLS=20
|
FROM=20
|
||||||
DEADLINE_MS=21
|
RESULT_BYTES=21
|
||||||
QUERY=22
|
RPC_CALLS=22
|
||||||
REFLECTION=23
|
DEADLINE_MS=23
|
||||||
SPECIALIZE=24
|
QUERY=24
|
||||||
ROOT=25
|
REFLECTION=25
|
||||||
DOCUMENT=26
|
SPECIALIZE=26
|
||||||
FRAGMENTS=27
|
ROOT=27
|
||||||
VIEW=28
|
DOCUMENT=28
|
||||||
MAX=29
|
FRAGMENTS=29
|
||||||
ALLOW=30
|
VIEW=30
|
||||||
POLL=31
|
MAX=31
|
||||||
QUERYABLE=32
|
ALLOW=32
|
||||||
RPC=33
|
POLL=33
|
||||||
QUERY_REASON=34
|
QUERYABLE=34
|
||||||
TYPE=35
|
RPC=35
|
||||||
OBJECT=36
|
QUERY_REASON=36
|
||||||
STORABLE=37
|
TYPE=37
|
||||||
IMPLEMENTS=38
|
OBJECT=38
|
||||||
REF=39
|
STORABLE=39
|
||||||
FRAGMENT=40
|
IMPLEMENTS=40
|
||||||
IMPORT=41
|
REF=41
|
||||||
EXTERNAL=42
|
FRAGMENT=42
|
||||||
ATOM=43
|
IMPORT=43
|
||||||
INTERFACE=44
|
EXTERNAL=44
|
||||||
INTERFACES=45
|
ATOM=45
|
||||||
PACKAGE=46
|
INTERFACE=46
|
||||||
VALUE=47
|
INTERFACES=47
|
||||||
RELATION=48
|
PACKAGE=48
|
||||||
OPERATION=49
|
VALUE=49
|
||||||
FUNCTION=50
|
RELATION=50
|
||||||
CONSTRUCTOR=51
|
OPERATION=51
|
||||||
CONSTRUCTS=52
|
FUNCTION=52
|
||||||
INPUT=53
|
CONSTRUCTOR=53
|
||||||
CONFORM=54
|
CONSTRUCTS=54
|
||||||
AS=55
|
INPUT=55
|
||||||
BIND=56
|
CONFORM=56
|
||||||
STATIC=57
|
AS=57
|
||||||
TO=58
|
BIND=58
|
||||||
PRIVATE=59
|
STATIC=59
|
||||||
SHARED=60
|
TO=60
|
||||||
STATE=61
|
PRIVATE=61
|
||||||
EDGE=62
|
SHARED=62
|
||||||
PROJECTION=63
|
STATE=63
|
||||||
WITH=64
|
EDGE=64
|
||||||
USING=65
|
PROJECTION=65
|
||||||
ACCEPT=66
|
WITH=66
|
||||||
VIA=67
|
USING=67
|
||||||
MATERIALIZE=68
|
ACCEPT=68
|
||||||
IF=69
|
VIA=69
|
||||||
ABSENT=70
|
MATERIALIZE=70
|
||||||
ON=71
|
IF=71
|
||||||
POLICY=72
|
ABSENT=72
|
||||||
DEFAULT=73
|
ON=73
|
||||||
SOURCE=74
|
POLICY=74
|
||||||
REPOSITORY=75
|
DEFAULT=75
|
||||||
COMMIT=76
|
SOURCE=76
|
||||||
REVISION=77
|
REPOSITORY=77
|
||||||
SEMANTIC_MAJOR=78
|
COMMIT=78
|
||||||
ON_DELETE=79
|
REVISION=79
|
||||||
LIFECYCLE=80
|
SEMANTIC_MAJOR=80
|
||||||
RETAIN_OTHER=81
|
ON_DELETE=81
|
||||||
KEYED=82
|
LIFECYCLE=82
|
||||||
PUBLIC_TRAVERSAL=83
|
RETAIN_OTHER=83
|
||||||
ID=84
|
KEYED=84
|
||||||
DOC=85
|
PUBLIC_TRAVERSAL=85
|
||||||
MODE=86
|
ID=86
|
||||||
EMITS=87
|
DOC=87
|
||||||
RECEIVER=88
|
MODE=88
|
||||||
REQUIRES=89
|
EMITS=89
|
||||||
ANY=90
|
RECEIVER=90
|
||||||
GET=91
|
REQUIRES=91
|
||||||
SET=92
|
ANY=92
|
||||||
WATCH=93
|
GET=93
|
||||||
START=94
|
SET=94
|
||||||
STOP=95
|
WATCH=95
|
||||||
READ=96
|
START=96
|
||||||
WRITE=97
|
STOP=97
|
||||||
RESOLVE=98
|
READ=98
|
||||||
CONNECT=99
|
WRITE=99
|
||||||
DISCONNECT=100
|
RESOLVE=100
|
||||||
CALL=101
|
CONNECT=101
|
||||||
WATCH_START=102
|
DISCONNECT=102
|
||||||
WATCH_STOP=103
|
CALL=103
|
||||||
SUBSCRIBE=104
|
WATCH_START=104
|
||||||
UNSUBSCRIBE=105
|
WATCH_STOP=105
|
||||||
REGISTER=106
|
SUBSCRIBE=106
|
||||||
CRDT=107
|
UNSUBSCRIBE=107
|
||||||
OPTIONAL_ONE=108
|
REGISTER=108
|
||||||
EXACTLY_ONE=109
|
CRDT=109
|
||||||
MANY_UNIQUE=110
|
OPTIONAL_ONE=110
|
||||||
MANY=111
|
EXACTLY_ONE=111
|
||||||
ORDERED=112
|
MANY_UNIQUE=112
|
||||||
UNIT=113
|
MANY=113
|
||||||
WATCH_HANDLE=114
|
ORDERED=114
|
||||||
MESSAGE=115
|
UNIT=115
|
||||||
ATOM_REF=116
|
WATCH_HANDLE=116
|
||||||
INTERFACE_REF=117
|
MESSAGE=117
|
||||||
OPTIONAL=118
|
ATOM_REF=118
|
||||||
LIST=119
|
INTERFACE_REF=119
|
||||||
RECORD=120
|
OPTIONAL=120
|
||||||
BOOL=121
|
LIST=121
|
||||||
BYTES=122
|
RECORD=122
|
||||||
DOUBLE=123
|
BOOL=123
|
||||||
INT32=124
|
BYTES=124
|
||||||
INT64=125
|
DOUBLE=125
|
||||||
STRING=126
|
INT32=126
|
||||||
UINT32=127
|
INT64=127
|
||||||
UINT64=128
|
STRING=128
|
||||||
TRUE=129
|
UINT32=129
|
||||||
FALSE=130
|
UINT64=130
|
||||||
NULL=131
|
TRUE=131
|
||||||
ARROW=132
|
FALSE=132
|
||||||
COLON=133
|
NULL=133
|
||||||
SEMI=134
|
ARROW=134
|
||||||
COMMA=135
|
COLON=135
|
||||||
DOT=136
|
SEMI=136
|
||||||
LBRACE=137
|
COMMA=137
|
||||||
RBRACE=138
|
DOT=138
|
||||||
LBRACK=139
|
LBRACE=139
|
||||||
RBRACK=140
|
RBRACE=140
|
||||||
LPAREN=141
|
LBRACK=141
|
||||||
RPAREN=142
|
RBRACK=142
|
||||||
LT=143
|
LPAREN=143
|
||||||
GT=144
|
RPAREN=144
|
||||||
AMP=145
|
LT=145
|
||||||
EQUAL=146
|
GT=146
|
||||||
INTEGER=147
|
AMP=147
|
||||||
JSON_NUMBER=148
|
EQUAL=148
|
||||||
IDENTIFIER=149
|
INTEGER=149
|
||||||
STRING_LITERAL=150
|
JSON_NUMBER=150
|
||||||
LINE_COMMENT=151
|
IDENTIFIER=151
|
||||||
BLOCK_COMMENT=152
|
STRING_LITERAL=152
|
||||||
WS=153
|
LINE_COMMENT=153
|
||||||
|
BLOCK_COMMENT=154
|
||||||
|
WS=155
|
||||||
'edit'=1
|
'edit'=1
|
||||||
'module'=2
|
'module'=2
|
||||||
'contract'=3
|
'contract'=3
|
||||||
@@ -158,142 +160,144 @@ WS=153
|
|||||||
'subject'=5
|
'subject'=5
|
||||||
'for'=6
|
'for'=6
|
||||||
'creation'=7
|
'creation'=7
|
||||||
'initialize'=8
|
'variant'=8
|
||||||
'workspace'=9
|
'prepares'=9
|
||||||
'execution'=10
|
'initialize'=10
|
||||||
'snapshot'=11
|
'workspace'=11
|
||||||
'transaction'=12
|
'execution'=12
|
||||||
'remote'=13
|
'snapshot'=13
|
||||||
'targets'=14
|
'transaction'=14
|
||||||
'portable'=15
|
'remote'=15
|
||||||
'replica'=16
|
'targets'=16
|
||||||
'local'=17
|
'portable'=17
|
||||||
'from'=18
|
'replica'=18
|
||||||
'result-bytes'=19
|
'local'=19
|
||||||
'rpc-calls'=20
|
'from'=20
|
||||||
'deadline-ms'=21
|
'result-bytes'=21
|
||||||
'query'=22
|
'rpc-calls'=22
|
||||||
'reflection'=23
|
'deadline-ms'=23
|
||||||
'specialize'=24
|
'query'=24
|
||||||
'root'=25
|
'reflection'=25
|
||||||
'document'=26
|
'specialize'=26
|
||||||
'fragments'=27
|
'root'=27
|
||||||
'view'=28
|
'document'=28
|
||||||
'max'=29
|
'fragments'=29
|
||||||
'allow'=30
|
'view'=30
|
||||||
'poll'=31
|
'max'=31
|
||||||
'queryable'=32
|
'allow'=32
|
||||||
'rpc'=33
|
'poll'=33
|
||||||
'query-reason'=34
|
'queryable'=34
|
||||||
'type'=35
|
'rpc'=35
|
||||||
'object'=36
|
'query-reason'=36
|
||||||
'storable'=37
|
'type'=37
|
||||||
'implements'=38
|
'object'=38
|
||||||
'ref'=39
|
'storable'=39
|
||||||
'fragment'=40
|
'implements'=40
|
||||||
'import'=41
|
'ref'=41
|
||||||
'external'=42
|
'fragment'=42
|
||||||
'atom'=43
|
'import'=43
|
||||||
'interface'=44
|
'external'=44
|
||||||
'interfaces'=45
|
'atom'=45
|
||||||
'package'=46
|
'interface'=46
|
||||||
'value'=47
|
'interfaces'=47
|
||||||
'relation'=48
|
'package'=48
|
||||||
'operation'=49
|
'value'=49
|
||||||
'function'=50
|
'relation'=50
|
||||||
'constructor'=51
|
'operation'=51
|
||||||
'constructs'=52
|
'function'=52
|
||||||
'input'=53
|
'constructor'=53
|
||||||
'conform'=54
|
'constructs'=54
|
||||||
'as'=55
|
'input'=55
|
||||||
'bind'=56
|
'conform'=56
|
||||||
'static'=57
|
'as'=57
|
||||||
'to'=58
|
'bind'=58
|
||||||
'private'=59
|
'static'=59
|
||||||
'shared'=60
|
'to'=60
|
||||||
'state'=61
|
'private'=61
|
||||||
'edge'=62
|
'shared'=62
|
||||||
'projection'=63
|
'state'=63
|
||||||
'with'=64
|
'edge'=64
|
||||||
'using'=65
|
'projection'=65
|
||||||
'accept'=66
|
'with'=66
|
||||||
'via'=67
|
'using'=67
|
||||||
'materialize'=68
|
'accept'=68
|
||||||
'if'=69
|
'via'=69
|
||||||
'absent'=70
|
'materialize'=70
|
||||||
'on'=71
|
'if'=71
|
||||||
'policy'=72
|
'absent'=72
|
||||||
'default'=73
|
'on'=73
|
||||||
'source'=74
|
'policy'=74
|
||||||
'repository'=75
|
'default'=75
|
||||||
'commit'=76
|
'source'=76
|
||||||
'revision'=77
|
'repository'=77
|
||||||
'semantic-major'=78
|
'commit'=78
|
||||||
'on-delete'=79
|
'revision'=79
|
||||||
'lifecycle'=80
|
'semantic-major'=80
|
||||||
'retain-other'=81
|
'on-delete'=81
|
||||||
'keyed'=82
|
'lifecycle'=82
|
||||||
'public-traversal'=83
|
'retain-other'=83
|
||||||
'id'=84
|
'keyed'=84
|
||||||
'doc'=85
|
'public-traversal'=85
|
||||||
'mode'=86
|
'id'=86
|
||||||
'emits'=87
|
'doc'=87
|
||||||
'receiver'=88
|
'mode'=88
|
||||||
'requires'=89
|
'emits'=89
|
||||||
'any'=90
|
'receiver'=90
|
||||||
'get'=91
|
'requires'=91
|
||||||
'set'=92
|
'any'=92
|
||||||
'watch'=93
|
'get'=93
|
||||||
'start'=94
|
'set'=94
|
||||||
'stop'=95
|
'watch'=95
|
||||||
'read'=96
|
'start'=96
|
||||||
'write'=97
|
'stop'=97
|
||||||
'resolve'=98
|
'read'=98
|
||||||
'connect'=99
|
'write'=99
|
||||||
'disconnect'=100
|
'resolve'=100
|
||||||
'call'=101
|
'connect'=101
|
||||||
'watch-start'=102
|
'disconnect'=102
|
||||||
'watch-stop'=103
|
'call'=103
|
||||||
'subscribe'=104
|
'watch-start'=104
|
||||||
'unsubscribe'=105
|
'watch-stop'=105
|
||||||
'register'=106
|
'subscribe'=106
|
||||||
'crdt'=107
|
'unsubscribe'=107
|
||||||
'optional-one'=108
|
'register'=108
|
||||||
'exactly-one'=109
|
'crdt'=109
|
||||||
'many-unique'=110
|
'optional-one'=110
|
||||||
'many'=111
|
'exactly-one'=111
|
||||||
'ordered'=112
|
'many-unique'=112
|
||||||
'unit'=113
|
'many'=113
|
||||||
'watch-handle'=114
|
'ordered'=114
|
||||||
'message'=115
|
'unit'=115
|
||||||
'atom-ref'=116
|
'watch-handle'=116
|
||||||
'interface-ref'=117
|
'message'=117
|
||||||
'optional'=118
|
'atom-ref'=118
|
||||||
'list'=119
|
'interface-ref'=119
|
||||||
'record'=120
|
'optional'=120
|
||||||
'bool'=121
|
'list'=121
|
||||||
'bytes'=122
|
'record'=122
|
||||||
'double'=123
|
'bool'=123
|
||||||
'int32'=124
|
'bytes'=124
|
||||||
'int64'=125
|
'double'=125
|
||||||
'string'=126
|
'int32'=126
|
||||||
'uint32'=127
|
'int64'=127
|
||||||
'uint64'=128
|
'string'=128
|
||||||
'true'=129
|
'uint32'=129
|
||||||
'false'=130
|
'uint64'=130
|
||||||
'null'=131
|
'true'=131
|
||||||
'->'=132
|
'false'=132
|
||||||
':'=133
|
'null'=133
|
||||||
';'=134
|
'->'=134
|
||||||
','=135
|
':'=135
|
||||||
'.'=136
|
';'=136
|
||||||
'{'=137
|
','=137
|
||||||
'}'=138
|
'.'=138
|
||||||
'['=139
|
'{'=139
|
||||||
']'=140
|
'}'=140
|
||||||
'('=141
|
'['=141
|
||||||
')'=142
|
']'=142
|
||||||
'<'=143
|
'('=143
|
||||||
'>'=144
|
')'=144
|
||||||
'&'=145
|
'<'=145
|
||||||
'='=146
|
'>'=146
|
||||||
|
'&'=147
|
||||||
|
'='=148
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,152 +5,154 @@ PRESENTATION=4
|
|||||||
SUBJECT=5
|
SUBJECT=5
|
||||||
FOR=6
|
FOR=6
|
||||||
CREATION=7
|
CREATION=7
|
||||||
INITIALIZE=8
|
VARIANT=8
|
||||||
WORKSPACE=9
|
PREPARES=9
|
||||||
EXECUTION=10
|
INITIALIZE=10
|
||||||
SNAPSHOT=11
|
WORKSPACE=11
|
||||||
TRANSACTION=12
|
EXECUTION=12
|
||||||
REMOTE=13
|
SNAPSHOT=13
|
||||||
TARGETS=14
|
TRANSACTION=14
|
||||||
PORTABLE=15
|
REMOTE=15
|
||||||
REPLICA=16
|
TARGETS=16
|
||||||
LOCAL=17
|
PORTABLE=17
|
||||||
FROM=18
|
REPLICA=18
|
||||||
RESULT_BYTES=19
|
LOCAL=19
|
||||||
RPC_CALLS=20
|
FROM=20
|
||||||
DEADLINE_MS=21
|
RESULT_BYTES=21
|
||||||
QUERY=22
|
RPC_CALLS=22
|
||||||
REFLECTION=23
|
DEADLINE_MS=23
|
||||||
SPECIALIZE=24
|
QUERY=24
|
||||||
ROOT=25
|
REFLECTION=25
|
||||||
DOCUMENT=26
|
SPECIALIZE=26
|
||||||
FRAGMENTS=27
|
ROOT=27
|
||||||
VIEW=28
|
DOCUMENT=28
|
||||||
MAX=29
|
FRAGMENTS=29
|
||||||
ALLOW=30
|
VIEW=30
|
||||||
POLL=31
|
MAX=31
|
||||||
QUERYABLE=32
|
ALLOW=32
|
||||||
RPC=33
|
POLL=33
|
||||||
QUERY_REASON=34
|
QUERYABLE=34
|
||||||
TYPE=35
|
RPC=35
|
||||||
OBJECT=36
|
QUERY_REASON=36
|
||||||
STORABLE=37
|
TYPE=37
|
||||||
IMPLEMENTS=38
|
OBJECT=38
|
||||||
REF=39
|
STORABLE=39
|
||||||
FRAGMENT=40
|
IMPLEMENTS=40
|
||||||
IMPORT=41
|
REF=41
|
||||||
EXTERNAL=42
|
FRAGMENT=42
|
||||||
ATOM=43
|
IMPORT=43
|
||||||
INTERFACE=44
|
EXTERNAL=44
|
||||||
INTERFACES=45
|
ATOM=45
|
||||||
PACKAGE=46
|
INTERFACE=46
|
||||||
VALUE=47
|
INTERFACES=47
|
||||||
RELATION=48
|
PACKAGE=48
|
||||||
OPERATION=49
|
VALUE=49
|
||||||
FUNCTION=50
|
RELATION=50
|
||||||
CONSTRUCTOR=51
|
OPERATION=51
|
||||||
CONSTRUCTS=52
|
FUNCTION=52
|
||||||
INPUT=53
|
CONSTRUCTOR=53
|
||||||
CONFORM=54
|
CONSTRUCTS=54
|
||||||
AS=55
|
INPUT=55
|
||||||
BIND=56
|
CONFORM=56
|
||||||
STATIC=57
|
AS=57
|
||||||
TO=58
|
BIND=58
|
||||||
PRIVATE=59
|
STATIC=59
|
||||||
SHARED=60
|
TO=60
|
||||||
STATE=61
|
PRIVATE=61
|
||||||
EDGE=62
|
SHARED=62
|
||||||
PROJECTION=63
|
STATE=63
|
||||||
WITH=64
|
EDGE=64
|
||||||
USING=65
|
PROJECTION=65
|
||||||
ACCEPT=66
|
WITH=66
|
||||||
VIA=67
|
USING=67
|
||||||
MATERIALIZE=68
|
ACCEPT=68
|
||||||
IF=69
|
VIA=69
|
||||||
ABSENT=70
|
MATERIALIZE=70
|
||||||
ON=71
|
IF=71
|
||||||
POLICY=72
|
ABSENT=72
|
||||||
DEFAULT=73
|
ON=73
|
||||||
SOURCE=74
|
POLICY=74
|
||||||
REPOSITORY=75
|
DEFAULT=75
|
||||||
COMMIT=76
|
SOURCE=76
|
||||||
REVISION=77
|
REPOSITORY=77
|
||||||
SEMANTIC_MAJOR=78
|
COMMIT=78
|
||||||
ON_DELETE=79
|
REVISION=79
|
||||||
LIFECYCLE=80
|
SEMANTIC_MAJOR=80
|
||||||
RETAIN_OTHER=81
|
ON_DELETE=81
|
||||||
KEYED=82
|
LIFECYCLE=82
|
||||||
PUBLIC_TRAVERSAL=83
|
RETAIN_OTHER=83
|
||||||
ID=84
|
KEYED=84
|
||||||
DOC=85
|
PUBLIC_TRAVERSAL=85
|
||||||
MODE=86
|
ID=86
|
||||||
EMITS=87
|
DOC=87
|
||||||
RECEIVER=88
|
MODE=88
|
||||||
REQUIRES=89
|
EMITS=89
|
||||||
ANY=90
|
RECEIVER=90
|
||||||
GET=91
|
REQUIRES=91
|
||||||
SET=92
|
ANY=92
|
||||||
WATCH=93
|
GET=93
|
||||||
START=94
|
SET=94
|
||||||
STOP=95
|
WATCH=95
|
||||||
READ=96
|
START=96
|
||||||
WRITE=97
|
STOP=97
|
||||||
RESOLVE=98
|
READ=98
|
||||||
CONNECT=99
|
WRITE=99
|
||||||
DISCONNECT=100
|
RESOLVE=100
|
||||||
CALL=101
|
CONNECT=101
|
||||||
WATCH_START=102
|
DISCONNECT=102
|
||||||
WATCH_STOP=103
|
CALL=103
|
||||||
SUBSCRIBE=104
|
WATCH_START=104
|
||||||
UNSUBSCRIBE=105
|
WATCH_STOP=105
|
||||||
REGISTER=106
|
SUBSCRIBE=106
|
||||||
CRDT=107
|
UNSUBSCRIBE=107
|
||||||
OPTIONAL_ONE=108
|
REGISTER=108
|
||||||
EXACTLY_ONE=109
|
CRDT=109
|
||||||
MANY_UNIQUE=110
|
OPTIONAL_ONE=110
|
||||||
MANY=111
|
EXACTLY_ONE=111
|
||||||
ORDERED=112
|
MANY_UNIQUE=112
|
||||||
UNIT=113
|
MANY=113
|
||||||
WATCH_HANDLE=114
|
ORDERED=114
|
||||||
MESSAGE=115
|
UNIT=115
|
||||||
ATOM_REF=116
|
WATCH_HANDLE=116
|
||||||
INTERFACE_REF=117
|
MESSAGE=117
|
||||||
OPTIONAL=118
|
ATOM_REF=118
|
||||||
LIST=119
|
INTERFACE_REF=119
|
||||||
RECORD=120
|
OPTIONAL=120
|
||||||
BOOL=121
|
LIST=121
|
||||||
BYTES=122
|
RECORD=122
|
||||||
DOUBLE=123
|
BOOL=123
|
||||||
INT32=124
|
BYTES=124
|
||||||
INT64=125
|
DOUBLE=125
|
||||||
STRING=126
|
INT32=126
|
||||||
UINT32=127
|
INT64=127
|
||||||
UINT64=128
|
STRING=128
|
||||||
TRUE=129
|
UINT32=129
|
||||||
FALSE=130
|
UINT64=130
|
||||||
NULL=131
|
TRUE=131
|
||||||
ARROW=132
|
FALSE=132
|
||||||
COLON=133
|
NULL=133
|
||||||
SEMI=134
|
ARROW=134
|
||||||
COMMA=135
|
COLON=135
|
||||||
DOT=136
|
SEMI=136
|
||||||
LBRACE=137
|
COMMA=137
|
||||||
RBRACE=138
|
DOT=138
|
||||||
LBRACK=139
|
LBRACE=139
|
||||||
RBRACK=140
|
RBRACE=140
|
||||||
LPAREN=141
|
LBRACK=141
|
||||||
RPAREN=142
|
RBRACK=142
|
||||||
LT=143
|
LPAREN=143
|
||||||
GT=144
|
RPAREN=144
|
||||||
AMP=145
|
LT=145
|
||||||
EQUAL=146
|
GT=146
|
||||||
INTEGER=147
|
AMP=147
|
||||||
JSON_NUMBER=148
|
EQUAL=148
|
||||||
IDENTIFIER=149
|
INTEGER=149
|
||||||
STRING_LITERAL=150
|
JSON_NUMBER=150
|
||||||
LINE_COMMENT=151
|
IDENTIFIER=151
|
||||||
BLOCK_COMMENT=152
|
STRING_LITERAL=152
|
||||||
WS=153
|
LINE_COMMENT=153
|
||||||
|
BLOCK_COMMENT=154
|
||||||
|
WS=155
|
||||||
'edit'=1
|
'edit'=1
|
||||||
'module'=2
|
'module'=2
|
||||||
'contract'=3
|
'contract'=3
|
||||||
@@ -158,142 +160,144 @@ WS=153
|
|||||||
'subject'=5
|
'subject'=5
|
||||||
'for'=6
|
'for'=6
|
||||||
'creation'=7
|
'creation'=7
|
||||||
'initialize'=8
|
'variant'=8
|
||||||
'workspace'=9
|
'prepares'=9
|
||||||
'execution'=10
|
'initialize'=10
|
||||||
'snapshot'=11
|
'workspace'=11
|
||||||
'transaction'=12
|
'execution'=12
|
||||||
'remote'=13
|
'snapshot'=13
|
||||||
'targets'=14
|
'transaction'=14
|
||||||
'portable'=15
|
'remote'=15
|
||||||
'replica'=16
|
'targets'=16
|
||||||
'local'=17
|
'portable'=17
|
||||||
'from'=18
|
'replica'=18
|
||||||
'result-bytes'=19
|
'local'=19
|
||||||
'rpc-calls'=20
|
'from'=20
|
||||||
'deadline-ms'=21
|
'result-bytes'=21
|
||||||
'query'=22
|
'rpc-calls'=22
|
||||||
'reflection'=23
|
'deadline-ms'=23
|
||||||
'specialize'=24
|
'query'=24
|
||||||
'root'=25
|
'reflection'=25
|
||||||
'document'=26
|
'specialize'=26
|
||||||
'fragments'=27
|
'root'=27
|
||||||
'view'=28
|
'document'=28
|
||||||
'max'=29
|
'fragments'=29
|
||||||
'allow'=30
|
'view'=30
|
||||||
'poll'=31
|
'max'=31
|
||||||
'queryable'=32
|
'allow'=32
|
||||||
'rpc'=33
|
'poll'=33
|
||||||
'query-reason'=34
|
'queryable'=34
|
||||||
'type'=35
|
'rpc'=35
|
||||||
'object'=36
|
'query-reason'=36
|
||||||
'storable'=37
|
'type'=37
|
||||||
'implements'=38
|
'object'=38
|
||||||
'ref'=39
|
'storable'=39
|
||||||
'fragment'=40
|
'implements'=40
|
||||||
'import'=41
|
'ref'=41
|
||||||
'external'=42
|
'fragment'=42
|
||||||
'atom'=43
|
'import'=43
|
||||||
'interface'=44
|
'external'=44
|
||||||
'interfaces'=45
|
'atom'=45
|
||||||
'package'=46
|
'interface'=46
|
||||||
'value'=47
|
'interfaces'=47
|
||||||
'relation'=48
|
'package'=48
|
||||||
'operation'=49
|
'value'=49
|
||||||
'function'=50
|
'relation'=50
|
||||||
'constructor'=51
|
'operation'=51
|
||||||
'constructs'=52
|
'function'=52
|
||||||
'input'=53
|
'constructor'=53
|
||||||
'conform'=54
|
'constructs'=54
|
||||||
'as'=55
|
'input'=55
|
||||||
'bind'=56
|
'conform'=56
|
||||||
'static'=57
|
'as'=57
|
||||||
'to'=58
|
'bind'=58
|
||||||
'private'=59
|
'static'=59
|
||||||
'shared'=60
|
'to'=60
|
||||||
'state'=61
|
'private'=61
|
||||||
'edge'=62
|
'shared'=62
|
||||||
'projection'=63
|
'state'=63
|
||||||
'with'=64
|
'edge'=64
|
||||||
'using'=65
|
'projection'=65
|
||||||
'accept'=66
|
'with'=66
|
||||||
'via'=67
|
'using'=67
|
||||||
'materialize'=68
|
'accept'=68
|
||||||
'if'=69
|
'via'=69
|
||||||
'absent'=70
|
'materialize'=70
|
||||||
'on'=71
|
'if'=71
|
||||||
'policy'=72
|
'absent'=72
|
||||||
'default'=73
|
'on'=73
|
||||||
'source'=74
|
'policy'=74
|
||||||
'repository'=75
|
'default'=75
|
||||||
'commit'=76
|
'source'=76
|
||||||
'revision'=77
|
'repository'=77
|
||||||
'semantic-major'=78
|
'commit'=78
|
||||||
'on-delete'=79
|
'revision'=79
|
||||||
'lifecycle'=80
|
'semantic-major'=80
|
||||||
'retain-other'=81
|
'on-delete'=81
|
||||||
'keyed'=82
|
'lifecycle'=82
|
||||||
'public-traversal'=83
|
'retain-other'=83
|
||||||
'id'=84
|
'keyed'=84
|
||||||
'doc'=85
|
'public-traversal'=85
|
||||||
'mode'=86
|
'id'=86
|
||||||
'emits'=87
|
'doc'=87
|
||||||
'receiver'=88
|
'mode'=88
|
||||||
'requires'=89
|
'emits'=89
|
||||||
'any'=90
|
'receiver'=90
|
||||||
'get'=91
|
'requires'=91
|
||||||
'set'=92
|
'any'=92
|
||||||
'watch'=93
|
'get'=93
|
||||||
'start'=94
|
'set'=94
|
||||||
'stop'=95
|
'watch'=95
|
||||||
'read'=96
|
'start'=96
|
||||||
'write'=97
|
'stop'=97
|
||||||
'resolve'=98
|
'read'=98
|
||||||
'connect'=99
|
'write'=99
|
||||||
'disconnect'=100
|
'resolve'=100
|
||||||
'call'=101
|
'connect'=101
|
||||||
'watch-start'=102
|
'disconnect'=102
|
||||||
'watch-stop'=103
|
'call'=103
|
||||||
'subscribe'=104
|
'watch-start'=104
|
||||||
'unsubscribe'=105
|
'watch-stop'=105
|
||||||
'register'=106
|
'subscribe'=106
|
||||||
'crdt'=107
|
'unsubscribe'=107
|
||||||
'optional-one'=108
|
'register'=108
|
||||||
'exactly-one'=109
|
'crdt'=109
|
||||||
'many-unique'=110
|
'optional-one'=110
|
||||||
'many'=111
|
'exactly-one'=111
|
||||||
'ordered'=112
|
'many-unique'=112
|
||||||
'unit'=113
|
'many'=113
|
||||||
'watch-handle'=114
|
'ordered'=114
|
||||||
'message'=115
|
'unit'=115
|
||||||
'atom-ref'=116
|
'watch-handle'=116
|
||||||
'interface-ref'=117
|
'message'=117
|
||||||
'optional'=118
|
'atom-ref'=118
|
||||||
'list'=119
|
'interface-ref'=119
|
||||||
'record'=120
|
'optional'=120
|
||||||
'bool'=121
|
'list'=121
|
||||||
'bytes'=122
|
'record'=122
|
||||||
'double'=123
|
'bool'=123
|
||||||
'int32'=124
|
'bytes'=124
|
||||||
'int64'=125
|
'double'=125
|
||||||
'string'=126
|
'int32'=126
|
||||||
'uint32'=127
|
'int64'=127
|
||||||
'uint64'=128
|
'string'=128
|
||||||
'true'=129
|
'uint32'=129
|
||||||
'false'=130
|
'uint64'=130
|
||||||
'null'=131
|
'true'=131
|
||||||
'->'=132
|
'false'=132
|
||||||
':'=133
|
'null'=133
|
||||||
';'=134
|
'->'=134
|
||||||
','=135
|
':'=135
|
||||||
'.'=136
|
';'=136
|
||||||
'{'=137
|
','=137
|
||||||
'}'=138
|
'.'=138
|
||||||
'['=139
|
'{'=139
|
||||||
']'=140
|
'}'=140
|
||||||
'('=141
|
'['=141
|
||||||
')'=142
|
']'=142
|
||||||
'<'=143
|
'('=143
|
||||||
'>'=144
|
')'=144
|
||||||
'&'=145
|
'<'=145
|
||||||
'='=146
|
'>'=146
|
||||||
|
'&'=147
|
||||||
|
'='=148
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,7 @@ import { FragmentVariableContext } from "./QuixosCapabilityParser.js";
|
|||||||
import { PackageOperationExportContext } from "./QuixosCapabilityParser.js";
|
import { PackageOperationExportContext } from "./QuixosCapabilityParser.js";
|
||||||
import { PackageFunctionExportContext } from "./QuixosCapabilityParser.js";
|
import { PackageFunctionExportContext } from "./QuixosCapabilityParser.js";
|
||||||
import { AcceptanceClauseContext } from "./QuixosCapabilityParser.js";
|
import { AcceptanceClauseContext } from "./QuixosCapabilityParser.js";
|
||||||
|
import { PreparationClauseContext } from "./QuixosCapabilityParser.js";
|
||||||
import { ExecutionClauseContext } from "./QuixosCapabilityParser.js";
|
import { ExecutionClauseContext } from "./QuixosCapabilityParser.js";
|
||||||
import { SelectedOperationsContext } from "./QuixosCapabilityParser.js";
|
import { SelectedOperationsContext } from "./QuixosCapabilityParser.js";
|
||||||
import { PackageConstructorExportContext } from "./QuixosCapabilityParser.js";
|
import { PackageConstructorExportContext } from "./QuixosCapabilityParser.js";
|
||||||
@@ -77,6 +78,7 @@ import { DependencyBindingContext } from "./QuixosCapabilityParser.js";
|
|||||||
import { ConstructorBindingDeclContext } from "./QuixosCapabilityParser.js";
|
import { ConstructorBindingDeclContext } from "./QuixosCapabilityParser.js";
|
||||||
import { ValueTypeContext } from "./QuixosCapabilityParser.js";
|
import { ValueTypeContext } from "./QuixosCapabilityParser.js";
|
||||||
import { RecordFieldContext } from "./QuixosCapabilityParser.js";
|
import { RecordFieldContext } from "./QuixosCapabilityParser.js";
|
||||||
|
import { VariantCaseContext } from "./QuixosCapabilityParser.js";
|
||||||
import { ScalarTypeContext } from "./QuixosCapabilityParser.js";
|
import { ScalarTypeContext } from "./QuixosCapabilityParser.js";
|
||||||
import { CardinalityContext } from "./QuixosCapabilityParser.js";
|
import { CardinalityContext } from "./QuixosCapabilityParser.js";
|
||||||
import { JsonLiteralContext } from "./QuixosCapabilityParser.js";
|
import { JsonLiteralContext } from "./QuixosCapabilityParser.js";
|
||||||
@@ -317,6 +319,12 @@ export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Re
|
|||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
visitAcceptanceClause?: (ctx: AcceptanceClauseContext) => Result;
|
visitAcceptanceClause?: (ctx: AcceptanceClauseContext) => Result;
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by `QuixosCapabilityParser.preparationClause`.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
visitPreparationClause?: (ctx: PreparationClauseContext) => Result;
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by `QuixosCapabilityParser.executionClause`.
|
* Visit a parse tree produced by `QuixosCapabilityParser.executionClause`.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
@@ -545,6 +553,12 @@ export class QuixosCapabilityVisitor<Result> extends AbstractParseTreeVisitor<Re
|
|||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
visitRecordField?: (ctx: RecordFieldContext) => Result;
|
visitRecordField?: (ctx: RecordFieldContext) => Result;
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by `QuixosCapabilityParser.variantCase`.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
visitVariantCase?: (ctx: VariantCaseContext) => Result;
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by `QuixosCapabilityParser.scalarType`.
|
* Visit a parse tree produced by `QuixosCapabilityParser.scalarType`.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export class GenericSourceTypes {
|
|||||||
}
|
}
|
||||||
if (type.kind === "list" || type.kind === "optional") return storable(type.value, depth + 1);
|
if (type.kind === "list" || type.kind === "optional") return storable(type.value, depth + 1);
|
||||||
if (type.kind === "record") return Object.values(type.fields).every((field) => storable(field, depth + 1));
|
if (type.kind === "record") return Object.values(type.fields).every((field) => storable(field, depth + 1));
|
||||||
|
if (type.kind === "variant") return Object.values(type.cases).every((entry) => storable(entry.value, depth + 1));
|
||||||
if (type.kind === "alias") {
|
if (type.kind === "alias") {
|
||||||
const alias = aliases.get(type.definitionId);
|
const alias = aliases.get(type.definitionId);
|
||||||
if (!alias || alias.parameters.length !== type.arguments.length) return false;
|
if (!alias || alias.parameters.length !== type.arguments.length) return false;
|
||||||
@@ -301,6 +302,24 @@ export class GenericSourceTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expression(context: ValueTypeContext): ValueTypeExpression {
|
expression(context: ValueTypeContext): ValueTypeExpression {
|
||||||
|
if (context.VARIANT()) {
|
||||||
|
const entries = context.variantCase().map(
|
||||||
|
(entry) =>
|
||||||
|
[
|
||||||
|
entry.identifier().getText(),
|
||||||
|
{
|
||||||
|
id: literal(entry.stringLiteral()),
|
||||||
|
value: this.expression(entry.valueType()),
|
||||||
|
},
|
||||||
|
] as const,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
new Set(entries.map(([label]) => label)).size !== entries.length ||
|
||||||
|
new Set(entries.map(([, entry]) => entry.id)).size !== entries.length
|
||||||
|
)
|
||||||
|
throw new GenericTypeError("duplicate-variant-case", "variant", "Variant labels and case IDs must be unique");
|
||||||
|
return { kind: "variant", id: literal(context.stringLiteral()!), cases: Object.fromEntries(entries) };
|
||||||
|
}
|
||||||
if (context.scalarType())
|
if (context.scalarType())
|
||||||
return {
|
return {
|
||||||
kind: "scalar",
|
kind: "scalar",
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ const lowerValueType = (state: LoweringState, context: ValueTypeContext | null):
|
|||||||
throw new Error("Missing value type after a successful parse");
|
throw new Error("Missing value type after a successful parse");
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
context.VARIANT() ||
|
||||||
context.REF() ||
|
context.REF() ||
|
||||||
context.typeArguments() ||
|
context.typeArguments() ||
|
||||||
(context.identifier() && !context.ATOM_REF() && !context.INTERFACE_REF())
|
(context.identifier() && !context.ATOM_REF() && !context.INTERFACE_REF())
|
||||||
@@ -1658,6 +1659,13 @@ const lowerPackage = (
|
|||||||
const operation = exportContext.packageOperationExport();
|
const operation = exportContext.packageOperationExport();
|
||||||
const generic = operation ?? exportContext.packageFunctionExport();
|
const generic = operation ?? exportContext.packageFunctionExport();
|
||||||
if (generic?.typeParameters()) {
|
if (generic?.typeParameters()) {
|
||||||
|
if (generic.preparationClause())
|
||||||
|
loweringIssue(
|
||||||
|
state,
|
||||||
|
generic,
|
||||||
|
"unsupported-generic-preparation",
|
||||||
|
"Creation preparation currently requires a closed export",
|
||||||
|
);
|
||||||
if (generic.acceptanceClause())
|
if (generic.acceptanceClause())
|
||||||
loweringIssue(
|
loweringIssue(
|
||||||
state,
|
state,
|
||||||
@@ -1689,6 +1697,19 @@ const lowerPackage = (
|
|||||||
// Resolve after all exports have been declared, so checks can follow bodies.
|
// Resolve after all exports have been declared, so checks can follow bodies.
|
||||||
for (const source of context.packageExport()) {
|
for (const source of context.packageExport()) {
|
||||||
const declaration = source.packageOperationExport() ?? source.packageFunctionExport();
|
const declaration = source.packageOperationExport() ?? source.packageFunctionExport();
|
||||||
|
const preparation = declaration?.preparationClause();
|
||||||
|
if (declaration && preparation && !declaration.typeParameters()) {
|
||||||
|
const entry = exports.find((entry) => entry.displayName === identifier(declaration.identifier()));
|
||||||
|
const port = entry?.dependencyPorts.find((port) => port.displayName === identifier(preparation.identifier()));
|
||||||
|
if (!entry || port?.requirement.kind !== "creation")
|
||||||
|
loweringIssue(
|
||||||
|
state,
|
||||||
|
preparation,
|
||||||
|
"invalid-creation-preparation",
|
||||||
|
"Preparation must name a creation port of this export",
|
||||||
|
);
|
||||||
|
else entry.preparation = { port: port.id, key: stringValue(preparation.stringLiteral()) };
|
||||||
|
}
|
||||||
const clause = declaration?.acceptanceClause();
|
const clause = declaration?.acceptanceClause();
|
||||||
if (!declaration || !clause || declaration.typeParameters()) continue;
|
if (!declaration || !clause || declaration.typeParameters()) continue;
|
||||||
const entry = exports.find((entry) => entry.displayName === identifier(declaration.identifier()));
|
const entry = exports.find((entry) => entry.displayName === identifier(declaration.identifier()));
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type ValueTypeExpression =
|
|||||||
| Extract<ValueType, { kind: "builtin" | "scalar" | "message" }>
|
| Extract<ValueType, { kind: "builtin" | "scalar" | "message" }>
|
||||||
| { kind: "parameter"; parameterId: string }
|
| { kind: "parameter"; parameterId: string }
|
||||||
| { kind: "record"; fields: Record<string, ValueTypeExpression> }
|
| { kind: "record"; fields: Record<string, ValueTypeExpression> }
|
||||||
|
| { kind: "variant"; id: string; cases: Record<string, { id: string; value: ValueTypeExpression }> }
|
||||||
| { kind: "list" | "optional" | "change"; value: ValueTypeExpression }
|
| { kind: "list" | "optional" | "change"; value: ValueTypeExpression }
|
||||||
| { kind: "object-ref"; expectation: ObjectTypeExpression }
|
| { kind: "object-ref"; expectation: ObjectTypeExpression }
|
||||||
| { kind: "alias"; definitionId: string; arguments: TypeArgumentExpression[] };
|
| { kind: "alias"; definitionId: string; arguments: TypeArgumentExpression[] };
|
||||||
@@ -195,6 +196,8 @@ export const isStorableType = (
|
|||||||
return false;
|
return false;
|
||||||
case "record":
|
case "record":
|
||||||
return Object.values(value.fields).every((field) => visit(field, depth + 1));
|
return Object.values(value.fields).every((field) => visit(field, depth + 1));
|
||||||
|
case "variant":
|
||||||
|
return Object.values(value.cases).every((entry) => visit(entry.value, depth + 1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return visit(type, 0);
|
return visit(type, 0);
|
||||||
@@ -221,6 +224,14 @@ export const canonicalTypeArgument = (argument: ClosedTypeArgument): string => {
|
|||||||
return [value.kind, value.name];
|
return [value.kind, value.name];
|
||||||
case "message":
|
case "message":
|
||||||
return ["message", value.descriptorId];
|
return ["message", value.descriptorId];
|
||||||
|
case "variant":
|
||||||
|
return [
|
||||||
|
"variant",
|
||||||
|
value.id,
|
||||||
|
Object.keys(value.cases)
|
||||||
|
.sort()
|
||||||
|
.map((label) => [label, value.cases[label].id, type(value.cases[label].value, depth + 1)]),
|
||||||
|
];
|
||||||
case "object-ref":
|
case "object-ref":
|
||||||
return ["object-ref", target(value.expectation)];
|
return ["object-ref", target(value.expectation)];
|
||||||
case "change":
|
case "change":
|
||||||
@@ -323,6 +334,17 @@ export class TypeSubstitution {
|
|||||||
value(expression: ValueTypeExpression, path = "type", depth = 0): ValueType {
|
value(expression: ValueTypeExpression, path = "type", depth = 0): ValueType {
|
||||||
this.budget.enter(path, depth);
|
this.budget.enter(path, depth);
|
||||||
switch (expression.kind) {
|
switch (expression.kind) {
|
||||||
|
case "variant":
|
||||||
|
return {
|
||||||
|
kind: "variant",
|
||||||
|
id: expression.id,
|
||||||
|
cases: Object.fromEntries(
|
||||||
|
Object.entries(expression.cases).map(([label, entry]) => [
|
||||||
|
label,
|
||||||
|
{ id: entry.id, value: this.value(entry.value, `${path}.${label}`, depth + 1) },
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
};
|
||||||
case "builtin":
|
case "builtin":
|
||||||
return { kind: "builtin", name: expression.name };
|
return { kind: "builtin", name: expression.name };
|
||||||
case "scalar":
|
case "scalar":
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export type ValueType =
|
|||||||
| { kind: "scalar"; name: ScalarValueTypeName }
|
| { kind: "scalar"; name: ScalarValueTypeName }
|
||||||
| { kind: "message"; descriptorId: string }
|
| { kind: "message"; descriptorId: string }
|
||||||
| { kind: "record"; fields: Record<string, ValueType> }
|
| { kind: "record"; fields: Record<string, ValueType> }
|
||||||
|
| { kind: "variant"; id: string; cases: Record<string, { id: string; value: ValueType }> }
|
||||||
| { kind: "object-ref"; expectation: ObjectExpectation }
|
| { kind: "object-ref"; expectation: ObjectExpectation }
|
||||||
| { kind: "optional"; value: ValueType }
|
| { kind: "optional"; value: ValueType }
|
||||||
| { kind: "list"; value: ValueType }
|
| { kind: "list"; value: ValueType }
|
||||||
@@ -270,6 +271,8 @@ export interface DependencyPort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PackageExportBase {
|
interface PackageExportBase {
|
||||||
|
/** Returns this declared birth, with unchanged operation input as its initializer. */
|
||||||
|
preparation?: { port: DependencyPortId; key: string };
|
||||||
/** Required authoritative snapshot export in this package. */
|
/** Required authoritative snapshot export in this package. */
|
||||||
acceptanceExportId?: PackageExportId;
|
acceptanceExportId?: PackageExportId;
|
||||||
execution?: ExecutionContract;
|
execution?: ExecutionContract;
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ const requireText = (issues: CapabilityValidationIssue[], value: string, path: s
|
|||||||
|
|
||||||
const typeLabel = (type: ValueType): string => {
|
const typeLabel = (type: ValueType): string => {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant":
|
||||||
|
return `variant:${type.id}{${Object.entries(type.cases)
|
||||||
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
|
.map(([label, entry]) => `${label}:${entry.id}:${typeLabel(entry.value)}`)
|
||||||
|
.join(";")}}`;
|
||||||
case "builtin":
|
case "builtin":
|
||||||
case "scalar":
|
case "scalar":
|
||||||
return type.name;
|
return type.name;
|
||||||
@@ -176,6 +181,19 @@ export const valueTypesEqual = (left: ValueType, right: ValueType): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (left.kind) {
|
switch (left.kind) {
|
||||||
|
case "variant": {
|
||||||
|
const other = right as typeof left;
|
||||||
|
return (
|
||||||
|
left.id === other.id &&
|
||||||
|
Object.keys(left.cases).length === Object.keys(other.cases).length &&
|
||||||
|
Object.entries(left.cases).every(
|
||||||
|
([label, entry]) =>
|
||||||
|
Object.hasOwn(other.cases, label) &&
|
||||||
|
entry.id === other.cases[label].id &&
|
||||||
|
valueTypesEqual(entry.value, other.cases[label].value),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
case "builtin":
|
case "builtin":
|
||||||
case "scalar":
|
case "scalar":
|
||||||
return left.name === (right as typeof left).name;
|
return left.name === (right as typeof left).name;
|
||||||
@@ -232,6 +250,20 @@ const validateValueType = (
|
|||||||
indexes: Pick<ValidationIndexes, "atoms" | "interfaces">,
|
indexes: Pick<ValidationIndexes, "atoms" | "interfaces">,
|
||||||
) => {
|
) => {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant": {
|
||||||
|
requireText(issues, type.id, `${path}.id`, "Variant identity");
|
||||||
|
const entries = Object.entries(type.cases),
|
||||||
|
ids = new Set<string>();
|
||||||
|
if (!entries.length || entries.length > 256) issue(issues, "invalid-value-type", path, "Use 1–256 variant cases");
|
||||||
|
for (const [label, entry] of entries) {
|
||||||
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(label)) issue(issues, "invalid-value-type", path, "Invalid variant label");
|
||||||
|
requireText(issues, entry.id, `${path}.${label}.id`, "Case identity");
|
||||||
|
if (ids.has(entry.id)) issue(issues, "invalid-value-type", path, "Duplicate case identity");
|
||||||
|
ids.add(entry.id);
|
||||||
|
validateValueType(issues, entry.value, `${path}.${label}`, indexes);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
case "record":
|
case "record":
|
||||||
for (const [name, field] of Object.entries(type.fields)) {
|
for (const [name, field] of Object.entries(type.fields)) {
|
||||||
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
||||||
@@ -569,6 +601,7 @@ const collectIdentityIndexes = (
|
|||||||
if (member.kind === "value") {
|
if (member.kind === "value") {
|
||||||
const queryValue = (type: ValueType): boolean =>
|
const queryValue = (type: ValueType): boolean =>
|
||||||
type.kind === "scalar" ||
|
type.kind === "scalar" ||
|
||||||
|
(type.kind === "variant" && isStorableType(type)) ||
|
||||||
((type.kind === "optional" || type.kind === "list") && queryValue(type.value)) ||
|
((type.kind === "optional" || type.kind === "list") && queryValue(type.value)) ||
|
||||||
(type.kind === "record" &&
|
(type.kind === "record" &&
|
||||||
Object.keys(type.fields).length > 0 &&
|
Object.keys(type.fields).length > 0 &&
|
||||||
@@ -893,6 +926,7 @@ const validateAttachments = (issues: CapabilityValidationIssue[], indexes: Valid
|
|||||||
type.kind === "object-ref" ||
|
type.kind === "object-ref" ||
|
||||||
type.kind === "change" ||
|
type.kind === "change" ||
|
||||||
(type.kind === "record" && Object.values(type.fields).some(containsRpcType)) ||
|
(type.kind === "record" && Object.values(type.fields).some(containsRpcType)) ||
|
||||||
|
(type.kind === "variant" && Object.values(type.cases).some((entry) => containsRpcType(entry.value))) ||
|
||||||
((type.kind === "optional" || type.kind === "list") && containsRpcType(type.value));
|
((type.kind === "optional" || type.kind === "list") && containsRpcType(type.value));
|
||||||
if (
|
if (
|
||||||
containsRpcType(attachment.valueType) ||
|
containsRpcType(attachment.valueType) ||
|
||||||
@@ -999,6 +1033,26 @@ const validatePackages = (
|
|||||||
const exportPath = `${path}.exports[${exportIndex}]`;
|
const exportPath = `${path}.exports[${exportIndex}]`;
|
||||||
validateValueType(issues, entry.inputType, `${exportPath}.inputType`, indexes);
|
validateValueType(issues, entry.inputType, `${exportPath}.inputType`, indexes);
|
||||||
validateValueType(issues, entry.outputType, `${exportPath}.outputType`, indexes);
|
validateValueType(issues, entry.outputType, `${exportPath}.outputType`, indexes);
|
||||||
|
if (entry.preparation) {
|
||||||
|
const port = entry.dependencyPorts.find((port) => port.id === entry.preparation!.port);
|
||||||
|
if (
|
||||||
|
entry.execution?.kind !== "transaction" ||
|
||||||
|
entry.kind === "constructor" ||
|
||||||
|
(entry.kind === "operation" && entry.mode !== "call") ||
|
||||||
|
!entry.preparation.key ||
|
||||||
|
port?.requirement.kind !== "creation" ||
|
||||||
|
!valueTypesEqual(port.requirement.inputType, entry.inputType) ||
|
||||||
|
entry.outputType.kind !== "object-ref" ||
|
||||||
|
(entry.outputType.expectation.kind === "atom" &&
|
||||||
|
entry.outputType.expectation.atomId !== port.requirement.atomId)
|
||||||
|
)
|
||||||
|
issue(
|
||||||
|
issues,
|
||||||
|
"invalid-dependency-binding",
|
||||||
|
exportPath,
|
||||||
|
"Prepared creation requires a transaction returning its declared creation, with matching captured input types and a fixed key",
|
||||||
|
);
|
||||||
|
}
|
||||||
if (entry.acceptanceExportId !== undefined) {
|
if (entry.acceptanceExportId !== undefined) {
|
||||||
const check = revision.exports.find((candidate) => candidate.id === entry.acceptanceExportId);
|
const check = revision.exports.find((candidate) => candidate.id === entry.acceptanceExportId);
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -9,10 +9,15 @@ import type {
|
|||||||
} from "../capability-model/types.js";
|
} from "../capability-model/types.js";
|
||||||
import { type ExecutionPlan, ExecutionPlanError, hexDigest } from "./plan.js";
|
import { type ExecutionPlan, ExecutionPlanError, hexDigest } from "./plan.js";
|
||||||
import { portableValueSchema } from "../bindings/portable.js";
|
import { portableValueSchema } from "../bindings/portable.js";
|
||||||
function preparedInputPaths(type: ValueType, path: (string | null)[] = []): (string | null)[][] {
|
type PreparedStep = string | null | { case: string; id: string };
|
||||||
|
function preparedInputPaths(type: ValueType, path: PreparedStep[] = []): PreparedStep[][] {
|
||||||
if (type.kind === "change") return [path];
|
if (type.kind === "change") return [path];
|
||||||
if (type.kind === "optional") return preparedInputPaths(type.value, path);
|
if (type.kind === "optional") return preparedInputPaths(type.value, path);
|
||||||
if (type.kind === "list") return preparedInputPaths(type.value, [...path, null]);
|
if (type.kind === "list") return preparedInputPaths(type.value, [...path, null]);
|
||||||
|
if (type.kind === "variant")
|
||||||
|
return Object.entries(type.cases).flatMap(([label, entry]) =>
|
||||||
|
preparedInputPaths(entry.value, [...path, { case: label, id: entry.id }]),
|
||||||
|
);
|
||||||
if (type.kind === "record")
|
if (type.kind === "record")
|
||||||
return Object.entries(type.fields).flatMap(([key, value]) => preparedInputPaths(value, [...path, key]));
|
return Object.entries(type.fields).flatMap(([key, value]) => preparedInputPaths(value, [...path, key]));
|
||||||
return [];
|
return [];
|
||||||
@@ -210,6 +215,7 @@ export function compileKernelProgram(plan: ExecutionPlan): unknown {
|
|||||||
package: binding.packageRevisionId,
|
package: binding.packageRevisionId,
|
||||||
export: binding.exportId,
|
export: binding.exportId,
|
||||||
ports,
|
ports,
|
||||||
|
...(entry.preparation ? { preparation: entry.preparation } : {}),
|
||||||
...(check
|
...(check
|
||||||
? { acceptance: { export: check.id, ports: compilePorts(check, binding.acceptanceDependencies ?? []) } }
|
? { acceptance: { export: check.id, ports: compilePorts(check, binding.acceptanceDependencies ?? []) } }
|
||||||
: {}),
|
: {}),
|
||||||
@@ -237,6 +243,7 @@ export function compileKernelProgram(plan: ExecutionPlan): unknown {
|
|||||||
revision: pkg.revisionId,
|
revision: pkg.revisionId,
|
||||||
exports: pkg.exports.map((entry) => ({
|
exports: pkg.exports.map((entry) => ({
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
|
...(entry.preparation ? { preparation: entry.preparation } : {}),
|
||||||
...(entry.acceptanceExportId ? { acceptance: entry.acceptanceExportId } : {}),
|
...(entry.acceptanceExportId ? { acceptance: entry.acceptanceExportId } : {}),
|
||||||
effect: entry.execution?.kind ?? "remote",
|
effect: entry.execution?.kind ?? "remote",
|
||||||
targets: entry.execution && entry.execution.kind !== "remote" ? entry.execution.targets : [],
|
targets: entry.execution && entry.execution.kind !== "remote" ? entry.execution.targets : [],
|
||||||
|
|||||||
@@ -142,6 +142,9 @@ export function resolvePlannedOperation(
|
|||||||
/** Bound argument/state codec subset. A parsed type alone does not mean a runtime implements it. */
|
/** Bound argument/state codec subset. A parsed type alone does not mean a runtime implements it. */
|
||||||
function checkValue(type: ValueType, path: string, allowChange = false): void {
|
function checkValue(type: ValueType, path: string, allowChange = false): void {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
|
case "variant":
|
||||||
|
for (const [label, entry] of Object.entries(type.cases)) checkValue(entry.value, `${path}.${label}`, allowChange);
|
||||||
|
return;
|
||||||
case "scalar":
|
case "scalar":
|
||||||
case "object-ref":
|
case "object-ref":
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -126,8 +126,29 @@ export function querySchema(
|
|||||||
);
|
);
|
||||||
return views[0]!.interfaceRevisionId;
|
return views[0]!.interfaceRevisionId;
|
||||||
};
|
};
|
||||||
|
const variants = new Map<string, GraphQLScalarType>();
|
||||||
const scalar = (type: ValueType): GraphQLOutputType & GraphQLInputType => {
|
const scalar = (type: ValueType): GraphQLOutputType & GraphQLInputType => {
|
||||||
if (type.kind === "optional") return scalar(type.value);
|
if (type.kind === "optional") return scalar(type.value);
|
||||||
|
if (type.kind === "variant") {
|
||||||
|
const name = `QxVariant_${createHash("sha256").update(JSON.stringify(type)).digest("hex").slice(0, 16)}`;
|
||||||
|
let result = variants.get(name);
|
||||||
|
if (!result) {
|
||||||
|
result = new GraphQLScalarType({
|
||||||
|
name,
|
||||||
|
description: "Whole checked Camino variant value. Branch selection and predicates are not supported.",
|
||||||
|
serialize: (value) => value,
|
||||||
|
parseValue() {
|
||||||
|
throw new QueryCompileError("QUERY_TYPE", "Variant query inputs are not supported");
|
||||||
|
},
|
||||||
|
parseLiteral() {
|
||||||
|
throw new QueryCompileError("QUERY_TYPE", "Variant query inputs are not supported");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
variants.set(name, result);
|
||||||
|
scalarTypes.set(name, type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
if (type.kind !== "scalar")
|
if (type.kind !== "scalar")
|
||||||
throw new QueryCompileError("QUERY_TYPE", "Only scalar/optional scalar query fields are supported");
|
throw new QueryCompileError("QUERY_TYPE", "Only scalar/optional scalar query fields are supported");
|
||||||
return queryScalars[type.name];
|
return queryScalars[type.name];
|
||||||
|
|||||||
@@ -154,3 +154,20 @@ await writeFile("dist/portable-reader/compound-crdt/kernel.json", JSON.stringify
|
|||||||
const { emitExecutionScaffold } = await import("./execution-scaffold-fixture.js");
|
const { emitExecutionScaffold } = await import("./execution-scaffold-fixture.js");
|
||||||
const executionScaffold = await emitExecutionScaffold("dist/portable-reader/execution-scaffold/source");
|
const executionScaffold = await emitExecutionScaffold("dist/portable-reader/execution-scaffold/source");
|
||||||
await writeFile("dist/portable-reader/execution-scaffold/plan.json", JSON.stringify(executionScaffold));
|
await writeFile("dist/portable-reader/execution-scaffold/plan.json", JSON.stringify(executionScaffold));
|
||||||
|
|
||||||
|
// Native variant fixture shared by generated-type and browser editing qualification.
|
||||||
|
const { readFile } = await import("node:fs/promises");
|
||||||
|
const { compileCapabilityResourceSource } = await import("../src/capability-language/index.js");
|
||||||
|
const { portableValueSchema, tsType } = await import("../src/bindings/portable.js");
|
||||||
|
const variantSource = compileCapabilityResourceSource(await readFile("test/fixtures/canvas-content.qx", "utf8"), {
|
||||||
|
source: { repository: "https://example.test/canvas-content.git", commit: "a".repeat(40) },
|
||||||
|
});
|
||||||
|
if (!variantSource.ok || variantSource.resource.kind !== "interface") throw Error("Invalid variant fixture");
|
||||||
|
const entries = variantSource.resource.revision.members[0];
|
||||||
|
if (entries.kind !== "value") throw Error("Expected entries value");
|
||||||
|
await mkdir("dist/portable-reader/variants", { recursive: true });
|
||||||
|
await writeFile("dist/portable-reader/variants/schema.json", JSON.stringify(portableValueSchema(entries.valueType)));
|
||||||
|
await writeFile(
|
||||||
|
"dist/portable-reader/variants/entries.ts",
|
||||||
|
"export type Entries = " + tsType(entries.valueType) + ";\n",
|
||||||
|
);
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
type Point = record { x:double; y:double; };
|
||||||
|
type Entry = variant id "canvas:entry@1" {
|
||||||
|
paragraph id "canvas:paragraph" : record { text:string; };
|
||||||
|
stroke id "canvas:stroke" : record { points:list<Point>; };
|
||||||
|
image id "canvas:image" : record { asset:string; };
|
||||||
|
};
|
||||||
|
interface CanvasContent id "canvas" revision "canvas@1" {
|
||||||
|
queryable value entries id "entries" : list<Entry> {
|
||||||
|
get id "entries:get" execution snapshot targets [browser,server];
|
||||||
|
edit id "entries:edit" execution transaction targets [browser,server];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { test } from "node:test";
|
||||||
|
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
|
||||||
|
import { compileQuery } from "../src/query/compile.js";
|
||||||
|
import { defaultQueryBudgets } from "../src/query/types.js";
|
||||||
|
import { portableValueSchema, tsType } from "../src/bindings/portable.js";
|
||||||
|
import { isStorableType, TypeSubstitution } from "../src/capability-model/index.js";
|
||||||
|
|
||||||
|
const source = { repository: "https://example.test/variants.git", commit: "a".repeat(40) };
|
||||||
|
const declaration = readFileSync("test/fixtures/canvas-content.qx", "utf8");
|
||||||
|
const result = compileCapabilityResourceSource(declaration, { source });
|
||||||
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
||||||
|
if (result.resource.kind !== "interface") throw Error("Expected interface");
|
||||||
|
const iface = result.resource.revision;
|
||||||
|
const entries = iface.members[0];
|
||||||
|
if (entries.kind !== "value") throw Error("Expected value");
|
||||||
|
|
||||||
|
test("native variant aliases lower into nominal cases with typed payloads", () => {
|
||||||
|
assert.equal(entries.valueType.kind, "list");
|
||||||
|
assert.ok(isStorableType(entries.valueType));
|
||||||
|
const schema = portableValueSchema(entries.valueType) as any;
|
||||||
|
assert.equal(schema.value.id, "canvas:entry@1");
|
||||||
|
assert.equal(schema.value.cases.stroke.id, "canvas:stroke");
|
||||||
|
assert.match(tsType(entries.valueType), /kind: "stroke"/);
|
||||||
|
assert.match(tsType(entries.valueType), /ReadonlyArray<.*"x": number/);
|
||||||
|
const substitution = new TypeSubstitution({
|
||||||
|
arguments: new Map([["T", { kind: "value", type: { kind: "scalar", name: "string" } }]]),
|
||||||
|
applyInterface: () => {
|
||||||
|
throw Error("unused");
|
||||||
|
},
|
||||||
|
implementsInterface: () => false,
|
||||||
|
});
|
||||||
|
assert.deepEqual(
|
||||||
|
substitution.value({
|
||||||
|
kind: "variant",
|
||||||
|
id: "box",
|
||||||
|
cases: { some: { id: "box:some", value: { kind: "parameter", parameterId: "T" } } },
|
||||||
|
}),
|
||||||
|
{ kind: "variant", id: "box", cases: { some: { id: "box:some", value: { kind: "scalar", name: "string" } } } },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test("variant query scalars keep their checked payload type and reject branch selection", async () => {
|
||||||
|
const query = {
|
||||||
|
id: "canvas-query",
|
||||||
|
displayName: "Canvas",
|
||||||
|
root: { kind: "object" as const, interface: iface.revisionId },
|
||||||
|
document: "canvas.graphql",
|
||||||
|
operation: "Canvas",
|
||||||
|
fragments: [],
|
||||||
|
views: [],
|
||||||
|
allowances: [],
|
||||||
|
budgets: { ...defaultQueryBudgets },
|
||||||
|
watch: true,
|
||||||
|
};
|
||||||
|
const checked = await compileQuery(query, [iface], async () => `query Canvas { root { entries } }`);
|
||||||
|
assert.equal(checked.output.kind, "record");
|
||||||
|
if (checked.output.kind !== "record") return;
|
||||||
|
const root = checked.output.fields.root;
|
||||||
|
assert.equal(root.kind, "record");
|
||||||
|
if (root.kind !== "record") return;
|
||||||
|
assert.deepEqual(root.fields.entries, entries.valueType);
|
||||||
|
await assert.rejects(compileQuery(query, [iface], async () => `query Canvas { root { entries { kind value } } }`));
|
||||||
|
});
|
||||||
|
test("unknown and duplicate cases fail; reference payloads remain nonstorable", () => {
|
||||||
|
const duplicate = compileCapabilityResourceSource(declaration.replace('"canvas:image"', '"canvas:stroke"'), {
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
assert.equal(duplicate.ok, false);
|
||||||
|
assert.ok(
|
||||||
|
!isStorableType({
|
||||||
|
kind: "variant",
|
||||||
|
id: "transport",
|
||||||
|
cases: {
|
||||||
|
ref: {
|
||||||
|
id: "ref",
|
||||||
|
value: { kind: "object-ref", expectation: { kind: "interface", interfaceRevisionId: iface.revisionId } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user