@cynodia/axiom-server 0.6.0-alpha.1 → 0.6.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/conformance/authorization.json +7 -1
- package/conformance/constraint-rolls-back.json +1 -0
- package/conformance/for-each-provisional.json +5 -1
- package/conformance/guard-refuses.json +2 -0
- package/conformance/idempotent-retry.json +12 -2
- package/conformance/manifest.json +143 -0
- package/conformance/mutation-commits.json +2 -1
- package/conformance/restart.json +8 -2
- package/conformance/transition-constraint.json +6 -1
- package/dist/host.d.ts +17 -0
- package/dist/host.js +17 -0
- package/dist/node-host.d.ts +45 -0
- package/dist/node-host.js +27 -1
- package/dist/protocol.d.ts +19 -2
- package/dist/server.d.ts +0 -12
- package/dist/server.js +135 -25
- package/package.json +8 -4
- package/schema/protocol.v1.schema.json +342 -0
- package/schema/server-ir.v1.schema.json +1309 -0
package/README.md
CHANGED
|
@@ -18,16 +18,33 @@ SQLite), `TransportAdapter` (in-process and HTTP), and `ServerHost` for time, id
|
|
|
18
18
|
and authentication. Nothing in an ApplicationGraph mentions HTTP, SQL or a route.
|
|
19
19
|
|
|
20
20
|
Main exports: `createAxiomServer`, `createMemoryPersistence`, `createSqlitePersistence`,
|
|
21
|
-
`createServerHost`, `
|
|
22
|
-
`serveOverHttp`, `
|
|
21
|
+
`createServerHost`, `createDeterministicServerHost`, `createDirectTransport`,
|
|
22
|
+
`createHttpTransport`, `createRemoteGateway`, `serveOverHttp`, `serveAxiomApplication`,
|
|
23
|
+
`SERVER_DIAGNOSTIC_CODES`.
|
|
23
24
|
|
|
24
|
-
`
|
|
25
|
-
|
|
25
|
+
`serveAxiomApplication` is the whole deployment story: it serves the generated page at `GET /`
|
|
26
|
+
and the semantic endpoint at `POST /axiom`, from one process, for any Axiom application. No
|
|
27
|
+
route, controller, handler or SQL statement is written by an application author.
|
|
28
|
+
|
|
29
|
+
### Portable artifacts
|
|
30
|
+
|
|
31
|
+
`axiom.server.v1` is a frozen, language-independent contract, and this package ships what an
|
|
32
|
+
implementation in another language needs to conform to it:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
@cynodia/axiom-server/conformance the fixture manifest
|
|
36
|
+
@cynodia/axiom-server/conformance/<name>.json one fixture: IR, state, invocations, expectations
|
|
37
|
+
@cynodia/axiom-server/schema/server-ir.v1.schema.json JSON Schema for the IR
|
|
38
|
+
@cynodia/axiom-server/schema/protocol.v1.schema.json JSON Schema for the protocol
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The fixtures are pure data. Running them requires no part of this implementation — which is
|
|
42
|
+
the point: the semantic contract, the schemas and these files are the whole specification.
|
|
26
43
|
|
|
27
44
|
## Installation
|
|
28
45
|
|
|
29
46
|
```bash
|
|
30
|
-
npm install @cynodia/axiom-server
|
|
47
|
+
npm install @cynodia/axiom-server
|
|
31
48
|
```
|
|
32
49
|
|
|
33
50
|
This package is **not** re-exported by the `@cynodia/axiom` facade, because it imports
|
|
@@ -727,6 +727,7 @@
|
|
|
727
727
|
},
|
|
728
728
|
"expect": {
|
|
729
729
|
"ok": false,
|
|
730
|
+
"changedStates": [],
|
|
730
731
|
"diagnosticCodes": [
|
|
731
732
|
"AUTHORIZATION_DENIED"
|
|
732
733
|
]
|
|
@@ -740,6 +741,7 @@
|
|
|
740
741
|
},
|
|
741
742
|
"expect": {
|
|
742
743
|
"ok": false,
|
|
744
|
+
"changedStates": [],
|
|
743
745
|
"diagnosticCodes": [
|
|
744
746
|
"AUTHORIZATION_DENIED"
|
|
745
747
|
]
|
|
@@ -753,7 +755,11 @@
|
|
|
753
755
|
"param_stock": 1
|
|
754
756
|
},
|
|
755
757
|
"expect": {
|
|
756
|
-
"ok": true
|
|
758
|
+
"ok": true,
|
|
759
|
+
"changedStates": [
|
|
760
|
+
"state_parts",
|
|
761
|
+
"state_total"
|
|
762
|
+
]
|
|
757
763
|
}
|
|
758
764
|
}
|
|
759
765
|
],
|
|
@@ -727,6 +727,7 @@
|
|
|
727
727
|
},
|
|
728
728
|
"expect": {
|
|
729
729
|
"ok": false,
|
|
730
|
+
"changedStates": [],
|
|
730
731
|
"diagnosticCodes": [
|
|
731
732
|
"PRECONDITION_FAILED"
|
|
732
733
|
],
|
|
@@ -743,6 +744,7 @@
|
|
|
743
744
|
},
|
|
744
745
|
"expect": {
|
|
745
746
|
"ok": false,
|
|
747
|
+
"changedStates": [],
|
|
746
748
|
"diagnosticCodes": [
|
|
747
749
|
"PRECONDITION_FAILED"
|
|
748
750
|
],
|
|
@@ -727,7 +727,12 @@
|
|
|
727
727
|
"param_qty": 3
|
|
728
728
|
},
|
|
729
729
|
"expect": {
|
|
730
|
-
"ok": true
|
|
730
|
+
"ok": true,
|
|
731
|
+
"changedStates": [
|
|
732
|
+
"state_parts",
|
|
733
|
+
"state_ledger",
|
|
734
|
+
"state_total"
|
|
735
|
+
]
|
|
731
736
|
}
|
|
732
737
|
},
|
|
733
738
|
{
|
|
@@ -739,7 +744,12 @@
|
|
|
739
744
|
},
|
|
740
745
|
"expect": {
|
|
741
746
|
"ok": true,
|
|
742
|
-
"replayed": true
|
|
747
|
+
"replayed": true,
|
|
748
|
+
"changedStates": [
|
|
749
|
+
"state_parts",
|
|
750
|
+
"state_ledger",
|
|
751
|
+
"state_total"
|
|
752
|
+
]
|
|
743
753
|
}
|
|
744
754
|
}
|
|
745
755
|
],
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"conformance": "axiom.conformance.v1",
|
|
3
|
+
"contract": "axiom.server.v1",
|
|
4
|
+
"protocol": "axiom.protocol.v1",
|
|
5
|
+
"release": "0.6.2-alpha.1",
|
|
6
|
+
"description": "Portable conformance fixtures for the Axiom Server IR. Each entry is a self-contained JSON document: the Server IR, the state to start from, the principals, the invocations to perform and the results required. Running them needs no part of this implementation.",
|
|
7
|
+
"areas": [
|
|
8
|
+
"action guards",
|
|
9
|
+
"argument validation",
|
|
10
|
+
"authorization",
|
|
11
|
+
"concurrent mutation",
|
|
12
|
+
"constraints",
|
|
13
|
+
"derived state",
|
|
14
|
+
"expression evaluation",
|
|
15
|
+
"failure modes",
|
|
16
|
+
"for-each provisional writes",
|
|
17
|
+
"idempotency",
|
|
18
|
+
"mutation",
|
|
19
|
+
"persistence",
|
|
20
|
+
"restart",
|
|
21
|
+
"rollback",
|
|
22
|
+
"transition constraints"
|
|
23
|
+
],
|
|
24
|
+
"fixtures": [
|
|
25
|
+
{
|
|
26
|
+
"name": "mutation-commits",
|
|
27
|
+
"file": "mutation-commits.json",
|
|
28
|
+
"covers": [
|
|
29
|
+
"expression evaluation",
|
|
30
|
+
"mutation",
|
|
31
|
+
"derived state"
|
|
32
|
+
],
|
|
33
|
+
"description": "An action within its guards commits every write, and derived state follows.",
|
|
34
|
+
"concurrent": false,
|
|
35
|
+
"restartAndReassert": false,
|
|
36
|
+
"invocations": 1
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "guard-refuses",
|
|
40
|
+
"file": "guard-refuses.json",
|
|
41
|
+
"covers": [
|
|
42
|
+
"action guards",
|
|
43
|
+
"failure modes"
|
|
44
|
+
],
|
|
45
|
+
"description": "A guard that does not hold refuses the invocation, naming its failure mode.",
|
|
46
|
+
"concurrent": false,
|
|
47
|
+
"restartAndReassert": false,
|
|
48
|
+
"invocations": 2
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "constraint-rolls-back",
|
|
52
|
+
"file": "constraint-rolls-back.json",
|
|
53
|
+
"covers": [
|
|
54
|
+
"constraints",
|
|
55
|
+
"rollback",
|
|
56
|
+
"for-each provisional writes"
|
|
57
|
+
],
|
|
58
|
+
"description": "Iteration N sees the writes of iterations before it, so two lines for one part are counted together — and the invariant then rolls the whole action back.",
|
|
59
|
+
"concurrent": false,
|
|
60
|
+
"restartAndReassert": false,
|
|
61
|
+
"invocations": 1
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "for-each-provisional",
|
|
65
|
+
"file": "for-each-provisional.json",
|
|
66
|
+
"covers": [
|
|
67
|
+
"for-each provisional writes"
|
|
68
|
+
],
|
|
69
|
+
"description": "The same two-line shape, within stock: 10 − 4 − 4, not 10 − 4 twice.",
|
|
70
|
+
"concurrent": false,
|
|
71
|
+
"restartAndReassert": false,
|
|
72
|
+
"invocations": 1
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "transition-constraint",
|
|
76
|
+
"file": "transition-constraint.json",
|
|
77
|
+
"covers": [
|
|
78
|
+
"transition constraints"
|
|
79
|
+
],
|
|
80
|
+
"description": "A rule about how state may change refuses a raise, and permits a fall.",
|
|
81
|
+
"concurrent": false,
|
|
82
|
+
"restartAndReassert": false,
|
|
83
|
+
"invocations": 2
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "authorization",
|
|
87
|
+
"file": "authorization.json",
|
|
88
|
+
"covers": [
|
|
89
|
+
"authorization"
|
|
90
|
+
],
|
|
91
|
+
"description": "Authorization is evaluated on the authority, with the caller bound to PRINCIPAL.",
|
|
92
|
+
"concurrent": false,
|
|
93
|
+
"restartAndReassert": false,
|
|
94
|
+
"invocations": 3
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "argument-validation",
|
|
98
|
+
"file": "argument-validation.json",
|
|
99
|
+
"covers": [
|
|
100
|
+
"argument validation"
|
|
101
|
+
],
|
|
102
|
+
"description": "Untrusted arguments are checked against declared parameter types.",
|
|
103
|
+
"concurrent": false,
|
|
104
|
+
"restartAndReassert": false,
|
|
105
|
+
"invocations": 4
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "idempotent-retry",
|
|
109
|
+
"file": "idempotent-retry.json",
|
|
110
|
+
"covers": [
|
|
111
|
+
"persistence",
|
|
112
|
+
"idempotency"
|
|
113
|
+
],
|
|
114
|
+
"description": "A repeated request id is answered from the record, not executed again.",
|
|
115
|
+
"concurrent": false,
|
|
116
|
+
"restartAndReassert": false,
|
|
117
|
+
"invocations": 2
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "concurrent-invocations",
|
|
121
|
+
"file": "concurrent-invocations.json",
|
|
122
|
+
"covers": [
|
|
123
|
+
"concurrent mutation"
|
|
124
|
+
],
|
|
125
|
+
"description": "Three callers each want four of a stock of five. Exactly one may commit; the rest are refused by the guard against authoritative state.",
|
|
126
|
+
"concurrent": true,
|
|
127
|
+
"restartAndReassert": false,
|
|
128
|
+
"invocations": 3
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "restart",
|
|
132
|
+
"file": "restart.json",
|
|
133
|
+
"covers": [
|
|
134
|
+
"persistence",
|
|
135
|
+
"restart"
|
|
136
|
+
],
|
|
137
|
+
"description": "Committed state is restored exactly on restart, and a rolled-back write never reappears.",
|
|
138
|
+
"concurrent": false,
|
|
139
|
+
"restartAndReassert": true,
|
|
140
|
+
"invocations": 2
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
package/conformance/restart.json
CHANGED
|
@@ -727,7 +727,12 @@
|
|
|
727
727
|
"param_qty": 3
|
|
728
728
|
},
|
|
729
729
|
"expect": {
|
|
730
|
-
"ok": true
|
|
730
|
+
"ok": true,
|
|
731
|
+
"changedStates": [
|
|
732
|
+
"state_parts",
|
|
733
|
+
"state_ledger",
|
|
734
|
+
"state_total"
|
|
735
|
+
]
|
|
731
736
|
}
|
|
732
737
|
},
|
|
733
738
|
{
|
|
@@ -737,7 +742,8 @@
|
|
|
737
742
|
"param_qty": 99
|
|
738
743
|
},
|
|
739
744
|
"expect": {
|
|
740
|
-
"ok": false
|
|
745
|
+
"ok": false,
|
|
746
|
+
"changedStates": []
|
|
741
747
|
}
|
|
742
748
|
}
|
|
743
749
|
],
|
|
@@ -727,6 +727,7 @@
|
|
|
727
727
|
},
|
|
728
728
|
"expect": {
|
|
729
729
|
"ok": false,
|
|
730
|
+
"changedStates": [],
|
|
730
731
|
"diagnosticCodes": [
|
|
731
732
|
"TRANSITION_CONSTRAINT_VIOLATION"
|
|
732
733
|
]
|
|
@@ -740,7 +741,11 @@
|
|
|
740
741
|
"param_stock": 4
|
|
741
742
|
},
|
|
742
743
|
"expect": {
|
|
743
|
-
"ok": true
|
|
744
|
+
"ok": true,
|
|
745
|
+
"changedStates": [
|
|
746
|
+
"state_parts",
|
|
747
|
+
"state_total"
|
|
748
|
+
]
|
|
744
749
|
}
|
|
745
750
|
}
|
|
746
751
|
],
|
package/dist/host.d.ts
CHANGED
|
@@ -53,5 +53,22 @@ export declare function createServerHost(overrides?: Partial<ServerHost>): Serve
|
|
|
53
53
|
* A deterministic host, for conformance runs and tests. `now` and `uuid` count rather than
|
|
54
54
|
* varying, so an expected result is stable.
|
|
55
55
|
*/
|
|
56
|
+
/**
|
|
57
|
+
* The host model the conformance suite runs against.
|
|
58
|
+
*
|
|
59
|
+
* `now()` and `uuid()` are the only two places semantics can depend on something outside the
|
|
60
|
+
* graph, so a portable fixture needs both pinned. The model is one counter, shared, starting
|
|
61
|
+
* at zero and incremented **before** each value is produced, so the nth host call in an
|
|
62
|
+
* execution — whichever of the two it is — is always the same value:
|
|
63
|
+
*
|
|
64
|
+
* ```
|
|
65
|
+
* uuid() → "id-<n>"
|
|
66
|
+
* now() → "2026-01-01T00:00:<n, two digits>.000Z"
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* A runtime in another language reproduces this exactly by counting host calls in execution
|
|
70
|
+
* order. The counter is per host instance, and a fixture that restarts the authority keeps
|
|
71
|
+
* the same host — a restart does not rewind it.
|
|
72
|
+
*/
|
|
56
73
|
export declare function createDeterministicServerHost(overrides?: Partial<ServerHost>): ServerHost;
|
|
57
74
|
//# sourceMappingURL=host.d.ts.map
|
package/dist/host.js
CHANGED
|
@@ -12,6 +12,23 @@ export function createServerHost(overrides = {}) {
|
|
|
12
12
|
* A deterministic host, for conformance runs and tests. `now` and `uuid` count rather than
|
|
13
13
|
* varying, so an expected result is stable.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* The host model the conformance suite runs against.
|
|
17
|
+
*
|
|
18
|
+
* `now()` and `uuid()` are the only two places semantics can depend on something outside the
|
|
19
|
+
* graph, so a portable fixture needs both pinned. The model is one counter, shared, starting
|
|
20
|
+
* at zero and incremented **before** each value is produced, so the nth host call in an
|
|
21
|
+
* execution — whichever of the two it is — is always the same value:
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* uuid() → "id-<n>"
|
|
25
|
+
* now() → "2026-01-01T00:00:<n, two digits>.000Z"
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* A runtime in another language reproduces this exactly by counting host calls in execution
|
|
29
|
+
* order. The counter is per host instance, and a fixture that restarts the authority keeps
|
|
30
|
+
* the same host — a restart does not rewind it.
|
|
31
|
+
*/
|
|
15
32
|
export function createDeterministicServerHost(overrides = {}) {
|
|
16
33
|
let counter = 0;
|
|
17
34
|
return {
|
package/dist/node-host.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { AxiomServer } from './server.js';
|
|
2
|
+
import type { PrincipalRecord } from './host.js';
|
|
3
|
+
import type { PersistenceAdapter } from './persistence.js';
|
|
4
|
+
import type { ServerIR } from './deps.js';
|
|
2
5
|
/**
|
|
3
6
|
* The reference Node host.
|
|
4
7
|
*
|
|
@@ -11,6 +14,11 @@ export interface NodeHostOptions {
|
|
|
11
14
|
port?: number;
|
|
12
15
|
/** The single semantic endpoint. */
|
|
13
16
|
path?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The generated browser page. When given, `GET /` serves it — so one process hands out
|
|
19
|
+
* the client and answers it. Omit to run a bare authority.
|
|
20
|
+
*/
|
|
21
|
+
page?: string;
|
|
14
22
|
}
|
|
15
23
|
export interface RunningNodeHost {
|
|
16
24
|
/** The port actually bound, which matters when `port: 0` was requested. */
|
|
@@ -19,4 +27,41 @@ export interface RunningNodeHost {
|
|
|
19
27
|
close(): Promise<void>;
|
|
20
28
|
}
|
|
21
29
|
export declare function serveOverHttp(options: NodeHostOptions): Promise<RunningNodeHost>;
|
|
30
|
+
/**
|
|
31
|
+
* One graph, one process: the generated page and the authority that answers it.
|
|
32
|
+
*
|
|
33
|
+
* This is the whole deployment story for an application with server-authoritative state.
|
|
34
|
+
* The caller supplies the two compiled artifacts — the Server IR and the page — plus how to
|
|
35
|
+
* authenticate a credential and where to keep state. It defines no route, no verb and no
|
|
36
|
+
* handler, because there are none to define: `GET /` is the page and `POST /axiom` is the
|
|
37
|
+
* semantic endpoint, for every Axiom application there will ever be.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* const running = await serveAxiomApplication({
|
|
41
|
+
* serverIR: compileToServerIR(graph),
|
|
42
|
+
* page: compileToHtml(graph),
|
|
43
|
+
* persistence: createSqlitePersistence({ file: 'app.db' }),
|
|
44
|
+
* authenticate: (credential) => users.get(credential ?? '') ?? null,
|
|
45
|
+
* port: 3000,
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export interface AxiomApplicationOptions {
|
|
50
|
+
serverIR: ServerIR;
|
|
51
|
+
/** The generated browser page, exactly as `compileToHtml` produced it. */
|
|
52
|
+
page: string;
|
|
53
|
+
persistence?: PersistenceAdapter;
|
|
54
|
+
/** Resolves opaque credential material to a principal record, or null for anonymous. */
|
|
55
|
+
authenticate?: (credential: string | null) => PrincipalRecord | null | Promise<PrincipalRecord | null>;
|
|
56
|
+
port?: number;
|
|
57
|
+
/** The semantic endpoint. Change it only if something else already owns `/axiom`. */
|
|
58
|
+
path?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface RunningAxiomApplication extends RunningNodeHost {
|
|
61
|
+
/** The page's address, which is what a person opens. */
|
|
62
|
+
pageUrl: string;
|
|
63
|
+
/** The authority itself, for tests and administrative inspection. */
|
|
64
|
+
server: AxiomServer;
|
|
65
|
+
}
|
|
66
|
+
export declare function serveAxiomApplication(options: AxiomApplicationOptions): Promise<RunningAxiomApplication>;
|
|
22
67
|
//# sourceMappingURL=node-host.d.ts.map
|
package/dist/node-host.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
2
|
import { dispatch } from './transport.js';
|
|
3
|
+
import { createAxiomServer } from './server.js';
|
|
4
|
+
import { createServerHost } from './host.js';
|
|
3
5
|
const MAX_BODY_BYTES = 1024 * 1024;
|
|
4
6
|
async function readBody(request) {
|
|
5
7
|
const chunks = [];
|
|
@@ -22,7 +24,15 @@ export async function serveOverHttp(options) {
|
|
|
22
24
|
await options.server.start();
|
|
23
25
|
const http = createServer((request, response) => {
|
|
24
26
|
void (async () => {
|
|
25
|
-
|
|
27
|
+
const target = (request.url ?? '').split('?')[0];
|
|
28
|
+
if (options.page !== undefined && request.method === 'GET' && (target === '/' || target === '/index.html')) {
|
|
29
|
+
// The generated page and the endpoint it talks to, from one process. Nothing here is
|
|
30
|
+
// application-specific: every Axiom application is served by exactly this handler.
|
|
31
|
+
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
32
|
+
response.end(options.page);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (request.method !== 'POST' || target !== path) {
|
|
26
36
|
response.writeHead(404, { 'content-type': 'application/json' });
|
|
27
37
|
response.end(JSON.stringify({ kind: 'error', diagnostics: [] }));
|
|
28
38
|
return;
|
|
@@ -61,3 +71,19 @@ export async function serveOverHttp(options) {
|
|
|
61
71
|
}),
|
|
62
72
|
};
|
|
63
73
|
}
|
|
74
|
+
export async function serveAxiomApplication(options) {
|
|
75
|
+
const server = createAxiomServer({
|
|
76
|
+
ir: options.serverIR,
|
|
77
|
+
...(options.persistence ? { persistence: options.persistence } : {}),
|
|
78
|
+
...(options.authenticate
|
|
79
|
+
? { host: createServerHost({ authenticate: options.authenticate }) }
|
|
80
|
+
: {}),
|
|
81
|
+
});
|
|
82
|
+
const running = await serveOverHttp({
|
|
83
|
+
server,
|
|
84
|
+
page: options.page,
|
|
85
|
+
...(options.port === undefined ? {} : { port: options.port }),
|
|
86
|
+
...(options.path === undefined ? {} : { path: options.path }),
|
|
87
|
+
});
|
|
88
|
+
return { ...running, pageUrl: `http://127.0.0.1:${running.port}/`, server };
|
|
89
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -13,7 +13,15 @@ export interface SnapshotRequest {
|
|
|
13
13
|
kind: 'snapshot';
|
|
14
14
|
protocol: typeof PROTOCOL_VERSION;
|
|
15
15
|
credential?: Credential;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Ask for an incremental snapshot: a revision this caller already holds.
|
|
18
|
+
*
|
|
19
|
+
* The response then omits every observable state the authority can **prove** has not
|
|
20
|
+
* changed since that revision, and sets `partial`. Omission always means unchanged;
|
|
21
|
+
* inclusion never promises changed. A value that is not a non-negative safe integer is a
|
|
22
|
+
* malformed request; a value the authority cannot reason about — ahead of its own
|
|
23
|
+
* revision — is answered with a complete snapshot, which is always a valid answer.
|
|
24
|
+
*/
|
|
17
25
|
sinceRevision?: number;
|
|
18
26
|
}
|
|
19
27
|
export interface InvokeRequest {
|
|
@@ -30,10 +38,19 @@ export interface InvokeRequest {
|
|
|
30
38
|
requestId?: string;
|
|
31
39
|
}
|
|
32
40
|
export type ServerRequest = SnapshotRequest | InvokeRequest;
|
|
33
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The authoritative value of the observable states the caller may see.
|
|
43
|
+
*
|
|
44
|
+
* Complete unless `partial` is set. A complete snapshot names every observable state; a
|
|
45
|
+
* partial one names those that may have changed since the `sinceRevision` that was asked
|
|
46
|
+
* for, and a state it does not name is unchanged since then. `revision` is the authority's
|
|
47
|
+
* current revision either way, so a client can use it as the next `sinceRevision`.
|
|
48
|
+
*/
|
|
34
49
|
export interface StateSnapshot {
|
|
35
50
|
revision: number;
|
|
36
51
|
states: Record<NodeId, unknown>;
|
|
52
|
+
/** Present and true only for the answer to a `sinceRevision` request. */
|
|
53
|
+
partial?: boolean;
|
|
37
54
|
}
|
|
38
55
|
export interface InvokeResponse {
|
|
39
56
|
kind: 'result';
|
package/dist/server.d.ts
CHANGED
|
@@ -42,17 +42,5 @@ export interface AxiomServer {
|
|
|
42
42
|
mutationLog(): MutationLogEntry[];
|
|
43
43
|
stop(): Promise<void>;
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
46
|
-
* The authoritative runtime.
|
|
47
|
-
*
|
|
48
|
-
* It executes the **same semantic engine** the client runs, given an IR that contains no UI
|
|
49
|
-
* and no routes. That is deliberate rather than convenient: transaction boundaries,
|
|
50
|
-
* provisional writes, `for-each` ordering, constraint and transition evaluation, rollback
|
|
51
|
-
* and the mutation log are not reimplemented here, so a graph cannot behave differently
|
|
52
|
-
* merely because execution moved to the authority.
|
|
53
|
-
*
|
|
54
|
-
* Requests are serialized. One action runs at a time, and its persistence commit completes
|
|
55
|
-
* before the next begins, so two callers cannot both commit from the same snapshot.
|
|
56
|
-
*/
|
|
57
45
|
export declare function createAxiomServer(options: AxiomServerOptions): AxiomServer;
|
|
58
46
|
//# sourceMappingURL=server.d.ts.map
|