cojson 0.8.12 → 0.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/CHANGELOG.md +95 -83
  2. package/dist/native/PeerKnownStates.js +6 -1
  3. package/dist/native/PeerKnownStates.js.map +1 -1
  4. package/dist/native/PeerState.js +4 -3
  5. package/dist/native/PeerState.js.map +1 -1
  6. package/dist/native/PriorityBasedMessageQueue.js +1 -10
  7. package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
  8. package/dist/native/SyncStateSubscriptionManager.js +70 -0
  9. package/dist/native/SyncStateSubscriptionManager.js.map +1 -0
  10. package/dist/native/base64url.js.map +1 -1
  11. package/dist/native/base64url.test.js +1 -1
  12. package/dist/native/base64url.test.js.map +1 -1
  13. package/dist/native/coValue.js.map +1 -1
  14. package/dist/native/coValueCore.js +141 -149
  15. package/dist/native/coValueCore.js.map +1 -1
  16. package/dist/native/coValueState.js.map +1 -1
  17. package/dist/native/coValues/account.js +6 -6
  18. package/dist/native/coValues/account.js.map +1 -1
  19. package/dist/native/coValues/coList.js +2 -3
  20. package/dist/native/coValues/coList.js.map +1 -1
  21. package/dist/native/coValues/coMap.js +1 -1
  22. package/dist/native/coValues/coMap.js.map +1 -1
  23. package/dist/native/coValues/coStream.js +3 -5
  24. package/dist/native/coValues/coStream.js.map +1 -1
  25. package/dist/native/coValues/group.js +11 -11
  26. package/dist/native/coValues/group.js.map +1 -1
  27. package/dist/native/coreToCoValue.js +2 -2
  28. package/dist/native/coreToCoValue.js.map +1 -1
  29. package/dist/native/crypto/PureJSCrypto.js +4 -4
  30. package/dist/native/crypto/PureJSCrypto.js.map +1 -1
  31. package/dist/native/crypto/crypto.js.map +1 -1
  32. package/dist/native/exports.js +12 -12
  33. package/dist/native/exports.js.map +1 -1
  34. package/dist/native/ids.js.map +1 -1
  35. package/dist/native/jsonStringify.js.map +1 -1
  36. package/dist/native/localNode.js +5 -7
  37. package/dist/native/localNode.js.map +1 -1
  38. package/dist/native/permissions.js +4 -7
  39. package/dist/native/permissions.js.map +1 -1
  40. package/dist/native/priority.js.map +1 -1
  41. package/dist/native/storage/FileSystem.js.map +1 -1
  42. package/dist/native/storage/chunksAndKnownStates.js +2 -4
  43. package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
  44. package/dist/native/storage/index.js +6 -15
  45. package/dist/native/storage/index.js.map +1 -1
  46. package/dist/native/streamUtils.js.map +1 -1
  47. package/dist/native/sync.js +57 -7
  48. package/dist/native/sync.js.map +1 -1
  49. package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  50. package/dist/native/typeUtils/expectGroup.js.map +1 -1
  51. package/dist/native/typeUtils/isAccountID.js.map +1 -1
  52. package/dist/native/typeUtils/isCoValue.js +1 -1
  53. package/dist/native/typeUtils/isCoValue.js.map +1 -1
  54. package/dist/web/PeerKnownStates.js +6 -1
  55. package/dist/web/PeerKnownStates.js.map +1 -1
  56. package/dist/web/PeerState.js +4 -3
  57. package/dist/web/PeerState.js.map +1 -1
  58. package/dist/web/PriorityBasedMessageQueue.js +1 -10
  59. package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
  60. package/dist/web/SyncStateSubscriptionManager.js +70 -0
  61. package/dist/web/SyncStateSubscriptionManager.js.map +1 -0
  62. package/dist/web/base64url.js.map +1 -1
  63. package/dist/web/base64url.test.js +1 -1
  64. package/dist/web/base64url.test.js.map +1 -1
  65. package/dist/web/coValue.js.map +1 -1
  66. package/dist/web/coValueCore.js +141 -149
  67. package/dist/web/coValueCore.js.map +1 -1
  68. package/dist/web/coValueState.js.map +1 -1
  69. package/dist/web/coValues/account.js +6 -6
  70. package/dist/web/coValues/account.js.map +1 -1
  71. package/dist/web/coValues/coList.js +2 -3
  72. package/dist/web/coValues/coList.js.map +1 -1
  73. package/dist/web/coValues/coMap.js +1 -1
  74. package/dist/web/coValues/coMap.js.map +1 -1
  75. package/dist/web/coValues/coStream.js +3 -5
  76. package/dist/web/coValues/coStream.js.map +1 -1
  77. package/dist/web/coValues/group.js +11 -11
  78. package/dist/web/coValues/group.js.map +1 -1
  79. package/dist/web/coreToCoValue.js +2 -2
  80. package/dist/web/coreToCoValue.js.map +1 -1
  81. package/dist/web/crypto/PureJSCrypto.js +4 -4
  82. package/dist/web/crypto/PureJSCrypto.js.map +1 -1
  83. package/dist/web/crypto/WasmCrypto.js +5 -5
  84. package/dist/web/crypto/WasmCrypto.js.map +1 -1
  85. package/dist/web/crypto/crypto.js.map +1 -1
  86. package/dist/web/exports.js +12 -12
  87. package/dist/web/exports.js.map +1 -1
  88. package/dist/web/ids.js.map +1 -1
  89. package/dist/web/jsonStringify.js.map +1 -1
  90. package/dist/web/localNode.js +5 -7
  91. package/dist/web/localNode.js.map +1 -1
  92. package/dist/web/permissions.js +4 -7
  93. package/dist/web/permissions.js.map +1 -1
  94. package/dist/web/priority.js.map +1 -1
  95. package/dist/web/storage/FileSystem.js.map +1 -1
  96. package/dist/web/storage/chunksAndKnownStates.js +2 -4
  97. package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
  98. package/dist/web/storage/index.js +6 -15
  99. package/dist/web/storage/index.js.map +1 -1
  100. package/dist/web/streamUtils.js.map +1 -1
  101. package/dist/web/sync.js +57 -7
  102. package/dist/web/sync.js.map +1 -1
  103. package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  104. package/dist/web/typeUtils/expectGroup.js.map +1 -1
  105. package/dist/web/typeUtils/isAccountID.js.map +1 -1
  106. package/dist/web/typeUtils/isCoValue.js +1 -1
  107. package/dist/web/typeUtils/isCoValue.js.map +1 -1
  108. package/package.json +4 -14
  109. package/src/PeerKnownStates.ts +98 -90
  110. package/src/PeerState.ts +92 -73
  111. package/src/PriorityBasedMessageQueue.ts +42 -49
  112. package/src/SyncStateSubscriptionManager.ts +124 -0
  113. package/src/base64url.test.ts +24 -24
  114. package/src/base64url.ts +44 -45
  115. package/src/coValue.ts +45 -45
  116. package/src/coValueCore.ts +746 -785
  117. package/src/coValueState.ts +82 -72
  118. package/src/coValues/account.ts +143 -150
  119. package/src/coValues/coList.ts +520 -522
  120. package/src/coValues/coMap.ts +283 -285
  121. package/src/coValues/coStream.ts +320 -324
  122. package/src/coValues/group.ts +306 -305
  123. package/src/coreToCoValue.ts +28 -31
  124. package/src/crypto/PureJSCrypto.ts +188 -194
  125. package/src/crypto/WasmCrypto.ts +236 -254
  126. package/src/crypto/crypto.ts +302 -309
  127. package/src/exports.ts +116 -116
  128. package/src/ids.ts +9 -9
  129. package/src/jsonStringify.ts +46 -46
  130. package/src/jsonValue.ts +24 -10
  131. package/src/localNode.ts +635 -660
  132. package/src/media.ts +3 -3
  133. package/src/permissions.ts +272 -278
  134. package/src/priority.ts +21 -19
  135. package/src/storage/FileSystem.ts +91 -99
  136. package/src/storage/chunksAndKnownStates.ts +110 -115
  137. package/src/storage/index.ts +466 -497
  138. package/src/streamUtils.ts +60 -60
  139. package/src/sync.ts +656 -608
  140. package/src/tests/PeerKnownStates.test.ts +38 -34
  141. package/src/tests/PeerState.test.ts +101 -64
  142. package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
  143. package/src/tests/SyncStateSubscriptionManager.test.ts +232 -0
  144. package/src/tests/account.test.ts +59 -59
  145. package/src/tests/coList.test.ts +65 -65
  146. package/src/tests/coMap.test.ts +137 -137
  147. package/src/tests/coStream.test.ts +254 -257
  148. package/src/tests/coValueCore.test.ts +153 -156
  149. package/src/tests/crypto.test.ts +136 -144
  150. package/src/tests/cryptoImpl.test.ts +205 -197
  151. package/src/tests/group.test.ts +24 -24
  152. package/src/tests/permissions.test.ts +1306 -1371
  153. package/src/tests/priority.test.ts +65 -82
  154. package/src/tests/sync.test.ts +1573 -1263
  155. package/src/tests/testUtils.ts +85 -53
  156. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
  157. package/src/typeUtils/expectGroup.ts +9 -9
  158. package/src/typeUtils/isAccountID.ts +1 -1
  159. package/src/typeUtils/isCoValue.ts +9 -9
  160. package/tsconfig.json +4 -6
  161. package/tsconfig.native.json +9 -11
  162. package/tsconfig.web.json +4 -10
  163. package/.eslintrc.cjs +0 -25
  164. package/.prettierrc.js +0 -9
