cojson 0.1.8 → 0.1.10

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 (76) hide show
  1. package/dist/account.d.ts +6 -3
  2. package/dist/account.js +4 -2
  3. package/dist/account.js.map +1 -1
  4. package/dist/coValue.d.ts +44 -80
  5. package/dist/coValue.js +4 -348
  6. package/dist/coValue.js.map +1 -1
  7. package/dist/coValueCore.d.ts +84 -0
  8. package/dist/coValueCore.js +356 -0
  9. package/dist/coValueCore.js.map +1 -0
  10. package/dist/coValues/coList.d.ts +114 -0
  11. package/dist/{contentTypes → coValues}/coList.js +59 -19
  12. package/dist/coValues/coList.js.map +1 -0
  13. package/dist/{contentTypes → coValues}/coMap.d.ts +25 -7
  14. package/dist/{contentTypes → coValues}/coMap.js +34 -15
  15. package/dist/coValues/coMap.js.map +1 -0
  16. package/dist/coValues/coStream.d.ts +69 -0
  17. package/dist/coValues/coStream.js +131 -0
  18. package/dist/coValues/coStream.js.map +1 -0
  19. package/dist/coValues/static.d.ts +14 -0
  20. package/dist/coValues/static.js +20 -0
  21. package/dist/coValues/static.js.map +1 -0
  22. package/dist/group.d.ts +57 -9
  23. package/dist/group.js +94 -28
  24. package/dist/group.js.map +1 -1
  25. package/dist/index.d.ts +19 -10
  26. package/dist/index.js +7 -5
  27. package/dist/index.js.map +1 -1
  28. package/dist/node.d.ts +59 -5
  29. package/dist/node.js +36 -15
  30. package/dist/node.js.map +1 -1
  31. package/dist/permissions.d.ts +2 -2
  32. package/dist/permissions.js +1 -1
  33. package/dist/permissions.js.map +1 -1
  34. package/dist/sync.d.ts +3 -3
  35. package/dist/sync.js +2 -2
  36. package/dist/sync.js.map +1 -1
  37. package/dist/testUtils.d.ts +2 -2
  38. package/dist/testUtils.js +1 -1
  39. package/dist/testUtils.js.map +1 -1
  40. package/package.json +2 -2
  41. package/src/account.test.ts +1 -1
  42. package/src/account.ts +8 -5
  43. package/src/coValue.test.ts +335 -129
  44. package/src/coValue.ts +52 -576
  45. package/src/coValueCore.test.ts +180 -0
  46. package/src/coValueCore.ts +592 -0
  47. package/src/{contentTypes → coValues}/coList.ts +91 -42
  48. package/src/{contentTypes → coValues}/coMap.ts +40 -20
  49. package/src/coValues/coStream.ts +249 -0
  50. package/src/coValues/static.ts +31 -0
  51. package/src/group.test.ts +47 -0
  52. package/src/group.ts +120 -50
  53. package/src/index.ts +43 -28
  54. package/src/node.ts +48 -27
  55. package/src/permissions.test.ts +32 -32
  56. package/src/permissions.ts +5 -5
  57. package/src/sync.test.ts +77 -77
  58. package/src/sync.ts +5 -5
  59. package/src/testUtils.ts +1 -1
  60. package/tsconfig.json +1 -2
  61. package/dist/contentType.d.ts +0 -15
  62. package/dist/contentType.js +0 -7
  63. package/dist/contentType.js.map +0 -1
  64. package/dist/contentTypes/coList.d.ts +0 -77
  65. package/dist/contentTypes/coList.js.map +0 -1
  66. package/dist/contentTypes/coMap.js.map +0 -1
  67. package/dist/contentTypes/coStream.d.ts +0 -11
  68. package/dist/contentTypes/coStream.js +0 -16
  69. package/dist/contentTypes/coStream.js.map +0 -1
  70. package/dist/contentTypes/static.d.ts +0 -11
  71. package/dist/contentTypes/static.js +0 -14
  72. package/dist/contentTypes/static.js.map +0 -1
  73. package/src/contentType.test.ts +0 -284
  74. package/src/contentType.ts +0 -26
  75. package/src/contentTypes/coStream.ts +0 -24
  76. package/src/contentTypes/static.ts +0 -22
