-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
284 lines (272 loc) · 10.5 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
import type { Context, Group, Read, Subject, Update } from 'json-rql';
import type { Observable } from 'rxjs';
/**
* A **m-ld** clone represents domain data to an app. This is the abstract
* interface implemented by a clone engine. It adheres to the **m-ld** data
* [concurrency](https://m-ld.org/doc/#concurrency) contract.
*
* > This abstract definition will be realised differently depending on the
* > capabilities and idioms of the engine platform. It may offer additional
* > data features, such as data persistence between re-starts; and API features
* > such as language-specific convenience methods. Refer to the clone
* > [engine](https://m-ld.org/doc/#platforms) documentation to begin using it.
*/
export interface MeldClone {
/**
* Actively reads data from the domain.
*
* An engine can legitimately offer a limited subset of the full **json-rql**
* syntax for the `request` parameter, and reject patterns that it does not
* support with an `Unsupported pattern` error.
*
* @param request the declarative read description
* @returns an observable stream of subjects.
* @see [Read](http://json-rql.org/interfaces/read.html)
* @see [Subject](http://json-rql.org/interfaces/subject.html)
*/
read(request: Read): Observable<Subject>;
/**
* Actively writes data to the domain. A write can be:
* - A [Subject](http://json-rql.org/interfaces/subject.html) (any JSON object
* not a Read, Group or Update). Interpreted as data to be inserted.
* - A [Group](http://json-rql.org/interfaces/group.html) containing only a
* `@graph` key. Interpreted as containing the data to be inserted.
* - An [Update](http://json-rql.org/interfaces/update.html) with either an
* `@insert`, `@delete`, or both.
*
* An engine can legitimately offer a limited subset of the full **json-rql**
* syntax for the `request` parameter, and reject patterns that it does not
* support with an `Unsupported pattern` error.
*
* @param request the declarative transaction description
* @returns final completion or error of the transaction.
*/
write(request: Subject | Group | Update): Promise<unknown>;
/**
* Follow updates from the domain. All data changes are signalled through the
* returned stream, strictly ordered according to the clone's logical clock.
* The updates can therefore be correctly used to maintain some other view of
* data, for example in a user interface or separate database.
*
* @param after updates will be emitted to the returned stream after (not
* including) the given tick count for the clone's logical clock. This tick
* count can be in the past or future. If the clone is unable to recall
* updates from a too-distant past, the stream will fail with `Updates
* unavailable`.
* @returns an observable stream of updates from the domain.
*/
follow(after?: number): Observable<MeldUpdate>;
/**
* The current and future status of a clone. This stream is hot and
* continuous, terminating when the clone closes (and can therefore be used to
* detect closure).
*/
readonly status: LiveStatus;
}
/**
* An update event signalling a write operation, which may have been transacted
* locally in this clone, or remotely on another clone.
*/
export interface MeldUpdate {
/**
* Partial subjects, containing properties that have been deleted from the
* domain. Note that deletion of a property (even of all properties) does not
* necessarily indicate that the subject's identity is not longer represented
* in the domain.
*/
'@delete': Subject[];
/**
* Partial subjects, containing properties that have been inserted into the
* domain.
*/
'@insert': Subject[];
/**
* Current local clock ticks at the time of the update.
* @see MeldStatus.ticks
*/
'@ticks': number;
}
/**
* A means to obtain the current status, and await future statuses, of a clone.
*/
export interface LiveStatus extends Observable<MeldStatus> {
/**
* The current clone status
*/
readonly value: MeldStatus;
/**
* @returns a promise of a future status matching the given partial status
* (with the exception of the `@ticks` field, which is ignored if specified).
* If the clone never achieves the requested status, the promise will resolve
* to `undefined` when the clone closes.
*/
becomes: (match?: Partial<MeldStatus>) => Promise<MeldStatus | undefined>;
}
export interface MeldStatus {
/**
* Whether the clone is attached to the domain and able to receive updates.
*/
online: boolean;
/**
* Whether the clone needs to catch-up with the latest updates from the
* domain. For convenience, this flag will have the value `false` in
* indeterminate scenarios such as if there are no other live clones on the
* domain (this is a "silo").
*/
outdated: boolean;
/**
* Whether this clone is the only one attached to a domain. Being a silo may
* be a danger to data safety, as any changes made to a silo clone are not
* being backed-up on any other clone.
*/
silo: boolean;
/**
* Current local clock ticks at the time of the status change. This can be
* used in a subsequent call to {@link MeldClone.follow}, to ensure no updates
* are missed.
*
* This clock is *strictly* local, and there is no relationship between the
* clock ticks of one clone and that of another, even for the same transaction.
*
* @see MeldUpdate.@ticks
*/
ticks: number;
}
/**
* **m-ld** clone configuration, used to initialise a {@link MeldClone} for use.
* The use of this interface is optional, since clone initialisation is part of
* an engine's application API.
*/
export interface MeldConfig {
/**
* The local identity of the m-ld clone session, used for message bus identity
* and logging. This identity does not need to be the same across re-starts of
* a clone with persistent data. It must be unique among the clones for the
* domain.
*/
'@id': string;
/**
* A URI domain name, which defines the universal identity of the dataset
* being manipulated by a set of clones (for example, on the configured
* message bus). For a clone with persistent data from a prior session, this
* *must* be the same as the previous session.
*/
'@domain': string;
/**
* Set to `true` to indicate that this clone will be 'genesis'; that is, the
* first new clone on a new domain. This flag will be ignored if the clone is
* not new. If `false`, and this clone is new, successful clone initialisation
* is dependent on the availability of another clone. If set to true, and
* subsequently another non-new clone appears on the same domain, either or
* both clones will immediately close to preserve their data integrity.
*/
genesis: boolean;
/**
* An optional JSON-LD context for the domain data. If not specified:
* * `@base` defaults to `http://{domain}`
* * `@vocab` defaults to the resolution of `/#` against the base
*/
'@context'?: Context;
}
// noinspection JSUnusedGlobalSymbols
/**
* Errors that occur in a **m-ld** engine should be signalled with the given
* error codes where appropriate. The means by which errors are signalled is
* platform-specific.
*/
export enum MeldErrorStatus {
/**
* No error has occurred.
*/
'No error' = 0,
//////////////////////////////////////////////////////////////////////////////
// Bad request errors
/**
* A **json-rql** pattern has been specified that neither reads nor writes
* data, for example a Group with variable content.
*/
'Pattern is not read or writeable' = 4001,
/**
* The requested transaction results in a delta that is too large to transmit
* using the current message publishing implementation.
*/
'Delta too big' = 4002,
//////////////////////////////////////////////////////////////////////////////
// Unauthorised error
/**
* A request was made for data for which the current security principal does
* not have access rights.
*/
'Unauthorised' = 4030,
//////////////////////////////////////////////////////////////////////////////
// Not found error
/**
* A request was made for updates in the too-distant past. This can occur when
* following clone updates, or when re-starting a clone that has been offline
* for too long.
*/
'Updates unavailable' = 4041,
//////////////////////////////////////////////////////////////////////////////
// Internal errors
/**
* A serious error has occurred in the engine implementation.
*/
'Unknown error' = 5000,
/**
* The engine has received an update that it cannot parse. This may be due to
* a version inconsistency, or a bad actor.
*/
'Bad update' = 5001,
/**
* The engine has received a response that it cannot parse. This may be due
* to a version inconsistency, or a bad actor.
*/
'Bad response' = 5002,
/**
* The engine has received a rejection from another engine on the domain to
* one of its requests. This could lead to the clone failing to initialise, or
* shutting down shortly after initialisation.
*/
'Request rejected' = 5003,
/**
* The engine has attempted an operation that requires other clones to be
* visible. This indicates a concurrency problem in the engine.
*/
'Meld is offline' = 5004,
/**
* An update from another clone has arrived out-of-order, and the clone has
* not been able to recover. This indicates a concurrency problem in the
* engine.
*/
'Update out of order' = 5005,
//////////////////////////////////////////////////////////////////////////////
// Unsupported operations
/**
* The engine does not support the pattern in the transaction request.
*/
'Unsupported pattern' = 5011,
//////////////////////////////////////////////////////////////////////////////
// Service unavailable
/**
* This is a new clone on the domain, but no other clones are visible,
* possibly due to a network partition. The clone cannot initialise.
*/
'No visible clones' = 5031,
/**
* This clone has been closed, explicitly by the app or due to an error. All
* subsequent transactions will fail.
*/
'Clone has closed' = 5032,
/**
* The clone data is not writeable due to a platform limitation such as file
* locking, or concurrent access controls.
*/
'Clone data is locked' = 5034,
/**
* The clone's data is out of date and no other clones have kept sufficient
* information to recover it. The app could re-try initialising the clone
* later if, for example, the architecture includes a clone which keeps a long
* history, but it is currently unavailable.
*/
'Clone outdated' = 5035
}