@@ -1,577 +1,575 @@
1
- import { JsonObject, JsonValue } from "../jsonValue.js";
2
1
  import { CoID, RawCoValue } from "../coValue.js";
3
- import { isCoValue } from "../typeUtils/isCoValue.js";
4
2
  import { CoValueCore } from "../coValueCore.js";
5
- import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
6
3
  import { AgentID, SessionID, TransactionID } from "../ids.js";
4
+ import { JsonObject, JsonValue } from "../jsonValue.js";
5
+ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
6
+ import { isCoValue } from "../typeUtils/isCoValue.js";
7
7
  import { RawAccountID } from "./account.js";
8
8
  import { RawGroup } from "./group.js";
9
9
 
10
10
  type OpID = TransactionID & { changeIdx: number };
11
11
 
12
12
  type InsertionOpPayload<T extends JsonValue> =
13
- | {
14
- op: "pre";
15
- value: T;
16
- before: OpID | "end";
17
- }
18
- | {
19
- op: "app";
20
- value: T;
21
- after: OpID | "start";
22
- };
13
+ | {
14
+ op: "pre";
15
+ value: T;
16
+ before: OpID | "end";
17
+ }
18
+ | {
19
+ op: "app";
20
+ value: T;
21
+ after: OpID | "start";
22
+ };
23
23
 
24
24
  type DeletionOpPayload = {
25
- op: "del";
26
- insertion: OpID;
25
+ op: "del";
26
+ insertion: OpID;
27
27
  };