@@ -1,9 +1,9 @@
1
1
  import { JsonObject, JsonValue } from "../jsonValue.js";
2
- import { CoID } from "../contentType.js";
3
- import { CoValue, accountOrAgentIDfromSessionID } from "../coValue.js";
2
+ import { CoID, ReadableCoValue, WriteableCoValue } from "../coValue.js";
3
+ import { CoValueCore, accountOrAgentIDfromSessionID } from "../coValueCore.js";
4
4
  import { SessionID, TransactionID } from "../ids.js";
5
- import { AccountID, Group } from "../index.js";
6
- import { isAccountID } from "../account.js";
5
+ import { Group } from "../group.js";
6
+ import { AccountID, isAccountID } from "../account.js";
7
7
 
8
8
  type OpID = TransactionID & { changeIdx: number };
9
9
 
@@ -39,15 +39,17 @@ type DeletionEntry = {
39
39
  deletionID: OpID;
40
40
  } & DeletionOpPayload;
41
41
 
42
- export class CoList<
43
- T extends JsonValue,
44
- Meta extends JsonObject | null = null
45
- > {
42
+ export class CoList<T extends JsonValue, Meta extends JsonObject | null = null>
43
+ implements ReadableCoValue
44
+ {
46
45
  id: CoID<CoList<T, Meta>>;
47
46
  type = "colist" as const;
48
- coValue: CoValue;
47
+ core: CoValueCore;
48
+ /** @internal */
49
49
  afterStart: OpID[];
50
+ /** @internal */
50
51
  beforeEnd: OpID[];
52
+ /** @internal */
51
53
  insertions: {
52
54
  [sessionID: SessionID]: {
53
55
  [txIdx: number]: {
@@ -55,6 +57,7 @@ export class CoList<
55
57
  };
56
58
  };
57
59
  };
60
+ /** @internal */
58
61
  deletionsByInsertion: {
59
62
  [deletedSessionID: SessionID]: {
60
63
  [deletedTxIdx: number]: {
@@ -63,9 +66,10 @@ export class CoList<
63
66
  };
64
67
  };
65
68
 
66
- constructor(coValue: CoValue) {
67
- this.id = coValue.id as CoID<CoList<T, Meta>>;
68
- this.coValue = coValue;
69
+ /** @internal */
70
+ constructor(core: CoValueCore) {
71
+ this.id = core.id as CoID<CoList<T, Meta>>;
72
+ this.core = core;
69
73
  this.afterStart = [];
70
74
  this.beforeEnd = [];
71
75
  this.insertions = {};
@@ -74,15 +78,15 @@ export class CoList<
74
78
  this.fillOpsFromCoValue();
75
79
  }
76
80
 
77
-
78
81
  get meta(): Meta {
79
- return this.coValue.header.meta as Meta;
82
+ return this.core.header.meta as Meta;
80
83
  }
81
84
 
82
85
  get group(): Group {
83
- return this.coValue.getGroup();
86
+ return this.core.getGroup();
84
87
  }
85
88
 
89
+ /** @internal */
86
90
  protected fillOpsFromCoValue() {
87
91
  this.insertions = {};
88
92
  this.deletionsByInsertion = {};
@@ -93,7 +97,7 @@ export class CoList<
93
97
  txID,
94
98
  changes,
95
99
  madeAt,
96
- } of this.coValue.getValidSortedTransactions()) {
100
+ } of this.core.getValidSortedTransactions()) {
97
101
  for (const [changeIdx, changeUntyped] of changes.entries()) {
98
102
  const change = changeUntyped as ListOpPayload<T>;
99
103
 
@@ -195,6 +199,20 @@ export class CoList<
195
199
  }
196
200
  }
197
201
 
202
+ /** Get the item currently at `idx`. */
203
+ get(idx: number): T | undefined {
204
+ const entry = this.entries()[idx];
205
+ if (!entry) {
206
+ return undefined;
207
+ }
208
+ return entry.value;
209
+ }
210
+
211
+ /** Returns the current items in the CoList as an array. */
212
+ asArray(): T[] {
213
+ return this.entries().map((entry) => entry.value);
214
+ }
215
+
198
216
  entries(): { value: T; madeAt: number; opID: OpID }[] {
199
217
  const arr: { value: T; madeAt: number; opID: OpID }[] = [];
200
218
  for (const opID of this.afterStart) {
@@ -206,6 +224,7 @@ export class CoList<
206
224
  return arr;
207
225
  }
208
226
 
227
+ /** @internal */
209
228
  private fillArrayFromOpID(
210
229
  opID: OpID,
211
230
  arr: { value: T; madeAt: number; opID: OpID }[]
@@ -234,6 +253,7 @@ export class CoList<
234
253
  }
235
254
  }
236
255
 
256
+ /** Returns the accountID of the account that inserted value at the given index. */
237
257
  whoInserted(idx: number): AccountID | undefined {
238
258
  const entry = this.entries()[idx];
239
259
  if (!entry) {
@@ -247,19 +267,16 @@ export class CoList<
247
267
  }
248
268
  }
249
269
 
270
+ /** Returns the current items in the CoList as an array. (alias of `asArray`) */
250
271
  toJSON(): T[] {
251
272
  return this.asArray();
252
273
  }
253
274
 
254
- asArray(): T[] {
255
- return this.entries().map((entry) => entry.value);
256
- }
257
-
258
275
  map<U>(mapper: (value: T, idx: number) => U): U[] {
259
276
  return this.entries().map((entry, idx) => mapper(entry.value, idx));
260
277
  }
261
278
 
262
- filter<U extends T>(predicate: (value: T, idx: number) => value is U): U[]
279
+ filter<U extends T>(predicate: (value: T, idx: number) => value is U): U[];
263
280
  filter(predicate: (value: T, idx: number) => boolean): T[] {
264
281
  return this.entries()
265
282
  .filter((entry, idx) => predicate(entry.value, idx))
@@ -271,31 +288,45 @@ export class CoList<
271
288
  initialValue: U
272
289
  ): U {
273
290
  return this.entries().reduce(
274
- (accumulator, entry, idx) =>
275
- reducer(accumulator, entry.value, idx),
291
+ (accumulator, entry, idx) => reducer(accumulator, entry.value, idx),
276
292
  initialValue
277
293
  );
278
294
  }
279
295
 
296
+ subscribe(listener: (coMap: CoList<T, Meta>) => void): () => void {
297
+ return this.core.subscribe((content) => {
298
+ listener(content as CoList<T, Meta>);
299
+ });
300
+ }
301
+
280
302
  edit(
281
303
  changer: (editable: WriteableCoList<T, Meta>) => void
282
304
  ): CoList<T, Meta> {
283
- const editable = new WriteableCoList<T, Meta>(this.coValue);
305
+ const editable = new WriteableCoList<T, Meta>(this.core);
284
306
  changer(editable);
285
- return new CoList(this.coValue);
286
- }
287
-
288
- subscribe(listener: (coMap: CoList<T, Meta>) => void): () => void {
289
- return this.coValue.subscribe((content) => {
290
- listener(content as CoList<T, Meta>);
291
- });
307
+ return new CoList(this.core);
292
308
  }
293
309
  }
294
310
 
295
311
  export class WriteableCoList<
296
- T extends JsonValue,
297
- Meta extends JsonObject | null = null
298
- > extends CoList<T, Meta> {
312
+ T extends JsonValue,
313
+ Meta extends JsonObject | null = null
314
+ >
315
+ extends CoList<T, Meta>
316
+ implements WriteableCoValue
317
+ {
318
+ /** @internal */
319
+ edit(
320
+ _changer: (editable: WriteableCoList<T, Meta>) => void
321
+ ): CoList<T, Meta> {
322
+ throw new Error("Already editing.");
323
+ }
324
+
325
+ /** Appends a new item after index `after`.
326
+ *
327
+ * 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.
328
+ *
329
+ * 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. */
299
330
  append(
300
331
  after: number,
301
332
  value: T,
@@ -315,7 +346,7 @@ export class WriteableCoList<
315
346
  }
316
347
  opIDBefore = "start";
317
348
  }
318
- this.coValue.makeTransaction(
349
+ this.core.makeTransaction(
319
350
  [
320
351
  {
321
352
  op: "app",
@@ -329,12 +360,28 @@ export class WriteableCoList<
329
360
  this.fillOpsFromCoValue();
330
361
  }
331
362
 
363
+ /** Pushes a new item to the end of the list.
364
+ *
365
+ * 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.
366
+ *
367
+ * 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. */
332
368
  push(value: T, privacy: "private" | "trusting" = "private"): void {
333
369
  // TODO: optimize
334
370
  const entries = this.entries();
335
- this.append(entries.length > 0 ? entries.length - 1 : 0, value, privacy);
371
+ this.append(
372
+ entries.length > 0 ? entries.length - 1 : 0,
373
+ value,
374
+ privacy
375
+ );
336
376
  }
337
377
 
378
+ /**
379
+ * Prepends a new item before index `before`.
380
+ *
381
+ * 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.
382
+ *
383
+ * 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.
384
+ */
338
385
  prepend(
339
386
  before: number,
340
387
  value: T,
@@ -358,7 +405,7 @@ export class WriteableCoList<
358
405
  }
359
406
  opIDAfter = "end";
360
407
  }
361
- this.coValue.makeTransaction(
408
+ this.core.makeTransaction(
362
409
  [
363
410
  {
364
411
  op: "pre",
@@ -372,16 +419,18 @@ export class WriteableCoList<
372
419
  this.fillOpsFromCoValue();
373
420
  }
374
421
 
375
- delete(
376
- at: number,
377
- privacy: "private" | "trusting" = "private"
378
- ): void {
422
+ /** Deletes the item at index `at` from the list.
423
+ *
424
+ * 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.
425
+ *
426
+ * 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. */
427
+ delete(at: number, privacy: "private" | "trusting" = "private"): void {
379
428
  const entries = this.entries();
380
429
  const entry = entries[at];
381
430
  if (!entry) {
382
431
  throw new Error("Invalid index " + at);
383
432
  }
384
- this.coValue.makeTransaction(
433
+ this.core.makeTransaction(
385
434
  [
386
435
  {
387
436
  op: "del",
@@ -1,7 +1,7 @@
1
1
  import { JsonObject, JsonValue } from '../jsonValue.js';
2
2
  import { TransactionID } from '../ids.js';
3
- import { CoID } from '../contentType.js';
4
- import { CoValue, accountOrAgentIDfromSessionID } from '../coValue.js';
3
+ import { CoID, ReadableCoValue, WriteableCoValue } from '../coValue.js';
4
+ import { CoValueCore, accountOrAgentIDfromSessionID } from '../coValueCore.js';
5
5
  import { AccountID, isAccountID } from '../account.js';
6
6
  import { Group } from '../group.js';
7
7
 
@@ -28,37 +28,41 @@ export type MapM<M extends { [key: string]: JsonValue; }> = {
28
28
  [KK in MapK<M>]: M[KK];
29
29
  }
30
30
 
31
+ /** A collaborative map with precise shape `M` and optional static metadata `Meta` */
31
32
  export class CoMap<
32
33
  M extends { [key: string]: JsonValue; },
33
34
  Meta extends JsonObject | null = null,
34
- > {
35
+ > implements ReadableCoValue {
35
36
  id: CoID<CoMap<MapM<M>, Meta>>;
36
- coValue: CoValue;
37
37
  type = "comap" as const;
38
+ core: CoValueCore;
39
+ /** @internal */
38
40
  ops: {
39
41
  [KK in MapK<M>]?: MapOp<KK, M[KK]>[];
40
42
  };
41
43
 
42
- constructor(coValue: CoValue) {
43
- this.id = coValue.id as CoID<CoMap<MapM<M>, Meta>>;
44
- this.coValue = coValue;
44
+ /** @internal */
45
+ constructor(core: CoValueCore) {
46
+ this.id = core.id as CoID<CoMap<MapM<M>, Meta>>;
47
+ this.core = core;
45
48
  this.ops = {};
46
49
 
47
50
  this.fillOpsFromCoValue();
48
51
  }
49
52
 
50
53
  get meta(): Meta {
51
- return this.coValue.header.meta as Meta;
54
+ return this.core.header.meta as Meta;
52
55
  }
53
56
 
54
57
  get group(): Group {
55
- return this.coValue.getGroup();
58
+ return this.core.getGroup();
56
59
  }
57
60
 
61
+ /** @internal */
58
62
  protected fillOpsFromCoValue() {
59
63
  this.ops = {};
60
64
 
61
- for (const { txID, changes, madeAt } of this.coValue.getValidSortedTransactions()) {
65
+ for (const { txID, changes, madeAt } of this.core.getValidSortedTransactions()) {
62
66
  for (const [changeIdx, changeUntyped] of (
63
67
  changes
64
68
  ).entries()) {
@@ -82,6 +86,7 @@ export class CoMap<
82
86
  return Object.keys(this.ops) as MapK<M>[];
83
87
  }
84
88
 
89
+ /** Returns the current value for the given key. */
85
90
  get<K extends MapK<M>>(key: K): M[K] | undefined {
86
91
  const ops = this.ops[key];
87
92
  if (!ops) {
@@ -116,6 +121,7 @@ export class CoMap<
116
121
  }
117
122
  }
118
123
 
124
+ /** Returns the accountID of the last account to modify the value for the given key. */
119
125
  whoEdited<K extends MapK<M>>(key: K): AccountID | undefined {
120
126
  const tx = this.getLastTxID(key);
121
127
  if (!tx) {
@@ -187,26 +193,35 @@ export class CoMap<
187
193
  return json;
188
194
  }
189
195
 
190
- edit(changer: (editable: WriteableCoMap<M, Meta>) => void): CoMap<M, Meta> {
191
- const editable = new WriteableCoMap<M, Meta>(this.coValue);
192
- changer(editable);
193
- return new CoMap(this.coValue);
194
- }
195
-
196
196
  subscribe(listener: (coMap: CoMap<M, Meta>) => void): () => void {
197
- return this.coValue.subscribe((content) => {
197
+ return this.core.subscribe((content) => {
198
198
  listener(content as CoMap<M, Meta>);
199
199
  });
200
200
  }
201
+
202
+ edit(changer: (editable: WriteableCoMap<M, Meta>) => void): CoMap<M, Meta> {
203
+ const editable = new WriteableCoMap<M, Meta>(this.core);
204
+ changer(editable);
205
+ return new CoMap(this.core);
206
+ }
201
207
  }
202
208
 
203
209
  export class WriteableCoMap<
204
210
  M extends { [key: string]: JsonValue; },
205
211
  Meta extends JsonObject | null = null,
212
+ > extends CoMap<M, Meta> implements WriteableCoValue {
213
+ /** @internal */
214
+ edit(_changer: (editable: WriteableCoMap<M, Meta>) => void): CoMap<M, Meta> {
215
+ throw new Error("Already editing.");
216
+ }
206
217
 
207
- > extends CoMap<M, Meta> {
218
+ /** Sets a new value for the given key.
219
+ *
220
+ * 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.
221
+ *
222
+ * 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. */
208
223
  set<K extends MapK<M>>(key: K, value: M[K], privacy: "private" | "trusting" = "private"): void {
209
- this.coValue.makeTransaction([
224
+ this.core.makeTransaction([
210
225
  {
211
226
  op: "set",
212
227
  key,
@@ -217,8 +232,13 @@ export class WriteableCoMap<
217
232
  this.fillOpsFromCoValue();
218
233
  }
219
234
 
235
+ /** Deletes the value for the given key (setting it to undefined).
236
+ *
237
+ * 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.
238
+ *
239
+ * If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers. */
220
240
  delete(key: MapK<M>, privacy: "private" | "trusting" = "private"): void {
221
- this.coValue.makeTransaction([
241
+ this.core.makeTransaction([
222
242
  {
223
243
  op: "del",
224
244
  key,
@@ -0,0 +1,249 @@
1
+ import { JsonObject, JsonValue } from "../jsonValue.js";
2
+ import { CoID, ReadableCoValue, WriteableCoValue } from "../coValue.js";
3
+ import { CoValueCore } from "../coValueCore.js";
4
+ import { Group } from "../group.js";
5
+ import { SessionID } from "../ids.js";
6
+ import { base64url } from "@scure/base";
7
+
8
+ export type BinaryChunkInfo = {
9
+ mimeType: string;
10
+ fileName?: string;
11
+ totalSizeBytes?: number;
12
+ };
13
+
14
+ export type BinaryStreamStart = {
15
+ type: "start";
16
+ } & BinaryChunkInfo;
17
+
18
+ export type BinaryStreamChunk = {
19
+ type: "chunk";
20
+ chunk: `U${string}`;
21
+ };
22
+
23
+ export type BinaryStreamEnd = {
24
+ type: "end";
25
+ };
26
+
27
+ export type BinaryCoStreamMeta = JsonObject & { type: "binary" };
28
+
29
+ export type BinaryStreamItem =
30
+ | BinaryStreamStart
31
+ | BinaryStreamChunk
32
+ | BinaryStreamEnd;
33
+
34
+ export class CoStream<
35
+ T extends JsonValue,
36
+ Meta extends JsonObject | null = null
37
+ > implements ReadableCoValue
38
+ {
39
+ id: CoID<CoStream<T, Meta>>;
40
+ type = "costream" as const;
41
+ core: CoValueCore;
42
+ items: {
43
+ [key: SessionID]: T[];
44
+ };
45
+
46
+ constructor(core: CoValueCore) {
47
+ this.id = core.id as CoID<CoStream<T, Meta>>;
48
+ this.core = core;
49
+ this.items = {};
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 fillFromCoValue() {
62
+ this.items = {};
63
+
64
+ for (const {
65
+ txID,
66
+ changes,
67
+ } of this.core.getValidSortedTransactions()) {
68
+ for (const changeUntyped of changes) {
69
+ const change = changeUntyped as T;
70
+ let entries = this.items[txID.sessionID];
71
+ if (!entries) {
72
+ entries = [];
73
+ this.items[txID.sessionID] = entries;
74
+ }
75
+ entries.push(change);
76
+ }
77
+ }
78
+ }
79
+
80
+ getSingleStream(): T[] | undefined {
81
+ if (Object.keys(this.items).length === 0) {
82
+ return undefined;
83
+ } else if (Object.keys(this.items).length !== 1) {
84
+ throw new Error(
85
+ "CoStream.getSingleStream() can only be called when there is exactly one stream"
86
+ );
87
+ }
88
+
89
+ return Object.values(this.items)[0];
90
+ }
91
+
92
+ toJSON(): {
93
+ [key: SessionID]: T[];
94
+ } {
95
+ return this.items;
96
+ }
97
+
98
+ subscribe(listener: (coMap: CoStream<T, Meta>) => void): () => void {
99
+ return this.core.subscribe((content) => {
100
+ listener(content as CoStream<T, Meta>);
101
+ });
102
+ }
103
+
104
+ edit(
105
+ changer: (editable: WriteableCoStream<T, Meta>) => void
106
+ ): CoStream<T, Meta> {
107
+ const editable = new WriteableCoStream<T, Meta>(this.core);
108
+ changer(editable);
109
+ return new CoStream(this.core);
110
+ }
111
+ }
112
+
113
+ export class BinaryCoStream<
114
+ Meta extends BinaryCoStreamMeta = { type: "binary" }
115
+ >
116
+ extends CoStream<BinaryStreamItem, Meta>
117
+ implements ReadableCoValue
118
+ {
119
+ id!: CoID<BinaryCoStream<Meta>>;
120
+
121
+ getBinaryChunks():
122
+ | (BinaryChunkInfo & { chunks: Uint8Array[]; finished: boolean })
123
+ | undefined {
124
+ const items = this.getSingleStream();
125
+
126
+ if (!items) return;
127
+
128
+ const start = items[0];
129
+
130
+ if (start?.type !== "start") {
131
+ console.error("Invalid binary stream start", start);
132
+ return;
133
+ }
134
+
135
+ const chunks: Uint8Array[] = [];
136
+
137
+ for (const item of items.slice(1)) {
138
+ if (item.type === "end") {
139
+ return {
140
+ mimeType: start.mimeType,
141
+ fileName: start.fileName,
142
+ totalSizeBytes: start.totalSizeBytes,
143
+ chunks,
144
+ finished: true,
145
+ };
146
+ }
147
+
148
+ if (item.type !== "chunk") {
149
+ console.error("Invalid binary stream chunk", item);
150
+ return undefined;
151
+ }
152
+
153
+ chunks.push(base64url.decode(item.chunk.slice(1)));
154
+ }
155
+
156
+ return {
157
+ mimeType: start.mimeType,
158
+ fileName: start.fileName,
159
+ totalSizeBytes: start.totalSizeBytes,
160
+ chunks,
161
+ finished: false,
162
+ };
163
+ }
164
+
165
+ edit(
166
+ changer: (editable: WriteableBinaryCoStream<Meta>) => void
167
+ ): BinaryCoStream<Meta> {
168
+ const editable = new WriteableBinaryCoStream<Meta>(this.core);
169
+ changer(editable);
170
+ return new BinaryCoStream(this.core);
171
+ }
172
+ }
173
+
174
+ export class WriteableCoStream<
175
+ T extends JsonValue,
176
+ Meta extends JsonObject | null = null
177
+ >
178
+ extends CoStream<T, Meta>
179
+ implements WriteableCoValue
180
+ {
181
+ /** @internal */
182
+ edit(
183
+ _changer: (editable: WriteableCoStream<T, Meta>) => void
184
+ ): CoStream<T, Meta> {
185
+ throw new Error("Already editing.");
186
+ }
187
+
188
+ push(item: T, privacy: "private" | "trusting" = "private") {
189
+ this.core.makeTransaction([item], privacy);
190
+ this.fillFromCoValue();
191
+ }
192
+ }
193
+
194
+ export class WriteableBinaryCoStream<
195
+ Meta extends BinaryCoStreamMeta = { type: "binary" }
196
+ >
197
+ extends BinaryCoStream<Meta>
198
+ implements WriteableCoValue
199
+ {
200
+ /** @internal */
201
+ edit(
202
+ _changer: (editable: WriteableBinaryCoStream<Meta>) => void
203
+ ): BinaryCoStream<Meta> {
204
+ throw new Error("Already editing.");
205
+ }
206
+
207
+ /** @internal */
208
+ push(
209
+ item: BinaryStreamItem,
210
+ privacy: "private" | "trusting" = "private"
211
+ ) {
212
+ WriteableCoStream.prototype.push.call(this, item, privacy);
213
+ }
214
+
215
+ startBinaryStream(
216
+ settings: BinaryChunkInfo,
217
+ privacy: "private" | "trusting" = "private"
218
+ ) {
219
+ this.push(
220
+ {
221
+ type: "start",
222
+ ...settings,
223
+ } satisfies BinaryStreamStart,
224
+ privacy
225
+ );
226
+ }
227
+
228
+ pushBinaryStreamChunk(
229
+ chunk: Uint8Array,
230
+ privacy: "private" | "trusting" = "private"
231
+ ) {
232
+ this.push(
233
+ {
234
+ type: "chunk",
235
+ chunk: `U${base64url.encode(chunk)}`,
236
+ } satisfies BinaryStreamChunk,
237
+ privacy
238
+ );
239
+ }
240
+
241
+ endBinaryStream(privacy: "private" | "trusting" = "private") {
242
+ this.push(
243
+ {
244
+ type: "end",
245
+ } satisfies BinaryStreamEnd,
246
+ privacy
247
+ );
248
+ }
249
+ }
@@ -0,0 +1,31 @@
1
+ import { JsonObject } from '../jsonValue.js';
2
+ import { CoID, ReadableCoValue } from '../coValue.js';
3
+ import { CoValueCore } from '../coValueCore.js';
4
+ import { Group } from '../index.js';
5
+
6
+ export class Static<T extends JsonObject> implements ReadableCoValue{
7
+ id: CoID<Static<T>>;
8
+ type = "static" as const;
9
+ core: CoValueCore;
10
+
11
+ constructor(core: CoValueCore) {
12
+ this.id = core.id as CoID<Static<T>>;
13
+ this.core = core;
14
+ }
15
+
16
+ get meta(): T {
17
+ return this.core.header.meta as T;
18
+ }
19
+
20
+ get group(): Group {
21
+ return this.core.getGroup();
22
+ }
23
+
24
+ toJSON(): JsonObject {
25
+ throw new Error("Method not implemented.");
26
+ }
27
+
28
+ subscribe(_listener: (coMap: Static<T>) => void): () => void {
29
+ throw new Error("Method not implemented.");
30
+ }
31
+ }