cojson 0.8.12 → 0.8.17
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/CHANGELOG.md +95 -83
- package/dist/native/PeerKnownStates.js +6 -1
- package/dist/native/PeerKnownStates.js.map +1 -1
- package/dist/native/PeerState.js +4 -3
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/PriorityBasedMessageQueue.js +1 -10
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/native/SyncStateSubscriptionManager.js +70 -0
- package/dist/native/SyncStateSubscriptionManager.js.map +1 -0
- package/dist/native/base64url.js.map +1 -1
- package/dist/native/base64url.test.js +1 -1
- package/dist/native/base64url.test.js.map +1 -1
- package/dist/native/coValue.js.map +1 -1
- package/dist/native/coValueCore.js +141 -149
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValueState.js.map +1 -1
- package/dist/native/coValues/account.js +6 -6
- package/dist/native/coValues/account.js.map +1 -1
- package/dist/native/coValues/coList.js +2 -3
- package/dist/native/coValues/coList.js.map +1 -1
- package/dist/native/coValues/coMap.js +1 -1
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/coStream.js +3 -5
- package/dist/native/coValues/coStream.js.map +1 -1
- package/dist/native/coValues/group.js +11 -11
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/coreToCoValue.js +2 -2
- package/dist/native/coreToCoValue.js.map +1 -1
- package/dist/native/crypto/PureJSCrypto.js +4 -4
- package/dist/native/crypto/PureJSCrypto.js.map +1 -1
- package/dist/native/crypto/crypto.js.map +1 -1
- package/dist/native/exports.js +12 -12
- package/dist/native/exports.js.map +1 -1
- package/dist/native/ids.js.map +1 -1
- package/dist/native/jsonStringify.js.map +1 -1
- package/dist/native/localNode.js +5 -7
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/permissions.js +4 -7
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/priority.js.map +1 -1
- package/dist/native/storage/FileSystem.js.map +1 -1
- package/dist/native/storage/chunksAndKnownStates.js +2 -4
- package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/native/storage/index.js +6 -15
- package/dist/native/storage/index.js.map +1 -1
- package/dist/native/streamUtils.js.map +1 -1
- package/dist/native/sync.js +57 -7
- package/dist/native/sync.js.map +1 -1
- package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/native/typeUtils/expectGroup.js.map +1 -1
- package/dist/native/typeUtils/isAccountID.js.map +1 -1
- package/dist/native/typeUtils/isCoValue.js +1 -1
- package/dist/native/typeUtils/isCoValue.js.map +1 -1
- package/dist/web/PeerKnownStates.js +6 -1
- package/dist/web/PeerKnownStates.js.map +1 -1
- package/dist/web/PeerState.js +4 -3
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/PriorityBasedMessageQueue.js +1 -10
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/web/SyncStateSubscriptionManager.js +70 -0
- package/dist/web/SyncStateSubscriptionManager.js.map +1 -0
- package/dist/web/base64url.js.map +1 -1
- package/dist/web/base64url.test.js +1 -1
- package/dist/web/base64url.test.js.map +1 -1
- package/dist/web/coValue.js.map +1 -1
- package/dist/web/coValueCore.js +141 -149
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValueState.js.map +1 -1
- package/dist/web/coValues/account.js +6 -6
- package/dist/web/coValues/account.js.map +1 -1
- package/dist/web/coValues/coList.js +2 -3
- package/dist/web/coValues/coList.js.map +1 -1
- package/dist/web/coValues/coMap.js +1 -1
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/coStream.js +3 -5
- package/dist/web/coValues/coStream.js.map +1 -1
- package/dist/web/coValues/group.js +11 -11
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/coreToCoValue.js +2 -2
- package/dist/web/coreToCoValue.js.map +1 -1
- package/dist/web/crypto/PureJSCrypto.js +4 -4
- package/dist/web/crypto/PureJSCrypto.js.map +1 -1
- package/dist/web/crypto/WasmCrypto.js +5 -5
- package/dist/web/crypto/WasmCrypto.js.map +1 -1
- package/dist/web/crypto/crypto.js.map +1 -1
- package/dist/web/exports.js +12 -12
- package/dist/web/exports.js.map +1 -1
- package/dist/web/ids.js.map +1 -1
- package/dist/web/jsonStringify.js.map +1 -1
- package/dist/web/localNode.js +5 -7
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/permissions.js +4 -7
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/priority.js.map +1 -1
- package/dist/web/storage/FileSystem.js.map +1 -1
- package/dist/web/storage/chunksAndKnownStates.js +2 -4
- package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/web/storage/index.js +6 -15
- package/dist/web/storage/index.js.map +1 -1
- package/dist/web/streamUtils.js.map +1 -1
- package/dist/web/sync.js +57 -7
- package/dist/web/sync.js.map +1 -1
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/web/typeUtils/expectGroup.js.map +1 -1
- package/dist/web/typeUtils/isAccountID.js.map +1 -1
- package/dist/web/typeUtils/isCoValue.js +1 -1
- package/dist/web/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -14
- package/src/PeerKnownStates.ts +98 -90
- package/src/PeerState.ts +92 -73
- package/src/PriorityBasedMessageQueue.ts +42 -49
- package/src/SyncStateSubscriptionManager.ts +124 -0
- package/src/base64url.test.ts +24 -24
- package/src/base64url.ts +44 -45
- package/src/coValue.ts +45 -45
- package/src/coValueCore.ts +746 -785
- package/src/coValueState.ts +82 -72
- package/src/coValues/account.ts +143 -150
- package/src/coValues/coList.ts +520 -522
- package/src/coValues/coMap.ts +283 -285
- package/src/coValues/coStream.ts +320 -324
- package/src/coValues/group.ts +306 -305
- package/src/coreToCoValue.ts +28 -31
- package/src/crypto/PureJSCrypto.ts +188 -194
- package/src/crypto/WasmCrypto.ts +236 -254
- package/src/crypto/crypto.ts +302 -309
- package/src/exports.ts +116 -116
- package/src/ids.ts +9 -9
- package/src/jsonStringify.ts +46 -46
- package/src/jsonValue.ts +24 -10
- package/src/localNode.ts +635 -660
- package/src/media.ts +3 -3
- package/src/permissions.ts +272 -278
- package/src/priority.ts +21 -19
- package/src/storage/FileSystem.ts +91 -99
- package/src/storage/chunksAndKnownStates.ts +110 -115
- package/src/storage/index.ts +466 -497
- package/src/streamUtils.ts +60 -60
- package/src/sync.ts +656 -608
- package/src/tests/PeerKnownStates.test.ts +38 -34
- package/src/tests/PeerState.test.ts +101 -64
- package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
- package/src/tests/SyncStateSubscriptionManager.test.ts +232 -0
- package/src/tests/account.test.ts +59 -59
- package/src/tests/coList.test.ts +65 -65
- package/src/tests/coMap.test.ts +137 -137
- package/src/tests/coStream.test.ts +254 -257
- package/src/tests/coValueCore.test.ts +153 -156
- package/src/tests/crypto.test.ts +136 -144
- package/src/tests/cryptoImpl.test.ts +205 -197
- package/src/tests/group.test.ts +24 -24
- package/src/tests/permissions.test.ts +1306 -1371
- package/src/tests/priority.test.ts +65 -82
- package/src/tests/sync.test.ts +1573 -1263
- package/src/tests/testUtils.ts +85 -53
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
- package/src/typeUtils/expectGroup.ts +9 -9
- package/src/typeUtils/isAccountID.ts +1 -1
- package/src/typeUtils/isCoValue.ts +9 -9
- package/tsconfig.json +4 -6
- package/tsconfig.native.json +9 -11
- package/tsconfig.web.json +4 -10
- package/.eslintrc.cjs +0 -25
- package/.prettierrc.js +0 -9
package/src/coValues/coMap.ts
CHANGED
|
@@ -1,327 +1,325 @@
|
|
|
1
|
-
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
2
|
-
import { AgentID, TransactionID } from "../ids.js";
|
|
3
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
4
|
-
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
5
2
|
import { CoValueCore } from "../coValueCore.js";
|
|
3
|
+
import { AgentID, TransactionID } from "../ids.js";
|
|
4
|
+
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
6
5
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
6
|
+
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
7
7
|
import { RawAccountID } from "./account.js";
|
|
8
8
|
import type { RawGroup } from "./group.js";
|
|
9
9
|
|
|
10
10
|
type MapOp<K extends string, V extends JsonValue | undefined> = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
txID: TransactionID;
|
|
12
|
+
madeAt: number;
|
|
13
|
+
changeIdx: number;
|
|
14
14
|
} & MapOpPayload<K, V>;
|
|
15
15
|
// TODO: add after TransactionID[] for conflicts/ordering
|
|
16
16
|
|
|
17
17
|
export type MapOpPayload<K extends string, V extends JsonValue | undefined> =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
| {
|
|
19
|
+
op: "set";
|
|
20
|
+
key: K;
|
|
21
|
+
value: V;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
op: "del";
|
|
25
|
+
key: K;
|
|
26
|
+
};
|
|
27
27
|
|
|
28
28
|
export class RawCoMapView<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
Shape extends { [key: string]: JsonValue | undefined } = {
|
|
30
|
+
[key: string]: JsonValue | undefined;
|
|
31
|
+
},
|
|
32
|
+
Meta extends JsonObject | null = JsonObject | null,
|
|
33
33
|
> implements RawCoValue
|
|
34
34
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
entries.push({
|
|
73
|
-
txID,
|
|
74
|
-
madeAt,
|
|
75
|
-
changeIdx,
|
|
76
|
-
...(change as MapOpPayload<
|
|
77
|
-
keyof Shape & string,
|
|
78
|
-
Shape[keyof Shape & string]
|
|
79
|
-
>),
|
|
80
|
-
});
|
|
81
|
-
}
|
|
35
|
+
/** @category 6. Meta */
|
|
36
|
+
id: CoID<this>;
|
|
37
|
+
/** @category 6. Meta */
|
|
38
|
+
type = "comap" as const;
|
|
39
|
+
/** @category 6. Meta */
|
|
40
|
+
core: CoValueCore;
|
|
41
|
+
/** @internal */
|
|
42
|
+
ops: {
|
|
43
|
+
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
|
|
44
|
+
};
|
|
45
|
+
/** @internal */
|
|
46
|
+
atTimeFilter?: number = undefined;
|
|
47
|
+
/** @category 6. Meta */
|
|
48
|
+
readonly _shape!: Shape;
|
|
49
|
+
|
|
50
|
+
/** @internal */
|
|
51
|
+
constructor(
|
|
52
|
+
core: CoValueCore,
|
|
53
|
+
options?: { ignorePrivateTransactions: true },
|
|
54
|
+
) {
|
|
55
|
+
this.id = core.id as CoID<this>;
|
|
56
|
+
this.core = core;
|
|
57
|
+
this.ops = {};
|
|
58
|
+
|
|
59
|
+
for (const { txID, changes, madeAt } of core.getValidSortedTransactions(
|
|
60
|
+
options,
|
|
61
|
+
)) {
|
|
62
|
+
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
63
|
+
const change = changeUntyped as MapOpPayload<
|
|
64
|
+
keyof Shape & string,
|
|
65
|
+
Shape[keyof Shape & string]
|
|
66
|
+
>;
|
|
67
|
+
let entries = this.ops[change.key];
|
|
68
|
+
if (!entries) {
|
|
69
|
+
entries = [];
|
|
70
|
+
this.ops[change.key] = entries;
|
|
82
71
|
}
|
|
72
|
+
entries.push({
|
|
73
|
+
txID,
|
|
74
|
+
madeAt,
|
|
75
|
+
changeIdx,
|
|
76
|
+
...(change as MapOpPayload<
|
|
77
|
+
keyof Shape & string,
|
|
78
|
+
Shape[keyof Shape & string]
|
|
79
|
+
>),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
83
82
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @category 6. Meta */
|
|
86
|
+
get headerMeta(): Meta {
|
|
87
|
+
return this.core.header.meta as Meta;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** @category 6. Meta */
|
|
91
|
+
get group(): RawGroup {
|
|
92
|
+
return this.core.getGroup();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @category 4. Time travel */
|
|
96
|
+
atTime(time: number): this {
|
|
97
|
+
const clone = Object.create(this) as this;
|
|
98
|
+
clone.id = this.id;
|
|
99
|
+
clone.type = this.type;
|
|
100
|
+
clone.core = this.core;
|
|
101
|
+
clone.ops = this.ops;
|
|
102
|
+
clone.atTimeFilter = time;
|
|
103
|
+
return clone;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @internal */
|
|
107
|
+
timeFilteredOps<K extends keyof Shape & string>(
|
|
108
|
+
key: K,
|
|
109
|
+
): MapOp<K, Shape[K]>[] | undefined {
|
|
110
|
+
if (this.atTimeFilter) {
|
|
111
|
+
return this.ops[key]?.filter((op) => op.madeAt <= this.atTimeFilter!);
|
|
112
|
+
} else {
|
|
113
|
+
return this.ops[key];
|
|
93
114
|
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get all keys currently in the map.
|
|
119
|
+
*
|
|
120
|
+
* @category 1. Reading */
|
|
121
|
+
keys<K extends keyof Shape & string = keyof Shape & string>(): K[] {
|
|
122
|
+
return (Object.keys(this.ops) as K[]).filter((key) => {
|
|
123
|
+
const ops = this.ops[key];
|
|
124
|
+
if (!ops) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
94
127
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
clone.type = this.type;
|
|
100
|
-
clone.core = this.core;
|
|
101
|
-
clone.ops = this.ops;
|
|
102
|
-
clone.atTimeFilter = time;
|
|
103
|
-
return clone;
|
|
104
|
-
}
|
|
128
|
+
const includeUntil = this.atTimeFilter;
|
|
129
|
+
const lastEntry = includeUntil
|
|
130
|
+
? ops.findLast((entry) => entry.madeAt <= includeUntil)
|
|
131
|
+
: ops[ops.length - 1]!;
|
|
105
132
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
if (lastEntry?.op === "del") {
|
|
134
|
+
return false;
|
|
135
|
+
} else {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns the current value for the given key.
|
|
143
|
+
*
|
|
144
|
+
* @category 1. Reading
|
|
145
|
+
**/
|
|
146
|
+
get<K extends keyof Shape & string>(key: K): Shape[K] | undefined {
|
|
147
|
+
const ops = this.ops[key];
|
|
148
|
+
if (!ops) {
|
|
149
|
+
return undefined;
|
|
117
150
|
}
|
|
118
151
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
keys<K extends keyof Shape & string = keyof Shape & string>(): K[] {
|
|
124
|
-
return (Object.keys(this.ops) as K[]).filter((key) => {
|
|
125
|
-
const ops = this.ops[key];
|
|
126
|
-
if (!ops) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const includeUntil = this.atTimeFilter;
|
|
131
|
-
const lastEntry = includeUntil
|
|
132
|
-
? ops.findLast((entry) => entry.madeAt <= includeUntil)
|
|
133
|
-
: ops[ops.length - 1]!;
|
|
134
|
-
|
|
135
|
-
if (lastEntry?.op === "del") {
|
|
136
|
-
return false;
|
|
137
|
-
} else {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
152
|
+
const includeUntil = this.atTimeFilter;
|
|
153
|
+
const lastEntry = includeUntil
|
|
154
|
+
? ops.findLast((entry) => entry.madeAt <= includeUntil)
|
|
155
|
+
: ops[ops.length - 1]!;
|
|
142
156
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
**/
|
|
148
|
-
get<K extends keyof Shape & string>(key: K): Shape[K] | undefined {
|
|
149
|
-
const ops = this.ops[key];
|
|
150
|
-
if (!ops) {
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const includeUntil = this.atTimeFilter;
|
|
155
|
-
const lastEntry = includeUntil
|
|
156
|
-
? ops.findLast((entry) => entry.madeAt <= includeUntil)
|
|
157
|
-
: ops[ops.length - 1]!;
|
|
158
|
-
|
|
159
|
-
if (lastEntry?.op === "del") {
|
|
160
|
-
return undefined;
|
|
161
|
-
} else {
|
|
162
|
-
return lastEntry?.value;
|
|
163
|
-
}
|
|
157
|
+
if (lastEntry?.op === "del") {
|
|
158
|
+
return undefined;
|
|
159
|
+
} else {
|
|
160
|
+
return lastEntry?.value;
|
|
164
161
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return object as {
|
|
182
|
-
[K in keyof Shape & string]: Shape[K];
|
|
183
|
-
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** @category 1. Reading */
|
|
165
|
+
asObject(): {
|
|
166
|
+
[K in keyof Shape & string]: Shape[K];
|
|
167
|
+
} {
|
|
168
|
+
const object: Partial<{
|
|
169
|
+
[K in keyof Shape & string]: Shape[K];
|
|
170
|
+
}> = {};
|
|
171
|
+
|
|
172
|
+
for (const key of this.keys()) {
|
|
173
|
+
const value = this.get(key);
|
|
174
|
+
if (value !== undefined) {
|
|
175
|
+
object[key] = value;
|
|
176
|
+
}
|
|
184
177
|
}
|
|
185
178
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
return object as {
|
|
180
|
+
[K in keyof Shape & string]: Shape[K];
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** @category 1. Reading */
|
|
185
|
+
toJSON(): {
|
|
186
|
+
[K in keyof Shape & string]: Shape[K];
|
|
187
|
+
} {
|
|
188
|
+
return this.asObject();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** @category 5. Edit history */
|
|
192
|
+
nthEditAt<K extends keyof Shape & string>(
|
|
193
|
+
key: K,
|
|
194
|
+
n: number,
|
|
195
|
+
):
|
|
196
|
+
| {
|
|
197
|
+
by: RawAccountID | AgentID;
|
|
198
|
+
tx: TransactionID;
|
|
199
|
+
at: Date;
|
|
200
|
+
value?: Shape[K];
|
|
201
|
+
}
|
|
202
|
+
| undefined {
|
|
203
|
+
const ops = this.timeFilteredOps(key);
|
|
204
|
+
if (!ops || ops.length <= n) {
|
|
205
|
+
return undefined;
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
|
|
194
|
-
nthEditAt<K extends keyof Shape & string>(
|
|
195
|
-
key: K,
|
|
196
|
-
n: number,
|
|
197
|
-
):
|
|
198
|
-
| {
|
|
199
|
-
by: RawAccountID | AgentID;
|
|
200
|
-
tx: TransactionID;
|
|
201
|
-
at: Date;
|
|
202
|
-
value?: Shape[K];
|
|
203
|
-
}
|
|
204
|
-
| undefined {
|
|
205
|
-
const ops = this.timeFilteredOps(key);
|
|
206
|
-
if (!ops || ops.length <= n) {
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
208
|
+
const entry = ops[n]!;
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (this.atTimeFilter && entry.madeAt > this.atTimeFilter) {
|
|
213
|
-
return undefined;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
by: accountOrAgentIDfromSessionID(entry.txID.sessionID),
|
|
218
|
-
tx: entry.txID,
|
|
219
|
-
at: new Date(entry.madeAt),
|
|
220
|
-
value: entry.op === "del" ? undefined : entry.value,
|
|
221
|
-
};
|
|
210
|
+
if (this.atTimeFilter && entry.madeAt > this.atTimeFilter) {
|
|
211
|
+
return undefined;
|
|
222
212
|
}
|
|
223
213
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
at: Date;
|
|
232
|
-
value?: Shape[K];
|
|
233
|
-
}
|
|
234
|
-
| undefined {
|
|
235
|
-
const ops = this.timeFilteredOps(key);
|
|
236
|
-
if (!ops || ops.length === 0) {
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
return this.nthEditAt(key, ops.length - 1);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/** @category 5. Edit history */
|
|
243
|
-
*editsAt<K extends keyof Shape & string>(key: K) {
|
|
244
|
-
const ops = this.timeFilteredOps(key);
|
|
245
|
-
if (!ops) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
214
|
+
return {
|
|
215
|
+
by: accountOrAgentIDfromSessionID(entry.txID.sessionID),
|
|
216
|
+
tx: entry.txID,
|
|
217
|
+
at: new Date(entry.madeAt),
|
|
218
|
+
value: entry.op === "del" ? undefined : entry.value,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
248
221
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
222
|
+
/** @category 5. Edit history */
|
|
223
|
+
lastEditAt<K extends keyof Shape & string>(
|
|
224
|
+
key: K,
|
|
225
|
+
):
|
|
226
|
+
| {
|
|
227
|
+
by: RawAccountID | AgentID;
|
|
228
|
+
tx: TransactionID;
|
|
229
|
+
at: Date;
|
|
230
|
+
value?: Shape[K];
|
|
231
|
+
}
|
|
232
|
+
| undefined {
|
|
233
|
+
const ops = this.timeFilteredOps(key);
|
|
234
|
+
if (!ops || ops.length === 0) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
return this.nthEditAt(key, ops.length - 1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** @category 5. Edit history */
|
|
241
|
+
*editsAt<K extends keyof Shape & string>(key: K) {
|
|
242
|
+
const ops = this.timeFilteredOps(key);
|
|
243
|
+
if (!ops) {
|
|
244
|
+
return;
|
|
252
245
|
}
|
|
253
246
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return this.core.subscribe((content) => {
|
|
257
|
-
listener(content as this);
|
|
258
|
-
});
|
|
247
|
+
for (let i = 0; i < ops.length; i++) {
|
|
248
|
+
yield this.nthEditAt(key, i)!;
|
|
259
249
|
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** @category 3. Subscription */
|
|
253
|
+
subscribe(listener: (coMap: this) => void): () => void {
|
|
254
|
+
return this.core.subscribe((content) => {
|
|
255
|
+
listener(content as this);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
260
258
|
}
|
|
261
259
|
|
|
262
260
|
/** A collaborative map with precise shape `Shape` and optional static metadata `Meta` */
|
|
263
261
|
export class RawCoMap<
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
262
|
+
Shape extends { [key: string]: JsonValue | undefined } = {
|
|
263
|
+
[key: string]: JsonValue | undefined;
|
|
264
|
+
},
|
|
265
|
+
Meta extends JsonObject | null = JsonObject | null,
|
|
266
|
+
>
|
|
267
|
+
extends RawCoMapView<Shape, Meta>
|
|
268
|
+
implements RawCoValue
|
|
271
269
|
{
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
270
|
+
/** Set a new value for the given key.
|
|
271
|
+
*
|
|
272
|
+
* If `privacy` is `"private"` **(default)**, both `key` and `value` are encrypted in the transaction, only readable by other members of the group this `CoMap` belongs to. Not even sync servers can see the content in plaintext.
|
|
273
|
+
*
|
|
274
|
+
* If `privacy` is `"trusting"`, both `key` and `value` are stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
275
|
+
*
|
|
276
|
+
* @category 2. Editing
|
|
277
|
+
**/
|
|
278
|
+
set<K extends keyof Shape & string>(
|
|
279
|
+
key: K,
|
|
280
|
+
value: Shape[K],
|
|
281
|
+
privacy: "private" | "trusting" = "private",
|
|
282
|
+
): void {
|
|
283
|
+
this.core.makeTransaction(
|
|
284
|
+
[
|
|
285
|
+
{
|
|
286
|
+
op: "set",
|
|
287
|
+
key,
|
|
288
|
+
value: isCoValue(value) ? value.id : value,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
privacy,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const after = new RawCoMap(this.core) as this;
|
|
295
|
+
|
|
296
|
+
this.ops = after.ops;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Delete the given key (setting it to undefined).
|
|
300
|
+
*
|
|
301
|
+
* If `privacy` is `"private"` **(default)**, `key` is encrypted in the transaction, only readable by other members of the group this `CoMap` belongs to. Not even sync servers can see the content in plaintext.
|
|
302
|
+
*
|
|
303
|
+
* If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
304
|
+
*
|
|
305
|
+
* @category 2. Editing
|
|
306
|
+
**/
|
|
307
|
+
delete(
|
|
308
|
+
key: keyof Shape & string,
|
|
309
|
+
privacy: "private" | "trusting" = "private",
|
|
310
|
+
) {
|
|
311
|
+
this.core.makeTransaction(
|
|
312
|
+
[
|
|
313
|
+
{
|
|
314
|
+
op: "del",
|
|
315
|
+
key,
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
privacy,
|
|
319
|
+
);
|
|
300
320
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
306
|
-
*
|
|
307
|
-
* @category 2. Editing
|
|
308
|
-
**/
|
|
309
|
-
delete(
|
|
310
|
-
key: keyof Shape & string,
|
|
311
|
-
privacy: "private" | "trusting" = "private",
|
|
312
|
-
) {
|
|
313
|
-
this.core.makeTransaction(
|
|
314
|
-
[
|
|
315
|
-
{
|
|
316
|
-
op: "del",
|
|
317
|
-
key,
|
|
318
|
-
},
|
|
319
|
-
],
|
|
320
|
-
privacy,
|
|
321
|
-
);
|
|
322
|
-
|
|
323
|
-
const after = new RawCoMap(this.core) as this;
|
|
324
|
-
|
|
325
|
-
this.ops = after.ops;
|
|
326
|
-
}
|
|
321
|
+
const after = new RawCoMap(this.core) as this;
|
|
322
|
+
|
|
323
|
+
this.ops = after.ops;
|
|
324
|
+
}
|
|
327
325
|
}
|