28
28
 
29
29
  export type ListOpPayload<T extends JsonValue> =
30
- | InsertionOpPayload<T>
31
- | DeletionOpPayload;
30
+ | InsertionOpPayload<T>
31
+ | DeletionOpPayload;
32
32
 
33
33
  type InsertionEntry<T extends JsonValue> = {
34
- madeAt: number;
35
- predecessors: OpID[];
36
- successors: OpID[];
34
+ madeAt: number;
35
+ predecessors: OpID[];
36
+ successors: OpID[];
37
37
  } & InsertionOpPayload<T>;
38
38
 
39
39
  type DeletionEntry = {
40
- madeAt: number;
41
- deletionID: OpID;
40
+ madeAt: number;
41
+ deletionID: OpID;
42
42
  } & DeletionOpPayload;
43
43
 
44
44
  export class RawCoListView<
45
- Item extends JsonValue = JsonValue,
46
- Meta extends JsonObject | null = null,
45
+ Item extends JsonValue = JsonValue,
46
+ Meta extends JsonObject | null = null,
47
47
  > implements RawCoValue
48
48
  {
49
- /** @category 6. Meta */
50
- id: CoID<this>;
51
- /** @category 6. Meta */
52
- type = "colist" as const;
53
- /** @category 6. Meta */
54
- core: CoValueCore;
55
- /** @internal */
56
- afterStart: OpID[];
57
- /** @internal */
58
- beforeEnd: OpID[];
59
- /** @internal */
60
- insertions: {
61
- [sessionID: SessionID]: {
62
- [txIdx: number]: {
63
- [changeIdx: number]: InsertionEntry<Item>;
64
- };
65
- };
49
+ /** @category 6. Meta */
50
+ id: CoID<this>;
51
+ /** @category 6. Meta */
52
+ type = "colist" as const;
53
+ /** @category 6. Meta */
54
+ core: CoValueCore;
55
+ /** @internal */
56
+ afterStart: OpID[];
57
+ /** @internal */
58
+ beforeEnd: OpID[];
59
+ /** @internal */
60
+ insertions: {
61
+ [sessionID: SessionID]: {
62
+ [txIdx: number]: {
63
+ [changeIdx: number]: InsertionEntry<Item>;
64
+ };
66
65
  };
67
- /** @internal */
68
- deletionsByInsertion: {
69
- [deletedSessionID: SessionID]: {
70
- [deletedTxIdx: number]: {
71
- [deletedChangeIdx: number]: DeletionEntry[];
72
- };
73
- };
66
+ };
67
+ /** @internal */
68
+ deletionsByInsertion: {
69
+ [deletedSessionID: SessionID]: {
70
+ [deletedTxIdx: number]: {
71
+ [deletedChangeIdx: number]: DeletionEntry[];
72
+ };
74
73
  };
75
- /** @category 6. Meta */
76
- readonly _item!: Item;
74
+ };
75
+ /** @category 6. Meta */
76
+ readonly _item!: Item;
77
77
 
78
- /** @internal */
79
- _cachedEntries?: {
80
- value: Item;
81
- madeAt: number;
82
- opID: OpID;
83
- }[];
84
-
85
- /** @internal */
86
- constructor(core: CoValueCore) {
87
- this.id = core.id as CoID<this>;
88
- this.core = core;
89
- this.afterStart = [];
90
- this.beforeEnd = [];
91
- this.insertions = {};
92
- this.deletionsByInsertion = {};
93
-
94
- this.insertions = {};
95
- this.deletionsByInsertion = {};
96
- this.afterStart = [];
97
- this.beforeEnd = [];
98
-
99
- for (const {
100
- txID,
101
- changes,
78
+ /** @internal */
79
+ _cachedEntries?: {
80
+ value: Item;
81
+ madeAt: number;
82
+ opID: OpID;
83
+ }[];
84
+
85
+ /** @internal */
86
+ constructor(core: CoValueCore) {
87
+ this.id = core.id as CoID<this>;
88
+ this.core = core;
89
+ this.afterStart = [];
90
+ this.beforeEnd = [];
91
+ this.insertions = {};
92
+ this.deletionsByInsertion = {};
93
+
94
+ this.insertions = {};
95
+ this.deletionsByInsertion = {};
96
+ this.afterStart = [];
97
+ this.beforeEnd = [];
98
+
99
+ for (const {
100
+ txID,
101
+ changes,
102
+ madeAt,
103
+ } of this.core.getValidSortedTransactions()) {
104
+ for (const [changeIdx, changeUntyped] of changes.entries()) {
105
+ const change = changeUntyped as ListOpPayload<Item>;
106
+
107
+ if (change.op === "pre" || change.op === "app") {
108
+ let sessionEntry = this.insertions[txID.sessionID];
109
+ if (!sessionEntry) {
110
+ sessionEntry = {};
111
+ this.insertions[txID.sessionID] = sessionEntry;
112
+ }
113
+ let txEntry = sessionEntry[txID.txIndex];
114
+ if (!txEntry) {
115
+ txEntry = {};
116
+ sessionEntry[txID.txIndex] = txEntry;
117
+ }
118
+ txEntry[changeIdx] = {
102
119
  madeAt,
103
- } of this.core.getValidSortedTransactions()) {
104
- for (const [changeIdx, changeUntyped] of changes.entries()) {
105
- const change = changeUntyped as ListOpPayload<Item>;
106
-
107
- if (change.op === "pre" || change.op === "app") {
108
- let sessionEntry = this.insertions[txID.sessionID];
109
- if (!sessionEntry) {
110
- sessionEntry = {};
111
- this.insertions[txID.sessionID] = sessionEntry;
112
- }
113
- let txEntry = sessionEntry[txID.txIndex];
114
- if (!txEntry) {
115
- txEntry = {};
116
- sessionEntry[txID.txIndex] = txEntry;
117
- }
118
- txEntry[changeIdx] = {
119
- madeAt,
120
- predecessors: [],
121
- successors: [],
122
- ...change,
123
- };
124
- if (change.op === "pre") {
125
- if (change.before === "end") {
126
- this.beforeEnd.push({
127
- ...txID,
128
- changeIdx,
129
- });
130
- } else {
131
- const beforeEntry =
132
- this.insertions[change.before.sessionID]?.[
133
- change.before.txIndex
134
- ]?.[change.before.changeIdx];
135
- if (!beforeEntry) {
136
- // console.error(
137
- // "Insertion before missing op " +
138
- // change.before
139
- // );
140
- continue;
141
- }
142
- beforeEntry.predecessors.splice(0, 0, {
143
- ...txID,
144
- changeIdx,
145
- });
146
- }
147
- } else {
148
- if (change.after === "start") {
149
- this.afterStart.push({
150
- ...txID,
151
- changeIdx,
152
- });
153
- } else {
154
- const afterEntry =
155
- this.insertions[change.after.sessionID]?.[
156
- change.after.txIndex
157
- ]?.[change.after.changeIdx];
158
- if (!afterEntry) {
159
- // console.error(
160
- // "Insertion after missing op " + change.after
161
- // );
162
- continue;
163
- }
164
- afterEntry.successors.push({
165
- ...txID,
166
- changeIdx,
167
- });
168
- }
169
- }
170
- } else if (change.op === "del") {
171
- let sessionEntry =
172
- this.deletionsByInsertion[change.insertion.sessionID];
173
- if (!sessionEntry) {
174
- sessionEntry = {};
175
- this.deletionsByInsertion[change.insertion.sessionID] =
176
- sessionEntry;
177
- }
178
- let txEntry = sessionEntry[change.insertion.txIndex];
179
- if (!txEntry) {
180
- txEntry = {};
181
- sessionEntry[change.insertion.txIndex] = txEntry;
182
- }
183
- let changeEntry = txEntry[change.insertion.changeIdx];
184
- if (!changeEntry) {
185
- changeEntry = [];
186
- txEntry[change.insertion.changeIdx] = changeEntry;
187
- }
188
- changeEntry.push({
189
- madeAt,
190
- deletionID: {
191
- ...txID,
192
- changeIdx,
193
- },
194
- ...change,
195
- });
196
- } else {
197
- throw new Error(
198
- "Unknown list operation " +
199
- (change as { op: unknown }).op,
200
- );
201
- }
120
+ predecessors: [],
121
+ successors: [],
122
+ ...change,
123
+ };
124
+ if (change.op === "pre") {
125
+ if (change.before === "end") {
126
+ this.beforeEnd.push({
127
+ ...txID,
128
+ changeIdx,
129
+ });
130
+ } else {
131
+ const beforeEntry =
132
+ this.insertions[change.before.sessionID]?.[
133
+ change.before.txIndex
134
+ ]?.[change.before.changeIdx];
135
+ if (!beforeEntry) {
136
+ // console.error(
137
+ // "Insertion before missing op " +
138
+ // change.before
139
+ // );
140
+ continue;
141
+ }
142
+ beforeEntry.predecessors.splice(0, 0, {
143
+ ...txID,
144
+ changeIdx,
145
+ });
146
+ }
147
+ } else {
148
+ if (change.after === "start") {
149
+ this.afterStart.push({
150
+ ...txID,
151
+ changeIdx,
152
+ });
153
+ } else {
154
+ const afterEntry =
155
+ this.insertions[change.after.sessionID]?.[
156
+ change.after.txIndex
157
+ ]?.[change.after.changeIdx];
158
+ if (!afterEntry) {
159
+ // console.error(
160
+ // "Insertion after missing op " + change.after
161
+ // );
162
+ continue;
163
+ }
164
+ afterEntry.successors.push({
165
+ ...txID,
166
+ changeIdx,
167
+ });
202
168
  }
169
+ }
170
+ } else if (change.op === "del") {
171
+ let sessionEntry =
172
+ this.deletionsByInsertion[change.insertion.sessionID];
173
+ if (!sessionEntry) {
174
+ sessionEntry = {};
175
+ this.deletionsByInsertion[change.insertion.sessionID] =
176
+ sessionEntry;
177
+ }
178
+ let txEntry = sessionEntry[change.insertion.txIndex];
179
+ if (!txEntry) {
180
+ txEntry = {};
181
+ sessionEntry[change.insertion.txIndex] = txEntry;
182
+ }
183
+ let changeEntry = txEntry[change.insertion.changeIdx];
184
+ if (!changeEntry) {
185
+ changeEntry = [];
186
+ txEntry[change.insertion.changeIdx] = changeEntry;
187
+ }
188
+ changeEntry.push({
189
+ madeAt,
190
+ deletionID: {
191
+ ...txID,
192
+ changeIdx,
193
+ },
194
+ ...change,
195
+ });
196
+ } else {
197
+ throw new Error(
198
+ "Unknown list operation " + (change as { op: unknown }).op,
199
+ );
203
200
  }
201
+ }
204
202
  }
205
-
206
- /** @category 6. Meta */
207
- get headerMeta(): Meta {
208
- return this.core.header.meta as Meta;
203
+ }
204
+
205
+ /** @category 6. Meta */
206
+ get headerMeta(): Meta {
207
+ return this.core.header.meta as Meta;
208
+ }
209
+
210
+ /** @category 6. Meta */
211
+ get group(): RawGroup {
212
+ return this.core.getGroup();
213
+ }
214
+
215
+ /**
216
+ * Not yet implemented
217
+ *
218
+ * @category 4. Time travel
219
+ */
220
+ atTime(_time: number): this {
221
+ throw new Error("Not yet implemented");
222
+ }
223
+
224
+ /**
225
+ * Get the item currently at `idx`.
226
+ *
227
+ * @category 1. Reading
228
+ */
229
+ get(idx: number): Item | undefined {
230
+ const entry = this.entries()[idx];
231
+ if (!entry) {
232
+ return undefined;
209
233
  }
210
-
211
- /** @category 6. Meta */
212
- get group(): RawGroup {
213
- return this.core.getGroup();
234
+ return entry.value;
235
+ }
236
+
237
+ /**
238
+ * Returns the current items in the CoList as an array.
239
+ *
240
+ * @category 1. Reading
241
+ **/
242
+ asArray(): Item[] {
243
+ return this.entries().map((entry) => entry.value);
244
+ }
245
+
246
+ /** @internal */
247
+ entries(): {
248
+ value: Item;
249
+ madeAt: number;
250
+ opID: OpID;
251
+ }[] {
252
+ if (this._cachedEntries) {
253
+ return this._cachedEntries;
214
254
  }
215
-
216
- /**
217
- * Not yet implemented
218
- *
219
- * @category 4. Time travel
220
- */
221
- atTime(_time: number): this {
222
- throw new Error("Not yet implemented");
255
+ const arr = this.entriesUncached();
256
+ this._cachedEntries = arr;
257
+ return arr;
258
+ }
259
+
260
+ /** @internal */
261
+ entriesUncached(): {
262
+ value: Item;
263
+ madeAt: number;
264
+ opID: OpID;
265
+ }[] {
266
+ const arr: {
267
+ value: Item;
268
+ madeAt: number;
269
+ opID: OpID;
270
+ }[] = [];
271
+ for (const opID of this.afterStart) {
272
+ this.fillArrayFromOpID(opID, arr);
223
273
  }
224
-
225
- /**
226
- * Get the item currently at `idx`.
227
- *
228
- * @category 1. Reading
229
- */
230
- get(idx: number): Item | undefined {
231
- const entry = this.entries()[idx];
232
- if (!entry) {
233
- return undefined;
234
- }
235
- return entry.value;
274
+ for (const opID of this.beforeEnd) {
275
+ this.fillArrayFromOpID(opID, arr);
236
276
  }
237
-
238
- /**
239
- * Returns the current items in the CoList as an array.
240
- *
241
- * @category 1. Reading
242
- **/
243
- asArray(): Item[] {
244
- return this.entries().map((entry) => entry.value);
277
+ return arr;
278
+ }
279
+
280
+ /** @internal */
281
+ private fillArrayFromOpID(
282
+ opID: OpID,
283
+ arr: {
284
+ value: Item;
285
+ madeAt: number;
286
+ opID: OpID;
287
+ }[],
288
+ ) {
289
+ const entry =
290
+ this.insertions[opID.sessionID]?.[opID.txIndex]?.[opID.changeIdx];
291
+ if (!entry) {
292
+ throw new Error("Missing op " + opID);
245
293
  }
246
-
247
- /** @internal */
248
- entries(): {
249
- value: Item;
250
- madeAt: number;
251
- opID: OpID;
252
- }[] {
253
- if (this._cachedEntries) {
254
- return this._cachedEntries;
255
- }
256
- const arr = this.entriesUncached();
257
- this._cachedEntries = arr;
258
- return arr;
294
+ for (const predecessor of entry.predecessors) {
295
+ this.fillArrayFromOpID(predecessor, arr);
259
296
  }
260
-
261
- /** @internal */
262
- entriesUncached(): {
263
- value: Item;
264
- madeAt: number;
265
- opID: OpID;
266
- }[] {
267
- const arr: {
268
- value: Item;
269
- madeAt: number;
270
- opID: OpID;
271
- }[] = [];
272
- for (const opID of this.afterStart) {
273
- this.fillArrayFromOpID(opID, arr);
274
- }
275
- for (const opID of this.beforeEnd) {
276
- this.fillArrayFromOpID(opID, arr);
277
- }
278
- return arr;
297
+ const deleted =
298
+ (this.deletionsByInsertion[opID.sessionID]?.[opID.txIndex]?.[
299
+ opID.changeIdx
300
+ ]?.length || 0) > 0;
301
+ if (!deleted) {
302
+ arr.push({
303
+ value: entry.value,
304
+ madeAt: entry.madeAt,
305
+ opID,
306
+ });
279
307
  }
280
-
281
- /** @internal */
282
- private fillArrayFromOpID(
283
- opID: OpID,
284
- arr: {
285
- value: Item;
286
- madeAt: number;
287
- opID: OpID;
288
- }[],
289
- ) {
290
- const entry =
291
- this.insertions[opID.sessionID]?.[opID.txIndex]?.[opID.changeIdx];
292
- if (!entry) {
293
- throw new Error("Missing op " + opID);
294
- }
295
- for (const predecessor of entry.predecessors) {
296
- this.fillArrayFromOpID(predecessor, arr);
297
- }
298
- const deleted =
299
- (this.deletionsByInsertion[opID.sessionID]?.[opID.txIndex]?.[
300
- opID.changeIdx
301
- ]?.length || 0) > 0;
302
- if (!deleted) {
303
- arr.push({
304
- value: entry.value,
305
- madeAt: entry.madeAt,
306
- opID,
307
- });
308
- }
309
- // traverse successors in reverse for correct insertion behavior
310
- for (let i = entry.successors.length - 1; i >= 0; i--) {
311
- const successor = entry.successors[i]!;
312
- this.fillArrayFromOpID(successor, arr);
313
- }
308
+ // traverse successors in reverse for correct insertion behavior
309
+ for (let i = entry.successors.length - 1; i >= 0; i--) {
310
+ const successor = entry.successors[i]!;
311
+ this.fillArrayFromOpID(successor, arr);
314
312
  }
315
-
316
- /**
317
- * Returns the current items in the CoList as an array. (alias of `asArray`)
318
- *
319
- * @category 1. Reading
320
- */
321
- toJSON(): Item[] {
322
- return this.asArray();
323
- }
324
-
325
- /** @category 5. Edit history */
326
- editAt(idx: number):
327
- | {
328
- by: RawAccountID | AgentID;
329
- tx: TransactionID;
330
- at: Date;
331
- value: Item;
332
- }
333
- | undefined {
334
- const entry = this.entries()[idx];
335
- if (!entry) {
336
- return undefined;
337
- }
338
- const madeAt = new Date(entry.madeAt);
339
- const by = accountOrAgentIDfromSessionID(entry.opID.sessionID);
340
- const value = entry.value;
341
- return {
342
- by,
343
- tx: {
344
- sessionID: entry.opID.sessionID,
345
- txIndex: entry.opID.txIndex,
346
- },
347
- at: madeAt,
348
- value,
349
- };
350
- }
351
-
352
- /** @category 5. Edit history */
353
- deletionEdits(): {
313
+ }
314
+
315
+ /**
316
+ * Returns the current items in the CoList as an array. (alias of `asArray`)
317
+ *
318
+ * @category 1. Reading
319
+ */
320
+ toJSON(): Item[] {
321
+ return this.asArray();
322
+ }
323
+
324
+ /** @category 5. Edit history */
325
+ editAt(idx: number):
326
+ | {
354
327
  by: RawAccountID | AgentID;
355
328
  tx: TransactionID;
356
329
  at: Date;
357
- // TODO: add indices that are now before and after the deleted item
358
- }[] {
359
- const edits: {
360
- by: RawAccountID | AgentID;
361
- tx: TransactionID;
362
- at: Date;
363
- }[] = [];
364
-
365
- for (const sessionID in this.deletionsByInsertion) {
366
- const sessionEntry =
367
- this.deletionsByInsertion[sessionID as SessionID];
368
- for (const txIdx in sessionEntry) {
369
- const txEntry = sessionEntry[Number(txIdx)];
370
- for (const changeIdx in txEntry) {
371
- const changeEntry = txEntry[Number(changeIdx)];
372
- for (const deletion of changeEntry || []) {
373
- const madeAt = new Date(deletion.madeAt);
374
- const by = accountOrAgentIDfromSessionID(
375
- deletion.deletionID.sessionID,
376
- );
377
- edits.push({
378
- by,
379
- tx: deletion.deletionID,
380
- at: madeAt,
381
- });
382
- }
383
- }
384
- }
330
+ value: Item;
331
+ }
332
+ | undefined {
333
+ const entry = this.entries()[idx];
334
+ if (!entry) {
335
+ return undefined;
336
+ }
337
+ const madeAt = new Date(entry.madeAt);
338
+ const by = accountOrAgentIDfromSessionID(entry.opID.sessionID);
339
+ const value = entry.value;
340
+ return {
341
+ by,
342
+ tx: {
343
+ sessionID: entry.opID.sessionID,
344
+ txIndex: entry.opID.txIndex,
345
+ },
346
+ at: madeAt,
347
+ value,
348
+ };
349
+ }
350
+
351
+ /** @category 5. Edit history */
352
+ deletionEdits(): {
353
+ by: RawAccountID | AgentID;
354
+ tx: TransactionID;
355
+ at: Date;
356
+ // TODO: add indices that are now before and after the deleted item
357
+ }[] {
358
+ const edits: {
359
+ by: RawAccountID | AgentID;
360
+ tx: TransactionID;
361
+ at: Date;
362
+ }[] = [];
363
+
364
+ for (const sessionID in this.deletionsByInsertion) {
365
+ const sessionEntry = this.deletionsByInsertion[sessionID as SessionID];
366
+ for (const txIdx in sessionEntry) {
367
+ const txEntry = sessionEntry[Number(txIdx)];
368
+ for (const changeIdx in txEntry) {
369
+ const changeEntry = txEntry[Number(changeIdx)];
370
+ for (const deletion of changeEntry || []) {
371
+ const madeAt = new Date(deletion.madeAt);
372
+ const by = accountOrAgentIDfromSessionID(
373
+ deletion.deletionID.sessionID,
374
+ );
375
+ edits.push({
376
+ by,
377
+ tx: deletion.deletionID,
378
+ at: madeAt,
379
+ });
380
+ }
385
381
  }
386
-
387
- return edits;
382
+ }
388
383
  }
389
384
 
390
- /** @category 3. Subscription */
391
- subscribe(listener: (coList: this) => void): () => void {
392
- return this.core.subscribe((content) => {
393
- listener(content as this);
394
- });
395
- }
385
+ return edits;
386
+ }
387
+
388
+ /** @category 3. Subscription */
389
+ subscribe(listener: (coList: this) => void): () => void {
390
+ return this.core.subscribe((content) => {
391
+ listener(content as this);
392
+ });
393
+ }
396
394
  }
397
395
 
398
396
  export class RawCoList<
399
- Item extends JsonValue = JsonValue,
400
- Meta extends JsonObject | null = JsonObject | null,
401
- >
402
- extends RawCoListView<Item, Meta>
403
- implements RawCoValue
397
+ Item extends JsonValue = JsonValue,
398
+ Meta extends JsonObject | null = JsonObject | null,
399
+ >
400
+ extends RawCoListView<Item, Meta>
401
+ implements RawCoValue
404
402
  {
405
- /** Appends `item` after the item currently at index `after`.
406
- *
407
- * 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.
408
- *
409
- * If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
410
- *
411
- * @category 2. Editing
412
- **/
413
- append(
414
- item: Item,
415
- after?: number,
416
- privacy: "private" | "trusting" = "private",
417
- ) {
418
- const entries = this.entries();
419
- after =
420
- after === undefined
421
- ? entries.length > 0
422
- ? entries.length - 1
423
- : 0
424
- : after;
425
- let opIDBefore;
426
- if (entries.length > 0) {
427
- const entryBefore = entries[after];
428
- if (!entryBefore) {
429
- throw new Error("Invalid index " + after);
430
- }
431
- opIDBefore = entryBefore.opID;
432
- } else {
433
- if (after !== 0) {
434
- throw new Error("Invalid index " + after);
435
- }
436
- opIDBefore = "start";
437
- }
438
- this.core.makeTransaction(
439
- [
440
- {
441
- op: "app",
442
- value: isCoValue(item) ? item.id : item,
443
- after: opIDBefore,
444
- },
445
- ],
446
- privacy,
447
- );
448
-
449
- const listAfter = new RawCoList(this.core) as this;
450
-
451
- this.afterStart = listAfter.afterStart;
452
- this.beforeEnd = listAfter.beforeEnd;
453
- this.insertions = listAfter.insertions;
454
- this.deletionsByInsertion = listAfter.deletionsByInsertion;
455
- this._cachedEntries = undefined;
403
+ /** Appends `item` after the item currently at index `after`.
404
+ *
405
+ * 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.
406
+ *
407
+ * If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
408
+ *
409
+ * @category 2. Editing
410
+ **/
411
+ append(
412
+ item: Item,
413
+ after?: number,
414
+ privacy: "private" | "trusting" = "private",
415
+ ) {
416
+ const entries = this.entries();
417
+ after =
418
+ after === undefined
419
+ ? entries.length > 0
420
+ ? entries.length - 1
421
+ : 0
422
+ : after;
423
+ let opIDBefore;
424
+ if (entries.length > 0) {
425
+ const entryBefore = entries[after];
426
+ if (!entryBefore) {
427
+ throw new Error("Invalid index " + after);
428
+ }
429
+ opIDBefore = entryBefore.opID;
430
+ } else {
431
+ if (after !== 0) {
432
+ throw new Error("Invalid index " + after);
433
+ }
434
+ opIDBefore = "start";
456
435
  }
457
-
458
- /**
459
- * Prepends `item` before the item currently at index `before`.
460
- *
461
- * 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.
462
- *
463
- * If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
464
- *
465
- * @category 2. Editing
466
- */
467
- prepend(
468
- item: Item,
469
- before?: number,
470
- privacy: "private" | "trusting" = "private",
471
- ) {
472
- const entries = this.entries();
473
- before = before === undefined ? 0 : before;
474
- let opIDAfter;
475
- if (entries.length > 0) {
476
- const entryAfter = entries[before];
477
- if (entryAfter) {
478
- opIDAfter = entryAfter.opID;
479
- } else {
480
- if (before !== entries.length) {
481
- throw new Error("Invalid index " + before);
482
- }
483
- opIDAfter = "end";
484
- }
485
- } else {
486
- if (before !== 0) {
487
- throw new Error("Invalid index " + before);
488
- }
489
- opIDAfter = "end";
436
+ this.core.makeTransaction(
437
+ [
438
+ {
439
+ op: "app",
440
+ value: isCoValue(item) ? item.id : item,
441
+ after: opIDBefore,
442
+ },
443
+ ],
444
+ privacy,
445
+ );
446
+
447
+ const listAfter = new RawCoList(this.core) as this;
448
+
449
+ this.afterStart = listAfter.afterStart;
450
+ this.beforeEnd = listAfter.beforeEnd;
451
+ this.insertions = listAfter.insertions;
452
+ this.deletionsByInsertion = listAfter.deletionsByInsertion;
453
+ this._cachedEntries = undefined;
454
+ }
455
+
456
+ /**
457
+ * Prepends `item` before the item currently at index `before`.
458
+ *
459
+ * 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.
460
+ *
461
+ * If `privacy` is `"trusting"`, `item` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
462
+ *
463
+ * @category 2. Editing
464
+ */
465
+ prepend(
466
+ item: Item,
467
+ before?: number,
468
+ privacy: "private" | "trusting" = "private",
469
+ ) {
470
+ const entries = this.entries();
471
+ before = before === undefined ? 0 : before;
472
+ let opIDAfter;
473
+ if (entries.length > 0) {
474
+ const entryAfter = entries[before];
475
+ if (entryAfter) {
476
+ opIDAfter = entryAfter.opID;
477
+ } else {
478
+ if (before !== entries.length) {
479
+ throw new Error("Invalid index " + before);
490
480
  }
491
- this.core.makeTransaction(
492
- [
493
- {
494
- op: "pre",
495
- value: isCoValue(item) ? item.id : item,
496
- before: opIDAfter,
497
- },
498
- ],
499
- privacy,
500
- );
501
-
502
- const listAfter = new RawCoList(this.core) as this;
503
-
504
- this.afterStart = listAfter.afterStart;
505
- this.beforeEnd = listAfter.beforeEnd;
506
- this.insertions = listAfter.insertions;
507
- this.deletionsByInsertion = listAfter.deletionsByInsertion;
508
- this._cachedEntries = undefined;
481
+ opIDAfter = "end";
482
+ }
483
+ } else {
484
+ if (before !== 0) {
485
+ throw new Error("Invalid index " + before);
486
+ }
487
+ opIDAfter = "end";
509
488
  }
510
-
511
- /** Deletes the item at index `at`.
512
- *
513
- * 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.
514
- *
515
- * 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.
516
- *
517
- * @category 2. Editing
518
- **/
519
- delete(at: number, privacy: "private" | "trusting" = "private") {
520
- const entries = this.entries();
521
- const entry = entries[at];
522
- if (!entry) {
523
- throw new Error("Invalid index " + at);
524
- }
525
- this.core.makeTransaction(
526
- [
527
- {
528
- op: "del",
529
- insertion: entry.opID,
530
- },
531
- ],
532
- privacy,
533
- );
534
-
535
- const listAfter = new RawCoList(this.core) as this;
536
-
537
- this.afterStart = listAfter.afterStart;
538
- this.beforeEnd = listAfter.beforeEnd;
539
- this.insertions = listAfter.insertions;
540
- this.deletionsByInsertion = listAfter.deletionsByInsertion;
541
- this._cachedEntries = undefined;
489
+ this.core.makeTransaction(
490
+ [
491
+ {
492
+ op: "pre",
493
+ value: isCoValue(item) ? item.id : item,
494
+ before: opIDAfter,
495
+ },
496
+ ],
497
+ privacy,
498
+ );
499
+
500
+ const listAfter = new RawCoList(this.core) as this;
501
+
502
+ this.afterStart = listAfter.afterStart;
503
+ this.beforeEnd = listAfter.beforeEnd;
504
+ this.insertions = listAfter.insertions;
505
+ this.deletionsByInsertion = listAfter.deletionsByInsertion;
506
+ this._cachedEntries = undefined;
507
+ }
508
+
509
+ /** Deletes the item at index `at`.
510
+ *
511
+ * 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.
512
+ *
513
+ * 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.
514
+ *
515
+ * @category 2. Editing
516
+ **/
517
+ delete(at: number, privacy: "private" | "trusting" = "private") {
518
+ const entries = this.entries();
519
+ const entry = entries[at];
520
+ if (!entry) {
521
+ throw new Error("Invalid index " + at);
542
522
  }
543
-
544
- replace(
545
- at: number,
546
- newItem: Item,
547
- privacy: "private" | "trusting" = "private",
548
- ) {
549
- const entries = this.entries();
550
- const entry = entries[at];
551
- if (!entry) {
552
- throw new Error("Invalid index " + at);
553
- }
554
-
555
- this.core.makeTransaction(
556
- [
557
- {
558
- op: "app",
559
- value: isCoValue(newItem) ? newItem.id : newItem,
560
- after: entry.opID,
561
- },
562
- {
563
- op: "del",
564
- insertion: entry.opID,
565
- },
566
- ],
567
- privacy,
568
- );
569
- const listAfter = new RawCoList(this.core) as this;
570
-
571
- this.afterStart = listAfter.afterStart;
572
- this.beforeEnd = listAfter.beforeEnd;
573
- this.insertions = listAfter.insertions;
574
- this.deletionsByInsertion = listAfter.deletionsByInsertion;
575
- this._cachedEntries = undefined;
523
+ this.core.makeTransaction(
524
+ [
525
+ {
526
+ op: "del",
527
+ insertion: entry.opID,
528
+ },
529
+ ],
530
+ privacy,
531
+ );
532
+
533
+ const listAfter = new RawCoList(this.core) as this;
534
+
535
+ this.afterStart = listAfter.afterStart;
536
+ this.beforeEnd = listAfter.beforeEnd;
537
+ this.insertions = listAfter.insertions;
538
+ this.deletionsByInsertion = listAfter.deletionsByInsertion;
539
+ this._cachedEntries = undefined;
540
+ }
541
+
542
+ replace(
543
+ at: number,
544
+ newItem: Item,
545
+ privacy: "private" | "trusting" = "private",
546
+ ) {
547
+ const entries = this.entries();
548
+ const entry = entries[at];
549
+ if (!entry) {
550
+ throw new Error("Invalid index " + at);
576
551
  }
552
+
553
+ this.core.makeTransaction(
554
+ [
555
+ {
556
+ op: "app",
557
+ value: isCoValue(newItem) ? newItem.id : newItem,
558
+ after: entry.opID,
559
+ },
560
+ {
561
+ op: "del",
562
+ insertion: entry.opID,
563
+ },
564
+ ],
565
+ privacy,
566
+ );
567
+ const listAfter = new RawCoList(this.core) as this;
568
+
569
+ this.afterStart = listAfter.afterStart;
570
+ this.beforeEnd = listAfter.beforeEnd;
571
+ this.insertions = listAfter.insertions;
572
+ this.deletionsByInsertion = listAfter.deletionsByInsertion;
573
+ this._cachedEntries = undefined;
574
+ }
577
575
  }