cojson 0.2.3 → 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/dist/account.d.ts +1 -1
- package/dist/coValue.d.ts +5 -13
- package/dist/coValue.js +14 -7
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +6 -6
- package/dist/coValueCore.js +11 -14
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.d.ts +99 -34
- package/dist/coValues/coList.js +162 -72
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +96 -31
- package/dist/coValues/coMap.js +157 -114
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +67 -23
- package/dist/coValues/coStream.js +131 -59
- 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 +57 -23
- package/dist/group.js +75 -33
- package/dist/group.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{node.d.ts → localNode.d.ts} +16 -8
- package/dist/{node.js → localNode.js} +48 -40
- package/dist/localNode.js.map +1 -0
- 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 +29 -112
- package/dist/queries.js +44 -227
- package/dist/queries.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +1 -1
- package/dist/sync.js.map +1 -1
- package/dist/tests/testUtils.d.ts +1 -1
- package/dist/tests/testUtils.js +3 -3
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/account.ts +1 -1
- package/src/coValue.ts +25 -20
- package/src/coValueCore.ts +17 -21
- package/src/coValues/coList.ts +242 -128
- package/src/coValues/coMap.ts +293 -162
- package/src/coValues/coStream.ts +227 -94
- package/src/crypto.ts +37 -24
- package/src/group.ts +90 -63
- package/src/index.ts +35 -25
- package/src/{node.ts → localNode.ts} +64 -64
- 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 +83 -460
- package/src/sync.ts +2 -2
- package/src/tests/account.test.ts +3 -6
- package/src/tests/coValue.test.ts +116 -110
- package/src/tests/coValueCore.test.ts +1 -1
- package/src/tests/crypto.test.ts +19 -21
- package/src/tests/permissions.test.ts +255 -242
- package/src/tests/queries.test.ts +57 -40
- package/src/tests/sync.test.ts +30 -30
- package/src/tests/testUtils.ts +3 -3
- 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/src/coValues/static.ts +0 -31
package/src/coValueCore.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "@noble/hashes/utils";
|
|
2
|
-
import { AnyCoValue } from "./coValue.js";
|
|
3
|
-
import { Static } from "./coValues/static.js";
|
|
2
|
+
import { AnyCoValue, CoValue } from "./coValue.js";
|
|
4
3
|
import { BinaryCoStream, CoStream } from "./coValues/coStream.js";
|
|
5
4
|
import { CoMap } from "./coValues/coMap.js";
|
|
6
5
|
import {
|
|
@@ -28,7 +27,7 @@ import {
|
|
|
28
27
|
isKeyForKeyField,
|
|
29
28
|
} from "./permissions.js";
|
|
30
29
|
import { Group, expectGroupContent } from "./group.js";
|
|
31
|
-
import { LocalNode } from "./
|
|
30
|
+
import { LocalNode } from "./localNode.js";
|
|
32
31
|
import { CoValueKnownState, NewContentMessage } from "./sync.js";
|
|
33
32
|
import { AgentID, RawCoID, SessionID, TransactionID } from "./ids.js";
|
|
34
33
|
import { CoList } from "./coValues/coList.js";
|
|
@@ -99,8 +98,8 @@ export class CoValueCore {
|
|
|
99
98
|
node: LocalNode;
|
|
100
99
|
header: CoValueHeader;
|
|
101
100
|
_sessions: { [key: SessionID]: SessionLog };
|
|
102
|
-
_cachedContent?:
|
|
103
|
-
listeners: Set<(content?:
|
|
101
|
+
_cachedContent?: CoValue;
|
|
102
|
+
listeners: Set<(content?: CoValue) => void> = new Set();
|
|
104
103
|
_decryptionCache: {
|
|
105
104
|
[key: Encrypted<JsonValue[], JsonValue>]:
|
|
106
105
|
| Stringified<JsonValue[]>
|
|
@@ -376,7 +375,7 @@ export class CoValueCore {
|
|
|
376
375
|
}
|
|
377
376
|
}
|
|
378
377
|
|
|
379
|
-
subscribe(listener: (content?:
|
|
378
|
+
subscribe(listener: (content?: CoValue) => void): () => void {
|
|
380
379
|
this.listeners.add(listener);
|
|
381
380
|
listener(this.getCurrentContent());
|
|
382
381
|
|
|
@@ -487,13 +486,13 @@ export class CoValueCore {
|
|
|
487
486
|
);
|
|
488
487
|
|
|
489
488
|
if (success) {
|
|
490
|
-
void this.node.
|
|
489
|
+
void this.node.syncManager.syncCoValue(this);
|
|
491
490
|
}
|
|
492
491
|
|
|
493
492
|
return success;
|
|
494
493
|
}
|
|
495
494
|
|
|
496
|
-
getCurrentContent():
|
|
495
|
+
getCurrentContent(): CoValue {
|
|
497
496
|
if (this._cachedContent) {
|
|
498
497
|
return this._cachedContent;
|
|
499
498
|
}
|
|
@@ -508,8 +507,6 @@ export class CoValueCore {
|
|
|
508
507
|
} else {
|
|
509
508
|
this._cachedContent = new CoStream(this);
|
|
510
509
|
}
|
|
511
|
-
} else if (this.header.type === "static") {
|
|
512
|
-
this._cachedContent = new Static(this);
|
|
513
510
|
} else {
|
|
514
511
|
throw new Error(`Unknown coValue type ${this.header.type}`);
|
|
515
512
|
}
|
|
@@ -611,26 +608,24 @@ export class CoValueCore {
|
|
|
611
608
|
|
|
612
609
|
// Try to find key revelation for us
|
|
613
610
|
|
|
614
|
-
const
|
|
611
|
+
const lastReadyKeyEdit = content.lastEditAt(
|
|
615
612
|
`${keyID}_for_${this.node.account.id}`
|
|
616
613
|
);
|
|
617
614
|
|
|
618
|
-
if (
|
|
619
|
-
const revealer =
|
|
620
|
-
readKeyEntry.txID.sessionID
|
|
621
|
-
);
|
|
615
|
+
if (lastReadyKeyEdit?.value) {
|
|
616
|
+
const revealer = lastReadyKeyEdit.by;
|
|
622
617
|
const revealerAgent = this.node.resolveAccountAgent(
|
|
623
618
|
revealer,
|
|
624
619
|
"Expected to know revealer"
|
|
625
620
|
);
|
|
626
621
|
|
|
627
622
|
const secret = unseal(
|
|
628
|
-
|
|
623
|
+
lastReadyKeyEdit.value,
|
|
629
624
|
this.node.account.currentSealerSecret(),
|
|
630
625
|
getAgentSealerID(revealerAgent),
|
|
631
626
|
{
|
|
632
627
|
in: this.id,
|
|
633
|
-
tx:
|
|
628
|
+
tx: lastReadyKeyEdit.tx,
|
|
634
629
|
}
|
|
635
630
|
);
|
|
636
631
|
|
|
@@ -784,15 +779,16 @@ export class CoValueCore {
|
|
|
784
779
|
sessionEntry = {
|
|
785
780
|
after: sentState[sessionID] ?? 0,
|
|
786
781
|
newTransactions: [],
|
|
787
|
-
lastSignature: "WILL_BE_REPLACED" as Signature
|
|
782
|
+
lastSignature: "WILL_BE_REPLACED" as Signature,
|
|
788
783
|
};
|
|
789
784
|
currentPiece.new[sessionID] = sessionEntry;
|
|
790
785
|
}
|
|
791
786
|
|
|
792
787
|
sessionEntry.newTransactions.push(...txsToAdd);
|
|
793
|
-
sessionEntry.lastSignature =
|
|
794
|
-
|
|
795
|
-
|
|
788
|
+
sessionEntry.lastSignature =
|
|
789
|
+
nextKnownSignatureIdx === undefined
|
|
790
|
+
? log.lastSignature!
|
|
791
|
+
: log.signatureAfter[nextKnownSignatureIdx]!;
|
|
796
792
|
|
|
797
793
|
sentState[sessionID] =
|
|
798
794
|
(sentState[sessionID] || 0) + txsToAdd.length;
|
package/src/coValues/coList.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
2
2
|
import { CoID, CoValue, isCoValue } from "../coValue.js";
|
|
3
3
|
import { CoValueCore, accountOrAgentIDfromSessionID } from "../coValueCore.js";
|
|
4
|
-
import { SessionID, TransactionID } from "../ids.js";
|
|
4
|
+
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
5
5
|
import { Group } from "../group.js";
|
|
6
|
-
import { AccountID
|
|
6
|
+
import { AccountID } from "../account.js";
|
|
7
7
|
import { parseJSON } from "../jsonStringify.js";
|
|
8
8
|
|
|
9
9
|
type OpID = TransactionID & { changeIdx: number };
|
|
@@ -40,13 +40,16 @@ type DeletionEntry = {
|
|
|
40
40
|
deletionID: OpID;
|
|
41
41
|
} & DeletionOpPayload;
|
|
42
42
|
|
|
43
|
-
export class
|
|
44
|
-
|
|
43
|
+
export class CoListView<
|
|
44
|
+
Item extends JsonValue | CoValue,
|
|
45
45
|
Meta extends JsonObject | null = null
|
|
46
46
|
> implements CoValue
|
|
47
47
|
{
|
|
48
|
+
/** @category 6. Meta */
|
|
48
49
|
id: CoID<this>;
|
|
50
|
+
/** @category 6. Meta */
|
|
49
51
|
type = "colist" as const;
|
|
52
|
+
/** @category 6. Meta */
|
|
50
53
|
core: CoValueCore;
|
|
51
54
|
/** @internal */
|
|
52
55
|
afterStart: OpID[];
|
|
@@ -56,7 +59,7 @@ export class CoList<
|
|
|
56
59
|
insertions: {
|
|
57
60
|
[sessionID: SessionID]: {
|
|
58
61
|
[txIdx: number]: {
|
|
59
|
-
[changeIdx: number]: InsertionEntry<
|
|
62
|
+
[changeIdx: number]: InsertionEntry<Item>;
|
|
60
63
|
};
|
|
61
64
|
};
|
|
62
65
|
};
|
|
@@ -68,6 +71,8 @@ export class CoList<
|
|
|
68
71
|
};
|
|
69
72
|
};
|
|
70
73
|
};
|
|
74
|
+
/** @category 6. Meta */
|
|
75
|
+
readonly _item!: Item;
|
|
71
76
|
|
|
72
77
|
/** @internal */
|
|
73
78
|
constructor(core: CoValueCore) {
|
|
@@ -78,19 +83,6 @@ export class CoList<
|
|
|
78
83
|
this.insertions = {};
|
|
79
84
|
this.deletionsByInsertion = {};
|
|
80
85
|
|
|
81
|
-
this.fillOpsFromCoValue();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get meta(): Meta {
|
|
85
|
-
return this.core.header.meta as Meta;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get group(): Group {
|
|
89
|
-
return this.core.getGroup();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** @internal */
|
|
93
|
-
protected fillOpsFromCoValue() {
|
|
94
86
|
this.insertions = {};
|
|
95
87
|
this.deletionsByInsertion = {};
|
|
96
88
|
this.afterStart = [];
|
|
@@ -104,7 +96,7 @@ export class CoList<
|
|
|
104
96
|
for (const [changeIdx, changeUntyped] of parseJSON(
|
|
105
97
|
changes
|
|
106
98
|
).entries()) {
|
|
107
|
-
const change = changeUntyped as ListOpPayload<
|
|
99
|
+
const change = changeUntyped as ListOpPayload<Item>;
|
|
108
100
|
|
|
109
101
|
if (change.op === "pre" || change.op === "app") {
|
|
110
102
|
let sessionEntry = this.insertions[txID.sessionID];
|
|
@@ -204,10 +196,35 @@ export class CoList<
|
|
|
204
196
|
}
|
|
205
197
|
}
|
|
206
198
|
|
|
207
|
-
/**
|
|
199
|
+
/** @category 6. Meta */
|
|
200
|
+
get meta(): Meta {
|
|
201
|
+
return this.core.header.meta as Meta;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** @category 6. Meta */
|
|
205
|
+
get group(): Group {
|
|
206
|
+
return this.core.getGroup();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Not yet implemented
|
|
211
|
+
*
|
|
212
|
+
* @category 4. Time travel
|
|
213
|
+
*/
|
|
214
|
+
atTime(_time: number): this {
|
|
215
|
+
throw new Error("Not yet implemented");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get the item currently at `idx`.
|
|
220
|
+
*
|
|
221
|
+
* @category 1. Reading
|
|
222
|
+
*/
|
|
208
223
|
get(
|
|
209
224
|
idx: number
|
|
210
|
-
):
|
|
225
|
+
):
|
|
226
|
+
| (Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>)
|
|
227
|
+
| undefined {
|
|
211
228
|
const entry = this.entries()[idx];
|
|
212
229
|
if (!entry) {
|
|
213
230
|
return undefined;
|
|
@@ -215,18 +232,23 @@ export class CoList<
|
|
|
215
232
|
return entry.value;
|
|
216
233
|
}
|
|
217
234
|
|
|
218
|
-
/**
|
|
219
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Returns the current items in the CoList as an array.
|
|
237
|
+
*
|
|
238
|
+
* @category 1. Reading
|
|
239
|
+
**/
|
|
240
|
+
asArray(): (Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>)[] {
|
|
220
241
|
return this.entries().map((entry) => entry.value);
|
|
221
242
|
}
|
|
222
243
|
|
|
223
|
-
|
|
224
|
-
|
|
244
|
+
/** @internal */
|
|
245
|
+
entries(): {
|
|
246
|
+
value: Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>;
|
|
225
247
|
madeAt: number;
|
|
226
248
|
opID: OpID;
|
|
227
249
|
}[] {
|
|
228
250
|
const arr: {
|
|
229
|
-
value:
|
|
251
|
+
value: Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>;
|
|
230
252
|
madeAt: number;
|
|
231
253
|
opID: OpID;
|
|
232
254
|
}[] = [];
|
|
@@ -243,7 +265,7 @@ export class CoList<
|
|
|
243
265
|
private fillArrayFromOpID(
|
|
244
266
|
opID: OpID,
|
|
245
267
|
arr: {
|
|
246
|
-
value:
|
|
268
|
+
value: Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>;
|
|
247
269
|
madeAt: number;
|
|
248
270
|
opID: OpID;
|
|
249
271
|
}[]
|
|
@@ -272,101 +294,115 @@ export class CoList<
|
|
|
272
294
|
}
|
|
273
295
|
}
|
|
274
296
|
|
|
275
|
-
/**
|
|
276
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Returns the current items in the CoList as an array. (alias of `asArray`)
|
|
299
|
+
*
|
|
300
|
+
* @category 1. Reading
|
|
301
|
+
*/
|
|
302
|
+
toJSON(): (Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>)[] {
|
|
303
|
+
return this.asArray();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** @category 5. Edit history */
|
|
307
|
+
editAt(idx: number):
|
|
308
|
+
| {
|
|
309
|
+
by: AccountID | AgentID;
|
|
310
|
+
tx: TransactionID;
|
|
311
|
+
at: Date;
|
|
312
|
+
value: Item extends CoValue ? CoID<Item> : Exclude<Item, CoValue>;
|
|
313
|
+
}
|
|
314
|
+
| undefined {
|
|
277
315
|
const entry = this.entries()[idx];
|
|
278
316
|
if (!entry) {
|
|
279
317
|
return undefined;
|
|
280
318
|
}
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
319
|
+
const madeAt = new Date(entry.madeAt);
|
|
320
|
+
const by = accountOrAgentIDfromSessionID(entry.opID.sessionID);
|
|
321
|
+
const value = entry.value;
|
|
322
|
+
return {
|
|
323
|
+
by,
|
|
324
|
+
tx: {
|
|
325
|
+
sessionID: entry.opID.sessionID,
|
|
326
|
+
txIndex: entry.opID.txIndex,
|
|
327
|
+
},
|
|
328
|
+
at: madeAt,
|
|
329
|
+
value,
|
|
330
|
+
};
|
|
292
331
|
}
|
|
293
332
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
333
|
+
/** @category 5. Edit history */
|
|
334
|
+
deletionEdits(): {
|
|
335
|
+
by: AccountID | AgentID;
|
|
336
|
+
tx: TransactionID;
|
|
337
|
+
at: Date;
|
|
338
|
+
// TODO: add indices that are now before and after the deleted item
|
|
339
|
+
}[] {
|
|
340
|
+
const edits: {
|
|
341
|
+
by: AccountID | AgentID;
|
|
342
|
+
tx: TransactionID;
|
|
343
|
+
at: Date;
|
|
344
|
+
}[] = [];
|
|
302
345
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
346
|
+
for (const sessionID in this.deletionsByInsertion) {
|
|
347
|
+
const sessionEntry =
|
|
348
|
+
this.deletionsByInsertion[sessionID as SessionID];
|
|
349
|
+
for (const txIdx in sessionEntry) {
|
|
350
|
+
const txEntry = sessionEntry[Number(txIdx)];
|
|
351
|
+
for (const changeIdx in txEntry) {
|
|
352
|
+
const changeEntry = txEntry[Number(changeIdx)];
|
|
353
|
+
for (const deletion of changeEntry || []) {
|
|
354
|
+
const madeAt = new Date(deletion.madeAt);
|
|
355
|
+
const by = accountOrAgentIDfromSessionID(
|
|
356
|
+
deletion.deletionID.sessionID
|
|
357
|
+
);
|
|
358
|
+
edits.push({
|
|
359
|
+
by,
|
|
360
|
+
tx: deletion.deletionID,
|
|
361
|
+
at: madeAt,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
319
367
|
|
|
320
|
-
|
|
321
|
-
reducer: (
|
|
322
|
-
accumulator: U,
|
|
323
|
-
value: T extends CoValue ? CoID<T> : Exclude<T, CoValue>,
|
|
324
|
-
idx: number
|
|
325
|
-
) => U,
|
|
326
|
-
initialValue: U
|
|
327
|
-
): U {
|
|
328
|
-
return this.entries().reduce(
|
|
329
|
-
(accumulator, entry, idx) => reducer(accumulator, entry.value, idx),
|
|
330
|
-
initialValue
|
|
331
|
-
);
|
|
368
|
+
return edits;
|
|
332
369
|
}
|
|
333
370
|
|
|
371
|
+
/** @category 3. Subscription */
|
|
334
372
|
subscribe(listener: (coList: this) => void): () => void {
|
|
335
373
|
return this.core.subscribe((content) => {
|
|
336
374
|
listener(content as this);
|
|
337
375
|
});
|
|
338
376
|
}
|
|
339
|
-
|
|
340
|
-
edit(changer: (editable: WriteableCoList<T, Meta>) => void): this {
|
|
341
|
-
const editable = new WriteableCoList<T, Meta>(this.core);
|
|
342
|
-
changer(editable);
|
|
343
|
-
return new CoList(this.core) as this;
|
|
344
|
-
}
|
|
345
377
|
}
|
|
346
378
|
|
|
347
|
-
export class
|
|
348
|
-
|
|
379
|
+
export class CoList<
|
|
380
|
+
Item extends JsonValue | CoValue,
|
|
349
381
|
Meta extends JsonObject | null = null
|
|
350
382
|
>
|
|
351
|
-
extends
|
|
383
|
+
extends CoListView<Item, Meta>
|
|
352
384
|
implements CoValue
|
|
353
385
|
{
|
|
354
|
-
/**
|
|
355
|
-
edit(_changer: (editable: WriteableCoList<T, Meta>) => void): this {
|
|
356
|
-
throw new Error("Already editing.");
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/** Appends a new item after index `after`.
|
|
386
|
+
/** Returns a new version of this CoList with `item` appended after the item currently at index `after`.
|
|
360
387
|
*
|
|
361
|
-
* If `privacy` is `"private"` **(default)**,
|
|
388
|
+
* If `privacy` is `"private"` **(default)**, `item` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
362
389
|
*
|
|
363
|
-
* If `privacy` is `"trusting"`,
|
|
390
|
+
* If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
391
|
+
*
|
|
392
|
+
* @category 2. Editing
|
|
393
|
+
**/
|
|
364
394
|
append(
|
|
365
|
-
|
|
366
|
-
|
|
395
|
+
item: Item extends CoValue ? Item | CoID<Item> : Item,
|
|
396
|
+
after?: number,
|
|
367
397
|
privacy: "private" | "trusting" = "private"
|
|
368
|
-
):
|
|
398
|
+
): this {
|
|
369
399
|
const entries = this.entries();
|
|
400
|
+
after =
|
|
401
|
+
after === undefined
|
|
402
|
+
? entries.length > 0
|
|
403
|
+
? entries.length - 1
|
|
404
|
+
: 0
|
|
405
|
+
: 0;
|
|
370
406
|
let opIDBefore;
|
|
371
407
|
if (entries.length > 0) {
|
|
372
408
|
const entryBefore = entries[after];
|
|
@@ -384,47 +420,32 @@ export class WriteableCoList<
|
|
|
384
420
|
[
|
|
385
421
|
{
|
|
386
422
|
op: "app",
|
|
387
|
-
value: isCoValue(
|
|
423
|
+
value: isCoValue(item) ? item.id : item,
|
|
388
424
|
after: opIDBefore,
|
|
389
425
|
},
|
|
390
426
|
],
|
|
391
427
|
privacy
|
|
392
428
|
);
|
|
393
429
|
|
|
394
|
-
this.
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/** Pushes a new item to the end of the list.
|
|
398
|
-
*
|
|
399
|
-
* If `privacy` is `"private"` **(default)**, both `value` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
400
|
-
*
|
|
401
|
-
* If `privacy` is `"trusting"`, both `value` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers. */
|
|
402
|
-
push(
|
|
403
|
-
value: T extends CoValue ? T | CoID<T> : T,
|
|
404
|
-
privacy: "private" | "trusting" = "private"
|
|
405
|
-
): void {
|
|
406
|
-
// TODO: optimize
|
|
407
|
-
const entries = this.entries();
|
|
408
|
-
this.append(
|
|
409
|
-
entries.length > 0 ? entries.length - 1 : 0,
|
|
410
|
-
value,
|
|
411
|
-
privacy
|
|
412
|
-
);
|
|
430
|
+
return new CoList(this.core) as this;
|
|
413
431
|
}
|
|
414
432
|
|
|
415
433
|
/**
|
|
416
|
-
*
|
|
434
|
+
* Returns a new version of this CoList with `item` prepended before the item currently at index `before`.
|
|
417
435
|
*
|
|
418
|
-
* If `privacy` is `"private"` **(default)**,
|
|
436
|
+
* If `privacy` is `"private"` **(default)**, `item` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
419
437
|
*
|
|
420
|
-
* If `privacy` is `"trusting"`,
|
|
438
|
+
* If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
439
|
+
*
|
|
440
|
+
* @category 2. Editing
|
|
421
441
|
*/
|
|
422
442
|
prepend(
|
|
423
|
-
|
|
424
|
-
|
|
443
|
+
item: Item extends CoValue ? Item | CoID<Item> : Item,
|
|
444
|
+
before?: number,
|
|
425
445
|
privacy: "private" | "trusting" = "private"
|
|
426
|
-
):
|
|
446
|
+
): this {
|
|
427
447
|
const entries = this.entries();
|
|
448
|
+
before = before === undefined ? 0 : before;
|
|
428
449
|
let opIDAfter;
|
|
429
450
|
if (entries.length > 0) {
|
|
430
451
|
const entryAfter = entries[before];
|
|
@@ -446,22 +467,25 @@ export class WriteableCoList<
|
|
|
446
467
|
[
|
|
447
468
|
{
|
|
448
469
|
op: "pre",
|
|
449
|
-
value: isCoValue(
|
|
470
|
+
value: isCoValue(item) ? item.id : item,
|
|
450
471
|
before: opIDAfter,
|
|
451
472
|
},
|
|
452
473
|
],
|
|
453
474
|
privacy
|
|
454
475
|
);
|
|
455
476
|
|
|
456
|
-
this.
|
|
477
|
+
return new CoList(this.core) as this;
|
|
457
478
|
}
|
|
458
479
|
|
|
459
|
-
/**
|
|
480
|
+
/** Returns a new version of this CoList with the item at index `at` deleted from the list.
|
|
460
481
|
*
|
|
461
482
|
* If `privacy` is `"private"` **(default)**, the fact of this deletion is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
462
483
|
*
|
|
463
|
-
* If `privacy` is `"trusting"`, the fact of this deletion is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
464
|
-
|
|
484
|
+
* If `privacy` is `"trusting"`, the fact of this deletion is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
485
|
+
*
|
|
486
|
+
* @category 2. Editing
|
|
487
|
+
**/
|
|
488
|
+
delete(at: number, privacy: "private" | "trusting" = "private"): this {
|
|
465
489
|
const entries = this.entries();
|
|
466
490
|
const entry = entries[at];
|
|
467
491
|
if (!entry) {
|
|
@@ -477,6 +501,96 @@ export class WriteableCoList<
|
|
|
477
501
|
privacy
|
|
478
502
|
);
|
|
479
503
|
|
|
480
|
-
this.
|
|
504
|
+
return new CoList(this.core) as this;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** @category 2. Editing */
|
|
508
|
+
mutate(mutator: (mutable: MutableCoList<Item, Meta>) => void): this {
|
|
509
|
+
const mutable = new MutableCoList<Item, Meta>(this.core);
|
|
510
|
+
mutator(mutable);
|
|
511
|
+
return new CoList(this.core) as this;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** @deprecated Use `mutate` instead. */
|
|
515
|
+
edit(mutator: (mutable: MutableCoList<Item, Meta>) => void): this {
|
|
516
|
+
return this.mutate(mutator);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export class MutableCoList<
|
|
521
|
+
Item extends JsonValue | CoValue,
|
|
522
|
+
Meta extends JsonObject | null = null
|
|
523
|
+
>
|
|
524
|
+
extends CoListView<Item, Meta>
|
|
525
|
+
implements CoValue
|
|
526
|
+
{
|
|
527
|
+
/** Appends `item` after the item currently at index `after`.
|
|
528
|
+
*
|
|
529
|
+
* If `privacy` is `"private"` **(default)**, `item` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
530
|
+
*
|
|
531
|
+
* If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
532
|
+
*
|
|
533
|
+
* @category 2. Mutating
|
|
534
|
+
**/
|
|
535
|
+
append(
|
|
536
|
+
item: Item extends CoValue ? Item | CoID<Item> : Item,
|
|
537
|
+
after?: number,
|
|
538
|
+
privacy: "private" | "trusting" = "private"
|
|
539
|
+
): void {
|
|
540
|
+
const listAfter = CoList.prototype.append.call(
|
|
541
|
+
this,
|
|
542
|
+
item,
|
|
543
|
+
after,
|
|
544
|
+
privacy
|
|
545
|
+
) as CoList<Item, Meta>;
|
|
546
|
+
this.afterStart = listAfter.afterStart;
|
|
547
|
+
this.beforeEnd = listAfter.beforeEnd;
|
|
548
|
+
this.insertions = listAfter.insertions;
|
|
549
|
+
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/** Prepends `item` before the item currently at index `before`.
|
|
553
|
+
*
|
|
554
|
+
* If `privacy` is `"private"` **(default)**, `item` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
555
|
+
*
|
|
556
|
+
* If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
557
|
+
*
|
|
558
|
+
* * @category 2. Mutating
|
|
559
|
+
**/
|
|
560
|
+
prepend(
|
|
561
|
+
item: Item extends CoValue ? Item | CoID<Item> : Item,
|
|
562
|
+
before?: number,
|
|
563
|
+
privacy: "private" | "trusting" = "private"
|
|
564
|
+
): void {
|
|
565
|
+
const listAfter = CoList.prototype.prepend.call(
|
|
566
|
+
this,
|
|
567
|
+
item,
|
|
568
|
+
before,
|
|
569
|
+
privacy
|
|
570
|
+
) as CoList<Item, Meta>;
|
|
571
|
+
this.afterStart = listAfter.afterStart;
|
|
572
|
+
this.beforeEnd = listAfter.beforeEnd;
|
|
573
|
+
this.insertions = listAfter.insertions;
|
|
574
|
+
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/** Deletes the item at index `at` from the list.
|
|
578
|
+
*
|
|
579
|
+
* If `privacy` is `"private"` **(default)**, the fact of this deletion is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
580
|
+
*
|
|
581
|
+
* If `privacy` is `"trusting"`, the fact of this deletion is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
|
|
582
|
+
*
|
|
583
|
+
* * @category 2. Mutating
|
|
584
|
+
**/
|
|
585
|
+
delete(at: number, privacy: "private" | "trusting" = "private"): void {
|
|
586
|
+
const listAfter = CoList.prototype.delete.call(
|
|
587
|
+
this,
|
|
588
|
+
at,
|
|
589
|
+
privacy
|
|
590
|
+
) as CoList<Item, Meta>;
|
|
591
|
+
this.afterStart = listAfter.afterStart;
|
|
592
|
+
this.beforeEnd = listAfter.beforeEnd;
|
|
593
|
+
this.insertions = listAfter.insertions;
|
|
594
|
+
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
481
595
|
}
|
|
482
596
|
}
|