@dxos/echo-pipeline 0.4.8-next.06fa7e4 → 0.4.8-next.2e7285f
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/dist/lib/browser/{chunk-WAN2XUWE.mjs → chunk-3PPSCHNN.mjs} +652 -17
- package/dist/lib/browser/{chunk-WAN2XUWE.mjs.map → chunk-3PPSCHNN.mjs.map} +4 -4
- package/dist/lib/browser/index.mjs +6 -633
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +274 -2
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/{chunk-U6J2HC4T.cjs → chunk-EVKDEZDX.cjs} +644 -19
- package/dist/lib/node/chunk-EVKDEZDX.cjs.map +7 -0
- package/dist/lib/node/index.cjs +30 -647
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +282 -12
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/testing/database-test-rig.d.ts +67 -0
- package/dist/types/src/testing/database-test-rig.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +6 -2
- package/dist/types/src/testing/util.d.ts.map +1 -1
- 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 +33 -33
- package/src/space/control-pipeline.ts +1 -3
- package/src/testing/database-test-rig.ts +289 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/util.ts +26 -2
- package/src/tests/database.test.ts +100 -0
- package/dist/lib/node/chunk-U6J2HC4T.cjs.map +0 -7
|
@@ -1987,13 +1987,11 @@ var ControlPipeline = class {
|
|
|
1987
1987
|
queueMicrotask(async () => {
|
|
1988
1988
|
try {
|
|
1989
1989
|
const feed = await feedProvider(info.key);
|
|
1990
|
-
|
|
1991
|
-
await this._pipeline.addFeed(feed);
|
|
1992
|
-
}
|
|
1990
|
+
await this._pipeline.addFeed(feed);
|
|
1993
1991
|
} catch (err) {
|
|
1994
1992
|
log9.catch(err, void 0, {
|
|
1995
1993
|
F: __dxlog_file10,
|
|
1996
|
-
L:
|
|
1994
|
+
L: 83,
|
|
1997
1995
|
S: this,
|
|
1998
1996
|
C: (f, a) => f(...a)
|
|
1999
1997
|
});
|
|
@@ -2023,7 +2021,7 @@ var ControlPipeline = class {
|
|
|
2023
2021
|
tf: snapshot?.timeframe
|
|
2024
2022
|
}, {
|
|
2025
2023
|
F: __dxlog_file10,
|
|
2026
|
-
L:
|
|
2024
|
+
L: 111,
|
|
2027
2025
|
S: this,
|
|
2028
2026
|
C: (f, a) => f(...a)
|
|
2029
2027
|
});
|
|
@@ -2032,7 +2030,7 @@ var ControlPipeline = class {
|
|
|
2032
2030
|
}
|
|
2033
2031
|
log9("starting...", void 0, {
|
|
2034
2032
|
F: __dxlog_file10,
|
|
2035
|
-
L:
|
|
2033
|
+
L: 116,
|
|
2036
2034
|
S: this,
|
|
2037
2035
|
C: (f, a) => f(...a)
|
|
2038
2036
|
});
|
|
@@ -2042,7 +2040,7 @@ var ControlPipeline = class {
|
|
|
2042
2040
|
await this._pipeline.start();
|
|
2043
2041
|
log9("started", void 0, {
|
|
2044
2042
|
F: __dxlog_file10,
|
|
2045
|
-
L:
|
|
2043
|
+
L: 122,
|
|
2046
2044
|
S: this,
|
|
2047
2045
|
C: (f, a) => f(...a)
|
|
2048
2046
|
});
|
|
@@ -2059,7 +2057,7 @@ var ControlPipeline = class {
|
|
|
2059
2057
|
message
|
|
2060
2058
|
}, {
|
|
2061
2059
|
F: __dxlog_file10,
|
|
2062
|
-
L:
|
|
2060
|
+
L: 135,
|
|
2063
2061
|
S: this,
|
|
2064
2062
|
C: (f, a) => f(...a)
|
|
2065
2063
|
});
|
|
@@ -2081,7 +2079,7 @@ var ControlPipeline = class {
|
|
|
2081
2079
|
snapshot
|
|
2082
2080
|
}, {
|
|
2083
2081
|
F: __dxlog_file10,
|
|
2084
|
-
L:
|
|
2082
|
+
L: 151,
|
|
2085
2083
|
S: this,
|
|
2086
2084
|
C: (f, a) => f(...a)
|
|
2087
2085
|
});
|
|
@@ -2096,7 +2094,7 @@ var ControlPipeline = class {
|
|
|
2096
2094
|
} catch (err) {
|
|
2097
2095
|
log9.catch(err, void 0, {
|
|
2098
2096
|
F: __dxlog_file10,
|
|
2099
|
-
L:
|
|
2097
|
+
L: 164,
|
|
2100
2098
|
S: this,
|
|
2101
2099
|
C: (f, a) => f(...a)
|
|
2102
2100
|
});
|
|
@@ -2110,7 +2108,7 @@ var ControlPipeline = class {
|
|
|
2110
2108
|
seq: msg.seq
|
|
2111
2109
|
}, {
|
|
2112
2110
|
F: __dxlog_file10,
|
|
2113
|
-
L:
|
|
2111
|
+
L: 174,
|
|
2114
2112
|
S: this,
|
|
2115
2113
|
C: (f, a) => f(...a)
|
|
2116
2114
|
});
|
|
@@ -2125,7 +2123,7 @@ var ControlPipeline = class {
|
|
|
2125
2123
|
msg
|
|
2126
2124
|
}, {
|
|
2127
2125
|
F: __dxlog_file10,
|
|
2128
|
-
L:
|
|
2126
|
+
L: 183,
|
|
2129
2127
|
S: this,
|
|
2130
2128
|
C: (f, a) => f(...a)
|
|
2131
2129
|
});
|
|
@@ -2144,7 +2142,7 @@ var ControlPipeline = class {
|
|
|
2144
2142
|
async stop() {
|
|
2145
2143
|
log9("stopping...", void 0, {
|
|
2146
2144
|
F: __dxlog_file10,
|
|
2147
|
-
L:
|
|
2145
|
+
L: 203,
|
|
2148
2146
|
S: this,
|
|
2149
2147
|
C: (f, a) => f(...a)
|
|
2150
2148
|
});
|
|
@@ -2153,7 +2151,7 @@ var ControlPipeline = class {
|
|
|
2153
2151
|
await this._saveTargetTimeframe(this._pipeline.state.timeframe);
|
|
2154
2152
|
log9("stopped", void 0, {
|
|
2155
2153
|
F: __dxlog_file10,
|
|
2156
|
-
L:
|
|
2154
|
+
L: 207,
|
|
2157
2155
|
S: this,
|
|
2158
2156
|
C: (f, a) => f(...a)
|
|
2159
2157
|
});
|
|
@@ -2166,7 +2164,7 @@ var ControlPipeline = class {
|
|
|
2166
2164
|
} catch (err) {
|
|
2167
2165
|
log9(err, void 0, {
|
|
2168
2166
|
F: __dxlog_file10,
|
|
2169
|
-
L:
|
|
2167
|
+
L: 216,
|
|
2170
2168
|
S: this,
|
|
2171
2169
|
C: (f, a) => f(...a)
|
|
2172
2170
|
});
|
|
@@ -2769,6 +2767,638 @@ SpaceManager = _ts_decorate8([
|
|
|
2769
2767
|
trackLeaks4("open", "close")
|
|
2770
2768
|
], SpaceManager);
|
|
2771
2769
|
|
|
2770
|
+
// packages/core/echo/echo-pipeline/src/automerge/automerge-storage-adapter.ts
|
|
2771
|
+
import { arrayToBuffer as arrayToBuffer2, bufferToArray } from "@dxos/util";
|
|
2772
|
+
var AutomergeStorageAdapter = class {
|
|
2773
|
+
constructor(_directory) {
|
|
2774
|
+
this._directory = _directory;
|
|
2775
|
+
this._state = "opened";
|
|
2776
|
+
}
|
|
2777
|
+
async load(key) {
|
|
2778
|
+
if (this._state !== "opened") {
|
|
2779
|
+
return void 0;
|
|
2780
|
+
}
|
|
2781
|
+
const filename = this._getFilename(key);
|
|
2782
|
+
const file = this._directory.getOrCreateFile(filename);
|
|
2783
|
+
const { size } = await file.stat();
|
|
2784
|
+
if (!size || size === 0) {
|
|
2785
|
+
return void 0;
|
|
2786
|
+
}
|
|
2787
|
+
const buffer = await file.read(0, size);
|
|
2788
|
+
return bufferToArray(buffer);
|
|
2789
|
+
}
|
|
2790
|
+
async save(key, data) {
|
|
2791
|
+
if (this._state !== "opened") {
|
|
2792
|
+
return void 0;
|
|
2793
|
+
}
|
|
2794
|
+
const filename = this._getFilename(key);
|
|
2795
|
+
const file = this._directory.getOrCreateFile(filename);
|
|
2796
|
+
await file.write(0, arrayToBuffer2(data));
|
|
2797
|
+
await file.truncate?.(data.length);
|
|
2798
|
+
await file.flush?.();
|
|
2799
|
+
}
|
|
2800
|
+
async remove(key) {
|
|
2801
|
+
if (this._state !== "opened") {
|
|
2802
|
+
return void 0;
|
|
2803
|
+
}
|
|
2804
|
+
const filename = this._getFilename(key);
|
|
2805
|
+
const file = this._directory.getOrCreateFile(filename);
|
|
2806
|
+
await file.destroy();
|
|
2807
|
+
}
|
|
2808
|
+
async loadRange(keyPrefix) {
|
|
2809
|
+
if (this._state !== "opened") {
|
|
2810
|
+
return [];
|
|
2811
|
+
}
|
|
2812
|
+
const filename = this._getFilename(keyPrefix);
|
|
2813
|
+
const entries = await this._directory.list();
|
|
2814
|
+
return Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
|
|
2815
|
+
const file = this._directory.getOrCreateFile(entry);
|
|
2816
|
+
const { size } = await file.stat();
|
|
2817
|
+
const buffer = await file.read(0, size);
|
|
2818
|
+
return {
|
|
2819
|
+
key: this._getKeyFromFilename(entry),
|
|
2820
|
+
data: bufferToArray(buffer)
|
|
2821
|
+
};
|
|
2822
|
+
}));
|
|
2823
|
+
}
|
|
2824
|
+
async removeRange(keyPrefix) {
|
|
2825
|
+
if (this._state !== "opened") {
|
|
2826
|
+
return void 0;
|
|
2827
|
+
}
|
|
2828
|
+
const filename = this._getFilename(keyPrefix);
|
|
2829
|
+
const entries = await this._directory.list();
|
|
2830
|
+
await Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
|
|
2831
|
+
const file = this._directory.getOrCreateFile(entry);
|
|
2832
|
+
await file.destroy();
|
|
2833
|
+
}));
|
|
2834
|
+
}
|
|
2835
|
+
async close() {
|
|
2836
|
+
this._state = "closed";
|
|
2837
|
+
}
|
|
2838
|
+
_getFilename(key) {
|
|
2839
|
+
return key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-");
|
|
2840
|
+
}
|
|
2841
|
+
_getKeyFromFilename(filename) {
|
|
2842
|
+
return filename.split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"));
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
|
|
2846
|
+
// packages/core/echo/echo-pipeline/src/automerge/local-host-network-adapter.ts
|
|
2847
|
+
import { Trigger as Trigger2 } from "@dxos/async";
|
|
2848
|
+
import { NetworkAdapter, cbor } from "@dxos/automerge/automerge-repo";
|
|
2849
|
+
import { Stream as Stream2 } from "@dxos/codec-protobuf";
|
|
2850
|
+
import { invariant as invariant10 } from "@dxos/invariant";
|
|
2851
|
+
import { log as log13 } from "@dxos/log";
|
|
2852
|
+
var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/local-host-network-adapter.ts";
|
|
2853
|
+
var LocalHostNetworkAdapter = class extends NetworkAdapter {
|
|
2854
|
+
constructor() {
|
|
2855
|
+
super(...arguments);
|
|
2856
|
+
this._peers = /* @__PURE__ */ new Map();
|
|
2857
|
+
this._connected = new Trigger2();
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* Emits `ready` event. That signals to `Repo` that it can start using the adapter.
|
|
2861
|
+
*/
|
|
2862
|
+
ready() {
|
|
2863
|
+
this.emit("ready", {
|
|
2864
|
+
network: this
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
connect(peerId) {
|
|
2868
|
+
this.peerId = peerId;
|
|
2869
|
+
this._connected.wake();
|
|
2870
|
+
}
|
|
2871
|
+
send(message) {
|
|
2872
|
+
const peer = this._peers.get(message.targetId);
|
|
2873
|
+
invariant10(peer, "Peer not found.", {
|
|
2874
|
+
F: __dxlog_file14,
|
|
2875
|
+
L: 45,
|
|
2876
|
+
S: this,
|
|
2877
|
+
A: [
|
|
2878
|
+
"peer",
|
|
2879
|
+
"'Peer not found.'"
|
|
2880
|
+
]
|
|
2881
|
+
});
|
|
2882
|
+
peer.send(message);
|
|
2883
|
+
}
|
|
2884
|
+
async close() {
|
|
2885
|
+
this._peers.forEach((peer) => peer.disconnect());
|
|
2886
|
+
this.emit("close");
|
|
2887
|
+
}
|
|
2888
|
+
disconnect() {
|
|
2889
|
+
}
|
|
2890
|
+
syncRepo({ id, syncMessage }) {
|
|
2891
|
+
const peerId = this._getPeerId(id);
|
|
2892
|
+
return new Stream2(({ next, close }) => {
|
|
2893
|
+
invariant10(!this._peers.has(peerId), "Peer already connected.", {
|
|
2894
|
+
F: __dxlog_file14,
|
|
2895
|
+
L: 63,
|
|
2896
|
+
S: this,
|
|
2897
|
+
A: [
|
|
2898
|
+
"!this._peers.has(peerId)",
|
|
2899
|
+
"'Peer already connected.'"
|
|
2900
|
+
]
|
|
2901
|
+
});
|
|
2902
|
+
this._peers.set(peerId, {
|
|
2903
|
+
connected: true,
|
|
2904
|
+
send: (message) => {
|
|
2905
|
+
next({
|
|
2906
|
+
syncMessage: cbor.encode(message)
|
|
2907
|
+
});
|
|
2908
|
+
},
|
|
2909
|
+
disconnect: () => {
|
|
2910
|
+
this._peers.delete(peerId);
|
|
2911
|
+
close();
|
|
2912
|
+
this.emit("peer-disconnected", {
|
|
2913
|
+
peerId
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
});
|
|
2917
|
+
this._connected.wait({
|
|
2918
|
+
timeout: 1e3
|
|
2919
|
+
}).then(() => {
|
|
2920
|
+
this.emit("peer-candidate", {
|
|
2921
|
+
peerMetadata: {},
|
|
2922
|
+
peerId
|
|
2923
|
+
});
|
|
2924
|
+
}).catch((err) => log13.catch(err, void 0, {
|
|
2925
|
+
F: __dxlog_file14,
|
|
2926
|
+
L: 88,
|
|
2927
|
+
S: this,
|
|
2928
|
+
C: (f, a) => f(...a)
|
|
2929
|
+
}));
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
async sendSyncMessage({ id, syncMessage }) {
|
|
2933
|
+
await this._connected.wait({
|
|
2934
|
+
timeout: 1e3
|
|
2935
|
+
});
|
|
2936
|
+
const message = cbor.decode(syncMessage);
|
|
2937
|
+
this.emit("message", message);
|
|
2938
|
+
}
|
|
2939
|
+
async getHostInfo() {
|
|
2940
|
+
await this._connected.wait({
|
|
2941
|
+
timeout: 1e3
|
|
2942
|
+
});
|
|
2943
|
+
invariant10(this.peerId, "Peer id not set.", {
|
|
2944
|
+
F: __dxlog_file14,
|
|
2945
|
+
L: 100,
|
|
2946
|
+
S: this,
|
|
2947
|
+
A: [
|
|
2948
|
+
"this.peerId",
|
|
2949
|
+
"'Peer id not set.'"
|
|
2950
|
+
]
|
|
2951
|
+
});
|
|
2952
|
+
return {
|
|
2953
|
+
peerId: this.peerId
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
_getPeerId(id) {
|
|
2957
|
+
return id;
|
|
2958
|
+
}
|
|
2959
|
+
};
|
|
2960
|
+
|
|
2961
|
+
// packages/core/echo/echo-pipeline/src/automerge/mesh-network-adapter.ts
|
|
2962
|
+
import { Trigger as Trigger3 } from "@dxos/async";
|
|
2963
|
+
import { NetworkAdapter as NetworkAdapter2, cbor as cbor2 } from "@dxos/automerge/automerge-repo";
|
|
2964
|
+
import { invariant as invariant11 } from "@dxos/invariant";
|
|
2965
|
+
import { log as log14 } from "@dxos/log";
|
|
2966
|
+
import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
|
|
2967
|
+
var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-network-adapter.ts";
|
|
2968
|
+
var MeshNetworkAdapter = class extends NetworkAdapter2 {
|
|
2969
|
+
constructor() {
|
|
2970
|
+
super(...arguments);
|
|
2971
|
+
this._extensions = /* @__PURE__ */ new Map();
|
|
2972
|
+
this._connected = new Trigger3();
|
|
2973
|
+
}
|
|
2974
|
+
/**
|
|
2975
|
+
* Emits `ready` event. That signals to `Repo` that it can start using the adapter.
|
|
2976
|
+
*/
|
|
2977
|
+
ready() {
|
|
2978
|
+
this.emit("ready", {
|
|
2979
|
+
network: this
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
connect(peerId) {
|
|
2983
|
+
this.peerId = peerId;
|
|
2984
|
+
this._connected.wake();
|
|
2985
|
+
}
|
|
2986
|
+
send(message) {
|
|
2987
|
+
const receiverId = message.targetId;
|
|
2988
|
+
const extension = this._extensions.get(receiverId);
|
|
2989
|
+
invariant11(extension, "Extension not found.", {
|
|
2990
|
+
F: __dxlog_file15,
|
|
2991
|
+
L: 38,
|
|
2992
|
+
S: this,
|
|
2993
|
+
A: [
|
|
2994
|
+
"extension",
|
|
2995
|
+
"'Extension not found.'"
|
|
2996
|
+
]
|
|
2997
|
+
});
|
|
2998
|
+
extension.sendSyncMessage({
|
|
2999
|
+
payload: cbor2.encode(message)
|
|
3000
|
+
}).catch((err) => log14.catch(err, void 0, {
|
|
3001
|
+
F: __dxlog_file15,
|
|
3002
|
+
L: 39,
|
|
3003
|
+
S: this,
|
|
3004
|
+
C: (f, a) => f(...a)
|
|
3005
|
+
}));
|
|
3006
|
+
}
|
|
3007
|
+
disconnect() {
|
|
3008
|
+
}
|
|
3009
|
+
createExtension() {
|
|
3010
|
+
invariant11(this.peerId, "Peer id not set.", {
|
|
3011
|
+
F: __dxlog_file15,
|
|
3012
|
+
L: 47,
|
|
3013
|
+
S: this,
|
|
3014
|
+
A: [
|
|
3015
|
+
"this.peerId",
|
|
3016
|
+
"'Peer id not set.'"
|
|
3017
|
+
]
|
|
3018
|
+
});
|
|
3019
|
+
let peerInfo;
|
|
3020
|
+
const extension = new AutomergeReplicator({
|
|
3021
|
+
peerId: this.peerId
|
|
3022
|
+
}, {
|
|
3023
|
+
onStartReplication: async (info, remotePeerId) => {
|
|
3024
|
+
await this._connected.wait();
|
|
3025
|
+
log14("onStartReplication", {
|
|
3026
|
+
id: info.id,
|
|
3027
|
+
thisPeerId: this.peerId,
|
|
3028
|
+
remotePeerId: remotePeerId.toHex()
|
|
3029
|
+
}, {
|
|
3030
|
+
F: __dxlog_file15,
|
|
3031
|
+
L: 70,
|
|
3032
|
+
S: this,
|
|
3033
|
+
C: (f, a) => f(...a)
|
|
3034
|
+
});
|
|
3035
|
+
if (!this._extensions.has(info.id)) {
|
|
3036
|
+
peerInfo = info;
|
|
3037
|
+
this._extensions.set(info.id, extension);
|
|
3038
|
+
log14("peer-candidate", {
|
|
3039
|
+
id: info.id,
|
|
3040
|
+
thisPeerId: this.peerId,
|
|
3041
|
+
remotePeerId: remotePeerId.toHex()
|
|
3042
|
+
}, {
|
|
3043
|
+
F: __dxlog_file15,
|
|
3044
|
+
L: 76,
|
|
3045
|
+
S: this,
|
|
3046
|
+
C: (f, a) => f(...a)
|
|
3047
|
+
});
|
|
3048
|
+
this.emit("peer-candidate", {
|
|
3049
|
+
// TODO(mykola): Hack, stop abusing `peerMetadata` field.
|
|
3050
|
+
peerMetadata: {
|
|
3051
|
+
dxos_deviceKey: remotePeerId.toHex()
|
|
3052
|
+
},
|
|
3053
|
+
peerId: info.id
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
},
|
|
3057
|
+
onSyncMessage: async ({ payload }) => {
|
|
3058
|
+
if (!peerInfo) {
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
const message = cbor2.decode(payload);
|
|
3062
|
+
this.emit("message", message);
|
|
3063
|
+
},
|
|
3064
|
+
onClose: async () => {
|
|
3065
|
+
if (!peerInfo) {
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
this.emit("peer-disconnected", {
|
|
3069
|
+
peerId: peerInfo.id
|
|
3070
|
+
});
|
|
3071
|
+
this._extensions.delete(peerInfo.id);
|
|
3072
|
+
}
|
|
3073
|
+
});
|
|
3074
|
+
return extension;
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
|
|
3078
|
+
// packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
|
|
3079
|
+
import { next as automerge, getHeads } from "@dxos/automerge/automerge";
|
|
3080
|
+
import { Repo } from "@dxos/automerge/automerge-repo";
|
|
3081
|
+
import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
|
|
3082
|
+
import { Context as Context7 } from "@dxos/context";
|
|
3083
|
+
import { PublicKey as PublicKey8 } from "@dxos/keys";
|
|
3084
|
+
import { log as log15 } from "@dxos/log";
|
|
3085
|
+
import { idCodec } from "@dxos/protocols";
|
|
3086
|
+
import { StorageType } from "@dxos/random-access-storage";
|
|
3087
|
+
import { trace as trace6 } from "@dxos/tracing";
|
|
3088
|
+
import { ComplexMap as ComplexMap7, ComplexSet, defaultMap, mapValues } from "@dxos/util";
|
|
3089
|
+
|
|
3090
|
+
// packages/core/echo/echo-pipeline/src/automerge/automerge-storage–wrapper.ts
|
|
3091
|
+
var AutomergeStorageWrapper = class {
|
|
3092
|
+
constructor({ storage, callbacks }) {
|
|
3093
|
+
this._storage = storage;
|
|
3094
|
+
this._callbacks = callbacks;
|
|
3095
|
+
}
|
|
3096
|
+
async load(key) {
|
|
3097
|
+
return this._storage.load(key);
|
|
3098
|
+
}
|
|
3099
|
+
async save(key, value) {
|
|
3100
|
+
await this._callbacks.beforeSave?.(key);
|
|
3101
|
+
await this._storage.save(key, value);
|
|
3102
|
+
await this._callbacks.afterSave?.(key);
|
|
3103
|
+
}
|
|
3104
|
+
async remove(key) {
|
|
3105
|
+
return this._storage.remove(key);
|
|
3106
|
+
}
|
|
3107
|
+
async loadRange(keyPrefix) {
|
|
3108
|
+
return this._storage.loadRange(keyPrefix);
|
|
3109
|
+
}
|
|
3110
|
+
async removeRange(keyPrefix) {
|
|
3111
|
+
return this._storage.removeRange(keyPrefix);
|
|
3112
|
+
}
|
|
3113
|
+
async close() {
|
|
3114
|
+
if (this._storage instanceof AutomergeStorageAdapter) {
|
|
3115
|
+
return this._storage.close();
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
// packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
|
|
3121
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
3122
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3123
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
3124
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
3125
|
+
else
|
|
3126
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
3127
|
+
if (d = decorators[i])
|
|
3128
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
3129
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3130
|
+
}
|
|
3131
|
+
var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
|
|
3132
|
+
var AutomergeHost = class {
|
|
3133
|
+
constructor({ directory, metadata }) {
|
|
3134
|
+
this._ctx = new Context7();
|
|
3135
|
+
/**
|
|
3136
|
+
* spaceKey -> deviceKey[]
|
|
3137
|
+
*/
|
|
3138
|
+
this._authorizedDevices = new ComplexMap7(PublicKey8.hash);
|
|
3139
|
+
this._updatingMetadata = /* @__PURE__ */ new Map();
|
|
3140
|
+
this._requestedDocs = /* @__PURE__ */ new Set();
|
|
3141
|
+
this._metadata = metadata;
|
|
3142
|
+
this._meshNetwork = new MeshNetworkAdapter();
|
|
3143
|
+
this._clientNetwork = new LocalHostNetworkAdapter();
|
|
3144
|
+
this._storage = new AutomergeStorageWrapper({
|
|
3145
|
+
storage: (
|
|
3146
|
+
// TODO(mykola): Delete specific handling of IDB storage.
|
|
3147
|
+
directory.type === StorageType.IDB ? new IndexedDBStorageAdapter(directory.path, "data") : new AutomergeStorageAdapter(directory)
|
|
3148
|
+
),
|
|
3149
|
+
callbacks: {
|
|
3150
|
+
beforeSave: (params) => this._beforeSave(params)
|
|
3151
|
+
}
|
|
3152
|
+
});
|
|
3153
|
+
this._peerId = `host-${PublicKey8.random().toHex()}`;
|
|
3154
|
+
this._repo = new Repo({
|
|
3155
|
+
peerId: this._peerId,
|
|
3156
|
+
network: [
|
|
3157
|
+
this._clientNetwork,
|
|
3158
|
+
this._meshNetwork
|
|
3159
|
+
],
|
|
3160
|
+
storage: this._storage,
|
|
3161
|
+
// TODO(dmaretskyi): Share based on HALO permissions and space affinity.
|
|
3162
|
+
// Hosts, running in the worker, don't share documents unless requested by other peers.
|
|
3163
|
+
sharePolicy: async (peerId, documentId) => {
|
|
3164
|
+
if (peerId.startsWith("client-")) {
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3167
|
+
if (!documentId) {
|
|
3168
|
+
return false;
|
|
3169
|
+
}
|
|
3170
|
+
const doc = this._repo.handles[documentId]?.docSync();
|
|
3171
|
+
if (!doc) {
|
|
3172
|
+
const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
|
|
3173
|
+
log15("doc share policy check", {
|
|
3174
|
+
peerId,
|
|
3175
|
+
documentId,
|
|
3176
|
+
isRequested
|
|
3177
|
+
}, {
|
|
3178
|
+
F: __dxlog_file16,
|
|
3179
|
+
L: 96,
|
|
3180
|
+
S: this,
|
|
3181
|
+
C: (f, a) => f(...a)
|
|
3182
|
+
});
|
|
3183
|
+
return isRequested;
|
|
3184
|
+
}
|
|
3185
|
+
try {
|
|
3186
|
+
const spaceKey = getSpaceKeyFromDoc(doc);
|
|
3187
|
+
if (!spaceKey) {
|
|
3188
|
+
log15("space key not found for share policy check", {
|
|
3189
|
+
peerId,
|
|
3190
|
+
documentId
|
|
3191
|
+
}, {
|
|
3192
|
+
F: __dxlog_file16,
|
|
3193
|
+
L: 103,
|
|
3194
|
+
S: this,
|
|
3195
|
+
C: (f, a) => f(...a)
|
|
3196
|
+
});
|
|
3197
|
+
return false;
|
|
3198
|
+
}
|
|
3199
|
+
const authorizedDevices = this._authorizedDevices.get(PublicKey8.from(spaceKey));
|
|
3200
|
+
const deviceKeyHex = this.repo.peerMetadataByPeerId[peerId]?.dxos_deviceKey;
|
|
3201
|
+
if (!deviceKeyHex) {
|
|
3202
|
+
log15("device key not found for share policy check", {
|
|
3203
|
+
peerId,
|
|
3204
|
+
documentId
|
|
3205
|
+
}, {
|
|
3206
|
+
F: __dxlog_file16,
|
|
3207
|
+
L: 112,
|
|
3208
|
+
S: this,
|
|
3209
|
+
C: (f, a) => f(...a)
|
|
3210
|
+
});
|
|
3211
|
+
return false;
|
|
3212
|
+
}
|
|
3213
|
+
const deviceKey = PublicKey8.from(deviceKeyHex);
|
|
3214
|
+
const isAuthorized = authorizedDevices?.has(deviceKey) ?? false;
|
|
3215
|
+
log15("share policy check", {
|
|
3216
|
+
localPeer: this._peerId,
|
|
3217
|
+
remotePeer: peerId,
|
|
3218
|
+
documentId,
|
|
3219
|
+
deviceKey,
|
|
3220
|
+
spaceKey,
|
|
3221
|
+
isAuthorized
|
|
3222
|
+
}, {
|
|
3223
|
+
F: __dxlog_file16,
|
|
3224
|
+
L: 118,
|
|
3225
|
+
S: this,
|
|
3226
|
+
C: (f, a) => f(...a)
|
|
3227
|
+
});
|
|
3228
|
+
return isAuthorized;
|
|
3229
|
+
} catch (err) {
|
|
3230
|
+
log15.catch(err, void 0, {
|
|
3231
|
+
F: __dxlog_file16,
|
|
3232
|
+
L: 128,
|
|
3233
|
+
S: this,
|
|
3234
|
+
C: (f, a) => f(...a)
|
|
3235
|
+
});
|
|
3236
|
+
return false;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
});
|
|
3240
|
+
this._clientNetwork.ready();
|
|
3241
|
+
this._meshNetwork.ready();
|
|
3242
|
+
{
|
|
3243
|
+
const listener = ({ handle }) => this._onDocument(handle);
|
|
3244
|
+
this._repo.on("document", listener);
|
|
3245
|
+
this._ctx.onDispose(() => {
|
|
3246
|
+
this._repo.off("document", listener);
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
get repo() {
|
|
3251
|
+
return this._repo;
|
|
3252
|
+
}
|
|
3253
|
+
async _beforeSave(path) {
|
|
3254
|
+
const id = path[0];
|
|
3255
|
+
if (this._updatingMetadata.has(id)) {
|
|
3256
|
+
return this._updatingMetadata.get(id);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
_onDocument(handle) {
|
|
3260
|
+
const listener = (event) => this._onUpdate(event);
|
|
3261
|
+
handle.on("change", listener);
|
|
3262
|
+
this._ctx.onDispose(() => {
|
|
3263
|
+
handle.off("change", listener);
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
_onUpdate(event) {
|
|
3267
|
+
if (this._metadata == null) {
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
const objectIds = getInlineChanges(event);
|
|
3271
|
+
if (objectIds.length === 0) {
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
const heads = getHeads(event.doc);
|
|
3275
|
+
const lastAvailableHash = heads.at(-1);
|
|
3276
|
+
if (!lastAvailableHash) {
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
const encodedIds = objectIds.map((objectId) => idCodec.encode({
|
|
3280
|
+
documentId: event.handle.documentId,
|
|
3281
|
+
objectId
|
|
3282
|
+
}));
|
|
3283
|
+
const idToLastHash = new Map(encodedIds.map((id) => [
|
|
3284
|
+
id,
|
|
3285
|
+
lastAvailableHash
|
|
3286
|
+
]));
|
|
3287
|
+
const markingDirtyPromise = this._metadata.markDirty(idToLastHash).then(() => {
|
|
3288
|
+
this._updatingMetadata.delete(event.handle.documentId);
|
|
3289
|
+
}).catch((err) => {
|
|
3290
|
+
this._ctx.disposed && log15.catch(err, void 0, {
|
|
3291
|
+
F: __dxlog_file16,
|
|
3292
|
+
L: 188,
|
|
3293
|
+
S: this,
|
|
3294
|
+
C: (f, a) => f(...a)
|
|
3295
|
+
});
|
|
3296
|
+
});
|
|
3297
|
+
this._updatingMetadata.set(event.handle.documentId, markingDirtyPromise);
|
|
3298
|
+
}
|
|
3299
|
+
_automergeDocs() {
|
|
3300
|
+
return mapValues(this._repo.handles, (handle) => ({
|
|
3301
|
+
state: handle.state,
|
|
3302
|
+
hasDoc: !!handle.docSync(),
|
|
3303
|
+
heads: handle.docSync() ? automerge.getHeads(handle.docSync()) : null,
|
|
3304
|
+
data: handle.docSync()?.doc && mapValues(handle.docSync()?.doc, (value, key) => {
|
|
3305
|
+
try {
|
|
3306
|
+
switch (key) {
|
|
3307
|
+
case "access":
|
|
3308
|
+
case "links":
|
|
3309
|
+
return value;
|
|
3310
|
+
case "objects":
|
|
3311
|
+
return Object.keys(value);
|
|
3312
|
+
default:
|
|
3313
|
+
return `${value}`;
|
|
3314
|
+
}
|
|
3315
|
+
} catch (err) {
|
|
3316
|
+
return `${err}`;
|
|
3317
|
+
}
|
|
3318
|
+
})
|
|
3319
|
+
}));
|
|
3320
|
+
}
|
|
3321
|
+
_automergePeers() {
|
|
3322
|
+
return this._repo.peers;
|
|
3323
|
+
}
|
|
3324
|
+
async close() {
|
|
3325
|
+
await this._storage.close();
|
|
3326
|
+
await this._clientNetwork.close();
|
|
3327
|
+
await this._ctx.dispose();
|
|
3328
|
+
}
|
|
3329
|
+
//
|
|
3330
|
+
// Methods for client-services.
|
|
3331
|
+
//
|
|
3332
|
+
syncRepo(request) {
|
|
3333
|
+
return this._clientNetwork.syncRepo(request);
|
|
3334
|
+
}
|
|
3335
|
+
sendSyncMessage(request) {
|
|
3336
|
+
return this._clientNetwork.sendSyncMessage(request);
|
|
3337
|
+
}
|
|
3338
|
+
async getHostInfo() {
|
|
3339
|
+
return this._clientNetwork.getHostInfo();
|
|
3340
|
+
}
|
|
3341
|
+
//
|
|
3342
|
+
// Mesh replication.
|
|
3343
|
+
//
|
|
3344
|
+
createExtension() {
|
|
3345
|
+
return this._meshNetwork.createExtension();
|
|
3346
|
+
}
|
|
3347
|
+
authorizeDevice(spaceKey, deviceKey) {
|
|
3348
|
+
log15("authorizeDevice", {
|
|
3349
|
+
spaceKey,
|
|
3350
|
+
deviceKey
|
|
3351
|
+
}, {
|
|
3352
|
+
F: __dxlog_file16,
|
|
3353
|
+
L: 255,
|
|
3354
|
+
S: this,
|
|
3355
|
+
C: (f, a) => f(...a)
|
|
3356
|
+
});
|
|
3357
|
+
defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey8.hash)).add(deviceKey);
|
|
3358
|
+
}
|
|
3359
|
+
};
|
|
3360
|
+
_ts_decorate9([
|
|
3361
|
+
trace6.info()
|
|
3362
|
+
], AutomergeHost.prototype, "_peerId", void 0);
|
|
3363
|
+
_ts_decorate9([
|
|
3364
|
+
trace6.info({
|
|
3365
|
+
depth: null
|
|
3366
|
+
})
|
|
3367
|
+
], AutomergeHost.prototype, "_automergeDocs", null);
|
|
3368
|
+
_ts_decorate9([
|
|
3369
|
+
trace6.info({
|
|
3370
|
+
depth: null
|
|
3371
|
+
})
|
|
3372
|
+
], AutomergeHost.prototype, "_automergePeers", null);
|
|
3373
|
+
AutomergeHost = _ts_decorate9([
|
|
3374
|
+
trace6.resource()
|
|
3375
|
+
], AutomergeHost);
|
|
3376
|
+
var getInlineChanges = (event) => {
|
|
3377
|
+
const inlineChangedObjectIds = /* @__PURE__ */ new Set();
|
|
3378
|
+
for (const { path } of event.patches) {
|
|
3379
|
+
if (path.length < 2) {
|
|
3380
|
+
continue;
|
|
3381
|
+
}
|
|
3382
|
+
switch (path[0]) {
|
|
3383
|
+
case "objects":
|
|
3384
|
+
if (path.length >= 2) {
|
|
3385
|
+
inlineChangedObjectIds.add(path[1]);
|
|
3386
|
+
}
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
return [
|
|
3391
|
+
...inlineChangedObjectIds
|
|
3392
|
+
];
|
|
3393
|
+
};
|
|
3394
|
+
var getSpaceKeyFromDoc = (doc) => {
|
|
3395
|
+
const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
|
|
3396
|
+
if (rawSpaceKey == null) {
|
|
3397
|
+
return null;
|
|
3398
|
+
}
|
|
3399
|
+
return String(rawSpaceKey);
|
|
3400
|
+
};
|
|
3401
|
+
|
|
2772
3402
|
export {
|
|
2773
3403
|
codec,
|
|
2774
3404
|
valueEncoding,
|
|
@@ -2793,6 +3423,11 @@ export {
|
|
|
2793
3423
|
SpaceProtocol,
|
|
2794
3424
|
AuthStatus,
|
|
2795
3425
|
SpaceProtocolSession,
|
|
2796
|
-
SpaceManager
|
|
3426
|
+
SpaceManager,
|
|
3427
|
+
AutomergeStorageAdapter,
|
|
3428
|
+
LocalHostNetworkAdapter,
|
|
3429
|
+
MeshNetworkAdapter,
|
|
3430
|
+
AutomergeHost,
|
|
3431
|
+
getSpaceKeyFromDoc
|
|
2797
3432
|
};
|
|
2798
|
-
//# sourceMappingURL=chunk-
|
|
3433
|
+
//# sourceMappingURL=chunk-3PPSCHNN.mjs.map
|