cojson 0.2.2 → 0.3.0-alpha.0
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/.eslintrc.cjs +1 -0
- package/dist/account.d.ts +8 -8
- package/dist/account.js +2 -2
- package/dist/account.js.map +1 -1
- package/dist/coValue.d.ts +22 -27
- package/dist/coValue.js +21 -0
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +7 -7
- package/dist/coValueCore.js +11 -14
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.d.ts +107 -42
- package/dist/coValues/coList.js +163 -72
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +109 -50
- package/dist/coValues/coMap.js +161 -109
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +78 -33
- package/dist/coValues/coStream.js +134 -53
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/crypto.d.ts +8 -3
- package/dist/crypto.js +6 -6
- package/dist/crypto.js.map +1 -1
- package/dist/group.d.ts +59 -23
- package/dist/group.js +83 -25
- package/dist/group.js.map +1 -1
- package/dist/index.d.ts +14 -11
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{node.d.ts → localNode.d.ts} +23 -11
- package/dist/{node.js → localNode.js} +80 -42
- package/dist/localNode.js.map +1 -0
- package/dist/media.d.ts +1 -2
- package/dist/permissions.js +6 -3
- package/dist/permissions.js.map +1 -1
- package/dist/queriedCoValues/queriedCoList.d.ts +66 -0
- package/dist/queriedCoValues/queriedCoList.js +120 -0
- package/dist/queriedCoValues/queriedCoList.js.map +1 -0
- package/dist/queriedCoValues/queriedCoMap.d.ts +47 -0
- package/dist/queriedCoValues/queriedCoMap.js +83 -0
- package/dist/queriedCoValues/queriedCoMap.js.map +1 -0
- package/dist/queriedCoValues/queriedCoStream.d.ts +40 -0
- package/dist/queriedCoValues/queriedCoStream.js +72 -0
- package/dist/queriedCoValues/queriedCoStream.js.map +1 -0
- package/dist/queries.d.ts +31 -0
- package/dist/queries.js +77 -0
- package/dist/queries.js.map +1 -0
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +1 -1
- package/dist/sync.js.map +1 -1
- package/dist/{testUtils.d.ts → tests/testUtils.d.ts} +9 -9
- package/dist/{testUtils.js → tests/testUtils.js} +9 -7
- package/dist/tests/testUtils.js.map +1 -0
- package/package.json +2 -2
- package/src/account.ts +6 -6
- package/src/coValue.ts +65 -34
- package/src/coValueCore.ts +18 -22
- package/src/coValues/coList.ts +272 -122
- package/src/coValues/coMap.ts +349 -152
- package/src/coValues/coStream.ts +258 -94
- package/src/crypto.ts +37 -24
- package/src/group.ts +112 -46
- package/src/index.ts +42 -30
- package/src/{node.ts → localNode.ts} +117 -66
- package/src/media.ts +1 -2
- package/src/permissions.ts +15 -18
- package/src/queriedCoValues/queriedCoList.ts +248 -0
- package/src/queriedCoValues/queriedCoMap.ts +180 -0
- package/src/queriedCoValues/queriedCoStream.ts +125 -0
- package/src/queries.ts +142 -0
- package/src/sync.ts +2 -2
- package/src/{account.test.ts → tests/account.test.ts} +6 -9
- package/src/{coValue.test.ts → tests/coValue.test.ts} +120 -114
- package/src/{coValueCore.test.ts → tests/coValueCore.test.ts} +7 -7
- package/src/{crypto.test.ts → tests/crypto.test.ts} +19 -21
- package/src/{group.test.ts → tests/group.test.ts} +2 -2
- package/src/{permissions.test.ts → tests/permissions.test.ts} +260 -247
- package/src/tests/queries.test.ts +318 -0
- package/src/{sync.test.ts → tests/sync.test.ts} +39 -39
- package/src/{testUtils.ts → tests/testUtils.ts} +10 -8
- package/dist/coValues/static.d.ts +0 -14
- package/dist/coValues/static.js +0 -20
- package/dist/coValues/static.js.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/testUtils.js.map +0 -1
- package/src/coValues/static.ts +0 -31
package/src/coValues/coMap.ts
CHANGED
|
@@ -1,71 +1,70 @@
|
|
|
1
|
-
import { JsonObject, JsonValue } from
|
|
2
|
-
import { TransactionID } from
|
|
3
|
-
import { CoID,
|
|
4
|
-
import { CoValueCore, accountOrAgentIDfromSessionID } from
|
|
5
|
-
import { AccountID
|
|
6
|
-
import { Group } from
|
|
7
|
-
import { parseJSON } from
|
|
8
|
-
|
|
9
|
-
type MapOp<K extends string, V extends JsonValue | undefined> = {
|
|
1
|
+
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
2
|
+
import { AgentID, TransactionID } from "../ids.js";
|
|
3
|
+
import { CoID, CoValue, isCoValue } from "../coValue.js";
|
|
4
|
+
import { CoValueCore, accountOrAgentIDfromSessionID } from "../coValueCore.js";
|
|
5
|
+
import { AccountID } from "../account.js";
|
|
6
|
+
import { Group } from "../group.js";
|
|
7
|
+
import { parseJSON } from "../jsonStringify.js";
|
|
8
|
+
|
|
9
|
+
type MapOp<K extends string, V extends JsonValue | CoValue | undefined> = {
|
|
10
10
|
txID: TransactionID;
|
|
11
11
|
madeAt: number;
|
|
12
12
|
changeIdx: number;
|
|
13
13
|
} & MapOpPayload<K, V>;
|
|
14
14
|
// TODO: add after TransactionID[] for conflicts/ordering
|
|
15
15
|
|
|
16
|
-
export type MapOpPayload<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export type MapOpPayload<
|
|
17
|
+
K extends string,
|
|
18
|
+
V extends JsonValue | CoValue | undefined
|
|
19
|
+
> =
|
|
20
|
+
| {
|
|
21
|
+
op: "set";
|
|
22
|
+
key: K;
|
|
23
|
+
value: V extends CoValue ? CoID<V> : Exclude<V, CoValue>;
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
op: "del";
|
|
27
|
+
key: K;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class CoMapView<
|
|
31
|
+
Shape extends { [key: string]: JsonValue | CoValue | undefined },
|
|
32
|
+
Meta extends JsonObject | null = null
|
|
33
|
+
> implements CoValue
|
|
21
34
|
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export type MapK<M extends { [key: string]: JsonValue | undefined; }> = keyof M & string;
|
|
27
|
-
export type MapV<M extends { [key: string]: JsonValue | undefined; }> = M[MapK<M>];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/** A collaborative map with precise shape `M` and optional static metadata `Meta` */
|
|
31
|
-
export class CoMap<
|
|
32
|
-
M extends { [key: string]: JsonValue | undefined; },
|
|
33
|
-
Meta extends JsonObject | null = null,
|
|
34
|
-
> implements ReadableCoValue {
|
|
35
|
-
id: CoID<CoMap<M, Meta>>;
|
|
35
|
+
/** @category 6. Meta */
|
|
36
|
+
id: CoID<this>;
|
|
37
|
+
/** @category 6. Meta */
|
|
36
38
|
type = "comap" as const;
|
|
39
|
+
/** @category 6. Meta */
|
|
37
40
|
core: CoValueCore;
|
|
38
41
|
/** @internal */
|
|
39
42
|
ops: {
|
|
40
|
-
[
|
|
43
|
+
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
|
|
41
44
|
};
|
|
45
|
+
/** @internal */
|
|
46
|
+
atTimeFilter?: number = undefined;
|
|
47
|
+
/** @category 6. Meta */
|
|
48
|
+
readonly _shape!: Shape;
|
|
42
49
|
|
|
43
50
|
/** @internal */
|
|
44
51
|
constructor(core: CoValueCore) {
|
|
45
|
-
this.id = core.id as CoID<
|
|
52
|
+
this.id = core.id as CoID<this>;
|
|
46
53
|
this.core = core;
|
|
47
54
|
this.ops = {};
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
get group(): Group {
|
|
57
|
-
return this.core.getGroup();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** @internal */
|
|
61
|
-
protected fillOpsFromCoValue() {
|
|
62
|
-
this.ops = {};
|
|
63
|
-
|
|
64
|
-
for (const { txID, changes, madeAt } of this.core.getValidSortedTransactions()) {
|
|
65
|
-
for (const [changeIdx, changeUntyped] of (
|
|
66
|
-
parseJSON(changes)
|
|
56
|
+
for (const {
|
|
57
|
+
txID,
|
|
58
|
+
changes,
|
|
59
|
+
madeAt,
|
|
60
|
+
} of core.getValidSortedTransactions()) {
|
|
61
|
+
for (const [changeIdx, changeUntyped] of parseJSON(
|
|
62
|
+
changes
|
|
67
63
|
).entries()) {
|
|
68
|
-
const change = changeUntyped as MapOpPayload<
|
|
64
|
+
const change = changeUntyped as MapOpPayload<
|
|
65
|
+
keyof Shape & string,
|
|
66
|
+
Shape[keyof Shape & string]
|
|
67
|
+
>;
|
|
69
68
|
let entries = this.ops[change.key];
|
|
70
69
|
if (!entries) {
|
|
71
70
|
entries = [];
|
|
@@ -75,175 +74,373 @@ export class CoMap<
|
|
|
75
74
|
txID,
|
|
76
75
|
madeAt,
|
|
77
76
|
changeIdx,
|
|
78
|
-
...(change as MapOpPayload<
|
|
77
|
+
...(change as MapOpPayload<
|
|
78
|
+
keyof Shape & string,
|
|
79
|
+
Shape[keyof Shape & string]
|
|
80
|
+
>),
|
|
79
81
|
});
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
/** @category 6. Meta */
|
|
87
|
+
get meta(): Meta {
|
|
88
|
+
return this.core.header.meta as Meta;
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
/**
|
|
89
|
-
get
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return undefined;
|
|
93
|
-
}
|
|
91
|
+
/** @category 6. Meta */
|
|
92
|
+
get group(): Group {
|
|
93
|
+
return this.core.getGroup();
|
|
94
|
+
}
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
/** @category 4. Time travel */
|
|
97
|
+
atTime(time: number): this {
|
|
98
|
+
const clone = Object.create(this) as this;
|
|
99
|
+
clone.id = this.id;
|
|
100
|
+
clone.type = this.type;
|
|
101
|
+
clone.core = this.core;
|
|
102
|
+
clone.ops = this.ops;
|
|
103
|
+
clone.atTimeFilter = time;
|
|
104
|
+
return clone;
|
|
105
|
+
}
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
/** @internal */
|
|
108
|
+
timeFilteredOps<K extends keyof Shape & string>(
|
|
109
|
+
key: K
|
|
110
|
+
): MapOp<K, Shape[K]>[] | undefined {
|
|
111
|
+
if (this.atTimeFilter) {
|
|
112
|
+
return this.ops[key]?.filter(
|
|
113
|
+
(op) => op.madeAt <= this.atTimeFilter!
|
|
114
|
+
);
|
|
99
115
|
} else {
|
|
100
|
-
return
|
|
116
|
+
return this.ops[key];
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Get all keys currently in the map.
|
|
122
|
+
*
|
|
123
|
+
* @category 1. Reading */
|
|
124
|
+
keys(): (keyof Shape & string)[] {
|
|
125
|
+
const keys = Object.keys(this.ops) as (keyof Shape & string)[];
|
|
126
|
+
|
|
127
|
+
if (this.atTimeFilter) {
|
|
128
|
+
return keys.filter((key) => {
|
|
129
|
+
this.timeFilteredOps(key)?.length;
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
return keys;
|
|
108
133
|
}
|
|
134
|
+
}
|
|
109
135
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Returns the current value for the given key.
|
|
138
|
+
*
|
|
139
|
+
* @category 1. Reading
|
|
140
|
+
**/
|
|
141
|
+
get<K extends keyof Shape & string>(
|
|
142
|
+
key: K
|
|
143
|
+
):
|
|
144
|
+
| (Shape[K] extends CoValue
|
|
145
|
+
? CoID<Shape[K]>
|
|
146
|
+
: Exclude<Shape[K], CoValue>)
|
|
147
|
+
| undefined {
|
|
148
|
+
const ops = this.timeFilteredOps(key);
|
|
149
|
+
if (!ops) {
|
|
113
150
|
return undefined;
|
|
114
151
|
}
|
|
115
152
|
|
|
116
|
-
|
|
153
|
+
const includeUntil = this.atTimeFilter;
|
|
154
|
+
const lastEntry = includeUntil
|
|
155
|
+
? ops.findLast((entry) => entry.madeAt <= includeUntil)
|
|
156
|
+
: ops[ops.length - 1]!;
|
|
157
|
+
|
|
158
|
+
if (lastEntry?.op === "del") {
|
|
117
159
|
return undefined;
|
|
118
160
|
} else {
|
|
119
|
-
return
|
|
161
|
+
return lastEntry?.value;
|
|
120
162
|
}
|
|
121
163
|
}
|
|
122
164
|
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
165
|
+
/** @category 1. Reading */
|
|
166
|
+
asObject(): {
|
|
167
|
+
[K in keyof Shape & string]: Shape[K] extends CoValue
|
|
168
|
+
? CoID<Shape[K]>
|
|
169
|
+
: Exclude<Shape[K], CoValue>;
|
|
170
|
+
} {
|
|
171
|
+
const object: Partial<{
|
|
172
|
+
[K in keyof Shape & string]: Shape[K] extends CoValue
|
|
173
|
+
? CoID<Shape[K]>
|
|
174
|
+
: Exclude<Shape[K], CoValue>;
|
|
175
|
+
}> = {};
|
|
176
|
+
|
|
177
|
+
for (const key of this.keys()) {
|
|
178
|
+
const value = this.get(key);
|
|
179
|
+
if (value !== undefined) {
|
|
180
|
+
object[key] = value;
|
|
181
|
+
}
|
|
134
182
|
}
|
|
183
|
+
|
|
184
|
+
return object as {
|
|
185
|
+
[K in keyof Shape & string]: Shape[K] extends CoValue
|
|
186
|
+
? CoID<Shape[K]>
|
|
187
|
+
: Exclude<Shape[K], CoValue>;
|
|
188
|
+
};
|
|
135
189
|
}
|
|
136
190
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
191
|
+
/** @category 1. Reading */
|
|
192
|
+
toJSON(): {
|
|
193
|
+
[K in keyof Shape & string]: Shape[K] extends CoValue
|
|
194
|
+
? CoID<Shape[K]>
|
|
195
|
+
: Exclude<Shape[K], CoValue>;
|
|
196
|
+
} {
|
|
197
|
+
return this.asObject();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** @category 5. Edit history */
|
|
201
|
+
nthEditAt<K extends keyof Shape & string>(
|
|
202
|
+
key: K,
|
|
203
|
+
n: number
|
|
204
|
+
):
|
|
205
|
+
| {
|
|
206
|
+
by: AccountID | AgentID;
|
|
207
|
+
tx: TransactionID;
|
|
208
|
+
at: Date;
|
|
209
|
+
value?: Shape[K] extends CoValue
|
|
210
|
+
? CoID<Shape[K]>
|
|
211
|
+
: Exclude<Shape[K], CoValue>;
|
|
212
|
+
}
|
|
213
|
+
| undefined {
|
|
214
|
+
const ops = this.timeFilteredOps(key);
|
|
215
|
+
if (!ops || ops.length <= n) {
|
|
140
216
|
return undefined;
|
|
141
217
|
}
|
|
142
218
|
|
|
143
|
-
const
|
|
219
|
+
const entry = ops[n]!;
|
|
144
220
|
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
getLastEntry<K extends MapK<M>>(key: K): { at: number; txID: TransactionID; value: M[K]; } | undefined {
|
|
149
|
-
const ops = this.ops[key];
|
|
150
|
-
if (!ops) {
|
|
221
|
+
if (this.atTimeFilter && entry.madeAt > this.atTimeFilter) {
|
|
151
222
|
return undefined;
|
|
152
223
|
}
|
|
153
224
|
|
|
154
|
-
|
|
225
|
+
return {
|
|
226
|
+
by: accountOrAgentIDfromSessionID(entry.txID.sessionID),
|
|
227
|
+
tx: entry.txID,
|
|
228
|
+
at: new Date(entry.madeAt),
|
|
229
|
+
value: entry.op === "del" ? undefined : entry.value,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
155
232
|
|
|
156
|
-
|
|
233
|
+
/** @category 5. Edit history */
|
|
234
|
+
lastEditAt<K extends keyof Shape & string>(
|
|
235
|
+
key: K
|
|
236
|
+
):
|
|
237
|
+
| {
|
|
238
|
+
by: AccountID | AgentID;
|
|
239
|
+
tx: TransactionID;
|
|
240
|
+
at: Date;
|
|
241
|
+
value?: Shape[K] extends CoValue
|
|
242
|
+
? CoID<Shape[K]>
|
|
243
|
+
: Exclude<Shape[K], CoValue>;
|
|
244
|
+
}
|
|
245
|
+
| undefined {
|
|
246
|
+
const ops = this.timeFilteredOps(key);
|
|
247
|
+
if (!ops || ops.length === 0) {
|
|
157
248
|
return undefined;
|
|
158
|
-
} else {
|
|
159
|
-
return { at: lastEntry.madeAt, txID: lastEntry.txID, value: lastEntry.value };
|
|
160
249
|
}
|
|
250
|
+
return this.nthEditAt(key, ops.length - 1);
|
|
161
251
|
}
|
|
162
252
|
|
|
163
|
-
|
|
164
|
-
|
|
253
|
+
/** @category 5. Edit history */
|
|
254
|
+
*editsAt<K extends keyof Shape & string>(key: K) {
|
|
255
|
+
const ops = this.timeFilteredOps(key);
|
|
165
256
|
if (!ops) {
|
|
166
|
-
return
|
|
257
|
+
return;
|
|
167
258
|
}
|
|
168
259
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
for (const op of ops) {
|
|
172
|
-
if (op.op === "del") {
|
|
173
|
-
history.push({ at: op.madeAt, txID: op.txID, value: undefined });
|
|
174
|
-
} else {
|
|
175
|
-
history.push({ at: op.madeAt, txID: op.txID, value: op.value });
|
|
176
|
-
}
|
|
260
|
+
for (let i = 0; i < ops.length; i++) {
|
|
261
|
+
yield this.nthEditAt(key, i)!;
|
|
177
262
|
}
|
|
263
|
+
}
|
|
178
264
|
|
|
179
|
-
|
|
265
|
+
/** @category 3. Subscription */
|
|
266
|
+
subscribe(listener: (coMap: this) => void): () => void {
|
|
267
|
+
return this.core.subscribe((content) => {
|
|
268
|
+
listener(content as this);
|
|
269
|
+
});
|
|
180
270
|
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** A collaborative map with precise shape `Shape` and optional static metadata `Meta` */
|
|
274
|
+
export class CoMap<
|
|
275
|
+
Shape extends { [key: string]: JsonValue | CoValue | undefined },
|
|
276
|
+
Meta extends JsonObject | null = null
|
|
277
|
+
>
|
|
278
|
+
extends CoMapView<Shape, Meta>
|
|
279
|
+
implements CoValue
|
|
280
|
+
{
|
|
181
281
|
|
|
182
|
-
toJSON(): JsonObject {
|
|
183
|
-
const json: JsonObject = {};
|
|
184
282
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
283
|
+
/** Returns a new version of this CoMap with a new value for the given key.
|
|
284
|
+
*
|
|
285
|
+
* 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.
|
|
286
|
+
*
|
|
287
|
+
* 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.
|
|
288
|
+
*
|
|
289
|
+
* @category 2. Editing
|
|
290
|
+
**/
|
|
291
|
+
set<K extends keyof Shape & string>(
|
|
292
|
+
key: K,
|
|
293
|
+
value: Shape[K] extends CoValue ? Shape[K] | CoID<Shape[K]> : Shape[K],
|
|
294
|
+
privacy?: "private" | "trusting"
|
|
295
|
+
): this;
|
|
296
|
+
set(
|
|
297
|
+
kv: {
|
|
298
|
+
[K in keyof Shape & string]?: Shape[K] extends CoValue
|
|
299
|
+
? Shape[K] | CoID<Shape[K]>
|
|
300
|
+
: Shape[K];
|
|
301
|
+
},
|
|
302
|
+
privacy?: "private" | "trusting"
|
|
303
|
+
): this;
|
|
304
|
+
set<K extends keyof Shape & string>(
|
|
305
|
+
...args:
|
|
306
|
+
| [
|
|
307
|
+
{
|
|
308
|
+
[K in keyof Shape & string]?: Shape[K] extends CoValue
|
|
309
|
+
? Shape[K] | CoID<Shape[K]>
|
|
310
|
+
: Shape[K];
|
|
311
|
+
},
|
|
312
|
+
("private" | "trusting")?
|
|
313
|
+
]
|
|
314
|
+
| [
|
|
315
|
+
K,
|
|
316
|
+
Shape[K] extends CoValue
|
|
317
|
+
? Shape[K] | CoID<Shape[K]>
|
|
318
|
+
: Shape[K],
|
|
319
|
+
("private" | "trusting")?
|
|
320
|
+
]
|
|
321
|
+
): this {
|
|
322
|
+
if (typeof args[0] === "string") {
|
|
323
|
+
const [key, value, privacy = "private"] = args;
|
|
324
|
+
this.core.makeTransaction(
|
|
325
|
+
[
|
|
326
|
+
{
|
|
327
|
+
op: "set",
|
|
328
|
+
key,
|
|
329
|
+
value: isCoValue(value) ? value.id : value,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
privacy
|
|
333
|
+
);
|
|
334
|
+
} else {
|
|
335
|
+
const [kv, privacy = "private"] = args as [
|
|
336
|
+
{
|
|
337
|
+
[K in keyof Shape & string]: Shape[K] extends CoValue
|
|
338
|
+
? Shape[K] | CoID<Shape[K]>
|
|
339
|
+
: Shape[K];
|
|
340
|
+
},
|
|
341
|
+
"private" | "trusting" | undefined
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
for (const [key, value] of Object.entries(kv)) {
|
|
345
|
+
this.core.makeTransaction(
|
|
346
|
+
[
|
|
347
|
+
{
|
|
348
|
+
op: "set",
|
|
349
|
+
key,
|
|
350
|
+
value: isCoValue(value) ? value.id : value,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
privacy
|
|
354
|
+
);
|
|
189
355
|
}
|
|
190
356
|
}
|
|
191
357
|
|
|
192
|
-
return
|
|
358
|
+
return new CoMap(this.core) as this;
|
|
193
359
|
}
|
|
194
360
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
361
|
+
/** Returns a new version of this CoMap with the given key deleted (setting it to undefined).
|
|
362
|
+
*
|
|
363
|
+
* 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.
|
|
364
|
+
*
|
|
365
|
+
* If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
366
|
+
*
|
|
367
|
+
* @category 2. Editing
|
|
368
|
+
**/
|
|
369
|
+
delete(
|
|
370
|
+
key: keyof Shape & string,
|
|
371
|
+
privacy: "private" | "trusting" = "private"
|
|
372
|
+
): this {
|
|
373
|
+
this.core.makeTransaction(
|
|
374
|
+
[
|
|
375
|
+
{
|
|
376
|
+
op: "del",
|
|
377
|
+
key,
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
privacy
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
return new CoMap(this.core) as this;
|
|
199
384
|
}
|
|
200
385
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
386
|
+
/** @category 2. Editing */
|
|
387
|
+
mutate(mutator: (mutable: MutableCoMap<Shape, Meta>) => void): this {
|
|
388
|
+
const mutable = new MutableCoMap<Shape, Meta>(this.core);
|
|
389
|
+
mutator(mutable);
|
|
390
|
+
return new CoMap(this.core) as this;
|
|
205
391
|
}
|
|
206
|
-
}
|
|
207
392
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
> extends CoMap<M, Meta> implements WriteableCoValue {
|
|
212
|
-
/** @internal */
|
|
213
|
-
edit(_changer: (editable: WriteableCoMap<M, Meta>) => void): CoMap<M, Meta> {
|
|
214
|
-
throw new Error("Already editing.");
|
|
393
|
+
/** @deprecated Use `mutate` instead. */
|
|
394
|
+
edit(mutator: (mutable: MutableCoMap<Shape, Meta>) => void): this {
|
|
395
|
+
return this.mutate(mutator);
|
|
215
396
|
}
|
|
397
|
+
}
|
|
216
398
|
|
|
399
|
+
export class MutableCoMap<
|
|
400
|
+
Shape extends { [key: string]: JsonValue | CoValue | undefined },
|
|
401
|
+
Meta extends JsonObject | null = null
|
|
402
|
+
>
|
|
403
|
+
extends CoMapView<Shape, Meta>
|
|
404
|
+
implements CoValue
|
|
405
|
+
{
|
|
217
406
|
/** Sets a new value for the given key.
|
|
218
407
|
*
|
|
219
408
|
* 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.
|
|
220
409
|
*
|
|
221
|
-
* 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.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
410
|
+
* 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.
|
|
411
|
+
*
|
|
412
|
+
* @category 2. Mutation
|
|
413
|
+
*/
|
|
414
|
+
set<K extends keyof Shape & string>(
|
|
415
|
+
key: K,
|
|
416
|
+
value: Shape[K] extends CoValue ? Shape[K] | CoID<Shape[K]> : Shape[K],
|
|
417
|
+
privacy: "private" | "trusting" = "private"
|
|
418
|
+
): void {
|
|
419
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
420
|
+
const after = (CoMap.prototype.set as Function).call(
|
|
421
|
+
this,
|
|
422
|
+
key,
|
|
423
|
+
value,
|
|
424
|
+
privacy
|
|
425
|
+
) as CoMap<Shape, Meta>;
|
|
426
|
+
this.ops = after.ops;
|
|
232
427
|
}
|
|
233
428
|
|
|
234
429
|
/** Deletes the value for the given key (setting it to undefined).
|
|
235
430
|
*
|
|
236
431
|
* 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.
|
|
237
432
|
*
|
|
238
|
-
* If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
433
|
+
* If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
434
|
+
* @category 2. Mutation
|
|
435
|
+
*/
|
|
436
|
+
delete(
|
|
437
|
+
key: keyof Shape & string,
|
|
438
|
+
privacy: "private" | "trusting" = "private"
|
|
439
|
+
): void {
|
|
440
|
+
const after = CoMap.prototype.delete.call(this, key, privacy) as CoMap<
|
|
441
|
+
Shape,
|
|
442
|
+
Meta
|
|
443
|
+
>;
|
|
444
|
+
this.ops = after.ops;
|
|
248
445
|
}
|
|
249
446
|
}
|