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.
Files changed (85) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/dist/account.d.ts +8 -8
  3. package/dist/account.js +2 -2
  4. package/dist/account.js.map +1 -1
  5. package/dist/coValue.d.ts +22 -27
  6. package/dist/coValue.js +21 -0
  7. package/dist/coValue.js.map +1 -1
  8. package/dist/coValueCore.d.ts +7 -7
  9. package/dist/coValueCore.js +11 -14
  10. package/dist/coValueCore.js.map +1 -1
  11. package/dist/coValues/coList.d.ts +107 -42
  12. package/dist/coValues/coList.js +163 -72
  13. package/dist/coValues/coList.js.map +1 -1
  14. package/dist/coValues/coMap.d.ts +109 -50
  15. package/dist/coValues/coMap.js +161 -109
  16. package/dist/coValues/coMap.js.map +1 -1
  17. package/dist/coValues/coStream.d.ts +78 -33
  18. package/dist/coValues/coStream.js +134 -53
  19. package/dist/coValues/coStream.js.map +1 -1
  20. package/dist/crypto.d.ts +8 -3
  21. package/dist/crypto.js +6 -6
  22. package/dist/crypto.js.map +1 -1
  23. package/dist/group.d.ts +59 -23
  24. package/dist/group.js +83 -25
  25. package/dist/group.js.map +1 -1
  26. package/dist/index.d.ts +14 -11
  27. package/dist/index.js +8 -8
  28. package/dist/index.js.map +1 -1
  29. package/dist/{node.d.ts → localNode.d.ts} +23 -11
  30. package/dist/{node.js → localNode.js} +80 -42
  31. package/dist/localNode.js.map +1 -0
  32. package/dist/media.d.ts +1 -2
  33. package/dist/permissions.js +6 -3
  34. package/dist/permissions.js.map +1 -1
  35. package/dist/queriedCoValues/queriedCoList.d.ts +66 -0
  36. package/dist/queriedCoValues/queriedCoList.js +120 -0
  37. package/dist/queriedCoValues/queriedCoList.js.map +1 -0
  38. package/dist/queriedCoValues/queriedCoMap.d.ts +47 -0
  39. package/dist/queriedCoValues/queriedCoMap.js +83 -0
  40. package/dist/queriedCoValues/queriedCoMap.js.map +1 -0
  41. package/dist/queriedCoValues/queriedCoStream.d.ts +40 -0
  42. package/dist/queriedCoValues/queriedCoStream.js +72 -0
  43. package/dist/queriedCoValues/queriedCoStream.js.map +1 -0
  44. package/dist/queries.d.ts +31 -0
  45. package/dist/queries.js +77 -0
  46. package/dist/queries.js.map +1 -0
  47. package/dist/sync.d.ts +1 -1
  48. package/dist/sync.js +1 -1
  49. package/dist/sync.js.map +1 -1
  50. package/dist/{testUtils.d.ts → tests/testUtils.d.ts} +9 -9
  51. package/dist/{testUtils.js → tests/testUtils.js} +9 -7
  52. package/dist/tests/testUtils.js.map +1 -0
  53. package/package.json +2 -2
  54. package/src/account.ts +6 -6
  55. package/src/coValue.ts +65 -34
  56. package/src/coValueCore.ts +18 -22
  57. package/src/coValues/coList.ts +272 -122
  58. package/src/coValues/coMap.ts +349 -152
  59. package/src/coValues/coStream.ts +258 -94
  60. package/src/crypto.ts +37 -24
  61. package/src/group.ts +112 -46
  62. package/src/index.ts +42 -30
  63. package/src/{node.ts → localNode.ts} +117 -66
  64. package/src/media.ts +1 -2
  65. package/src/permissions.ts +15 -18
  66. package/src/queriedCoValues/queriedCoList.ts +248 -0
  67. package/src/queriedCoValues/queriedCoMap.ts +180 -0
  68. package/src/queriedCoValues/queriedCoStream.ts +125 -0
  69. package/src/queries.ts +142 -0
  70. package/src/sync.ts +2 -2
  71. package/src/{account.test.ts → tests/account.test.ts} +6 -9
  72. package/src/{coValue.test.ts → tests/coValue.test.ts} +120 -114
  73. package/src/{coValueCore.test.ts → tests/coValueCore.test.ts} +7 -7
  74. package/src/{crypto.test.ts → tests/crypto.test.ts} +19 -21
  75. package/src/{group.test.ts → tests/group.test.ts} +2 -2
  76. package/src/{permissions.test.ts → tests/permissions.test.ts} +260 -247
  77. package/src/tests/queries.test.ts +318 -0
  78. package/src/{sync.test.ts → tests/sync.test.ts} +39 -39
  79. package/src/{testUtils.ts → tests/testUtils.ts} +10 -8
  80. package/dist/coValues/static.d.ts +0 -14
  81. package/dist/coValues/static.js +0 -20
  82. package/dist/coValues/static.js.map +0 -1
  83. package/dist/node.js.map +0 -1
  84. package/dist/testUtils.js.map +0 -1
  85. package/src/coValues/static.ts +0 -31
