@dxos/echo-pipeline 0.1.24-next.6b1f434
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/LICENSE +8 -0
- package/README.md +22 -0
- package/dist/lib/browser/chunk-3GAE5CKD.mjs +1621 -0
- package/dist/lib/browser/chunk-3GAE5CKD.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +114 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/testing/index.mjs +238 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/index.cjs +1747 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/lib/node/testing/index.cjs +1835 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/types/src/common/codec.d.ts +12 -0
- package/dist/types/src/common/codec.d.ts.map +1 -0
- package/dist/types/src/common/feeds.d.ts +7 -0
- package/dist/types/src/common/feeds.d.ts.map +1 -0
- package/dist/types/src/common/index.d.ts +3 -0
- package/dist/types/src/common/index.d.ts.map +1 -0
- package/dist/types/src/dbhost/data-service-host.d.ts +22 -0
- package/dist/types/src/dbhost/data-service-host.d.ts.map +1 -0
- package/dist/types/src/dbhost/data-service.d.ts +21 -0
- package/dist/types/src/dbhost/data-service.d.ts.map +1 -0
- package/dist/types/src/dbhost/database-backend.d.ts +28 -0
- package/dist/types/src/dbhost/database-backend.d.ts.map +1 -0
- package/dist/types/src/dbhost/index.d.ts +6 -0
- package/dist/types/src/dbhost/index.d.ts.map +1 -0
- package/dist/types/src/dbhost/snapshot-manager.d.ts +17 -0
- package/dist/types/src/dbhost/snapshot-manager.d.ts.map +1 -0
- package/dist/types/src/dbhost/snapshot-store.d.ts +9 -0
- package/dist/types/src/dbhost/snapshot-store.d.ts.map +1 -0
- package/dist/types/src/dbhost/snapshot-store.test.d.ts +2 -0
- package/dist/types/src/dbhost/snapshot-store.test.d.ts.map +1 -0
- package/dist/types/src/errors/errors.d.ts +26 -0
- package/dist/types/src/errors/errors.d.ts.map +1 -0
- package/dist/types/src/errors/index.d.ts +2 -0
- package/dist/types/src/errors/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +7 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/metadata/index.d.ts +2 -0
- package/dist/types/src/metadata/index.d.ts.map +1 -0
- package/dist/types/src/metadata/metadata-store.d.ts +42 -0
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -0
- package/dist/types/src/pipeline/index.d.ts +3 -0
- package/dist/types/src/pipeline/index.d.ts.map +1 -0
- package/dist/types/src/pipeline/message-selector.d.ts +11 -0
- package/dist/types/src/pipeline/message-selector.d.ts.map +1 -0
- package/dist/types/src/pipeline/pipeline.d.ts +98 -0
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -0
- package/dist/types/src/pipeline/pipeline.test.d.ts +2 -0
- package/dist/types/src/pipeline/pipeline.test.d.ts.map +1 -0
- package/dist/types/src/pipeline/timeframe-clock.d.ts +19 -0
- package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -0
- package/dist/types/src/space/auth.d.ts +23 -0
- package/dist/types/src/space/auth.d.ts.map +1 -0
- package/dist/types/src/space/control-pipeline.d.ts +31 -0
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -0
- package/dist/types/src/space/control-pipeline.test.d.ts +2 -0
- package/dist/types/src/space/control-pipeline.test.d.ts.map +1 -0
- package/dist/types/src/space/data-pipeline-controller.d.ts +63 -0
- package/dist/types/src/space/data-pipeline-controller.d.ts.map +1 -0
- package/dist/types/src/space/genesis.d.ts +5 -0
- package/dist/types/src/space/genesis.d.ts.map +1 -0
- package/dist/types/src/space/index.d.ts +7 -0
- package/dist/types/src/space/index.d.ts.map +1 -0
- package/dist/types/src/space/replication.browser.test.d.ts +2 -0
- package/dist/types/src/space/replication.browser.test.d.ts.map +1 -0
- package/dist/types/src/space/space-manager.browser.test.d.ts +2 -0
- package/dist/types/src/space/space-manager.browser.test.d.ts.map +1 -0
- package/dist/types/src/space/space-manager.d.ts +41 -0
- package/dist/types/src/space/space-manager.d.ts.map +1 -0
- package/dist/types/src/space/space-protocol.browser.test.d.ts +2 -0
- package/dist/types/src/space/space-protocol.browser.test.d.ts.map +1 -0
- package/dist/types/src/space/space-protocol.d.ts +76 -0
- package/dist/types/src/space/space-protocol.d.ts.map +1 -0
- package/dist/types/src/space/space-protocol.test.d.ts +2 -0
- package/dist/types/src/space/space-protocol.test.d.ts.map +1 -0
- package/dist/types/src/space/space.d.ts +59 -0
- package/dist/types/src/space/space.d.ts.map +1 -0
- package/dist/types/src/space/space.test.d.ts +2 -0
- package/dist/types/src/space/space.test.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +4 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/test-agent-builder.d.ts +52 -0
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -0
- package/dist/types/src/testing/test-feed-builder.d.ts +9 -0
- package/dist/types/src/testing/test-feed-builder.d.ts.map +1 -0
- package/dist/types/src/testing/util.d.ts +14 -0
- package/dist/types/src/testing/util.d.ts.map +1 -0
- package/dist/types/src/tests/database.test.d.ts +2 -0
- package/dist/types/src/tests/database.test.d.ts.map +1 -0
- package/package.json +60 -0
- package/src/common/codec.ts +18 -0
- package/src/common/feeds.ts +23 -0
- package/src/common/index.ts +6 -0
- package/src/dbhost/data-service-host.ts +103 -0
- package/src/dbhost/data-service.ts +66 -0
- package/src/dbhost/database-backend.ts +64 -0
- package/src/dbhost/index.ts +9 -0
- package/src/dbhost/snapshot-manager.ts +68 -0
- package/src/dbhost/snapshot-store.test.ts +30 -0
- package/src/dbhost/snapshot-store.ts +45 -0
- package/src/errors/errors.ts +52 -0
- package/src/errors/index.ts +5 -0
- package/src/index.ts +10 -0
- package/src/metadata/index.ts +5 -0
- package/src/metadata/metadata-store.ts +173 -0
- package/src/pipeline/index.ts +6 -0
- package/src/pipeline/message-selector.ts +38 -0
- package/src/pipeline/pipeline.test.ts +71 -0
- package/src/pipeline/pipeline.ts +249 -0
- package/src/pipeline/timeframe-clock.ts +56 -0
- package/src/space/auth.ts +88 -0
- package/src/space/control-pipeline.test.ts +162 -0
- package/src/space/control-pipeline.ts +103 -0
- package/src/space/data-pipeline-controller.ts +234 -0
- package/src/space/genesis.ts +29 -0
- package/src/space/index.ts +10 -0
- package/src/space/replication.browser.test.ts +64 -0
- package/src/space/space-manager.browser.test.ts +44 -0
- package/src/space/space-manager.ts +92 -0
- package/src/space/space-protocol.browser.test.ts +135 -0
- package/src/space/space-protocol.test.ts +136 -0
- package/src/space/space-protocol.ts +222 -0
- package/src/space/space.test.ts +144 -0
- package/src/space/space.ts +199 -0
- package/src/testing/index.ts +7 -0
- package/src/testing/test-agent-builder.ts +193 -0
- package/src/testing/test-feed-builder.ts +19 -0
- package/src/testing/util.ts +77 -0
- package/src/tests/database.test.ts +53 -0
- package/testing.d.ts +11 -0
- package/testing.js +5 -0
|
@@ -0,0 +1,1621 @@
|
|
|
1
|
+
import "@dxos/node-std/globals"
|
|
2
|
+
|
|
3
|
+
// packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts
|
|
4
|
+
import assert from "@dxos/node-std/assert";
|
|
5
|
+
import { synchronized } from "@dxos/async";
|
|
6
|
+
import { log } from "@dxos/log";
|
|
7
|
+
import { schema } from "@dxos/protocols";
|
|
8
|
+
var __decorate = function(decorators, target, key, desc) {
|
|
9
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
10
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
11
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
12
|
+
else
|
|
13
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
14
|
+
if (d = decorators[i])
|
|
15
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
16
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
17
|
+
};
|
|
18
|
+
var STORAGE_VERSION = 1;
|
|
19
|
+
var emptyEchoMetadata = () => ({
|
|
20
|
+
version: STORAGE_VERSION,
|
|
21
|
+
spaces: [],
|
|
22
|
+
created: new Date(),
|
|
23
|
+
updated: new Date()
|
|
24
|
+
});
|
|
25
|
+
var MetadataStore = class {
|
|
26
|
+
// prettier-ignore
|
|
27
|
+
constructor(_directory) {
|
|
28
|
+
this._directory = _directory;
|
|
29
|
+
this._metadata = emptyEchoMetadata();
|
|
30
|
+
}
|
|
31
|
+
get version() {
|
|
32
|
+
var _a;
|
|
33
|
+
return (_a = this._metadata.version) != null ? _a : 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns a list of currently saved spaces. The list and objects in it can be modified addSpace and
|
|
37
|
+
* addSpaceFeed functions.
|
|
38
|
+
*/
|
|
39
|
+
get spaces() {
|
|
40
|
+
var _a;
|
|
41
|
+
return (_a = this._metadata.spaces) != null ? _a : [];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Loads metadata from persistent storage.
|
|
45
|
+
*/
|
|
46
|
+
async load() {
|
|
47
|
+
const file = this._directory.getOrCreateFile("EchoMetadata");
|
|
48
|
+
try {
|
|
49
|
+
const { size: fileLength } = await file.stat();
|
|
50
|
+
if (fileLength < 4) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const dataSize = fromBytesInt32(await file.read(0, 4));
|
|
54
|
+
log("loaded", {
|
|
55
|
+
size: dataSize
|
|
56
|
+
}, {
|
|
57
|
+
file: "metadata-store.ts",
|
|
58
|
+
line: 68,
|
|
59
|
+
scope: this,
|
|
60
|
+
callSite: (f, a) => f(...a)
|
|
61
|
+
});
|
|
62
|
+
{
|
|
63
|
+
if (fileLength < dataSize + 4) {
|
|
64
|
+
throw new Error("Metadata storage is corrupted");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const data = await file.read(4, dataSize);
|
|
68
|
+
this._metadata = schema.getCodecForType("dxos.echo.metadata.EchoMetadata").decode(data);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
log.error("failed to load metadata", {
|
|
71
|
+
err
|
|
72
|
+
}, {
|
|
73
|
+
file: "metadata-store.ts",
|
|
74
|
+
line: 80,
|
|
75
|
+
scope: this,
|
|
76
|
+
callSite: (f, a) => f(...a)
|
|
77
|
+
});
|
|
78
|
+
this._metadata = emptyEchoMetadata();
|
|
79
|
+
} finally {
|
|
80
|
+
await file.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async _save() {
|
|
84
|
+
var _a;
|
|
85
|
+
const data = {
|
|
86
|
+
...this._metadata,
|
|
87
|
+
version: STORAGE_VERSION,
|
|
88
|
+
created: (_a = this._metadata.created) != null ? _a : new Date(),
|
|
89
|
+
updated: new Date()
|
|
90
|
+
};
|
|
91
|
+
const file = this._directory.getOrCreateFile("EchoMetadata");
|
|
92
|
+
try {
|
|
93
|
+
const encoded = Buffer.from(schema.getCodecForType("dxos.echo.metadata.EchoMetadata").encode(data));
|
|
94
|
+
await file.write(0, toBytesInt32(encoded.length));
|
|
95
|
+
log("saved", {
|
|
96
|
+
size: encoded.length
|
|
97
|
+
}, {
|
|
98
|
+
file: "metadata-store.ts",
|
|
99
|
+
line: 103,
|
|
100
|
+
scope: this,
|
|
101
|
+
callSite: (f, a) => f(...a)
|
|
102
|
+
});
|
|
103
|
+
await file.write(4, encoded);
|
|
104
|
+
} finally {
|
|
105
|
+
await file.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Clears storage - doesn't work for now.
|
|
110
|
+
*/
|
|
111
|
+
async clear() {
|
|
112
|
+
log("clearing all metadata", {}, {
|
|
113
|
+
file: "metadata-store.ts",
|
|
114
|
+
line: 116,
|
|
115
|
+
scope: this,
|
|
116
|
+
callSite: (f, a) => f(...a)
|
|
117
|
+
});
|
|
118
|
+
await this._directory.delete();
|
|
119
|
+
}
|
|
120
|
+
getIdentityRecord() {
|
|
121
|
+
return this._metadata.identity;
|
|
122
|
+
}
|
|
123
|
+
async setIdentityRecord(record) {
|
|
124
|
+
assert(!this._metadata.identity, "Cannot overwrite existing identity in metadata");
|
|
125
|
+
this._metadata.identity = record;
|
|
126
|
+
await this._save();
|
|
127
|
+
}
|
|
128
|
+
async addSpace(record) {
|
|
129
|
+
var _a, _b, _c;
|
|
130
|
+
assert(!((_a = this._metadata.spaces) != null ? _a : []).find((space) => space.key === record.key), "Cannot overwrite existing space in metadata");
|
|
131
|
+
((_c = (_b = this._metadata).spaces) != null ? _c : _b.spaces = []).push(record);
|
|
132
|
+
await this._save();
|
|
133
|
+
}
|
|
134
|
+
async setSpaceLatestTimeframe(spaceKey, timeframe) {
|
|
135
|
+
const space = this.spaces.find((space2) => space2.key === spaceKey);
|
|
136
|
+
assert(space, "Space not found");
|
|
137
|
+
space.dataTimeframe = timeframe;
|
|
138
|
+
await this._save();
|
|
139
|
+
}
|
|
140
|
+
async setSpaceSnapshot(spaceKey, snapshot) {
|
|
141
|
+
const space = this.spaces.find((space2) => space2.key === spaceKey);
|
|
142
|
+
assert(space, "Space not found");
|
|
143
|
+
space.snapshot = snapshot;
|
|
144
|
+
await this._save();
|
|
145
|
+
}
|
|
146
|
+
async setWritableFeedKeys(spaceKey, controlFeedKey, dataFeedKey) {
|
|
147
|
+
const space = this.spaces.find((space2) => space2.key === spaceKey);
|
|
148
|
+
assert(space, "Space not found");
|
|
149
|
+
space.controlFeedKey = controlFeedKey;
|
|
150
|
+
space.dataFeedKey = dataFeedKey;
|
|
151
|
+
await this._save();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
__decorate([
|
|
155
|
+
synchronized
|
|
156
|
+
], MetadataStore.prototype, "load", null);
|
|
157
|
+
__decorate([
|
|
158
|
+
synchronized
|
|
159
|
+
], MetadataStore.prototype, "_save", null);
|
|
160
|
+
var toBytesInt32 = (num) => {
|
|
161
|
+
const buf = Buffer.alloc(4);
|
|
162
|
+
buf.writeInt32LE(num, 0);
|
|
163
|
+
return buf;
|
|
164
|
+
};
|
|
165
|
+
var fromBytesInt32 = (buf) => buf.readInt32LE(0);
|
|
166
|
+
|
|
167
|
+
// packages/core/echo/echo-pipeline/src/common/codec.ts
|
|
168
|
+
import { createCodecEncoding } from "@dxos/hypercore";
|
|
169
|
+
import { schema as schema2 } from "@dxos/protocols";
|
|
170
|
+
var codec = schema2.getCodecForType("dxos.echo.feed.FeedMessage");
|
|
171
|
+
var valueEncoding = createCodecEncoding(codec);
|
|
172
|
+
|
|
173
|
+
// packages/core/echo/echo-pipeline/src/common/feeds.ts
|
|
174
|
+
import assert2 from "@dxos/node-std/assert";
|
|
175
|
+
var createMappedFeedWriter = (mapper, writer) => {
|
|
176
|
+
assert2(mapper);
|
|
177
|
+
assert2(writer);
|
|
178
|
+
return {
|
|
179
|
+
write: async (data) => await writer.write(await mapper(data))
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts
|
|
184
|
+
import { Event } from "@dxos/async";
|
|
185
|
+
import { timed } from "@dxos/debug";
|
|
186
|
+
import { log as log2 } from "@dxos/log";
|
|
187
|
+
import { Timeframe } from "@dxos/timeframe";
|
|
188
|
+
var __decorate2 = function(decorators, target, key, desc) {
|
|
189
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
190
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
191
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
192
|
+
else
|
|
193
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
194
|
+
if (d = decorators[i])
|
|
195
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
196
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
197
|
+
};
|
|
198
|
+
var mapTimeframeToFeedIndexes = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
|
|
199
|
+
feedKey,
|
|
200
|
+
index
|
|
201
|
+
}));
|
|
202
|
+
var mapFeedIndexesToTimeframe = (indexes) => new Timeframe(indexes.map(({ feedKey, index }) => [
|
|
203
|
+
feedKey,
|
|
204
|
+
index
|
|
205
|
+
]));
|
|
206
|
+
var TimeframeClock = class {
|
|
207
|
+
// prettier-ignore
|
|
208
|
+
constructor(_timeframe = new Timeframe()) {
|
|
209
|
+
this._timeframe = _timeframe;
|
|
210
|
+
this.update = new Event();
|
|
211
|
+
}
|
|
212
|
+
get timeframe() {
|
|
213
|
+
return this._timeframe;
|
|
214
|
+
}
|
|
215
|
+
updateTimeframe(key, seq) {
|
|
216
|
+
this._timeframe = Timeframe.merge(this._timeframe, new Timeframe([
|
|
217
|
+
[
|
|
218
|
+
key,
|
|
219
|
+
seq
|
|
220
|
+
]
|
|
221
|
+
]));
|
|
222
|
+
this.update.emit(this._timeframe);
|
|
223
|
+
}
|
|
224
|
+
hasGaps(timeframe) {
|
|
225
|
+
const gaps = Timeframe.dependencies(timeframe, this._timeframe);
|
|
226
|
+
return !gaps.isEmpty();
|
|
227
|
+
}
|
|
228
|
+
async waitUntilReached(target) {
|
|
229
|
+
log2.debug("waitUntilReached", {
|
|
230
|
+
target,
|
|
231
|
+
current: this._timeframe
|
|
232
|
+
}, {
|
|
233
|
+
file: "timeframe-clock.ts",
|
|
234
|
+
line: 45,
|
|
235
|
+
scope: this,
|
|
236
|
+
callSite: (f, a) => f(...a)
|
|
237
|
+
});
|
|
238
|
+
await this.update.waitForCondition(() => {
|
|
239
|
+
log2("check if reached", {
|
|
240
|
+
target,
|
|
241
|
+
current: this._timeframe,
|
|
242
|
+
deps: Timeframe.dependencies(target, this._timeframe)
|
|
243
|
+
}, {
|
|
244
|
+
file: "timeframe-clock.ts",
|
|
245
|
+
line: 47,
|
|
246
|
+
scope: this,
|
|
247
|
+
callSite: (f, a) => f(...a)
|
|
248
|
+
});
|
|
249
|
+
return Timeframe.dependencies(target, this._timeframe).isEmpty();
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
__decorate2([
|
|
254
|
+
timed(5e3)
|
|
255
|
+
], TimeframeClock.prototype, "waitUntilReached", null);
|
|
256
|
+
|
|
257
|
+
// packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
|
|
258
|
+
import assert4 from "@dxos/node-std/assert";
|
|
259
|
+
import { sleep, Trigger } from "@dxos/async";
|
|
260
|
+
import { FeedSetIterator } from "@dxos/feed-store";
|
|
261
|
+
import { PublicKey } from "@dxos/keys";
|
|
262
|
+
import { log as log4 } from "@dxos/log";
|
|
263
|
+
import { Timeframe as Timeframe2 } from "@dxos/timeframe";
|
|
264
|
+
|
|
265
|
+
// packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts
|
|
266
|
+
import debug from "debug";
|
|
267
|
+
import assert3 from "@dxos/node-std/assert";
|
|
268
|
+
var log3 = debug("dxos:echo-db:message-selector");
|
|
269
|
+
var createMessageSelector = (timeframeClock) => {
|
|
270
|
+
return (messages) => {
|
|
271
|
+
for (let i = 0; i < messages.length; i++) {
|
|
272
|
+
const { data: { timeframe } } = messages[i];
|
|
273
|
+
assert3(timeframe);
|
|
274
|
+
if (!timeframeClock.hasGaps(timeframe)) {
|
|
275
|
+
return i;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
log3("Skipping...");
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
|
|
283
|
+
var PipelineState = class {
|
|
284
|
+
// prettier-ignore
|
|
285
|
+
constructor(_iterator, _timeframeClock) {
|
|
286
|
+
this._iterator = _iterator;
|
|
287
|
+
this._timeframeClock = _timeframeClock;
|
|
288
|
+
this.timeframeUpdate = this._timeframeClock.update;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Latest theoretical timeframe based on the last mutation in each feed.
|
|
292
|
+
* NOTE: This might never be reached if the mutation dependencies
|
|
293
|
+
*/
|
|
294
|
+
get endTimeframe() {
|
|
295
|
+
return mapFeedIndexesToTimeframe(this._iterator.feeds.filter((feed) => feed.properties.length > 0).map((feed) => ({
|
|
296
|
+
feedKey: feed.key,
|
|
297
|
+
index: feed.properties.length - 1
|
|
298
|
+
})));
|
|
299
|
+
}
|
|
300
|
+
get timeframe() {
|
|
301
|
+
return this._timeframeClock.timeframe;
|
|
302
|
+
}
|
|
303
|
+
get targetTimeframe() {
|
|
304
|
+
return this._targetTimeframe ? Timeframe2.merge(this.endTimeframe, this._targetTimeframe) : this.endTimeframe;
|
|
305
|
+
}
|
|
306
|
+
async waitUntilTimeframe(target) {
|
|
307
|
+
await this._timeframeClock.waitUntilReached(target);
|
|
308
|
+
}
|
|
309
|
+
setTargetTimeframe(target) {
|
|
310
|
+
this._targetTimeframe = target;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
|
|
314
|
+
*
|
|
315
|
+
* This function will resolve immediately if the pipeline is stalled.
|
|
316
|
+
*
|
|
317
|
+
* @param timeout Timeout in milliseconds to specify the maximum wait time.
|
|
318
|
+
*/
|
|
319
|
+
async waitUntilReachedTargetTimeframe({ timeout } = {}) {
|
|
320
|
+
var _a;
|
|
321
|
+
log4("waitUntilReachedTargetTimeframe", {
|
|
322
|
+
timeout,
|
|
323
|
+
current: this.timeframe,
|
|
324
|
+
target: this.targetTimeframe
|
|
325
|
+
}, {
|
|
326
|
+
file: "pipeline.ts",
|
|
327
|
+
line: 82,
|
|
328
|
+
scope: this,
|
|
329
|
+
callSite: (f, a) => f(...a)
|
|
330
|
+
});
|
|
331
|
+
(_a = this._reachedTargetPromise) != null ? _a : this._reachedTargetPromise = Promise.race([
|
|
332
|
+
this._timeframeClock.update.waitForCondition(() => {
|
|
333
|
+
return Timeframe2.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
|
|
334
|
+
}),
|
|
335
|
+
this._iterator.stalled.discardParameter().waitForCount(1)
|
|
336
|
+
]);
|
|
337
|
+
let done = false;
|
|
338
|
+
if (timeout) {
|
|
339
|
+
return Promise.race([
|
|
340
|
+
this._reachedTargetPromise.then(() => {
|
|
341
|
+
done = true;
|
|
342
|
+
}),
|
|
343
|
+
sleep(timeout).then(() => {
|
|
344
|
+
if (done) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
log4.warn("waitUntilReachedTargetTimeframe timed out", {
|
|
348
|
+
timeout,
|
|
349
|
+
current: this.timeframe,
|
|
350
|
+
target: this.targetTimeframe,
|
|
351
|
+
dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
|
|
352
|
+
}, {
|
|
353
|
+
file: "pipeline.ts",
|
|
354
|
+
line: 107,
|
|
355
|
+
scope: this,
|
|
356
|
+
callSite: (f, a) => f(...a)
|
|
357
|
+
});
|
|
358
|
+
})
|
|
359
|
+
]);
|
|
360
|
+
} else {
|
|
361
|
+
return this._reachedTargetPromise;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var Pipeline = class {
|
|
366
|
+
// prettier-ignore
|
|
367
|
+
constructor(_initialTimeframe = new Timeframe2()) {
|
|
368
|
+
this._initialTimeframe = _initialTimeframe;
|
|
369
|
+
this._timeframeClock = new TimeframeClock(this._initialTimeframe);
|
|
370
|
+
this._feedSetIterator = new FeedSetIterator(createMessageSelector(this._timeframeClock), {
|
|
371
|
+
start: mapTimeframeToFeedIndexes(this._initialTimeframe),
|
|
372
|
+
stallTimeout: 1e3
|
|
373
|
+
});
|
|
374
|
+
this._state = new PipelineState(this._feedSetIterator, this._timeframeClock);
|
|
375
|
+
this._processingTrigger = new Trigger().wake();
|
|
376
|
+
this._isOpen = false;
|
|
377
|
+
this._feedSetIterator.stalled.on((iterator) => {
|
|
378
|
+
log4.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
|
|
379
|
+
file: "pipeline.ts",
|
|
380
|
+
line: 183,
|
|
381
|
+
scope: this,
|
|
382
|
+
callSite: (f, a) => f(...a)
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
get state() {
|
|
387
|
+
return this._state;
|
|
388
|
+
}
|
|
389
|
+
get writer() {
|
|
390
|
+
assert4(this._writer, "Writer not set.");
|
|
391
|
+
return this._writer;
|
|
392
|
+
}
|
|
393
|
+
getFeeds() {
|
|
394
|
+
return this._feedSetIterator.feeds;
|
|
395
|
+
}
|
|
396
|
+
async addFeed(feed) {
|
|
397
|
+
await this._feedSetIterator.addFeed(feed);
|
|
398
|
+
}
|
|
399
|
+
setWriteFeed(feed) {
|
|
400
|
+
assert4(!this._writer, "Writer already set.");
|
|
401
|
+
assert4(feed.properties.writable, "Feed must be writable.");
|
|
402
|
+
this._writer = createMappedFeedWriter((payload) => ({
|
|
403
|
+
timeframe: this._timeframeClock.timeframe,
|
|
404
|
+
payload
|
|
405
|
+
}), feed.createFeedWriter());
|
|
406
|
+
}
|
|
407
|
+
async start() {
|
|
408
|
+
log4("starting...", {}, {
|
|
409
|
+
file: "pipeline.ts",
|
|
410
|
+
line: 218,
|
|
411
|
+
scope: this,
|
|
412
|
+
callSite: (f, a) => f(...a)
|
|
413
|
+
});
|
|
414
|
+
await this._feedSetIterator.open();
|
|
415
|
+
log4("started", {}, {
|
|
416
|
+
file: "pipeline.ts",
|
|
417
|
+
line: 220,
|
|
418
|
+
scope: this,
|
|
419
|
+
callSite: (f, a) => f(...a)
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
async stop() {
|
|
423
|
+
log4("stopping...", {}, {
|
|
424
|
+
file: "pipeline.ts",
|
|
425
|
+
line: 224,
|
|
426
|
+
scope: this,
|
|
427
|
+
callSite: (f, a) => f(...a)
|
|
428
|
+
});
|
|
429
|
+
await this._feedSetIterator.close();
|
|
430
|
+
await this._processingTrigger.wait();
|
|
431
|
+
log4("stopped", {}, {
|
|
432
|
+
file: "pipeline.ts",
|
|
433
|
+
line: 227,
|
|
434
|
+
scope: this,
|
|
435
|
+
callSite: (f, a) => f(...a)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Starts to iterate over the ordered messages from the added feeds.
|
|
440
|
+
* Updates the timeframe clock after the message has bee processed.
|
|
441
|
+
*/
|
|
442
|
+
async *consume() {
|
|
443
|
+
assert4(!this._isOpen, "Pipeline is already being consumed.");
|
|
444
|
+
this._isOpen = true;
|
|
445
|
+
for await (const block of this._feedSetIterator) {
|
|
446
|
+
this._processingTrigger.reset();
|
|
447
|
+
yield block;
|
|
448
|
+
this._processingTrigger.wake();
|
|
449
|
+
this._timeframeClock.updateTimeframe(PublicKey.from(block.feedKey), block.seq);
|
|
450
|
+
}
|
|
451
|
+
this._isOpen = false;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// packages/core/echo/echo-pipeline/src/space/auth.ts
|
|
456
|
+
import assert5 from "@dxos/node-std/assert";
|
|
457
|
+
import { runInContext, scheduleTask } from "@dxos/async";
|
|
458
|
+
import { Context } from "@dxos/context";
|
|
459
|
+
import { randomBytes } from "@dxos/crypto";
|
|
460
|
+
import { log as log5 } from "@dxos/log";
|
|
461
|
+
import { schema as schema3 } from "@dxos/protocols";
|
|
462
|
+
import { RpcExtension } from "@dxos/teleport";
|
|
463
|
+
var AuthExtension = class extends RpcExtension {
|
|
464
|
+
constructor(_authParams) {
|
|
465
|
+
super({
|
|
466
|
+
requested: {
|
|
467
|
+
AuthService: schema3.getService("dxos.mesh.teleport.auth.AuthService")
|
|
468
|
+
},
|
|
469
|
+
exposed: {
|
|
470
|
+
AuthService: schema3.getService("dxos.mesh.teleport.auth.AuthService")
|
|
471
|
+
},
|
|
472
|
+
timeout: 60 * 1e3
|
|
473
|
+
// Long timeout because auth can wait for sync in certain cases.
|
|
474
|
+
});
|
|
475
|
+
this._authParams = _authParams;
|
|
476
|
+
this._ctx = new Context({
|
|
477
|
+
onError: (err) => {
|
|
478
|
+
log5.catch(err, {}, {
|
|
479
|
+
file: "auth.ts",
|
|
480
|
+
line: 29,
|
|
481
|
+
scope: this,
|
|
482
|
+
callSite: (f, a) => f(...a)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
async getHandlers() {
|
|
488
|
+
return {
|
|
489
|
+
AuthService: {
|
|
490
|
+
authenticate: async ({ challenge }) => {
|
|
491
|
+
try {
|
|
492
|
+
const credential = await this._authParams.provider(challenge);
|
|
493
|
+
if (!credential) {
|
|
494
|
+
throw new Error("auth rejected");
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
credential
|
|
498
|
+
};
|
|
499
|
+
} catch (err) {
|
|
500
|
+
log5.error("failed to generate auth credentials", err, {
|
|
501
|
+
file: "auth.ts",
|
|
502
|
+
line: 56,
|
|
503
|
+
scope: this,
|
|
504
|
+
callSite: (f, a) => f(...a)
|
|
505
|
+
});
|
|
506
|
+
throw new Error("auth rejected");
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async onOpen(context) {
|
|
513
|
+
await super.onOpen(context);
|
|
514
|
+
scheduleTask(this._ctx, async () => {
|
|
515
|
+
try {
|
|
516
|
+
const challenge = randomBytes(32);
|
|
517
|
+
const { credential } = await this.rpc.AuthService.authenticate({
|
|
518
|
+
challenge
|
|
519
|
+
});
|
|
520
|
+
assert5((credential == null ? void 0 : credential.length) > 0, "invalid credential");
|
|
521
|
+
const success = await this._authParams.verifier(challenge, credential);
|
|
522
|
+
assert5(success, "credential not verified");
|
|
523
|
+
runInContext(this._ctx, () => this._authParams.onAuthSuccess());
|
|
524
|
+
} catch (err) {
|
|
525
|
+
log5.warn("auth failed", err, {
|
|
526
|
+
file: "auth.ts",
|
|
527
|
+
line: 75,
|
|
528
|
+
scope: this,
|
|
529
|
+
callSite: (f, a) => f(...a)
|
|
530
|
+
});
|
|
531
|
+
this.close();
|
|
532
|
+
this._authParams.onAuthFailure();
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
async onClose() {
|
|
537
|
+
await this._ctx.dispose();
|
|
538
|
+
await super.onClose();
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// packages/core/echo/echo-pipeline/src/space/space.ts
|
|
543
|
+
import assert6 from "@dxos/node-std/assert";
|
|
544
|
+
import { Event as Event2, synchronized as synchronized2, trackLeaks } from "@dxos/async";
|
|
545
|
+
import { log as log7, logInfo } from "@dxos/log";
|
|
546
|
+
import { AdmittedFeed as AdmittedFeed2 } from "@dxos/protocols/proto/dxos/halo/credentials";
|
|
547
|
+
import { Callback as Callback2 } from "@dxos/util";
|
|
548
|
+
|
|
549
|
+
// packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
|
|
550
|
+
import { SpaceStateMachine } from "@dxos/credentials";
|
|
551
|
+
import { PublicKey as PublicKey2 } from "@dxos/keys";
|
|
552
|
+
import { log as log6 } from "@dxos/log";
|
|
553
|
+
import { AdmittedFeed } from "@dxos/protocols/proto/dxos/halo/credentials";
|
|
554
|
+
import { Callback } from "@dxos/util";
|
|
555
|
+
var ControlPipeline = class {
|
|
556
|
+
constructor({ spaceKey, genesisFeed, feedProvider, initialTimeframe }) {
|
|
557
|
+
this.onFeedAdmitted = new Callback();
|
|
558
|
+
this._pipeline = new Pipeline(initialTimeframe);
|
|
559
|
+
void this._pipeline.addFeed(genesisFeed);
|
|
560
|
+
this._spaceStateMachine = new SpaceStateMachine(spaceKey);
|
|
561
|
+
this._spaceStateMachine.onFeedAdmitted.set(async (info) => {
|
|
562
|
+
log6("feed admitted", {
|
|
563
|
+
key: info.key
|
|
564
|
+
}, {
|
|
565
|
+
file: "control-pipeline.ts",
|
|
566
|
+
line: 41,
|
|
567
|
+
scope: this,
|
|
568
|
+
callSite: (f, a) => f(...a)
|
|
569
|
+
});
|
|
570
|
+
if (info.assertion.designation === AdmittedFeed.Designation.CONTROL && !info.key.equals(genesisFeed.key)) {
|
|
571
|
+
try {
|
|
572
|
+
const feed = await feedProvider(info.key);
|
|
573
|
+
await this._pipeline.addFeed(feed);
|
|
574
|
+
} catch (err) {
|
|
575
|
+
log6.catch(err, {}, {
|
|
576
|
+
file: "control-pipeline.ts",
|
|
577
|
+
line: 49,
|
|
578
|
+
scope: this,
|
|
579
|
+
callSite: (f, a) => f(...a)
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
await this.onFeedAdmitted.callIfSet(info);
|
|
584
|
+
});
|
|
585
|
+
this.onMemberAdmitted = this._spaceStateMachine.onMemberAdmitted;
|
|
586
|
+
this.onCredentialProcessed = this._spaceStateMachine.onCredentialProcessed;
|
|
587
|
+
}
|
|
588
|
+
get spaceState() {
|
|
589
|
+
return this._spaceStateMachine;
|
|
590
|
+
}
|
|
591
|
+
get pipeline() {
|
|
592
|
+
return this._pipeline;
|
|
593
|
+
}
|
|
594
|
+
setWriteFeed(feed) {
|
|
595
|
+
this._pipeline.setWriteFeed(feed);
|
|
596
|
+
}
|
|
597
|
+
async start() {
|
|
598
|
+
log6("starting...", {}, {
|
|
599
|
+
file: "control-pipeline.ts",
|
|
600
|
+
line: 73,
|
|
601
|
+
scope: this,
|
|
602
|
+
callSite: (f, a) => f(...a)
|
|
603
|
+
});
|
|
604
|
+
setTimeout(async () => {
|
|
605
|
+
for await (const msg of this._pipeline.consume()) {
|
|
606
|
+
try {
|
|
607
|
+
log6("processing", {
|
|
608
|
+
key: msg.feedKey,
|
|
609
|
+
seq: msg.seq
|
|
610
|
+
}, {
|
|
611
|
+
file: "control-pipeline.ts",
|
|
612
|
+
line: 78,
|
|
613
|
+
scope: this,
|
|
614
|
+
callSite: (f, a) => f(...a)
|
|
615
|
+
});
|
|
616
|
+
if (msg.data.payload.credential) {
|
|
617
|
+
const result = await this._spaceStateMachine.process(msg.data.payload.credential.credential, PublicKey2.from(msg.feedKey));
|
|
618
|
+
if (!result) {
|
|
619
|
+
log6.warn("processing failed", {
|
|
620
|
+
msg
|
|
621
|
+
}, {
|
|
622
|
+
file: "control-pipeline.ts",
|
|
623
|
+
line: 85,
|
|
624
|
+
scope: this,
|
|
625
|
+
callSite: (f, a) => f(...a)
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
} catch (err) {
|
|
630
|
+
log6.catch(err, {}, {
|
|
631
|
+
file: "control-pipeline.ts",
|
|
632
|
+
line: 89,
|
|
633
|
+
scope: this,
|
|
634
|
+
callSite: (f, a) => f(...a)
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
await this._pipeline.start();
|
|
640
|
+
log6("started", {}, {
|
|
641
|
+
file: "control-pipeline.ts",
|
|
642
|
+
line: 95,
|
|
643
|
+
scope: this,
|
|
644
|
+
callSite: (f, a) => f(...a)
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
async stop() {
|
|
648
|
+
log6("stopping...", {}, {
|
|
649
|
+
file: "control-pipeline.ts",
|
|
650
|
+
line: 99,
|
|
651
|
+
scope: this,
|
|
652
|
+
callSite: (f, a) => f(...a)
|
|
653
|
+
});
|
|
654
|
+
await this._pipeline.stop();
|
|
655
|
+
log6("stopped", {}, {
|
|
656
|
+
file: "control-pipeline.ts",
|
|
657
|
+
line: 101,
|
|
658
|
+
scope: this,
|
|
659
|
+
callSite: (f, a) => f(...a)
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
// packages/core/echo/echo-pipeline/src/space/space.ts
|
|
665
|
+
var __decorate3 = function(decorators, target, key, desc) {
|
|
666
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
667
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
668
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
669
|
+
else
|
|
670
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
671
|
+
if (d = decorators[i])
|
|
672
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
673
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
674
|
+
};
|
|
675
|
+
var Space = class Space2 {
|
|
676
|
+
constructor({ spaceKey, protocol, genesisFeed, feedProvider }) {
|
|
677
|
+
this.onCredentialProcessed = new Callback2();
|
|
678
|
+
this.stateUpdate = new Event2();
|
|
679
|
+
this._isOpen = false;
|
|
680
|
+
assert6(spaceKey && feedProvider);
|
|
681
|
+
this._key = spaceKey;
|
|
682
|
+
this._genesisFeedKey = genesisFeed.key;
|
|
683
|
+
this._feedProvider = feedProvider;
|
|
684
|
+
this._controlPipeline = new ControlPipeline({
|
|
685
|
+
spaceKey,
|
|
686
|
+
genesisFeed,
|
|
687
|
+
feedProvider
|
|
688
|
+
});
|
|
689
|
+
this._controlPipeline.onFeedAdmitted.set(async (info) => {
|
|
690
|
+
if (info.assertion.designation === AdmittedFeed2.Designation.DATA) {
|
|
691
|
+
if (this._dataPipeline) {
|
|
692
|
+
await this._dataPipeline.addFeed(await feedProvider(info.key));
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (!info.key.equals(genesisFeed.key)) {
|
|
696
|
+
this.protocol.addFeed(await feedProvider(info.key));
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
this._controlPipeline.onCredentialProcessed.set(async (credential) => {
|
|
700
|
+
await this.onCredentialProcessed.callIfSet(credential);
|
|
701
|
+
log7("onCredentialProcessed", {
|
|
702
|
+
credential
|
|
703
|
+
}, {
|
|
704
|
+
file: "space.ts",
|
|
705
|
+
line: 74,
|
|
706
|
+
scope: this,
|
|
707
|
+
callSite: (f, a) => f(...a)
|
|
708
|
+
});
|
|
709
|
+
this.stateUpdate.emit();
|
|
710
|
+
});
|
|
711
|
+
this.protocol = protocol;
|
|
712
|
+
this.protocol.addFeed(genesisFeed);
|
|
713
|
+
}
|
|
714
|
+
get key() {
|
|
715
|
+
return this._key;
|
|
716
|
+
}
|
|
717
|
+
get isOpen() {
|
|
718
|
+
return this._isOpen;
|
|
719
|
+
}
|
|
720
|
+
get genesisFeedKey() {
|
|
721
|
+
return this._genesisFeedKey;
|
|
722
|
+
}
|
|
723
|
+
get controlFeedKey() {
|
|
724
|
+
var _a;
|
|
725
|
+
return (_a = this._controlFeed) == null ? void 0 : _a.key;
|
|
726
|
+
}
|
|
727
|
+
get dataFeedKey() {
|
|
728
|
+
var _a;
|
|
729
|
+
return (_a = this._dataFeed) == null ? void 0 : _a.key;
|
|
730
|
+
}
|
|
731
|
+
get spaceState() {
|
|
732
|
+
return this._controlPipeline.spaceState;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* @test-only
|
|
736
|
+
*/
|
|
737
|
+
get controlPipeline() {
|
|
738
|
+
return this._controlPipeline.pipeline;
|
|
739
|
+
}
|
|
740
|
+
setControlFeed(feed) {
|
|
741
|
+
assert6(!this._controlFeed, "Control feed already set.");
|
|
742
|
+
this._controlFeed = feed;
|
|
743
|
+
this._controlPipeline.setWriteFeed(feed);
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
setDataFeed(feed) {
|
|
747
|
+
var _a;
|
|
748
|
+
assert6(!this._dataFeed, "Data feed already set.");
|
|
749
|
+
this._dataFeed = feed;
|
|
750
|
+
(_a = this._dataPipeline) == null ? void 0 : _a.setWriteFeed(feed);
|
|
751
|
+
return this;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Use for diagnostics.
|
|
755
|
+
*/
|
|
756
|
+
getControlFeeds() {
|
|
757
|
+
return Array.from(this._controlPipeline.spaceState.feeds.values());
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Use for diagnostics.
|
|
761
|
+
*/
|
|
762
|
+
// getDataFeeds(): FeedInfo[] {
|
|
763
|
+
// return this._dataPipeline?.getFeeds();
|
|
764
|
+
// }
|
|
765
|
+
async open() {
|
|
766
|
+
log7("opening...", {}, {
|
|
767
|
+
file: "space.ts",
|
|
768
|
+
line: 145,
|
|
769
|
+
scope: this,
|
|
770
|
+
callSite: (f, a) => f(...a)
|
|
771
|
+
});
|
|
772
|
+
if (this._isOpen) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
await this._controlPipeline.start();
|
|
776
|
+
await this.protocol.start();
|
|
777
|
+
this._isOpen = true;
|
|
778
|
+
log7("opened", {}, {
|
|
779
|
+
file: "space.ts",
|
|
780
|
+
line: 155,
|
|
781
|
+
scope: this,
|
|
782
|
+
callSite: (f, a) => f(...a)
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
async close() {
|
|
786
|
+
var _a;
|
|
787
|
+
log7("closing...", {
|
|
788
|
+
key: this._key
|
|
789
|
+
}, {
|
|
790
|
+
file: "space.ts",
|
|
791
|
+
line: 160,
|
|
792
|
+
scope: this,
|
|
793
|
+
callSite: (f, a) => f(...a)
|
|
794
|
+
});
|
|
795
|
+
if (!this._isOpen) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
await this.protocol.stop();
|
|
799
|
+
await ((_a = this._dataPipelineController) == null ? void 0 : _a.close());
|
|
800
|
+
await this._controlPipeline.stop();
|
|
801
|
+
this._isOpen = false;
|
|
802
|
+
log7("closed", {}, {
|
|
803
|
+
file: "space.ts",
|
|
804
|
+
line: 171,
|
|
805
|
+
scope: this,
|
|
806
|
+
callSite: (f, a) => f(...a)
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
async initDataPipeline(controller) {
|
|
810
|
+
assert6(this._isOpen, "Space must be open to initialize data pipeline.");
|
|
811
|
+
assert6(!this._dataPipelineController, "Data pipeline already initialized.");
|
|
812
|
+
assert6(!this._dataPipeline, "Data pipeline already initialized.");
|
|
813
|
+
this._dataPipelineController = controller;
|
|
814
|
+
await this._dataPipelineController.open({
|
|
815
|
+
openPipeline: async (start) => {
|
|
816
|
+
assert6(!this._dataPipeline, "Data pipeline already initialized.");
|
|
817
|
+
this._dataPipeline = new Pipeline(start);
|
|
818
|
+
if (this._dataFeed) {
|
|
819
|
+
this._dataPipeline.setWriteFeed(this._dataFeed);
|
|
820
|
+
}
|
|
821
|
+
for (const feed of this._controlPipeline.spaceState.feeds.values()) {
|
|
822
|
+
if (feed.assertion.designation === AdmittedFeed2.Designation.DATA) {
|
|
823
|
+
await this._dataPipeline.addFeed(await this._feedProvider(feed.key));
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
await this._dataPipeline.start();
|
|
827
|
+
return this._dataPipeline;
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
__decorate3([
|
|
833
|
+
logInfo
|
|
834
|
+
], Space.prototype, "key", null);
|
|
835
|
+
__decorate3([
|
|
836
|
+
synchronized2
|
|
837
|
+
], Space.prototype, "open", null);
|
|
838
|
+
__decorate3([
|
|
839
|
+
synchronized2
|
|
840
|
+
], Space.prototype, "close", null);
|
|
841
|
+
Space = __decorate3([
|
|
842
|
+
trackLeaks("open", "close")
|
|
843
|
+
], Space);
|
|
844
|
+
|
|
845
|
+
// packages/core/echo/echo-pipeline/src/space/space-protocol.ts
|
|
846
|
+
import { discoveryKey, sha256 } from "@dxos/crypto";
|
|
847
|
+
import { PublicKey as PublicKey3 } from "@dxos/keys";
|
|
848
|
+
import { log as log8, logInfo as logInfo2 } from "@dxos/log";
|
|
849
|
+
import { MMSTTopology } from "@dxos/network-manager";
|
|
850
|
+
import { Teleport } from "@dxos/teleport";
|
|
851
|
+
import { ReplicatorExtension } from "@dxos/teleport-extension-replicator";
|
|
852
|
+
import { ComplexMap } from "@dxos/util";
|
|
853
|
+
var __decorate4 = function(decorators, target, key, desc) {
|
|
854
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
855
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
856
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
857
|
+
else
|
|
858
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
859
|
+
if (d = decorators[i])
|
|
860
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
861
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
862
|
+
};
|
|
863
|
+
var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
|
|
864
|
+
var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
|
|
865
|
+
var SpaceProtocol = class {
|
|
866
|
+
constructor({ topic, swarmIdentity, networkManager, onSessionAuth }) {
|
|
867
|
+
this._feeds = /* @__PURE__ */ new Set();
|
|
868
|
+
this._sessions = new ComplexMap(PublicKey3.hash);
|
|
869
|
+
this._networkManager = networkManager;
|
|
870
|
+
this._swarmIdentity = swarmIdentity;
|
|
871
|
+
this._onSessionAuth = onSessionAuth;
|
|
872
|
+
this._topic = PublicKey3.from(discoveryKey(sha256(topic.toHex())));
|
|
873
|
+
}
|
|
874
|
+
get sessions() {
|
|
875
|
+
return this._sessions;
|
|
876
|
+
}
|
|
877
|
+
get feeds() {
|
|
878
|
+
return this._feeds;
|
|
879
|
+
}
|
|
880
|
+
get _ownPeerKey() {
|
|
881
|
+
return this._swarmIdentity.peerKey;
|
|
882
|
+
}
|
|
883
|
+
// TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
|
|
884
|
+
addFeed(feed) {
|
|
885
|
+
log8("addFeed", {
|
|
886
|
+
key: feed.key
|
|
887
|
+
}, {
|
|
888
|
+
file: "space-protocol.ts",
|
|
889
|
+
line: 84,
|
|
890
|
+
scope: this,
|
|
891
|
+
callSite: (f, a) => f(...a)
|
|
892
|
+
});
|
|
893
|
+
this._feeds.add(feed);
|
|
894
|
+
for (const session of this._sessions.values()) {
|
|
895
|
+
session.replicator.addFeed(feed);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
async start() {
|
|
899
|
+
if (this._connection) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
|
|
903
|
+
const topologyConfig = {
|
|
904
|
+
originateConnections: 4,
|
|
905
|
+
maxPeers: 10,
|
|
906
|
+
sampleSize: 20
|
|
907
|
+
};
|
|
908
|
+
log8("starting...", {}, {
|
|
909
|
+
file: "space-protocol.ts",
|
|
910
|
+
line: 107,
|
|
911
|
+
scope: this,
|
|
912
|
+
callSite: (f, a) => f(...a)
|
|
913
|
+
});
|
|
914
|
+
this._connection = await this._networkManager.joinSwarm({
|
|
915
|
+
protocolProvider: this._createProtocolProvider(credentials),
|
|
916
|
+
peerId: this._swarmIdentity.peerKey,
|
|
917
|
+
topic: this._topic,
|
|
918
|
+
topology: new MMSTTopology(topologyConfig),
|
|
919
|
+
label: `Protocol swarm: ${this._topic}`
|
|
920
|
+
});
|
|
921
|
+
log8("started", {}, {
|
|
922
|
+
file: "space-protocol.ts",
|
|
923
|
+
line: 116,
|
|
924
|
+
scope: this,
|
|
925
|
+
callSite: (f, a) => f(...a)
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
async stop() {
|
|
929
|
+
if (this._connection) {
|
|
930
|
+
log8("stopping...", {}, {
|
|
931
|
+
file: "space-protocol.ts",
|
|
932
|
+
line: 121,
|
|
933
|
+
scope: this,
|
|
934
|
+
callSite: (f, a) => f(...a)
|
|
935
|
+
});
|
|
936
|
+
await this._connection.close();
|
|
937
|
+
log8("stopped", {}, {
|
|
938
|
+
file: "space-protocol.ts",
|
|
939
|
+
line: 123,
|
|
940
|
+
scope: this,
|
|
941
|
+
callSite: (f, a) => f(...a)
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
_createProtocolProvider(credentials) {
|
|
946
|
+
return (wireParams) => {
|
|
947
|
+
const session = new SpaceProtocolSession({
|
|
948
|
+
wireParams,
|
|
949
|
+
swarmIdentity: this._swarmIdentity,
|
|
950
|
+
onSessionAuth: this._onSessionAuth
|
|
951
|
+
});
|
|
952
|
+
this._sessions.set(wireParams.remotePeerId, session);
|
|
953
|
+
for (const feed of this._feeds) {
|
|
954
|
+
session.replicator.addFeed(feed);
|
|
955
|
+
}
|
|
956
|
+
return session;
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
__decorate4([
|
|
961
|
+
logInfo2
|
|
962
|
+
], SpaceProtocol.prototype, "_topic", void 0);
|
|
963
|
+
__decorate4([
|
|
964
|
+
logInfo2
|
|
965
|
+
], SpaceProtocol.prototype, "_ownPeerKey", null);
|
|
966
|
+
var AuthStatus;
|
|
967
|
+
(function(AuthStatus2) {
|
|
968
|
+
AuthStatus2["INITIAL"] = "INITIAL";
|
|
969
|
+
AuthStatus2["SUCCESS"] = "SUCCESS";
|
|
970
|
+
AuthStatus2["FAILURE"] = "FAILURE";
|
|
971
|
+
})(AuthStatus || (AuthStatus = {}));
|
|
972
|
+
var SpaceProtocolSession = class {
|
|
973
|
+
// TODO(dmaretskyi): Allow to pass in extra extensions.
|
|
974
|
+
constructor({ wireParams, swarmIdentity, onSessionAuth }) {
|
|
975
|
+
// TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
|
|
976
|
+
this.replicator = new ReplicatorExtension().setOptions({
|
|
977
|
+
upload: true
|
|
978
|
+
});
|
|
979
|
+
this._authStatus = AuthStatus.INITIAL;
|
|
980
|
+
this._wireParams = wireParams;
|
|
981
|
+
this._swarmIdentity = swarmIdentity;
|
|
982
|
+
this._onSessionAuth = onSessionAuth;
|
|
983
|
+
this._teleport = new Teleport(wireParams);
|
|
984
|
+
}
|
|
985
|
+
get authStatus() {
|
|
986
|
+
return this._authStatus;
|
|
987
|
+
}
|
|
988
|
+
get stream() {
|
|
989
|
+
return this._teleport.stream;
|
|
990
|
+
}
|
|
991
|
+
async initialize() {
|
|
992
|
+
await this._teleport.open();
|
|
993
|
+
this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
|
|
994
|
+
provider: this._swarmIdentity.credentialProvider,
|
|
995
|
+
verifier: this._swarmIdentity.credentialAuthenticator,
|
|
996
|
+
onAuthSuccess: () => {
|
|
997
|
+
var _a;
|
|
998
|
+
this._authStatus = AuthStatus.SUCCESS;
|
|
999
|
+
log8("Peer authenticated", {}, {
|
|
1000
|
+
file: "space-protocol.ts",
|
|
1001
|
+
line: 206,
|
|
1002
|
+
scope: this,
|
|
1003
|
+
callSite: (f, a) => f(...a)
|
|
1004
|
+
});
|
|
1005
|
+
(_a = this._onSessionAuth) == null ? void 0 : _a.call(this, this._teleport);
|
|
1006
|
+
},
|
|
1007
|
+
onAuthFailure: () => {
|
|
1008
|
+
log8.warn("Auth failed", {}, {
|
|
1009
|
+
file: "space-protocol.ts",
|
|
1010
|
+
line: 211,
|
|
1011
|
+
scope: this,
|
|
1012
|
+
callSite: (f, a) => f(...a)
|
|
1013
|
+
});
|
|
1014
|
+
this._authStatus = AuthStatus.FAILURE;
|
|
1015
|
+
}
|
|
1016
|
+
}));
|
|
1017
|
+
this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
|
|
1018
|
+
}
|
|
1019
|
+
async destroy() {
|
|
1020
|
+
await this._teleport.close();
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
__decorate4([
|
|
1024
|
+
logInfo2
|
|
1025
|
+
], SpaceProtocolSession.prototype, "_wireParams", void 0);
|
|
1026
|
+
__decorate4([
|
|
1027
|
+
logInfo2
|
|
1028
|
+
], SpaceProtocolSession.prototype, "authStatus", null);
|
|
1029
|
+
|
|
1030
|
+
// packages/core/echo/echo-pipeline/src/space/space-manager.ts
|
|
1031
|
+
import { synchronized as synchronized3, trackLeaks as trackLeaks2 } from "@dxos/async";
|
|
1032
|
+
import { failUndefined } from "@dxos/debug";
|
|
1033
|
+
import { PublicKey as PublicKey4 } from "@dxos/keys";
|
|
1034
|
+
import { log as log9 } from "@dxos/log";
|
|
1035
|
+
import { ComplexMap as ComplexMap2 } from "@dxos/util";
|
|
1036
|
+
var __decorate5 = function(decorators, target, key, desc) {
|
|
1037
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1038
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
1039
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1040
|
+
else
|
|
1041
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
1042
|
+
if (d = decorators[i])
|
|
1043
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1044
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1045
|
+
};
|
|
1046
|
+
var SpaceManager = class SpaceManager2 {
|
|
1047
|
+
constructor({ feedStore, networkManager }) {
|
|
1048
|
+
this._spaces = new ComplexMap2(PublicKey4.hash);
|
|
1049
|
+
this._feedStore = feedStore;
|
|
1050
|
+
this._networkManager = networkManager;
|
|
1051
|
+
}
|
|
1052
|
+
// TODO(burdon): Remove.
|
|
1053
|
+
get spaces() {
|
|
1054
|
+
return this._spaces;
|
|
1055
|
+
}
|
|
1056
|
+
async open() {
|
|
1057
|
+
}
|
|
1058
|
+
async close() {
|
|
1059
|
+
await Promise.all([
|
|
1060
|
+
...this._spaces.values()
|
|
1061
|
+
].map((space) => space.close()));
|
|
1062
|
+
}
|
|
1063
|
+
async constructSpace({ metadata, swarmIdentity, onNetworkConnection }) {
|
|
1064
|
+
var _a;
|
|
1065
|
+
log9("constructing space...", {
|
|
1066
|
+
spaceKey: metadata.genesisFeedKey
|
|
1067
|
+
}, {
|
|
1068
|
+
file: "space-manager.ts",
|
|
1069
|
+
line: 70,
|
|
1070
|
+
scope: this,
|
|
1071
|
+
callSite: (f, a) => f(...a)
|
|
1072
|
+
});
|
|
1073
|
+
const genesisFeed = await this._feedStore.openFeed((_a = metadata.genesisFeedKey) != null ? _a : failUndefined());
|
|
1074
|
+
const spaceKey = metadata.key;
|
|
1075
|
+
const protocol = new SpaceProtocol({
|
|
1076
|
+
topic: spaceKey,
|
|
1077
|
+
swarmIdentity,
|
|
1078
|
+
networkManager: this._networkManager,
|
|
1079
|
+
onSessionAuth: onNetworkConnection
|
|
1080
|
+
});
|
|
1081
|
+
const space = new Space({
|
|
1082
|
+
spaceKey,
|
|
1083
|
+
protocol,
|
|
1084
|
+
genesisFeed,
|
|
1085
|
+
feedProvider: (feedKey) => this._feedStore.openFeed(feedKey)
|
|
1086
|
+
});
|
|
1087
|
+
this._spaces.set(space.key, space);
|
|
1088
|
+
return space;
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
__decorate5([
|
|
1092
|
+
synchronized3
|
|
1093
|
+
], SpaceManager.prototype, "open", null);
|
|
1094
|
+
__decorate5([
|
|
1095
|
+
synchronized3
|
|
1096
|
+
], SpaceManager.prototype, "close", null);
|
|
1097
|
+
SpaceManager = __decorate5([
|
|
1098
|
+
trackLeaks2("open", "close")
|
|
1099
|
+
], SpaceManager);
|
|
1100
|
+
|
|
1101
|
+
// packages/core/echo/echo-pipeline/src/space/genesis.ts
|
|
1102
|
+
import { CredentialGenerator } from "@dxos/credentials";
|
|
1103
|
+
import { failUndefined as failUndefined2 } from "@dxos/debug";
|
|
1104
|
+
import { AdmittedFeed as AdmittedFeed3 } from "@dxos/protocols/proto/dxos/halo/credentials";
|
|
1105
|
+
var spaceGenesis = async (keyring, signingContext, space) => {
|
|
1106
|
+
var _a, _b;
|
|
1107
|
+
const generator = new CredentialGenerator(keyring, signingContext.identityKey, signingContext.deviceKey);
|
|
1108
|
+
const credentials = [
|
|
1109
|
+
...await generator.createSpaceGenesis(space.key, (_a = space.controlFeedKey) != null ? _a : failUndefined2(), signingContext.profile),
|
|
1110
|
+
await generator.createFeedAdmission(space.key, (_b = space.dataFeedKey) != null ? _b : failUndefined2(), AdmittedFeed3.Designation.DATA)
|
|
1111
|
+
];
|
|
1112
|
+
for (const credential of credentials) {
|
|
1113
|
+
await space.controlPipeline.writer.write({
|
|
1114
|
+
credential: {
|
|
1115
|
+
credential
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
return credentials;
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
// packages/core/echo/echo-pipeline/src/dbhost/data-service-host.ts
|
|
1123
|
+
import assert7 from "@dxos/node-std/assert";
|
|
1124
|
+
import { Stream } from "@dxos/codec-protobuf";
|
|
1125
|
+
import { tagMutationsInBatch } from "@dxos/echo-db";
|
|
1126
|
+
import { log as log10 } from "@dxos/log";
|
|
1127
|
+
import { ComplexMap as ComplexMap3 } from "@dxos/util";
|
|
1128
|
+
var DataServiceHost = class {
|
|
1129
|
+
constructor(_itemManager, _itemDemuxer, _writeStream) {
|
|
1130
|
+
this._itemManager = _itemManager;
|
|
1131
|
+
this._itemDemuxer = _itemDemuxer;
|
|
1132
|
+
this._writeStream = _writeStream;
|
|
1133
|
+
this._clientTagMap = new ComplexMap3(([feedKey, seq]) => `${feedKey.toHex()}:${seq}`);
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Real-time subscription to data objects in a space.
|
|
1137
|
+
*/
|
|
1138
|
+
subscribe() {
|
|
1139
|
+
return new Stream(({ next, ctx }) => {
|
|
1140
|
+
const objects = Array.from(this._itemManager.entities.values()).map((entity) => entity.createSnapshot());
|
|
1141
|
+
next({
|
|
1142
|
+
batch: {
|
|
1143
|
+
objects
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
this._itemDemuxer.mutation.on(ctx, (mutation) => {
|
|
1147
|
+
var _a;
|
|
1148
|
+
log10("Object update", {
|
|
1149
|
+
mutation
|
|
1150
|
+
}, {
|
|
1151
|
+
file: "data-service-host.ts",
|
|
1152
|
+
line: 49,
|
|
1153
|
+
scope: this,
|
|
1154
|
+
callSite: (f, a) => f(...a)
|
|
1155
|
+
});
|
|
1156
|
+
const clientTag = this._clientTagMap.get([
|
|
1157
|
+
mutation.meta.feedKey,
|
|
1158
|
+
mutation.meta.seq
|
|
1159
|
+
]);
|
|
1160
|
+
const batch = {
|
|
1161
|
+
objects: [
|
|
1162
|
+
{
|
|
1163
|
+
...mutation.data,
|
|
1164
|
+
mutations: (_a = mutation.data.mutations) == null ? void 0 : _a.map((m, mutationIdx) => ({
|
|
1165
|
+
...m,
|
|
1166
|
+
meta: mutation.meta
|
|
1167
|
+
})),
|
|
1168
|
+
meta: mutation.meta
|
|
1169
|
+
}
|
|
1170
|
+
]
|
|
1171
|
+
};
|
|
1172
|
+
if (clientTag) {
|
|
1173
|
+
tagMutationsInBatch(batch, clientTag);
|
|
1174
|
+
}
|
|
1175
|
+
next({
|
|
1176
|
+
clientTag,
|
|
1177
|
+
feedKey: mutation.meta.feedKey,
|
|
1178
|
+
seq: mutation.meta.seq,
|
|
1179
|
+
batch
|
|
1180
|
+
});
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
async write(request) {
|
|
1185
|
+
var _a, _b;
|
|
1186
|
+
assert7(this._writeStream, "Cannot write mutations in readonly mode");
|
|
1187
|
+
assert7(((_a = request.batch.objects) == null ? void 0 : _a.length) === 1, "Only single object mutations are supported");
|
|
1188
|
+
const receipt = await this._writeStream.write({
|
|
1189
|
+
object: {
|
|
1190
|
+
...request.batch.objects[0],
|
|
1191
|
+
mutations: (_b = request.batch.objects[0].mutations) == null ? void 0 : _b.map((m) => ({
|
|
1192
|
+
...m,
|
|
1193
|
+
meta: void 0
|
|
1194
|
+
})),
|
|
1195
|
+
meta: void 0
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
if (request.clientTag) {
|
|
1199
|
+
this._clientTagMap.set([
|
|
1200
|
+
receipt.feedKey,
|
|
1201
|
+
receipt.seq
|
|
1202
|
+
], request.clientTag);
|
|
1203
|
+
}
|
|
1204
|
+
return receipt;
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
// packages/core/echo/echo-pipeline/src/dbhost/database-backend.ts
|
|
1209
|
+
import { ItemDemuxer } from "@dxos/echo-db";
|
|
1210
|
+
var DatabaseBackendHost = class {
|
|
1211
|
+
constructor(_outboundStream, _snapshot, _options = {}) {
|
|
1212
|
+
this._outboundStream = _outboundStream;
|
|
1213
|
+
this._snapshot = _snapshot;
|
|
1214
|
+
this._options = _options;
|
|
1215
|
+
}
|
|
1216
|
+
get isReadOnly() {
|
|
1217
|
+
return !!this._outboundStream;
|
|
1218
|
+
}
|
|
1219
|
+
get echoProcessor() {
|
|
1220
|
+
return this._echoProcessor;
|
|
1221
|
+
}
|
|
1222
|
+
async open(itemManager, modelFactory) {
|
|
1223
|
+
this._itemManager = itemManager;
|
|
1224
|
+
this._itemManager._debugLabel = "host";
|
|
1225
|
+
this._itemDemuxer = new ItemDemuxer(itemManager, modelFactory, this._options);
|
|
1226
|
+
this._echoProcessor = this._itemDemuxer.open();
|
|
1227
|
+
if (this._snapshot) {
|
|
1228
|
+
await this._itemDemuxer.restoreFromSnapshot(this._snapshot);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
async close() {
|
|
1232
|
+
}
|
|
1233
|
+
getWriteStream() {
|
|
1234
|
+
return this._outboundStream;
|
|
1235
|
+
}
|
|
1236
|
+
createSnapshot() {
|
|
1237
|
+
return this._itemDemuxer.createSnapshot();
|
|
1238
|
+
}
|
|
1239
|
+
createDataServiceHost() {
|
|
1240
|
+
var _a;
|
|
1241
|
+
return new DataServiceHost(this._itemManager, this._itemDemuxer, (_a = this._outboundStream) != null ? _a : void 0);
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
// packages/core/echo/echo-pipeline/src/dbhost/snapshot-manager.ts
|
|
1246
|
+
import { trackLeaks as trackLeaks3 } from "@dxos/async";
|
|
1247
|
+
import { schema as schema4 } from "@dxos/protocols";
|
|
1248
|
+
import { ObjectSync } from "@dxos/teleport-extension-object-sync";
|
|
1249
|
+
var __decorate6 = function(decorators, target, key, desc) {
|
|
1250
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1251
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
1252
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1253
|
+
else
|
|
1254
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
1255
|
+
if (d = decorators[i])
|
|
1256
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1257
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1258
|
+
};
|
|
1259
|
+
var SnapshotManager = class SnapshotManager2 {
|
|
1260
|
+
// prettier-ignore
|
|
1261
|
+
constructor(_snapshotStore) {
|
|
1262
|
+
this._snapshotStore = _snapshotStore;
|
|
1263
|
+
this._objectSync = new ObjectSync({
|
|
1264
|
+
getObject: async (id) => {
|
|
1265
|
+
const snapshot = await this._snapshotStore.loadSnapshot(id);
|
|
1266
|
+
if (!snapshot) {
|
|
1267
|
+
return void 0;
|
|
1268
|
+
}
|
|
1269
|
+
return {
|
|
1270
|
+
id,
|
|
1271
|
+
payload: schema4.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encodeAsAny(snapshot)
|
|
1272
|
+
};
|
|
1273
|
+
},
|
|
1274
|
+
setObject: async (data) => {
|
|
1275
|
+
const snapshot = schema4.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(data.payload);
|
|
1276
|
+
await this._snapshotStore.saveSnapshot(snapshot);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
get objectSync() {
|
|
1281
|
+
return this._objectSync;
|
|
1282
|
+
}
|
|
1283
|
+
async open() {
|
|
1284
|
+
await this._objectSync.open();
|
|
1285
|
+
}
|
|
1286
|
+
async close() {
|
|
1287
|
+
await this._objectSync.close();
|
|
1288
|
+
}
|
|
1289
|
+
async load(id) {
|
|
1290
|
+
const local = await this._snapshotStore.loadSnapshot(id);
|
|
1291
|
+
if (local) {
|
|
1292
|
+
return local;
|
|
1293
|
+
}
|
|
1294
|
+
const remote = await this._objectSync.download(id);
|
|
1295
|
+
return schema4.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(remote.payload);
|
|
1296
|
+
}
|
|
1297
|
+
async store(snapshot) {
|
|
1298
|
+
const id = await this._snapshotStore.saveSnapshot(snapshot);
|
|
1299
|
+
return id;
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
SnapshotManager = __decorate6([
|
|
1303
|
+
trackLeaks3("open", "close")
|
|
1304
|
+
], SnapshotManager);
|
|
1305
|
+
|
|
1306
|
+
// packages/core/echo/echo-pipeline/src/dbhost/snapshot-store.ts
|
|
1307
|
+
import { subtleCrypto } from "@dxos/crypto";
|
|
1308
|
+
import { schema as schema5 } from "@dxos/protocols";
|
|
1309
|
+
var SnapshotStore = class {
|
|
1310
|
+
// prettier-ignore
|
|
1311
|
+
constructor(_directory) {
|
|
1312
|
+
this._directory = _directory;
|
|
1313
|
+
}
|
|
1314
|
+
async saveSnapshot(snapshot) {
|
|
1315
|
+
const encoded = schema5.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encode(snapshot);
|
|
1316
|
+
const key = await subtleCrypto.digest("SHA-256", encoded);
|
|
1317
|
+
const keyString = Buffer.from(key).toString("hex");
|
|
1318
|
+
const file = await this._directory.getOrCreateFile(keyString);
|
|
1319
|
+
try {
|
|
1320
|
+
await file.write(0, Buffer.from(encoded));
|
|
1321
|
+
} finally {
|
|
1322
|
+
await file.close();
|
|
1323
|
+
}
|
|
1324
|
+
return keyString;
|
|
1325
|
+
}
|
|
1326
|
+
async loadSnapshot(key) {
|
|
1327
|
+
const file = await this._directory.getOrCreateFile(key);
|
|
1328
|
+
try {
|
|
1329
|
+
const { size } = await file.stat();
|
|
1330
|
+
if (size === 0) {
|
|
1331
|
+
return void 0;
|
|
1332
|
+
}
|
|
1333
|
+
const buffer = await file.read(0, size);
|
|
1334
|
+
return schema5.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(buffer);
|
|
1335
|
+
} finally {
|
|
1336
|
+
await file.close();
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
// packages/core/echo/echo-pipeline/src/dbhost/data-service.ts
|
|
1342
|
+
import assert8 from "@dxos/node-std/assert";
|
|
1343
|
+
import { raise } from "@dxos/debug";
|
|
1344
|
+
import { PublicKey as PublicKey5 } from "@dxos/keys";
|
|
1345
|
+
import { log as log11 } from "@dxos/log";
|
|
1346
|
+
import { ComplexMap as ComplexMap4 } from "@dxos/util";
|
|
1347
|
+
var DataServiceSubscriptions = class {
|
|
1348
|
+
constructor() {
|
|
1349
|
+
this._spaces = new ComplexMap4(PublicKey5.hash);
|
|
1350
|
+
}
|
|
1351
|
+
clear() {
|
|
1352
|
+
this._spaces.clear();
|
|
1353
|
+
}
|
|
1354
|
+
registerSpace(spaceKey, host) {
|
|
1355
|
+
log11("Registering space", {
|
|
1356
|
+
spaceKey
|
|
1357
|
+
}, {
|
|
1358
|
+
file: "data-service.ts",
|
|
1359
|
+
line: 31,
|
|
1360
|
+
scope: this,
|
|
1361
|
+
callSite: (f, a) => f(...a)
|
|
1362
|
+
});
|
|
1363
|
+
this._spaces.set(spaceKey, host);
|
|
1364
|
+
}
|
|
1365
|
+
unregisterSpace(spaceKey) {
|
|
1366
|
+
log11("Unregistering space", {
|
|
1367
|
+
spaceKey
|
|
1368
|
+
}, {
|
|
1369
|
+
file: "data-service.ts",
|
|
1370
|
+
line: 36,
|
|
1371
|
+
scope: this,
|
|
1372
|
+
callSite: (f, a) => f(...a)
|
|
1373
|
+
});
|
|
1374
|
+
this._spaces.delete(spaceKey);
|
|
1375
|
+
}
|
|
1376
|
+
getDataService(spaceKey) {
|
|
1377
|
+
return this._spaces.get(spaceKey);
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
var DataServiceImpl = class {
|
|
1381
|
+
constructor(_subscriptions) {
|
|
1382
|
+
this._subscriptions = _subscriptions;
|
|
1383
|
+
}
|
|
1384
|
+
subscribe(request) {
|
|
1385
|
+
var _a;
|
|
1386
|
+
assert8(request.spaceKey);
|
|
1387
|
+
const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : raise(new Error(`space not found: ${request.spaceKey}`));
|
|
1388
|
+
return host.subscribe();
|
|
1389
|
+
}
|
|
1390
|
+
write(request) {
|
|
1391
|
+
var _a;
|
|
1392
|
+
assert8(request.spaceKey);
|
|
1393
|
+
assert8(request.batch);
|
|
1394
|
+
const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : raise(new Error(`space not found: ${request.spaceKey}`));
|
|
1395
|
+
return host.write(request);
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
// packages/core/echo/echo-pipeline/src/space/data-pipeline-controller.ts
|
|
1400
|
+
import assert9 from "@dxos/node-std/assert";
|
|
1401
|
+
import { Event as Event3, scheduleTask as scheduleTask2, trackLeaks as trackLeaks4 } from "@dxos/async";
|
|
1402
|
+
import { Context as Context2 } from "@dxos/context";
|
|
1403
|
+
import { failUndefined as failUndefined3 } from "@dxos/debug";
|
|
1404
|
+
import { ItemManager } from "@dxos/echo-db";
|
|
1405
|
+
import { log as log12 } from "@dxos/log";
|
|
1406
|
+
import { Timeframe as Timeframe3 } from "@dxos/timeframe";
|
|
1407
|
+
var __decorate7 = function(decorators, target, key, desc) {
|
|
1408
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1409
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
1410
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1411
|
+
else
|
|
1412
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
1413
|
+
if (d = decorators[i])
|
|
1414
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1415
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1416
|
+
};
|
|
1417
|
+
var NoopDataPipelineController = class {
|
|
1418
|
+
async open(context) {
|
|
1419
|
+
}
|
|
1420
|
+
async close() {
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
var MESSAGES_PER_SNAPSHOT = 10;
|
|
1424
|
+
var AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL = 5e3;
|
|
1425
|
+
var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
|
|
1426
|
+
var DataPipelineControllerImpl = class DataPipelineControllerImpl2 {
|
|
1427
|
+
constructor(_params) {
|
|
1428
|
+
this._params = _params;
|
|
1429
|
+
this._ctx = new Context2();
|
|
1430
|
+
this._lastAutomaticSnapshotTimeframe = new Timeframe3();
|
|
1431
|
+
this.onTimeframeReached = new Event3();
|
|
1432
|
+
}
|
|
1433
|
+
get pipelineState() {
|
|
1434
|
+
var _a;
|
|
1435
|
+
return (_a = this._pipeline) == null ? void 0 : _a.state;
|
|
1436
|
+
}
|
|
1437
|
+
get snapshotTimeframe() {
|
|
1438
|
+
var _a;
|
|
1439
|
+
return (_a = this._snapshot) == null ? void 0 : _a.timeframe;
|
|
1440
|
+
}
|
|
1441
|
+
getStartTimeframe() {
|
|
1442
|
+
var _a;
|
|
1443
|
+
return snapshotTimeframeToStartingTimeframe((_a = this.snapshotTimeframe) != null ? _a : new Timeframe3());
|
|
1444
|
+
}
|
|
1445
|
+
async open(spaceContext) {
|
|
1446
|
+
var _a, _b, _c, _d;
|
|
1447
|
+
this._spaceContext = spaceContext;
|
|
1448
|
+
if (this._params.snapshotId) {
|
|
1449
|
+
this._snapshot = await this._params.snapshotManager.load(this._params.snapshotId);
|
|
1450
|
+
this._lastAutomaticSnapshotTimeframe = (_b = (_a = this._snapshot) == null ? void 0 : _a.timeframe) != null ? _b : new Timeframe3();
|
|
1451
|
+
}
|
|
1452
|
+
this._pipeline = await this._spaceContext.openPipeline(this.getStartTimeframe());
|
|
1453
|
+
const feedWriter = createMappedFeedWriter((data) => ({
|
|
1454
|
+
data
|
|
1455
|
+
}), (_c = this._pipeline.writer) != null ? _c : failUndefined3());
|
|
1456
|
+
this.databaseBackend = new DatabaseBackendHost(feedWriter, (_d = this._snapshot) == null ? void 0 : _d.database, {
|
|
1457
|
+
snapshots: true
|
|
1458
|
+
// TODO(burdon): Config.
|
|
1459
|
+
});
|
|
1460
|
+
this._itemManager = new ItemManager(this._params.modelFactory);
|
|
1461
|
+
await this.databaseBackend.open(this._itemManager, this._params.modelFactory);
|
|
1462
|
+
scheduleTask2(this._ctx, async () => {
|
|
1463
|
+
await this._consumePipeline();
|
|
1464
|
+
});
|
|
1465
|
+
this._createPeriodicSnapshots();
|
|
1466
|
+
}
|
|
1467
|
+
_createPeriodicSnapshots() {
|
|
1468
|
+
this.onTimeframeReached.debounce(TIMEFRAME_SAVE_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
|
|
1469
|
+
await this._saveLatestTimeframe();
|
|
1470
|
+
});
|
|
1471
|
+
this.onTimeframeReached.debounce(AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
|
|
1472
|
+
var _a, _b;
|
|
1473
|
+
const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
|
|
1474
|
+
if (!latestTimeframe) {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
if (latestTimeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
|
|
1478
|
+
const snapshot = await this._saveSnapshot();
|
|
1479
|
+
this._lastAutomaticSnapshotTimeframe = (_b = snapshot.timeframe) != null ? _b : failUndefined3();
|
|
1480
|
+
log12("save", {
|
|
1481
|
+
snapshot
|
|
1482
|
+
}, {
|
|
1483
|
+
file: "data-pipeline-controller.ts",
|
|
1484
|
+
line: 151,
|
|
1485
|
+
scope: this,
|
|
1486
|
+
callSite: (f, a) => f(...a)
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
async _saveSnapshot() {
|
|
1492
|
+
const snapshot = await this.createSnapshot();
|
|
1493
|
+
const snapshotKey = await this._params.snapshotManager.store(snapshot);
|
|
1494
|
+
await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
|
|
1495
|
+
return snapshot;
|
|
1496
|
+
}
|
|
1497
|
+
async _saveLatestTimeframe() {
|
|
1498
|
+
var _a;
|
|
1499
|
+
const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
|
|
1500
|
+
if (latestTimeframe) {
|
|
1501
|
+
await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, latestTimeframe);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
async close() {
|
|
1505
|
+
var _a, _b, _c;
|
|
1506
|
+
try {
|
|
1507
|
+
await this._saveLatestTimeframe();
|
|
1508
|
+
await this._saveSnapshot();
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
log12.catch(err, {}, {
|
|
1511
|
+
file: "data-pipeline-controller.ts",
|
|
1512
|
+
line: 175,
|
|
1513
|
+
scope: this,
|
|
1514
|
+
callSite: (f, a) => f(...a)
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
await this._ctx.dispose();
|
|
1518
|
+
await ((_a = this._pipeline) == null ? void 0 : _a.stop());
|
|
1519
|
+
await ((_b = this.databaseBackend) == null ? void 0 : _b.close());
|
|
1520
|
+
await ((_c = this._itemManager) == null ? void 0 : _c.destroy());
|
|
1521
|
+
await this._params.snapshotManager.close();
|
|
1522
|
+
}
|
|
1523
|
+
createSnapshot() {
|
|
1524
|
+
var _a, _b;
|
|
1525
|
+
return {
|
|
1526
|
+
spaceKey: this._params.spaceKey.asUint8Array(),
|
|
1527
|
+
timeframe: (_b = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe) != null ? _b : new Timeframe3(),
|
|
1528
|
+
database: this.databaseBackend.createSnapshot()
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
async _consumePipeline() {
|
|
1532
|
+
assert9(this._pipeline, "Pipeline is not initialized.");
|
|
1533
|
+
for await (const msg of this._pipeline.consume()) {
|
|
1534
|
+
const { feedKey, seq, data } = msg;
|
|
1535
|
+
log12("processing message", {
|
|
1536
|
+
msg
|
|
1537
|
+
}, {
|
|
1538
|
+
file: "data-pipeline-controller.ts",
|
|
1539
|
+
line: 196,
|
|
1540
|
+
scope: this,
|
|
1541
|
+
callSite: (f, a) => f(...a)
|
|
1542
|
+
});
|
|
1543
|
+
try {
|
|
1544
|
+
if (data.payload.data) {
|
|
1545
|
+
const feedInfo = this._params.feedInfoProvider(feedKey);
|
|
1546
|
+
if (!feedInfo) {
|
|
1547
|
+
log12.error("Could not find feed.", {
|
|
1548
|
+
feedKey
|
|
1549
|
+
}, {
|
|
1550
|
+
file: "data-pipeline-controller.ts",
|
|
1551
|
+
line: 202,
|
|
1552
|
+
scope: this,
|
|
1553
|
+
callSite: (f, a) => f(...a)
|
|
1554
|
+
});
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
await this.databaseBackend.echoProcessor({
|
|
1558
|
+
data: data.payload.data.object,
|
|
1559
|
+
meta: {
|
|
1560
|
+
feedKey,
|
|
1561
|
+
seq,
|
|
1562
|
+
timeframe: data.timeframe,
|
|
1563
|
+
memberKey: feedInfo.assertion.identityKey
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
this.onTimeframeReached.emit(data.timeframe);
|
|
1567
|
+
}
|
|
1568
|
+
} catch (err) {
|
|
1569
|
+
log12.catch(err, {}, {
|
|
1570
|
+
file: "data-pipeline-controller.ts",
|
|
1571
|
+
line: 218,
|
|
1572
|
+
scope: this,
|
|
1573
|
+
callSite: (f, a) => f(...a)
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
async waitUntilTimeframe(timeframe) {
|
|
1579
|
+
assert9(this._pipeline, "Pipeline is not initialized.");
|
|
1580
|
+
await this._pipeline.state.waitUntilTimeframe(timeframe);
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
DataPipelineControllerImpl = __decorate7([
|
|
1584
|
+
trackLeaks4("open", "close")
|
|
1585
|
+
], DataPipelineControllerImpl);
|
|
1586
|
+
var snapshotTimeframeToStartingTimeframe = (snapshotTimeframe) => {
|
|
1587
|
+
return snapshotTimeframe.map(([key, seq]) => [
|
|
1588
|
+
key,
|
|
1589
|
+
seq + 1
|
|
1590
|
+
]);
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
export {
|
|
1594
|
+
STORAGE_VERSION,
|
|
1595
|
+
MetadataStore,
|
|
1596
|
+
codec,
|
|
1597
|
+
valueEncoding,
|
|
1598
|
+
createMappedFeedWriter,
|
|
1599
|
+
mapTimeframeToFeedIndexes,
|
|
1600
|
+
mapFeedIndexesToTimeframe,
|
|
1601
|
+
TimeframeClock,
|
|
1602
|
+
Pipeline,
|
|
1603
|
+
AuthExtension,
|
|
1604
|
+
Space,
|
|
1605
|
+
MOCK_AUTH_PROVIDER,
|
|
1606
|
+
MOCK_AUTH_VERIFIER,
|
|
1607
|
+
SpaceProtocol,
|
|
1608
|
+
AuthStatus,
|
|
1609
|
+
SpaceProtocolSession,
|
|
1610
|
+
SpaceManager,
|
|
1611
|
+
spaceGenesis,
|
|
1612
|
+
DataServiceHost,
|
|
1613
|
+
DatabaseBackendHost,
|
|
1614
|
+
SnapshotManager,
|
|
1615
|
+
SnapshotStore,
|
|
1616
|
+
DataServiceSubscriptions,
|
|
1617
|
+
DataServiceImpl,
|
|
1618
|
+
NoopDataPipelineController,
|
|
1619
|
+
DataPipelineControllerImpl
|
|
1620
|
+
};
|
|
1621
|
+
//# sourceMappingURL=chunk-3GAE5CKD.mjs.map
|