@earendil-works/chord 0.87.1 → 0.99.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 +80 -27
- package/dist/api.d.ts +6 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +8 -3
- package/dist/api.js.map +1 -1
- package/dist/bundler.d.ts.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js.map +1 -1
- package/dist/delta/apply-immutable-trusted.d.ts +4 -0
- package/dist/delta/apply-immutable-trusted.d.ts.map +1 -0
- package/dist/delta/apply-immutable-trusted.js +127 -0
- package/dist/delta/apply-immutable-trusted.js.map +1 -0
- package/dist/{state → delta}/diff.d.ts +1 -1
- package/dist/delta/diff.d.ts.map +1 -0
- package/dist/delta/diff.js +433 -0
- package/dist/delta/diff.js.map +1 -0
- package/dist/delta/draft.d.ts +5 -0
- package/dist/delta/draft.d.ts.map +1 -0
- package/dist/delta/draft.js +2 -0
- package/dist/delta/draft.js.map +1 -0
- package/dist/delta/index.d.ts +11 -21
- package/dist/delta/index.d.ts.map +1 -1
- package/dist/delta/index.js +43 -1341
- package/dist/delta/index.js.map +1 -1
- package/dist/delta/revision-validator.d.ts +7 -0
- package/dist/delta/revision-validator.d.ts.map +1 -0
- package/dist/delta/revision-validator.js +71 -0
- package/dist/delta/revision-validator.js.map +1 -0
- package/dist/delta/tracker.d.ts +26 -0
- package/dist/delta/tracker.d.ts.map +1 -0
- package/dist/delta/tracker.js +2069 -0
- package/dist/delta/tracker.js.map +1 -0
- package/dist/facets/host.d.ts.map +1 -1
- package/dist/facets/host.js.map +1 -1
- package/dist/facets/loader.d.ts.map +1 -1
- package/dist/facets/loader.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts +6 -0
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +73 -6
- package/dist/json.js.map +1 -1
- package/dist/node/bundle-loader.d.ts.map +1 -1
- package/dist/node/bundle-loader.js.map +1 -1
- package/dist/node/bundle.d.ts.map +1 -1
- package/dist/node/bundle.js.map +1 -1
- package/dist/node/manifest.d.ts.map +1 -1
- package/dist/node/package.d.ts.map +1 -1
- package/dist/node/package.js.map +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/services/consumer.d.ts.map +1 -1
- package/dist/services/consumer.js +49 -1
- package/dist/services/consumer.js.map +1 -1
- package/dist/services/errors.d.ts.map +1 -1
- package/dist/services/errors.js.map +1 -1
- package/dist/services/handle.d.ts.map +1 -1
- package/dist/services/handle.js +3 -1
- package/dist/services/handle.js.map +1 -1
- package/dist/services/instances.d.ts +1 -0
- package/dist/services/instances.d.ts.map +1 -1
- package/dist/services/instances.js +3 -0
- package/dist/services/instances.js.map +1 -1
- package/dist/services/loopback.d.ts.map +1 -1
- package/dist/services/loopback.js.map +1 -1
- package/dist/services/provider.d.ts.map +1 -1
- package/dist/services/provider.js +48 -29
- package/dist/services/provider.js.map +1 -1
- package/dist/services/state-codec.d.ts.map +1 -1
- package/dist/services/state-codec.js +18 -0
- package/dist/services/state-codec.js.map +1 -1
- package/dist/services/state-internals.d.ts +5 -2
- package/dist/services/state-internals.d.ts.map +1 -1
- package/dist/services/state-internals.js.map +1 -1
- package/dist/services/state.d.ts +8 -3
- package/dist/services/state.d.ts.map +1 -1
- package/dist/services/state.js +288 -88
- package/dist/services/state.js.map +1 -1
- package/dist/services/wire.d.ts +3 -0
- package/dist/services/wire.d.ts.map +1 -1
- package/dist/services/wire.js +13 -0
- package/dist/services/wire.js.map +1 -1
- package/dist/types.d.ts +74 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/src/delta/README.md +143 -360
- package/dist/state/diff.d.ts.map +0 -1
- package/dist/state/diff.js +0 -259
- package/dist/state/diff.js.map +0 -1
- package/dist/state/draft.d.ts +0 -13
- package/dist/state/draft.d.ts.map +0 -1
- package/dist/state/draft.js +0 -540
- package/dist/state/draft.js.map +0 -1
- package/dist/state/value.d.ts +0 -9
- package/dist/state/value.d.ts.map +0 -1
- package/dist/state/value.js +0 -175
- package/dist/state/value.js.map +0 -1
package/dist/services/state.js
CHANGED
|
@@ -1,119 +1,295 @@
|
|
|
1
1
|
import { BACKGROUND_CONTEXT } from "../context/index.js";
|
|
2
|
-
import { applyImmutable, isBase } from "../delta/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import { produceWithMetadata } from "../state/draft.js";
|
|
5
|
-
import { JsonRevisionStore } from "../state/value.js";
|
|
2
|
+
import { applyImmutable, isBase, track } from "../delta/index.js";
|
|
3
|
+
import { JsonRevisionValidator } from "../delta/revision-validator.js";
|
|
6
4
|
import { registerReplicatedStateInternals } from "./state-internals.js";
|
|
7
|
-
|
|
5
|
+
/** One public subscription, independent of producer and other subscriber progress. */
|
|
6
|
+
class StateSubscriber {
|
|
7
|
+
#listener;
|
|
8
|
+
#reportError;
|
|
9
|
+
#pending = [];
|
|
10
|
+
#running = false;
|
|
11
|
+
#started = false;
|
|
12
|
+
#closed = false;
|
|
13
|
+
constructor(listener, reportError) {
|
|
14
|
+
this.#listener = listener;
|
|
15
|
+
this.#reportError = reportError;
|
|
16
|
+
}
|
|
17
|
+
push(frame) {
|
|
18
|
+
if (this.#closed)
|
|
19
|
+
return;
|
|
20
|
+
if (this.#pending.length === 100) {
|
|
21
|
+
// A cold replica can queue updates reentrantly before this subscriber's first hydration starts.
|
|
22
|
+
const hydration = this.#started ? undefined : this.#pending[0];
|
|
23
|
+
this.#pending.length = 0;
|
|
24
|
+
if (hydration !== undefined)
|
|
25
|
+
this.#pending.push(hydration);
|
|
26
|
+
}
|
|
27
|
+
this.#pending.push(frame);
|
|
28
|
+
}
|
|
29
|
+
drain() {
|
|
30
|
+
if (this.#running || this.#closed)
|
|
31
|
+
return;
|
|
32
|
+
this.#running = true;
|
|
33
|
+
for (let frame = this.#pending.shift(); frame !== undefined; frame = this.#pending.shift()) {
|
|
34
|
+
this.#started = true;
|
|
35
|
+
try {
|
|
36
|
+
const result = this.#listener(frame.value, frame.context, frame.delivery);
|
|
37
|
+
if (isPromiseLike(result)) {
|
|
38
|
+
void Promise.resolve(result).then(() => this.#resume(), (error) => {
|
|
39
|
+
this.#report(error);
|
|
40
|
+
this.#resume();
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.#report(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.#running = false;
|
|
50
|
+
}
|
|
51
|
+
clear() {
|
|
52
|
+
this.#pending.length = 0;
|
|
53
|
+
}
|
|
54
|
+
close() {
|
|
55
|
+
this.#closed = true;
|
|
56
|
+
this.clear();
|
|
57
|
+
}
|
|
58
|
+
#resume() {
|
|
59
|
+
this.#running = false;
|
|
60
|
+
this.drain();
|
|
61
|
+
}
|
|
62
|
+
#report(error) {
|
|
63
|
+
try {
|
|
64
|
+
this.#reportError(toError(error));
|
|
65
|
+
}
|
|
66
|
+
catch (reportError) {
|
|
67
|
+
reportErrorAsync(toError(reportError));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Maintains local publication order independently of how revisions are produced. */
|
|
72
|
+
class ReplicatedStatePublisher {
|
|
8
73
|
#listeners = new Map();
|
|
74
|
+
#reportError;
|
|
9
75
|
#sourceListeners = new Set();
|
|
10
|
-
#store = new JsonRevisionStore();
|
|
11
76
|
#publications = [];
|
|
12
77
|
#value;
|
|
13
78
|
#sequence = 0;
|
|
14
|
-
#changing = false;
|
|
15
79
|
#delivering = false;
|
|
80
|
+
constructor(initial, reportError = reportErrorAsync) {
|
|
81
|
+
this.#value = initial;
|
|
82
|
+
this.#reportError = reportError;
|
|
83
|
+
}
|
|
84
|
+
get value() {
|
|
85
|
+
return this.#value;
|
|
86
|
+
}
|
|
87
|
+
snapshot() {
|
|
88
|
+
return { value: this.#value, sequence: this.#sequence };
|
|
89
|
+
}
|
|
90
|
+
subscribe(listener) {
|
|
91
|
+
const { value, sequence } = this.snapshot();
|
|
92
|
+
const subscriber = new StateSubscriber(listener, this.#reportError);
|
|
93
|
+
this.#listeners.set(subscriber, sequence);
|
|
94
|
+
subscriber.push({ value, context: serviceDeliveryContext(), delivery: { kind: "hydrate", sequence } });
|
|
95
|
+
subscriber.drain();
|
|
96
|
+
return () => {
|
|
97
|
+
subscriber.close();
|
|
98
|
+
this.#listeners.delete(subscriber);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
subscribeSource(listener) {
|
|
102
|
+
this.#sourceListeners.add(listener);
|
|
103
|
+
return () => this.#sourceListeners.delete(listener);
|
|
104
|
+
}
|
|
105
|
+
/** Publish an already-prepared immutable revision and return isolated listener failures. */
|
|
106
|
+
publish(value, ops, context) {
|
|
107
|
+
this.#value = value;
|
|
108
|
+
this.#sequence += 1;
|
|
109
|
+
this.#publications.push({ value, ops, sequence: this.#sequence, context });
|
|
110
|
+
if (this.#delivering)
|
|
111
|
+
return [];
|
|
112
|
+
this.#delivering = true;
|
|
113
|
+
const errors = [];
|
|
114
|
+
try {
|
|
115
|
+
for (let publication = this.#publications.shift(); publication !== undefined; publication = this.#publications.shift()) {
|
|
116
|
+
for (const listener of [...this.#sourceListeners]) {
|
|
117
|
+
try {
|
|
118
|
+
listener(publication.ops, publication.sequence, publication.context);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
errors.push(error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const delivery = { kind: "update", sequence: publication.sequence };
|
|
125
|
+
for (const [subscriber, hydratedSequence] of [...this.#listeners]) {
|
|
126
|
+
if (publication.sequence <= hydratedSequence)
|
|
127
|
+
continue;
|
|
128
|
+
subscriber.push({ value: publication.value, context: publication.context, delivery });
|
|
129
|
+
subscriber.drain();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
this.#delivering = false;
|
|
135
|
+
}
|
|
136
|
+
return errors;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export class MutableReplicatedStateImpl {
|
|
140
|
+
#tracker;
|
|
141
|
+
#publisher;
|
|
142
|
+
#changing = false;
|
|
16
143
|
constructor(initial) {
|
|
17
|
-
this.#
|
|
18
|
-
|
|
144
|
+
this.#tracker = track(initial);
|
|
145
|
+
this.#publisher = new ReplicatedStatePublisher(this.#tracker.value);
|
|
19
146
|
registerReplicatedStateInternals(this, {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
get value() {
|
|
24
|
-
return thisSource.#value;
|
|
25
|
-
},
|
|
26
|
-
subscribe: (listener) => {
|
|
27
|
-
thisSource.#sourceListeners.add(listener);
|
|
28
|
-
return () => thisSource.#sourceListeners.delete(listener);
|
|
29
|
-
},
|
|
147
|
+
snapshot: () => this.#publisher.snapshot(),
|
|
148
|
+
subscribe: (listener) => this.#publisher.subscribeSource(listener),
|
|
30
149
|
});
|
|
31
150
|
}
|
|
32
151
|
get value() {
|
|
33
|
-
return this.#value;
|
|
152
|
+
return this.#tracker.value;
|
|
34
153
|
}
|
|
35
154
|
change(context, mutate) {
|
|
36
155
|
if (this.#changing)
|
|
37
156
|
throw new Error("Replicated state cannot be changed reentrantly from a change callback");
|
|
38
157
|
this.#changing = true;
|
|
39
|
-
let
|
|
158
|
+
let prepared;
|
|
40
159
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
160
|
+
const change = this.#tracker.beginChange();
|
|
161
|
+
try {
|
|
162
|
+
const outcome = mutate(change.state);
|
|
163
|
+
if (isPromiseLike(outcome)) {
|
|
164
|
+
void Promise.resolve(outcome).catch(() => undefined);
|
|
165
|
+
throw new TypeError("Replicated state change callbacks must be synchronous");
|
|
166
|
+
}
|
|
167
|
+
prepared = change.prepare();
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
change.abort();
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
45
173
|
}
|
|
46
174
|
finally {
|
|
47
175
|
this.#changing = false;
|
|
48
176
|
}
|
|
49
|
-
this.#
|
|
177
|
+
this.#tracker.adopt(prepared);
|
|
178
|
+
if (prepared.ops.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
throwCollectedErrors(this.#publisher.publish(prepared.value, prepared.ops, context), "Replicated state listeners failed");
|
|
50
181
|
}
|
|
51
182
|
replace(context, value) {
|
|
52
183
|
if (this.#changing)
|
|
53
184
|
throw new Error("Replicated state cannot be replaced from a change callback");
|
|
54
|
-
this.#
|
|
185
|
+
const prepared = this.#tracker.prepareReplace(value);
|
|
186
|
+
this.#tracker.adopt(prepared);
|
|
187
|
+
if (prepared.ops.length === 0)
|
|
188
|
+
return;
|
|
189
|
+
throwCollectedErrors(this.#publisher.publish(prepared.value, prepared.ops, context), "Replicated state listeners failed");
|
|
55
190
|
}
|
|
56
191
|
subscribe(listener) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
192
|
+
return this.#publisher.subscribe(listener);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
class AttachedReplicatedStateImpl {
|
|
196
|
+
#publisher;
|
|
197
|
+
#attachment;
|
|
198
|
+
#reportError;
|
|
199
|
+
#cursor;
|
|
200
|
+
#disposed = false;
|
|
201
|
+
constructor(attachment, options) {
|
|
202
|
+
const { snapshot } = attachment;
|
|
203
|
+
assertCursor(snapshot.cursor, "snapshot");
|
|
204
|
+
this.#attachment = attachment;
|
|
205
|
+
this.#cursor = snapshot.cursor;
|
|
206
|
+
this.#reportError = options.onError ?? reportErrorAsync;
|
|
207
|
+
this.#publisher = new ReplicatedStatePublisher(snapshot.value, (error) => this.#report(error));
|
|
208
|
+
registerReplicatedStateInternals(this, {
|
|
209
|
+
snapshot: () => this.#publisher.snapshot(),
|
|
210
|
+
subscribe: (listener) => this.#publisher.subscribeSource(listener),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
get value() {
|
|
214
|
+
return this.#publisher.value;
|
|
215
|
+
}
|
|
216
|
+
subscribe(listener) {
|
|
217
|
+
return this.#publisher.subscribe(listener);
|
|
218
|
+
}
|
|
219
|
+
activate() {
|
|
220
|
+
this.#attachment.activate((frame) => this.#receive(frame));
|
|
221
|
+
}
|
|
222
|
+
dispose() {
|
|
223
|
+
if (this.#disposed)
|
|
224
|
+
return;
|
|
225
|
+
this.#disposed = true;
|
|
226
|
+
this.#attachment.dispose();
|
|
227
|
+
}
|
|
228
|
+
#receive(frame) {
|
|
229
|
+
if (this.#disposed)
|
|
230
|
+
return;
|
|
60
231
|
try {
|
|
61
|
-
|
|
232
|
+
assertCursor(frame.cursor, "frame");
|
|
233
|
+
const expected = this.#cursor + 1;
|
|
234
|
+
if (frame.cursor !== expected) {
|
|
235
|
+
throw new Error(`Replicated state source cursor has a gap: expected ${expected}, received ${frame.cursor}`);
|
|
236
|
+
}
|
|
237
|
+
this.#cursor = frame.cursor;
|
|
238
|
+
const errors = this.#publisher.publish(frame.value, frame.ops, frame.context);
|
|
239
|
+
if (errors.length === 1)
|
|
240
|
+
this.#report(errors[0]);
|
|
241
|
+
else if (errors.length > 1)
|
|
242
|
+
this.#report(new AggregateError(errors, "Replicated state listeners failed"));
|
|
62
243
|
}
|
|
63
244
|
catch (error) {
|
|
64
|
-
this.#
|
|
65
|
-
throw error;
|
|
245
|
+
this.#fail(toError(error));
|
|
66
246
|
}
|
|
67
|
-
return () => this.#listeners.delete(listener);
|
|
68
247
|
}
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
if (ops.length === 0)
|
|
248
|
+
#fail(error) {
|
|
249
|
+
if (this.#disposed)
|
|
72
250
|
return;
|
|
73
|
-
this.#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
251
|
+
this.#disposed = true;
|
|
252
|
+
try {
|
|
253
|
+
this.#attachment.dispose();
|
|
254
|
+
}
|
|
255
|
+
catch (disposeError) {
|
|
256
|
+
this.#report(new AggregateError([error, disposeError], "Replicated state source contract failed"));
|
|
77
257
|
return;
|
|
78
|
-
|
|
79
|
-
|
|
258
|
+
}
|
|
259
|
+
this.#report(error);
|
|
260
|
+
}
|
|
261
|
+
#report(error) {
|
|
80
262
|
try {
|
|
81
|
-
|
|
82
|
-
for (const listener of [...this.#sourceListeners]) {
|
|
83
|
-
try {
|
|
84
|
-
listener(publication.ops, publication.sequence, publication.context);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
errors.push(error);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const delivery = { kind: "update", sequence: publication.sequence };
|
|
91
|
-
for (const [listener, hydratedSequence] of [...this.#listeners]) {
|
|
92
|
-
if (publication.sequence <= hydratedSequence)
|
|
93
|
-
continue;
|
|
94
|
-
try {
|
|
95
|
-
listener(publication.value, publication.context, delivery);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
errors.push(error);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
263
|
+
this.#reportError(toError(error));
|
|
102
264
|
}
|
|
103
|
-
|
|
104
|
-
|
|
265
|
+
catch (reportError) {
|
|
266
|
+
reportErrorAsync(toError(reportError));
|
|
105
267
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/** Attach a publication-only replicated state to one authoritative immutable source stream. */
|
|
271
|
+
export function attachReplicatedStateSource(source, options = {}) {
|
|
272
|
+
const attachment = source.attach();
|
|
273
|
+
try {
|
|
274
|
+
const state = new AttachedReplicatedStateImpl(attachment, options);
|
|
275
|
+
state.activate();
|
|
276
|
+
return state;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
try {
|
|
280
|
+
attachment.dispose();
|
|
281
|
+
}
|
|
282
|
+
catch (disposeError) {
|
|
283
|
+
throw new AggregateError([error, disposeError], "Failed to attach replicated state source");
|
|
284
|
+
}
|
|
285
|
+
throw error;
|
|
110
286
|
}
|
|
111
287
|
}
|
|
112
288
|
/** A cold read-only state used by service consumers until a complete snapshot arrives. */
|
|
113
289
|
export class ReplicatedStateReplica {
|
|
114
290
|
#listeners = new Set();
|
|
115
291
|
#reportError;
|
|
116
|
-
#
|
|
292
|
+
#validator = new JsonRevisionValidator();
|
|
117
293
|
#value;
|
|
118
294
|
#sequence;
|
|
119
295
|
constructor(reportError) {
|
|
@@ -123,21 +299,27 @@ export class ReplicatedStateReplica {
|
|
|
123
299
|
return this.#value;
|
|
124
300
|
}
|
|
125
301
|
subscribe(listener) {
|
|
126
|
-
this.#
|
|
302
|
+
const subscriber = new StateSubscriber(listener, this.#reportError);
|
|
303
|
+
this.#listeners.add(subscriber);
|
|
127
304
|
if (this.#value !== undefined) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
305
|
+
subscriber.push({
|
|
306
|
+
value: this.#value,
|
|
307
|
+
context: serviceDeliveryContext(),
|
|
308
|
+
delivery: { kind: "hydrate", sequence: this.#sequence },
|
|
131
309
|
});
|
|
310
|
+
subscriber.drain();
|
|
132
311
|
}
|
|
133
|
-
return () =>
|
|
312
|
+
return () => {
|
|
313
|
+
subscriber.close();
|
|
314
|
+
this.#listeners.delete(subscriber);
|
|
315
|
+
};
|
|
134
316
|
}
|
|
135
317
|
hydrate(sequence, ops, context) {
|
|
136
318
|
let next;
|
|
137
319
|
try {
|
|
138
320
|
if (!isBase(ops))
|
|
139
321
|
throw new Error("Replicated state snapshot is not a base operation batch");
|
|
140
|
-
next = this.#
|
|
322
|
+
next = this.#validator.validate(applyImmutable(undefined, ops));
|
|
141
323
|
}
|
|
142
324
|
catch (error) {
|
|
143
325
|
this.clear();
|
|
@@ -157,7 +339,7 @@ export class ReplicatedStateReplica {
|
|
|
157
339
|
}
|
|
158
340
|
let next;
|
|
159
341
|
try {
|
|
160
|
-
next = this.#
|
|
342
|
+
next = this.#validator.validate(applyImmutable(this.#value, ops));
|
|
161
343
|
}
|
|
162
344
|
catch (error) {
|
|
163
345
|
this.clear();
|
|
@@ -170,20 +352,19 @@ export class ReplicatedStateReplica {
|
|
|
170
352
|
clear() {
|
|
171
353
|
this.#value = undefined;
|
|
172
354
|
this.#sequence = undefined;
|
|
355
|
+
for (const subscriber of this.#listeners)
|
|
356
|
+
subscriber.clear();
|
|
173
357
|
}
|
|
174
358
|
#deliverAll(context, delivery) {
|
|
175
359
|
if (this.#value === undefined)
|
|
176
360
|
return;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
catch (error) {
|
|
185
|
-
this.#reportError(toError(error));
|
|
186
|
-
}
|
|
361
|
+
const frame = { value: this.#value, context, delivery };
|
|
362
|
+
const subscribers = [...this.#listeners];
|
|
363
|
+
// Enqueue for everyone before user code can publish another revision reentrantly.
|
|
364
|
+
for (const subscriber of subscribers)
|
|
365
|
+
subscriber.push(frame);
|
|
366
|
+
for (const subscriber of subscribers)
|
|
367
|
+
subscriber.drain();
|
|
187
368
|
}
|
|
188
369
|
}
|
|
189
370
|
/** @internal Context for synthetic service deliveries without a caller. */
|
|
@@ -191,6 +372,25 @@ export function serviceDeliveryContext() {
|
|
|
191
372
|
// TODO: Add delivery-scoped cancellation or metadata if deliveries gain an owned lifecycle.
|
|
192
373
|
return BACKGROUND_CONTEXT;
|
|
193
374
|
}
|
|
375
|
+
function assertCursor(cursor, kind) {
|
|
376
|
+
if (!Number.isSafeInteger(cursor))
|
|
377
|
+
throw new TypeError(`Replicated state source ${kind} cursor must be a safe integer`);
|
|
378
|
+
}
|
|
379
|
+
function isPromiseLike(value) {
|
|
380
|
+
return (((typeof value === "object" && value !== null) || typeof value === "function") &&
|
|
381
|
+
typeof value.then === "function");
|
|
382
|
+
}
|
|
383
|
+
function throwCollectedErrors(errors, message) {
|
|
384
|
+
if (errors.length === 1)
|
|
385
|
+
throw errors[0];
|
|
386
|
+
if (errors.length > 1)
|
|
387
|
+
throw new AggregateError(errors, message);
|
|
388
|
+
}
|
|
389
|
+
function reportErrorAsync(error) {
|
|
390
|
+
queueMicrotask(() => {
|
|
391
|
+
throw error;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
194
394
|
function toError(error) {
|
|
195
395
|
return error instanceof Error ? error : new Error(String(error));
|
|
196
396
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/services/state.ts"],"names":[],"mappings":"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","sourcesContent":["import { BACKGROUND_CONTEXT } from \"../context/index.ts\";\nimport { applyImmutable, isBase, type Op } from \"../delta/index.ts\";\nimport { diffRevisions } from \"../state/diff.ts\";\nimport { produceWithMetadata } from \"../state/draft.ts\";\nimport { JsonRevisionStore } from \"../state/value.ts\";\nimport type { Context, JsonValue, MutableReplicatedState, ReplicatedState, ReplicatedStateDelivery } from \"../types.ts\";\nimport { registerReplicatedStateInternals } from \"./state-internals.ts\";\n\ntype Publication<T> = {\n\tvalue: T;\n\tops: readonly Op[];\n\tsequence: number;\n\tcontext: Context;\n};\n\nexport class MutableReplicatedStateImpl<T extends object> implements MutableReplicatedState<T> {\n\treadonly #listeners = new Map<(value: T, context: Context, delivery: ReplicatedStateDelivery) => void, number>();\n\treadonly #sourceListeners = new Set<(ops: readonly Op[], sequence: number, context: Context) => void>();\n\treadonly #store = new JsonRevisionStore();\n\treadonly #publications: Publication<T>[] = [];\n\t#value: T;\n\t#sequence = 0;\n\t#changing = false;\n\t#delivering = false;\n\n\tconstructor(initial: T) {\n\t\tthis.#value = this.#store.import(initial);\n\t\tconst thisSource = this;\n\t\tregisterReplicatedStateInternals(this, {\n\t\t\tget sequence() {\n\t\t\t\treturn thisSource.#sequence;\n\t\t\t},\n\t\t\tget value() {\n\t\t\t\treturn thisSource.#value;\n\t\t\t},\n\t\t\tsubscribe: (listener) => {\n\t\t\t\tthisSource.#sourceListeners.add(listener);\n\t\t\t\treturn () => thisSource.#sourceListeners.delete(listener);\n\t\t\t},\n\t\t});\n\t}\n\n\tget value(): T {\n\t\treturn this.#value;\n\t}\n\n\tchange(context: Context, mutate: Parameters<MutableReplicatedState<T>[\"change\"]>[1]): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be changed reentrantly from a change callback\");\n\t\tthis.#changing = true;\n\t\tlet next: T;\n\t\ttry {\n\t\t\tconst produced = produceWithMetadata(this.#value, mutate);\n\t\t\tif (produced.value === this.#value) return;\n\t\t\tnext = this.#store.commit(produced.value, produced.owned);\n\t\t} finally {\n\t\t\tthis.#changing = false;\n\t\t}\n\t\tthis.#commit(next, context);\n\t}\n\n\treplace(context: Context, value: T): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be replaced from a change callback\");\n\t\tthis.#commit(this.#store.import(value), context);\n\t}\n\n\tsubscribe(listener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void): () => void {\n\t\tconst sequence = this.#sequence;\n\t\tconst value = this.#value;\n\t\tthis.#listeners.set(listener, sequence);\n\t\ttry {\n\t\t\tlistener(value, serviceDeliveryContext(), { kind: \"hydrate\", sequence });\n\t\t} catch (error) {\n\t\t\tthis.#listeners.delete(listener);\n\t\t\tthrow error;\n\t\t}\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t#commit(next: T, context: Context): void {\n\t\tconst ops = diffRevisions(this.#value as unknown as JsonValue, next as unknown as JsonValue);\n\t\tif (ops.length === 0) return;\n\t\tthis.#value = next;\n\t\tthis.#sequence += 1;\n\t\tthis.#publications.push({ value: next, ops, sequence: this.#sequence, context });\n\t\tif (this.#delivering) return;\n\t\tthis.#delivering = true;\n\t\tconst errors: unknown[] = [];\n\t\ttry {\n\t\t\tfor (\n\t\t\t\tlet publication = this.#publications.shift();\n\t\t\t\tpublication !== undefined;\n\t\t\t\tpublication = this.#publications.shift()\n\t\t\t) {\n\t\t\t\tfor (const listener of [...this.#sourceListeners]) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlistener(publication.ops, publication.sequence, publication.context);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst delivery = { kind: \"update\", sequence: publication.sequence } as const;\n\t\t\t\tfor (const [listener, hydratedSequence] of [...this.#listeners]) {\n\t\t\t\t\tif (publication.sequence <= hydratedSequence) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlistener(publication.value, publication.context, delivery);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#delivering = false;\n\t\t}\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Replicated state listeners failed\");\n\t}\n}\n\n/** A cold read-only state used by service consumers until a complete snapshot arrives. */\nexport class ReplicatedStateReplica<T extends JsonValue = JsonValue> implements ReplicatedState<T> {\n\treadonly #listeners = new Set<(value: T, context: Context, delivery: ReplicatedStateDelivery) => void>();\n\treadonly #reportError: (error: Error) => void;\n\treadonly #store = new JsonRevisionStore();\n\t#value: T | undefined;\n\t#sequence: number | undefined;\n\n\tconstructor(reportError: (error: Error) => void) {\n\t\tthis.#reportError = reportError;\n\t}\n\n\tget value(): T | undefined {\n\t\treturn this.#value;\n\t}\n\n\tsubscribe(listener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void): () => void {\n\t\tthis.#listeners.add(listener);\n\t\tif (this.#value !== undefined) {\n\t\t\tthis.#deliver(listener, this.#value, serviceDeliveryContext(), {\n\t\t\t\tkind: \"hydrate\",\n\t\t\t\tsequence: this.#sequence!,\n\t\t\t});\n\t\t}\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\thydrate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tlet next: T;\n\t\ttry {\n\t\t\tif (!isBase(ops)) throw new Error(\"Replicated state snapshot is not a base operation batch\");\n\t\t\tnext = this.#store.adopt(applyImmutable<T>(undefined, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"hydrate\", sequence });\n\t}\n\n\tupdate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tif (this.#sequence === undefined || this.#value === undefined) {\n\t\t\tthrow new Error(\"Replicated state received an update before hydration\");\n\t\t}\n\t\tif (sequence !== this.#sequence + 1) {\n\t\t\tthis.clear();\n\t\t\tthrow new Error(\"Replicated state update sequence has a gap\");\n\t\t}\n\t\tlet next: T;\n\t\ttry {\n\t\t\tnext = this.#store.adopt(applyImmutable(this.#value, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"update\", sequence });\n\t}\n\n\tclear(): void {\n\t\tthis.#value = undefined;\n\t\tthis.#sequence = undefined;\n\t}\n\n\t#deliverAll(context: Context, delivery: ReplicatedStateDelivery): void {\n\t\tif (this.#value === undefined) return;\n\t\tfor (const listener of this.#listeners) this.#deliver(listener, this.#value, context, delivery);\n\t}\n\n\t#deliver(\n\t\tlistener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void,\n\t\tvalue: T,\n\t\tcontext: Context,\n\t\tdelivery: ReplicatedStateDelivery,\n\t): void {\n\t\ttry {\n\t\t\tlistener(value, context, delivery);\n\t\t} catch (error) {\n\t\t\tthis.#reportError(toError(error));\n\t\t}\n\t}\n}\n\n/** @internal Context for synthetic service deliveries without a caller. */\nexport function serviceDeliveryContext(): Context {\n\t// TODO: Add delivery-scoped cancellation or metadata if deliveries gain an owned lifecycle.\n\treturn BACKGROUND_CONTEXT;\n}\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/services/state.ts"],"names":[],"mappings":"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","sourcesContent":["import { BACKGROUND_CONTEXT } from \"../context/index.ts\";\nimport { applyImmutable, isBase, type Op, type Prepared, type Tracker, track } from \"../delta/index.ts\";\nimport { JsonRevisionValidator } from \"../delta/revision-validator.ts\";\nimport type {\n\tAttachedReplicatedState,\n\tContext,\n\tJsonValue,\n\tMutableReplicatedState,\n\tReplicatedState,\n\tReplicatedStateDelivery,\n\tReplicatedStateSource,\n\tReplicatedStateSourceAttachment,\n\tReplicatedStateSourceFrame,\n\tReplicatedStateSourceOptions,\n} from \"../types.ts\";\nimport { registerReplicatedStateInternals } from \"./state-internals.ts\";\n\n/** The void signature also accepts synchronous callbacks that return an ignored value. */\ntype StateListener<T> = (value: T, context: Context, delivery: ReplicatedStateDelivery) => void;\n\ntype StateDelivery<T> = {\n\treadonly value: T;\n\treadonly context: Context;\n\treadonly delivery: ReplicatedStateDelivery;\n};\n\n/** One public subscription, independent of producer and other subscriber progress. */\nclass StateSubscriber<T> {\n\treadonly #listener: StateListener<T>;\n\treadonly #reportError: (error: Error) => void;\n\treadonly #pending: StateDelivery<T>[] = [];\n\t#running = false;\n\t#started = false;\n\t#closed = false;\n\n\tconstructor(listener: StateListener<T>, reportError: (error: Error) => void) {\n\t\tthis.#listener = listener;\n\t\tthis.#reportError = reportError;\n\t}\n\n\tpush(frame: StateDelivery<T>): void {\n\t\tif (this.#closed) return;\n\t\tif (this.#pending.length === 100) {\n\t\t\t// A cold replica can queue updates reentrantly before this subscriber's first hydration starts.\n\t\t\tconst hydration = this.#started ? undefined : this.#pending[0];\n\t\t\tthis.#pending.length = 0;\n\t\t\tif (hydration !== undefined) this.#pending.push(hydration);\n\t\t}\n\t\tthis.#pending.push(frame);\n\t}\n\n\tdrain(): void {\n\t\tif (this.#running || this.#closed) return;\n\t\tthis.#running = true;\n\t\tfor (let frame = this.#pending.shift(); frame !== undefined; frame = this.#pending.shift()) {\n\t\t\tthis.#started = true;\n\t\t\ttry {\n\t\t\t\tconst result: unknown = this.#listener(frame.value, frame.context, frame.delivery);\n\t\t\t\tif (isPromiseLike(result)) {\n\t\t\t\t\tvoid Promise.resolve(result).then(\n\t\t\t\t\t\t() => this.#resume(),\n\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\tthis.#report(error);\n\t\t\t\t\t\t\tthis.#resume();\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.#report(error);\n\t\t\t}\n\t\t}\n\t\tthis.#running = false;\n\t}\n\n\tclear(): void {\n\t\tthis.#pending.length = 0;\n\t}\n\n\tclose(): void {\n\t\tthis.#closed = true;\n\t\tthis.clear();\n\t}\n\n\t#resume(): void {\n\t\tthis.#running = false;\n\t\tthis.drain();\n\t}\n\n\t#report(error: unknown): void {\n\t\ttry {\n\t\t\tthis.#reportError(toError(error));\n\t\t} catch (reportError) {\n\t\t\treportErrorAsync(toError(reportError));\n\t\t}\n\t}\n}\n\ntype SourceListener = (ops: readonly Op[], sequence: number, context: Context) => void;\n\ntype Publication<T> = {\n\treadonly value: T;\n\treadonly ops: readonly Op[];\n\treadonly sequence: number;\n\treadonly context: Context;\n};\n\n/** Maintains local publication order independently of how revisions are produced. */\nclass ReplicatedStatePublisher<T> {\n\treadonly #listeners = new Map<StateSubscriber<T>, number>();\n\treadonly #reportError: (error: Error) => void;\n\treadonly #sourceListeners = new Set<SourceListener>();\n\treadonly #publications: Publication<T>[] = [];\n\t#value: T;\n\t#sequence = 0;\n\t#delivering = false;\n\n\tconstructor(initial: T, reportError: (error: Error) => void = reportErrorAsync) {\n\t\tthis.#value = initial;\n\t\tthis.#reportError = reportError;\n\t}\n\n\tget value(): T {\n\t\treturn this.#value;\n\t}\n\n\tsnapshot(): { readonly value: T; readonly sequence: number } {\n\t\treturn { value: this.#value, sequence: this.#sequence };\n\t}\n\n\tsubscribe(listener: StateListener<T>): () => void {\n\t\tconst { value, sequence } = this.snapshot();\n\t\tconst subscriber = new StateSubscriber(listener, this.#reportError);\n\t\tthis.#listeners.set(subscriber, sequence);\n\t\tsubscriber.push({ value, context: serviceDeliveryContext(), delivery: { kind: \"hydrate\", sequence } });\n\t\tsubscriber.drain();\n\t\treturn () => {\n\t\t\tsubscriber.close();\n\t\t\tthis.#listeners.delete(subscriber);\n\t\t};\n\t}\n\n\tsubscribeSource(listener: SourceListener): () => void {\n\t\tthis.#sourceListeners.add(listener);\n\t\treturn () => this.#sourceListeners.delete(listener);\n\t}\n\n\t/** Publish an already-prepared immutable revision and return isolated listener failures. */\n\tpublish(value: T, ops: readonly Op[], context: Context): unknown[] {\n\t\tthis.#value = value;\n\t\tthis.#sequence += 1;\n\t\tthis.#publications.push({ value, ops, sequence: this.#sequence, context });\n\t\tif (this.#delivering) return [];\n\n\t\tthis.#delivering = true;\n\t\tconst errors: unknown[] = [];\n\t\ttry {\n\t\t\tfor (\n\t\t\t\tlet publication = this.#publications.shift();\n\t\t\t\tpublication !== undefined;\n\t\t\t\tpublication = this.#publications.shift()\n\t\t\t) {\n\t\t\t\tfor (const listener of [...this.#sourceListeners]) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlistener(publication.ops, publication.sequence, publication.context);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst delivery = { kind: \"update\", sequence: publication.sequence } as const;\n\t\t\t\tfor (const [subscriber, hydratedSequence] of [...this.#listeners]) {\n\t\t\t\t\tif (publication.sequence <= hydratedSequence) continue;\n\t\t\t\t\tsubscriber.push({ value: publication.value, context: publication.context, delivery });\n\t\t\t\t\tsubscriber.drain();\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#delivering = false;\n\t\t}\n\t\treturn errors;\n\t}\n}\n\nexport class MutableReplicatedStateImpl<T extends object> implements MutableReplicatedState<T> {\n\treadonly #tracker: Tracker<T>;\n\treadonly #publisher: ReplicatedStatePublisher<T>;\n\t#changing = false;\n\n\tconstructor(initial: T) {\n\t\tthis.#tracker = track(initial);\n\t\tthis.#publisher = new ReplicatedStatePublisher(this.#tracker.value);\n\t\tregisterReplicatedStateInternals(this, {\n\t\t\tsnapshot: () => this.#publisher.snapshot(),\n\t\t\tsubscribe: (listener) => this.#publisher.subscribeSource(listener),\n\t\t});\n\t}\n\n\tget value(): T {\n\t\treturn this.#tracker.value;\n\t}\n\n\tchange(context: Context, mutate: Parameters<MutableReplicatedState<T>[\"change\"]>[1]): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be changed reentrantly from a change callback\");\n\t\tthis.#changing = true;\n\t\tlet prepared: Prepared<T>;\n\t\ttry {\n\t\t\tconst change = this.#tracker.beginChange();\n\t\t\ttry {\n\t\t\t\tconst outcome = (mutate as (draft: typeof change.state) => unknown)(change.state);\n\t\t\t\tif (isPromiseLike(outcome)) {\n\t\t\t\t\tvoid Promise.resolve(outcome).catch(() => undefined);\n\t\t\t\t\tthrow new TypeError(\"Replicated state change callbacks must be synchronous\");\n\t\t\t\t}\n\t\t\t\tprepared = change.prepare();\n\t\t\t} catch (error) {\n\t\t\t\tchange.abort();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#changing = false;\n\t\t}\n\n\t\tthis.#tracker.adopt(prepared);\n\t\tif (prepared.ops.length === 0) return;\n\t\tthrowCollectedErrors(\n\t\t\tthis.#publisher.publish(prepared.value, prepared.ops, context),\n\t\t\t\"Replicated state listeners failed\",\n\t\t);\n\t}\n\n\treplace(context: Context, value: T): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be replaced from a change callback\");\n\t\tconst prepared = this.#tracker.prepareReplace(value);\n\t\tthis.#tracker.adopt(prepared);\n\t\tif (prepared.ops.length === 0) return;\n\t\tthrowCollectedErrors(\n\t\t\tthis.#publisher.publish(prepared.value, prepared.ops, context),\n\t\t\t\"Replicated state listeners failed\",\n\t\t);\n\t}\n\n\tsubscribe(listener: StateListener<T>): () => void {\n\t\treturn this.#publisher.subscribe(listener);\n\t}\n}\n\nclass AttachedReplicatedStateImpl<T> implements AttachedReplicatedState<T> {\n\treadonly #publisher: ReplicatedStatePublisher<T>;\n\treadonly #attachment: ReplicatedStateSourceAttachment<T>;\n\treadonly #reportError: (error: Error) => void;\n\t#cursor: number;\n\t#disposed = false;\n\n\tconstructor(attachment: ReplicatedStateSourceAttachment<T>, options: ReplicatedStateSourceOptions) {\n\t\tconst { snapshot } = attachment;\n\t\tassertCursor(snapshot.cursor, \"snapshot\");\n\t\tthis.#attachment = attachment;\n\t\tthis.#cursor = snapshot.cursor;\n\t\tthis.#reportError = options.onError ?? reportErrorAsync;\n\t\tthis.#publisher = new ReplicatedStatePublisher(snapshot.value, (error) => this.#report(error));\n\t\tregisterReplicatedStateInternals(this, {\n\t\t\tsnapshot: () => this.#publisher.snapshot(),\n\t\t\tsubscribe: (listener) => this.#publisher.subscribeSource(listener),\n\t\t});\n\t}\n\n\tget value(): T {\n\t\treturn this.#publisher.value;\n\t}\n\n\tsubscribe(listener: StateListener<T>): () => void {\n\t\treturn this.#publisher.subscribe(listener);\n\t}\n\n\tactivate(): void {\n\t\tthis.#attachment.activate((frame) => this.#receive(frame));\n\t}\n\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tthis.#attachment.dispose();\n\t}\n\n\t#receive(frame: ReplicatedStateSourceFrame<T>): void {\n\t\tif (this.#disposed) return;\n\t\ttry {\n\t\t\tassertCursor(frame.cursor, \"frame\");\n\t\t\tconst expected = this.#cursor + 1;\n\t\t\tif (frame.cursor !== expected) {\n\t\t\t\tthrow new Error(`Replicated state source cursor has a gap: expected ${expected}, received ${frame.cursor}`);\n\t\t\t}\n\t\t\tthis.#cursor = frame.cursor;\n\t\t\tconst errors = this.#publisher.publish(frame.value, frame.ops, frame.context);\n\t\t\tif (errors.length === 1) this.#report(errors[0]);\n\t\t\telse if (errors.length > 1) this.#report(new AggregateError(errors, \"Replicated state listeners failed\"));\n\t\t} catch (error) {\n\t\t\tthis.#fail(toError(error));\n\t\t}\n\t}\n\n\t#fail(error: Error): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\ttry {\n\t\t\tthis.#attachment.dispose();\n\t\t} catch (disposeError) {\n\t\t\tthis.#report(new AggregateError([error, disposeError], \"Replicated state source contract failed\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.#report(error);\n\t}\n\n\t#report(error: unknown): void {\n\t\ttry {\n\t\t\tthis.#reportError(toError(error));\n\t\t} catch (reportError) {\n\t\t\treportErrorAsync(toError(reportError));\n\t\t}\n\t}\n}\n\n/** Attach a publication-only replicated state to one authoritative immutable source stream. */\nexport function attachReplicatedStateSource<T>(\n\tsource: ReplicatedStateSource<T>,\n\toptions: ReplicatedStateSourceOptions = {},\n): AttachedReplicatedState<T> {\n\tconst attachment = source.attach();\n\ttry {\n\t\tconst state = new AttachedReplicatedStateImpl(attachment, options);\n\t\tstate.activate();\n\t\treturn state;\n\t} catch (error) {\n\t\ttry {\n\t\t\tattachment.dispose();\n\t\t} catch (disposeError) {\n\t\t\tthrow new AggregateError([error, disposeError], \"Failed to attach replicated state source\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** A cold read-only state used by service consumers until a complete snapshot arrives. */\nexport class ReplicatedStateReplica<T extends JsonValue = JsonValue> implements ReplicatedState<T> {\n\treadonly #listeners = new Set<StateSubscriber<T>>();\n\treadonly #reportError: (error: Error) => void;\n\treadonly #validator = new JsonRevisionValidator();\n\t#value: T | undefined;\n\t#sequence: number | undefined;\n\n\tconstructor(reportError: (error: Error) => void) {\n\t\tthis.#reportError = reportError;\n\t}\n\n\tget value(): T | undefined {\n\t\treturn this.#value;\n\t}\n\n\tsubscribe(listener: StateListener<T>): () => void {\n\t\tconst subscriber = new StateSubscriber(listener, this.#reportError);\n\t\tthis.#listeners.add(subscriber);\n\t\tif (this.#value !== undefined) {\n\t\t\tsubscriber.push({\n\t\t\t\tvalue: this.#value,\n\t\t\t\tcontext: serviceDeliveryContext(),\n\t\t\t\tdelivery: { kind: \"hydrate\", sequence: this.#sequence! },\n\t\t\t});\n\t\t\tsubscriber.drain();\n\t\t}\n\t\treturn () => {\n\t\t\tsubscriber.close();\n\t\t\tthis.#listeners.delete(subscriber);\n\t\t};\n\t}\n\n\thydrate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tlet next: T;\n\t\ttry {\n\t\t\tif (!isBase(ops)) throw new Error(\"Replicated state snapshot is not a base operation batch\");\n\t\t\tnext = this.#validator.validate(applyImmutable<T>(undefined, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"hydrate\", sequence });\n\t}\n\n\tupdate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tif (this.#sequence === undefined || this.#value === undefined) {\n\t\t\tthrow new Error(\"Replicated state received an update before hydration\");\n\t\t}\n\t\tif (sequence !== this.#sequence + 1) {\n\t\t\tthis.clear();\n\t\t\tthrow new Error(\"Replicated state update sequence has a gap\");\n\t\t}\n\t\tlet next: T;\n\t\ttry {\n\t\t\tnext = this.#validator.validate(applyImmutable(this.#value, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"update\", sequence });\n\t}\n\n\tclear(): void {\n\t\tthis.#value = undefined;\n\t\tthis.#sequence = undefined;\n\t\tfor (const subscriber of this.#listeners) subscriber.clear();\n\t}\n\n\t#deliverAll(context: Context, delivery: ReplicatedStateDelivery): void {\n\t\tif (this.#value === undefined) return;\n\t\tconst frame = { value: this.#value, context, delivery };\n\t\tconst subscribers = [...this.#listeners];\n\t\t// Enqueue for everyone before user code can publish another revision reentrantly.\n\t\tfor (const subscriber of subscribers) subscriber.push(frame);\n\t\tfor (const subscriber of subscribers) subscriber.drain();\n\t}\n}\n\n/** @internal Context for synthetic service deliveries without a caller. */\nexport function serviceDeliveryContext(): Context {\n\t// TODO: Add delivery-scoped cancellation or metadata if deliveries gain an owned lifecycle.\n\treturn BACKGROUND_CONTEXT;\n}\n\nfunction assertCursor(cursor: number, kind: \"snapshot\" | \"frame\"): void {\n\tif (!Number.isSafeInteger(cursor))\n\t\tthrow new TypeError(`Replicated state source ${kind} cursor must be a safe integer`);\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n\treturn (\n\t\t((typeof value === \"object\" && value !== null) || typeof value === \"function\") &&\n\t\ttypeof (value as { then?: unknown }).then === \"function\"\n\t);\n}\n\nfunction throwCollectedErrors(errors: readonly unknown[], message: string): void {\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, message);\n}\n\nfunction reportErrorAsync(error: Error): void {\n\tqueueMicrotask(() => {\n\t\tthrow error;\n\t});\n}\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n"]}
|
package/dist/services/wire.d.ts
CHANGED
|
@@ -24,6 +24,9 @@ export type WireServiceProviderUpdate = {
|
|
|
24
24
|
readonly member: string;
|
|
25
25
|
readonly sequence: number;
|
|
26
26
|
readonly ops: readonly WireOp[];
|
|
27
|
+
} | {
|
|
28
|
+
readonly type: "reset";
|
|
29
|
+
readonly snapshot: WireServiceSubscriptionSnapshot;
|
|
27
30
|
} | {
|
|
28
31
|
readonly type: "unavailable";
|
|
29
32
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/services/wire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,KAAK,EACX,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,2BAA2B,EAC3B,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,yBAAyB,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEjH,MAAM,MAAM,2BAA2B,GAAG;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,2BAA2B,EAAE,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAClC;IACA,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,CAAA;CAAE,GAC7E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,CAAA;CAAE,GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAO1E,MAAM,MAAM,kBAAkB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAC9B;IACA,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC1B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,wBAAgB,0BAA0B,IAAI,WAAW,CAExD;AAED,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,WAAW,CAEpH;AAED,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,CAEhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,kBAAkB,GAAG,SAAS,CAqB1F;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAQ5D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,qBAAqB,EAAE,CAYtF;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,2BAA2B,CAG5F;AAED,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,OAAO,GAAG,+BAA+B,CAGpG;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAGhF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAGxF","sourcesContent":["import { assertValidOp, assertValidWireOp, type WireOp } from \"../delta/index.ts\";\nimport type {\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSubscriptionSnapshot,\n} from \"../types.ts\";\n\nexport type WireServiceMemberSnapshot =\n\t| { readonly name: string; readonly kind: \"method\" }\n\t| { readonly name: string; readonly kind: \"state\"; readonly sequence: number; readonly ops: readonly WireOp[] };\n\nexport type WireServiceInstanceSnapshot = {\n\treadonly instance?: ServiceInstanceAddress;\n\treadonly members: readonly WireServiceMemberSnapshot[];\n};\n\nexport type WireServiceSubscriptionSnapshot = {\n\treadonly serviceId: string;\n\treadonly mode: ServiceMode;\n\treadonly instances: readonly WireServiceInstanceSnapshot[];\n};\n\nexport type WireServiceProviderUpdate =\n\t| {\n\t\t\treadonly type: \"state\";\n\t\t\treadonly instance?: ServiceInstanceAddress;\n\t\t\treadonly member: string;\n\t\t\treadonly sequence: number;\n\t\t\treadonly ops: readonly WireOp[];\n\t }\n\t| { readonly type: \"unavailable\" }\n\t| { readonly type: \"replaced\"; readonly snapshot: WireServiceInstanceSnapshot }\n\t| { readonly type: \"spawned\"; readonly instance: WireServiceInstanceSnapshot }\n\t| { readonly type: \"closed\"; readonly instance: ServiceInstanceAddress };\n\nconst SERVICE_CONTROL_ID = \"$chord.service\";\nconst SERVICE_CATALOGUE_MEMBER = \"catalogue\";\nconst SERVICE_SUBSCRIBE_MEMBER = \"subscribe\";\nconst SERVICE_UNSUBSCRIBE_MEMBER = \"unsubscribe\";\n\nexport type ServiceControlCall =\n\t| { readonly type: \"catalogue\" }\n\t| {\n\t\t\treadonly type: \"subscribe\";\n\t\t\treadonly subscriptionId: string;\n\t\t\treadonly serviceId: string;\n\t\t\treadonly mode: ServiceMode;\n\t }\n\t| { readonly type: \"unsubscribe\"; readonly subscriptionId: string };\n\nexport function createServiceCatalogueCall(): ServiceCall {\n\treturn { serviceId: SERVICE_CONTROL_ID, member: SERVICE_CATALOGUE_MEMBER, args: [] };\n}\n\nexport function createServiceSubscribeCall(subscriptionId: string, serviceId: string, mode: ServiceMode): ServiceCall {\n\treturn { serviceId: SERVICE_CONTROL_ID, member: SERVICE_SUBSCRIBE_MEMBER, args: [subscriptionId, serviceId, mode] };\n}\n\nexport function createServiceUnsubscribeCall(subscriptionId: string): ServiceCall {\n\treturn { serviceId: SERVICE_CONTROL_ID, member: SERVICE_UNSUBSCRIBE_MEMBER, args: [subscriptionId] };\n}\n\nexport function decodeServiceControlCall(call: ServiceCall): ServiceControlCall | undefined {\n\tif (call.serviceId !== SERVICE_CONTROL_ID || call.instance !== undefined) return undefined;\n\tif (call.member === SERVICE_CATALOGUE_MEMBER && call.args.length === 0) return { type: \"catalogue\" };\n\tif (\n\t\tcall.member === SERVICE_SUBSCRIBE_MEMBER &&\n\t\tcall.args.length === 3 &&\n\t\tisId(call.args[0]) &&\n\t\tisId(call.args[1]) &&\n\t\t(call.args[2] === \"singleton\" || call.args[2] === \"keyed\")\n\t) {\n\t\treturn {\n\t\t\ttype: \"subscribe\",\n\t\t\tsubscriptionId: call.args[0],\n\t\t\tserviceId: call.args[1],\n\t\t\tmode: call.args[2],\n\t\t};\n\t}\n\tif (call.member === SERVICE_UNSUBSCRIBE_MEMBER && call.args.length === 1 && isId(call.args[0])) {\n\t\treturn { type: \"unsubscribe\", subscriptionId: call.args[0] };\n\t}\n\treturn undefined;\n}\n\nexport function parseServiceCall(value: unknown): ServiceCall {\n\tconst call = record(value, \"service call\");\n\tassertKeys(call, [\"serviceId\", \"member\", \"args\"], [\"instance\"], \"service call\");\n\tif (!isId(call.serviceId) || !isId(call.member) || !Array.isArray(call.args)) {\n\t\tthrow new TypeError(\"Invalid service call\");\n\t}\n\tif (call.instance !== undefined) assertAddress(call.instance);\n\treturn value as ServiceCall;\n}\n\nexport function parseServiceCatalogue(value: unknown): readonly ServiceCatalogueEntry[] {\n\tif (!Array.isArray(value)) throw new TypeError(\"Invalid service catalogue\");\n\tconst ids = new Set<string>();\n\tfor (const candidate of value) {\n\t\tconst entry = record(candidate, \"service catalogue entry\");\n\t\tassertKeys(entry, [\"serviceId\", \"mode\"], [], \"service catalogue entry\");\n\t\tif (!isId(entry.serviceId) || !isMode(entry.mode) || ids.has(entry.serviceId)) {\n\t\t\tthrow new TypeError(\"Invalid service catalogue\");\n\t\t}\n\t\tids.add(entry.serviceId);\n\t}\n\treturn value as unknown as readonly ServiceCatalogueEntry[];\n}\n\nexport function parseServiceSubscriptionSnapshot(value: unknown): ServiceSubscriptionSnapshot {\n\tassertSubscriptionSnapshot(value, assertValidOp);\n\treturn value as ServiceSubscriptionSnapshot;\n}\n\nexport function parseWireServiceSubscriptionSnapshot(value: unknown): WireServiceSubscriptionSnapshot {\n\tassertSubscriptionSnapshot(value, assertValidWireOp);\n\treturn value as WireServiceSubscriptionSnapshot;\n}\n\nexport function parseServiceProviderUpdate(value: unknown): ServiceProviderUpdate {\n\tassertProviderUpdate(value, assertValidOp);\n\treturn value as ServiceProviderUpdate;\n}\n\nexport function parseWireServiceProviderUpdate(value: unknown): WireServiceProviderUpdate {\n\tassertProviderUpdate(value, assertValidWireOp);\n\treturn value as WireServiceProviderUpdate;\n}\n\nfunction assertSubscriptionSnapshot(value: unknown, assertOp: (value: unknown) => void): void {\n\tconst snapshot = record(value, \"service subscription snapshot\");\n\tassertKeys(snapshot, [\"serviceId\", \"mode\", \"instances\"], [], \"service subscription snapshot\");\n\tif (!isId(snapshot.serviceId) || !isMode(snapshot.mode) || !Array.isArray(snapshot.instances)) {\n\t\tthrow new TypeError(\"Invalid service subscription snapshot\");\n\t}\n\tfor (const instance of snapshot.instances) assertInstance(instance, assertOp);\n}\n\nfunction assertProviderUpdate(value: unknown, assertOp: (value: unknown) => void): void {\n\tconst update = record(value, \"service provider update\");\n\tswitch (update.type) {\n\t\tcase \"state\":\n\t\t\tassertKeys(update, [\"type\", \"member\", \"sequence\", \"ops\"], [\"instance\"], \"state update\");\n\t\t\tif (!isId(update.member) || !isInteger(update.sequence, 1) || !Array.isArray(update.ops)) {\n\t\t\t\tthrow new TypeError(\"Invalid service state update\");\n\t\t\t}\n\t\t\tif (update.instance !== undefined) assertAddress(update.instance);\n\t\t\tfor (const op of update.ops) assertOp(op);\n\t\t\treturn;\n\t\tcase \"unavailable\":\n\t\t\tassertKeys(update, [\"type\"], [], \"unavailable update\");\n\t\t\treturn;\n\t\tcase \"replaced\":\n\t\t\tassertKeys(update, [\"type\", \"snapshot\"], [], \"replacement update\");\n\t\t\tassertInstance(update.snapshot, assertOp);\n\t\t\treturn;\n\t\tcase \"spawned\":\n\t\t\tassertKeys(update, [\"type\", \"instance\"], [], \"spawn update\");\n\t\t\tassertInstance(update.instance, assertOp);\n\t\t\treturn;\n\t\tcase \"closed\":\n\t\t\tassertKeys(update, [\"type\", \"instance\"], [], \"close update\");\n\t\t\tassertAddress(update.instance);\n\t\t\treturn;\n\t\tdefault:\n\t\t\tthrow new TypeError(\"Invalid service provider update\");\n\t}\n}\n\nfunction assertInstance(value: unknown, assertOp: (value: unknown) => void): void {\n\tconst instance = record(value, \"service instance snapshot\");\n\tassertKeys(instance, [\"members\"], [\"instance\"], \"service instance snapshot\");\n\tif (instance.instance !== undefined) assertAddress(instance.instance);\n\tif (!Array.isArray(instance.members)) throw new TypeError(\"Invalid service instance snapshot\");\n\tfor (const candidate of instance.members) {\n\t\tconst member = record(candidate, \"service member snapshot\");\n\t\tif (member.kind === \"method\") {\n\t\t\tassertKeys(member, [\"name\", \"kind\"], [], \"service method snapshot\");\n\t\t\tif (!isId(member.name)) throw new TypeError(\"Invalid service method snapshot\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (member.kind === \"state\") {\n\t\t\tassertKeys(member, [\"name\", \"kind\", \"sequence\", \"ops\"], [], \"service state snapshot\");\n\t\t\tif (!isId(member.name) || !isInteger(member.sequence, 0) || !Array.isArray(member.ops)) {\n\t\t\t\tthrow new TypeError(\"Invalid service state snapshot\");\n\t\t\t}\n\t\t\tfor (const op of member.ops) assertOp(op);\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(\"Invalid service member snapshot\");\n\t}\n}\n\nfunction assertAddress(value: unknown): asserts value is ServiceInstanceAddress {\n\tconst address = record(value, \"service instance address\");\n\tassertKeys(address, [\"key\", \"generation\"], [], \"service instance address\");\n\tif (!isId(address.key) || !isInteger(address.generation, 1)) {\n\t\tthrow new TypeError(\"Invalid service instance address\");\n\t}\n}\n\nfunction record(value: unknown, description: string): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new TypeError(`Invalid ${description}`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction assertKeys(\n\tvalue: Record<string, unknown>,\n\trequired: readonly string[],\n\toptional: readonly string[],\n\tdescription: string,\n): void {\n\tconst allowed = new Set([...required, ...optional]);\n\tif (required.some((key) => !Object.hasOwn(value, key)) || Object.keys(value).some((key) => !allowed.has(key))) {\n\t\tthrow new TypeError(`Invalid ${description}`);\n\t}\n}\n\nfunction isId(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.length > 0;\n}\n\nfunction isMode(value: unknown): value is ServiceMode {\n\treturn value === \"singleton\" || value === \"keyed\";\n}\n\nfunction isInteger(value: unknown, minimum: number): value is number {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value >= minimum;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/services/wire.ts"],"names":[],"mappings":"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"}
|
package/dist/services/wire.js
CHANGED
|
@@ -96,6 +96,19 @@ function assertProviderUpdate(value, assertOp) {
|
|
|
96
96
|
for (const op of update.ops)
|
|
97
97
|
assertOp(op);
|
|
98
98
|
return;
|
|
99
|
+
case "reset": {
|
|
100
|
+
assertKeys(update, ["type", "snapshot"], [], "reset update");
|
|
101
|
+
assertSubscriptionSnapshot(update.snapshot, assertOp);
|
|
102
|
+
const snapshot = update.snapshot;
|
|
103
|
+
for (const instance of snapshot.instances) {
|
|
104
|
+
for (const member of instance.members) {
|
|
105
|
+
if (member.kind === "state" && (member.ops.length !== 1 || member.ops[0]?.[0] !== "r")) {
|
|
106
|
+
throw new TypeError("Service reset must contain full root replacements");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
99
112
|
case "unavailable":
|
|
100
113
|
assertKeys(update, ["type"], [], "unavailable update");
|
|
101
114
|
return;
|