@@ -1,71 +1,70 @@
1
- import { JsonObject, JsonValue } from '../jsonValue.js';
2
- import { TransactionID } from '../ids.js';
3
- import { CoID, ReadableCoValue, WriteableCoValue } from '../coValue.js';
4
- import { CoValueCore, accountOrAgentIDfromSessionID } from '../coValueCore.js';
5
- import { AccountID, isAccountID } 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 | 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<K extends string, V extends JsonValue | undefined> = {
17
- op: "set";
18
- key: K;
19
- value: V;
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
- op: "del";
23
- key: K;
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
- [KK in MapK<M>]?: MapOp<KK, M[KK]>[];
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<CoMap<M, Meta>>;
52
+ this.id = core.id as CoID<this>;
46
53
  this.core = core;
47
54
  this.ops = {};
48
55
 
49
- this.fillOpsFromCoValue();
50
- }
51
-
52
- get meta(): Meta {
53
- return this.core.header.meta as Meta;
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<MapK<M>, MapV<M>>;
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<MapK<M>, MapV<M>>),
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
- keys(): MapK<M>[] {
85
- return Object.keys(this.ops) as MapK<M>[];
86
+ /** @category 6. Meta */
87
+ get meta(): Meta {
88
+ return this.core.header.meta as Meta;
86
89
  }
87
90
 
88
- /** Returns the current value for the given key. */
89
- get<K extends MapK<M>>(key: K): M[K] | undefined {
90
- const ops = this.ops[key];
91
- if (!ops) {
92
- return undefined;
93
- }
91
+ /** @category 6. Meta */
92
+ get group(): Group {
93
+ return this.core.getGroup();
94
+ }
94
95
 
95
- const lastEntry = ops[ops.length - 1]!;
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
- if (lastEntry.op === "del") {
98
- return undefined;
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 lastEntry.value;
116
+ return this.ops[key];
101
117
  }
102
118
  }
103
119
 
104
- getAtTime<K extends MapK<M>>(key: K, time: number): M[K] | undefined {
105
- const ops = this.ops[key];
106
- if (!ops) {
107
- return undefined;
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
- const lastOpBeforeOrAtTime = ops.findLast((op) => op.madeAt <= time);
111
-
112
- if (!lastOpBeforeOrAtTime) {
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
- if (lastOpBeforeOrAtTime.op === "del") {
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 lastOpBeforeOrAtTime.value;
161
+ return lastEntry?.value;
120
162
  }
121
163
  }
122
164
 
123
- /** Returns the accountID of the last account to modify the value for the given key. */
124
- whoEdited<K extends MapK<M>>(key: K): AccountID | undefined {
125
- const tx = this.getLastTxID(key);
126
- if (!tx) {
127
- return undefined;
128
- }
129
- const accountID = accountOrAgentIDfromSessionID(tx.sessionID);
130
- if (isAccountID(accountID)) {
131
- return accountID;
132
- } else {
133
- return undefined;
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
- getLastTxID<K extends MapK<M>>(key: K): TransactionID | undefined {
138
- const ops = this.ops[key];
139
- if (!ops) {
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 lastEntry = ops[ops.length - 1]!;
219
+ const entry = ops[n]!;
144
220
 
145
- return lastEntry.txID;
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
- const lastEntry = ops[ops.length - 1]!;
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
- if (lastEntry.op === "del") {
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
- getHistory<K extends MapK<M>>(key: K): { at: number; txID: TransactionID; value: M[K] | undefined; }[] {
164
- const ops = this.ops[key];
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
- const history: { at: number; txID: TransactionID; value: M[K] | undefined; }[] = [];
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
- return history;
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
- for (const key of this.keys()) {
186
- const value = this.get(key);
187
- if (value !== undefined) {
188
- json[key] = value;
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 json;
358
+ return new CoMap(this.core) as this;
193
359
  }
194
360
 
195
- subscribe(listener: (coMap: CoMap<M, Meta>) => void): () => void {
196
- return this.core.subscribe((content) => {
197
- listener(content as CoMap<M, Meta>);
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
- edit(changer: (editable: WriteableCoMap<M, Meta>) => void): CoMap<M, Meta> {
202
- const editable = new WriteableCoMap<M, Meta>(this.core);
203
- changer(editable);
204
- return new CoMap(this.core);
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
- export class WriteableCoMap<
209
- M extends { [key: string]: JsonValue | undefined; },
210
- Meta extends JsonObject | null = null,
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
- set<K extends MapK<M>>(key: K, value: M[K], privacy: "private" | "trusting" = "private"): void {
223
- this.core.makeTransaction([
224
- {
225
- op: "set",
226
- key,
227
- value,
228
- },
229
- ], privacy);
230
-
231
- this.fillOpsFromCoValue();
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
- delete(key: MapK<M>, privacy: "private" | "trusting" = "private"): void {
240
- this.core.makeTransaction([
241
- {
242
- op: "del",
243
- key,
244
- },
245
- ], privacy);
246
-
247
- this.fillOpsFromCoValue();
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
  }