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,327 +1,325 @@
1
- import { JsonObject, JsonValue } from "../jsonValue.js";
2
- import { AgentID, TransactionID } from "../ids.js";
3
1
  import { CoID, RawCoValue } from "../coValue.js";
4
- import { isCoValue } from "../typeUtils/isCoValue.js";
5
2
  import { CoValueCore } from "../coValueCore.js";
3
+ import { AgentID, TransactionID } from "../ids.js";
4
+ import { JsonObject, JsonValue } from "../jsonValue.js";
6
5
  import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
6
+ import { isCoValue } from "../typeUtils/isCoValue.js";
7
7
  import { RawAccountID } from "./account.js";
8
8
  import type { RawGroup } from "./group.js";
9
9
 
10
10
  type MapOp<K extends string, V extends JsonValue | undefined> = {
11
- txID: TransactionID;
12
- madeAt: number;
13
- changeIdx: number;
11
+ txID: TransactionID;
12
+ madeAt: number;
13
+ changeIdx: number;
14
14
  } & MapOpPayload<K, V>;
15
15
  // TODO: add after TransactionID[] for conflicts/ordering
16
16
 
17
17
  export type MapOpPayload<K extends string, V extends JsonValue | undefined> =
18
- | {
19
- op: "set";
20
- key: K;
21
- value: V;
22
- }
23
- | {
24
- op: "del";
25
- key: K;
26
- };
18
+ | {
19
+ op: "set";
20
+ key: K;
21
+ value: V;
22
+ }
23
+ | {
24
+ op: "del";
25
+ key: K;
26
+ };
27
27
 
28
28
  export class RawCoMapView<
29
- Shape extends { [key: string]: JsonValue | undefined } = {
30
- [key: string]: JsonValue | undefined;
31
- },
32
- Meta extends JsonObject | null = JsonObject | null,
29
+ Shape extends { [key: string]: JsonValue | undefined } = {
30
+ [key: string]: JsonValue | undefined;
31
+ },
32
+ Meta extends JsonObject | null = JsonObject | null,
33
33
  > implements RawCoValue
34
34
  {
35
- /** @category 6. Meta */
36
- id: CoID<this>;
37
- /** @category 6. Meta */
38
- type = "comap" as const;
39
- /** @category 6. Meta */
40
- core: CoValueCore;
41
- /** @internal */
42
- ops: {
43
- [Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
44
- };
45
- /** @internal */
46
- atTimeFilter?: number = undefined;
47
- /** @category 6. Meta */
48
- readonly _shape!: Shape;
49
-
50
- /** @internal */
51
- constructor(
52
- core: CoValueCore,
53
- options?: { ignorePrivateTransactions: true },
54
- ) {
55
- this.id = core.id as CoID<this>;
56
- this.core = core;
57
- this.ops = {};
58
-
59
- for (const { txID, changes, madeAt } of core.getValidSortedTransactions(
60
- options,
61
- )) {
62
- for (const [changeIdx, changeUntyped] of changes.entries()) {
63
- const change = changeUntyped as MapOpPayload<
64
- keyof Shape & string,
65
- Shape[keyof Shape & string]
66
- >;
67
- let entries = this.ops[change.key];
68
- if (!entries) {
69
- entries = [];
70
- this.ops[change.key] = entries;
71
- }
72
- entries.push({
73
- txID,
74
- madeAt,
75
- changeIdx,
76
- ...(change as MapOpPayload<
77
- keyof Shape & string,
78
- Shape[keyof Shape & string]
79
- >),
80
- });
81
- }
35
+ /** @category 6. Meta */
36
+ id: CoID<this>;
37
+ /** @category 6. Meta */
38
+ type = "comap" as const;
39
+ /** @category 6. Meta */
40
+ core: CoValueCore;
41
+ /** @internal */
42
+ ops: {
43
+ [Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
44
+ };
45
+ /** @internal */
46
+ atTimeFilter?: number = undefined;
47
+ /** @category 6. Meta */
48
+ readonly _shape!: Shape;
49
+
50
+ /** @internal */
51
+ constructor(
52
+ core: CoValueCore,
53
+ options?: { ignorePrivateTransactions: true },
54
+ ) {
55
+ this.id = core.id as CoID<this>;
56
+ this.core = core;
57
+ this.ops = {};
58
+
59
+ for (const { txID, changes, madeAt } of core.getValidSortedTransactions(
60
+ options,
61
+ )) {
62
+ for (const [changeIdx, changeUntyped] of changes.entries()) {
63
+ const change = changeUntyped as MapOpPayload<
64
+ keyof Shape & string,
65
+ Shape[keyof Shape & string]
66
+ >;
67
+ let entries = this.ops[change.key];
68
+ if (!entries) {
69
+ entries = [];
70
+ this.ops[change.key] = entries;
82
71
  }
72
+ entries.push({
73
+ txID,
74
+ madeAt,
75
+ changeIdx,
76
+ ...(change as MapOpPayload<
77
+ keyof Shape & string,
78
+ Shape[keyof Shape & string]
79
+ >),
80
+ });
81
+ }
83
82
  }
84
-
85
- /** @category 6. Meta */
86
- get headerMeta(): Meta {
87
- return this.core.header.meta as Meta;
88
- }
89
-
90
- /** @category 6. Meta */
91
- get group(): RawGroup {
92
- return this.core.getGroup();
83
+ }
84
+
85
+ /** @category 6. Meta */
86
+ get headerMeta(): Meta {
87
+ return this.core.header.meta as Meta;
88
+ }
89
+
90
+ /** @category 6. Meta */
91
+ get group(): RawGroup {
92
+ return this.core.getGroup();
93
+ }
94
+
95
+ /** @category 4. Time travel */
96
+ atTime(time: number): this {
97
+ const clone = Object.create(this) as this;
98
+ clone.id = this.id;
99
+ clone.type = this.type;
100
+ clone.core = this.core;
101
+ clone.ops = this.ops;
102
+ clone.atTimeFilter = time;
103
+ return clone;
104
+ }
105
+
106
+ /** @internal */
107
+ timeFilteredOps<K extends keyof Shape & string>(
108
+ key: K,
109
+ ): MapOp<K, Shape[K]>[] | undefined {
110
+ if (this.atTimeFilter) {
111
+ return this.ops[key]?.filter((op) => op.madeAt <= this.atTimeFilter!);
112
+ } else {
113
+ return this.ops[key];
93
114
  }
115
+ }
116
+
117
+ /**
118
+ * Get all keys currently in the map.
119
+ *
120
+ * @category 1. Reading */
121
+ keys<K extends keyof Shape & string = keyof Shape & string>(): K[] {
122
+ return (Object.keys(this.ops) as K[]).filter((key) => {
123
+ const ops = this.ops[key];
124
+ if (!ops) {
125
+ return undefined;
126
+ }
94
127
 
95
- /** @category 4. Time travel */
96
- atTime(time: number): this {
97
- const clone = Object.create(this) as this;
98
- clone.id = this.id;
99
- clone.type = this.type;
100
- clone.core = this.core;
101
- clone.ops = this.ops;
102
- clone.atTimeFilter = time;
103
- return clone;
104
- }
128
+ const includeUntil = this.atTimeFilter;
129
+ const lastEntry = includeUntil
130
+ ? ops.findLast((entry) => entry.madeAt <= includeUntil)
131
+ : ops[ops.length - 1]!;
105
132
 
106
- /** @internal */
107
- timeFilteredOps<K extends keyof Shape & string>(
108
- key: K,
109
- ): MapOp<K, Shape[K]>[] | undefined {
110
- if (this.atTimeFilter) {
111
- return this.ops[key]?.filter(
112
- (op) => op.madeAt <= this.atTimeFilter!,
113
- );
114
- } else {
115
- return this.ops[key];
116
- }
133
+ if (lastEntry?.op === "del") {
134
+ return false;
135
+ } else {
136
+ return true;
137
+ }
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Returns the current value for the given key.
143
+ *
144
+ * @category 1. Reading
145
+ **/
146
+ get<K extends keyof Shape & string>(key: K): Shape[K] | undefined {
147
+ const ops = this.ops[key];
148
+ if (!ops) {
149
+ return undefined;
117
150
  }
118
151
 
119
- /**
120
- * Get all keys currently in the map.
121
- *
122
- * @category 1. Reading */
123
- keys<K extends keyof Shape & string = keyof Shape & string>(): K[] {
124
- return (Object.keys(this.ops) as K[]).filter((key) => {
125
- const ops = this.ops[key];
126
- if (!ops) {
127
- return undefined;
128
- }
129
-
130
- const includeUntil = this.atTimeFilter;
131
- const lastEntry = includeUntil
132
- ? ops.findLast((entry) => entry.madeAt <= includeUntil)
133
- : ops[ops.length - 1]!;
134
-
135
- if (lastEntry?.op === "del") {
136
- return false;
137
- } else {
138
- return true;
139
- }
140
- });
141
- }
152
+ const includeUntil = this.atTimeFilter;
153
+ const lastEntry = includeUntil
154
+ ? ops.findLast((entry) => entry.madeAt <= includeUntil)
155
+ : ops[ops.length - 1]!;
142
156
 
143
- /**
144
- * Returns the current value for the given key.
145
- *
146
- * @category 1. Reading
147
- **/
148
- get<K extends keyof Shape & string>(key: K): Shape[K] | undefined {
149
- const ops = this.ops[key];
150
- if (!ops) {
151
- return undefined;
152
- }
153
-
154
- const includeUntil = this.atTimeFilter;
155
- const lastEntry = includeUntil
156
- ? ops.findLast((entry) => entry.madeAt <= includeUntil)
157
- : ops[ops.length - 1]!;
158
-
159
- if (lastEntry?.op === "del") {
160
- return undefined;
161
- } else {
162
- return lastEntry?.value;
163
- }
157
+ if (lastEntry?.op === "del") {
158
+ return undefined;
159
+ } else {
160
+ return lastEntry?.value;
164
161
  }
165
-
166
- /** @category 1. Reading */
167
- asObject(): {
168
- [K in keyof Shape & string]: Shape[K];
169
- } {
170
- const object: Partial<{
171
- [K in keyof Shape & string]: Shape[K];
172
- }> = {};
173
-
174
- for (const key of this.keys()) {
175
- const value = this.get(key);
176
- if (value !== undefined) {
177
- object[key] = value;
178
- }
179
- }
180
-
181
- return object as {
182
- [K in keyof Shape & string]: Shape[K];
183
- };
162
+ }
163
+
164
+ /** @category 1. Reading */
165
+ asObject(): {
166
+ [K in keyof Shape & string]: Shape[K];
167
+ } {
168
+ const object: Partial<{
169
+ [K in keyof Shape & string]: Shape[K];
170
+ }> = {};
171
+
172
+ for (const key of this.keys()) {
173
+ const value = this.get(key);
174
+ if (value !== undefined) {
175
+ object[key] = value;
176
+ }
184
177
  }
185
178
 
186
- /** @category 1. Reading */
187
- toJSON(): {
188
- [K in keyof Shape & string]: Shape[K];
189
- } {
190
- return this.asObject();
179
+ return object as {
180
+ [K in keyof Shape & string]: Shape[K];
181
+ };
182
+ }
183
+
184
+ /** @category 1. Reading */
185
+ toJSON(): {
186
+ [K in keyof Shape & string]: Shape[K];
187
+ } {
188
+ return this.asObject();
189
+ }
190
+
191
+ /** @category 5. Edit history */
192
+ nthEditAt<K extends keyof Shape & string>(
193
+ key: K,
194
+ n: number,
195
+ ):
196
+ | {
197
+ by: RawAccountID | AgentID;
198
+ tx: TransactionID;
199
+ at: Date;
200
+ value?: Shape[K];
201
+ }
202
+ | undefined {
203
+ const ops = this.timeFilteredOps(key);
204
+ if (!ops || ops.length <= n) {
205
+ return undefined;
191
206
  }
192
207
 
193
- /** @category 5. Edit history */
194
- nthEditAt<K extends keyof Shape & string>(
195
- key: K,
196
- n: number,
197
- ):
198
- | {
199
- by: RawAccountID | AgentID;
200
- tx: TransactionID;
201
- at: Date;
202
- value?: Shape[K];
203
- }
204
- | undefined {
205
- const ops = this.timeFilteredOps(key);
206
- if (!ops || ops.length <= n) {
207
- return undefined;
208
- }
208
+ const entry = ops[n]!;
209
209
 
210
- const entry = ops[n]!;
211
-
212
- if (this.atTimeFilter && entry.madeAt > this.atTimeFilter) {
213
- return undefined;
214
- }
215
-
216
- return {
217
- by: accountOrAgentIDfromSessionID(entry.txID.sessionID),
218
- tx: entry.txID,
219
- at: new Date(entry.madeAt),
220
- value: entry.op === "del" ? undefined : entry.value,
221
- };
210
+ if (this.atTimeFilter && entry.madeAt > this.atTimeFilter) {
211
+ return undefined;
222
212
  }
223
213
 
224
- /** @category 5. Edit history */
225
- lastEditAt<K extends keyof Shape & string>(
226
- key: K,
227
- ):
228
- | {
229
- by: RawAccountID | AgentID;
230
- tx: TransactionID;
231
- at: Date;
232
- value?: Shape[K];
233
- }
234
- | undefined {
235
- const ops = this.timeFilteredOps(key);
236
- if (!ops || ops.length === 0) {
237
- return undefined;
238
- }
239
- return this.nthEditAt(key, ops.length - 1);
240
- }
241
-
242
- /** @category 5. Edit history */
243
- *editsAt<K extends keyof Shape & string>(key: K) {
244
- const ops = this.timeFilteredOps(key);
245
- if (!ops) {
246
- return;
247
- }
214
+ return {
215
+ by: accountOrAgentIDfromSessionID(entry.txID.sessionID),
216
+ tx: entry.txID,
217
+ at: new Date(entry.madeAt),
218
+ value: entry.op === "del" ? undefined : entry.value,
219
+ };
220
+ }
248
221
 
249
- for (let i = 0; i < ops.length; i++) {
250
- yield this.nthEditAt(key, i)!;
251
- }
222
+ /** @category 5. Edit history */
223
+ lastEditAt<K extends keyof Shape & string>(
224
+ key: K,
225
+ ):
226
+ | {
227
+ by: RawAccountID | AgentID;
228
+ tx: TransactionID;
229
+ at: Date;
230
+ value?: Shape[K];
231
+ }
232
+ | undefined {
233
+ const ops = this.timeFilteredOps(key);
234
+ if (!ops || ops.length === 0) {
235
+ return undefined;
236
+ }
237
+ return this.nthEditAt(key, ops.length - 1);
238
+ }
239
+
240
+ /** @category 5. Edit history */
241
+ *editsAt<K extends keyof Shape & string>(key: K) {
242
+ const ops = this.timeFilteredOps(key);
243
+ if (!ops) {
244
+ return;
252
245
  }
253
246
 
254
- /** @category 3. Subscription */
255
- subscribe(listener: (coMap: this) => void): () => void {
256
- return this.core.subscribe((content) => {
257
- listener(content as this);
258
- });
247
+ for (let i = 0; i < ops.length; i++) {
248
+ yield this.nthEditAt(key, i)!;
259
249
  }
250
+ }
251
+
252
+ /** @category 3. Subscription */
253
+ subscribe(listener: (coMap: this) => void): () => void {
254
+ return this.core.subscribe((content) => {
255
+ listener(content as this);
256
+ });
257
+ }
260
258
  }
261
259
 
262
260
  /** A collaborative map with precise shape `Shape` and optional static metadata `Meta` */
263
261
  export class RawCoMap<
264
- Shape extends { [key: string]: JsonValue | undefined } = {
265
- [key: string]: JsonValue | undefined;
266
- },
267
- Meta extends JsonObject | null = JsonObject | null,
268
- >
269
- extends RawCoMapView<Shape, Meta>
270
- implements RawCoValue
262
+ Shape extends { [key: string]: JsonValue | undefined } = {
263
+ [key: string]: JsonValue | undefined;
264
+ },
265
+ Meta extends JsonObject | null = JsonObject | null,
266
+ >
267
+ extends RawCoMapView<Shape, Meta>
268
+ implements RawCoValue
271
269
  {
272
- /** Set a new value for the given key.
273
- *
274
- * 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.
275
- *
276
- * 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.
277
- *
278
- * @category 2. Editing
279
- **/
280
- set<K extends keyof Shape & string>(
281
- key: K,
282
- value: Shape[K],
283
- privacy: "private" | "trusting" = "private",
284
- ): void {
285
- this.core.makeTransaction(
286
- [
287
- {
288
- op: "set",
289
- key,
290
- value: isCoValue(value) ? value.id : value,
291
- },
292
- ],
293
- privacy,
294
- );
295
-
296
- const after = new RawCoMap(this.core) as this;
297
-
298
- this.ops = after.ops;
299
- }
270
+ /** Set a new value for the given key.
271
+ *
272
+ * 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.
273
+ *
274
+ * 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.
275
+ *
276
+ * @category 2. Editing
277
+ **/
278
+ set<K extends keyof Shape & string>(
279
+ key: K,
280
+ value: Shape[K],
281
+ privacy: "private" | "trusting" = "private",
282
+ ): void {
283
+ this.core.makeTransaction(
284
+ [
285
+ {
286
+ op: "set",
287
+ key,
288
+ value: isCoValue(value) ? value.id : value,
289
+ },
290
+ ],
291
+ privacy,
292
+ );
293
+
294
+ const after = new RawCoMap(this.core) as this;
295
+
296
+ this.ops = after.ops;
297
+ }
298
+
299
+ /** Delete the given key (setting it to undefined).
300
+ *
301
+ * 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.
302
+ *
303
+ * If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
304
+ *
305
+ * @category 2. Editing
306
+ **/
307
+ delete(
308
+ key: keyof Shape & string,
309
+ privacy: "private" | "trusting" = "private",
310
+ ) {
311
+ this.core.makeTransaction(
312
+ [
313
+ {
314
+ op: "del",
315
+ key,
316
+ },
317
+ ],
318
+ privacy,
319
+ );
300
320
 
301
- /** Delete the given key (setting it to undefined).
302
- *
303
- * 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.
304
- *
305
- * If `privacy` is `"trusting"`, `key` is stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
306
- *
307
- * @category 2. Editing
308
- **/
309
- delete(
310
- key: keyof Shape & string,
311
- privacy: "private" | "trusting" = "private",
312
- ) {
313
- this.core.makeTransaction(
314
- [
315
- {
316
- op: "del",
317
- key,
318
- },
319
- ],
320
- privacy,
321
- );
322
-
323
- const after = new RawCoMap(this.core) as this;
324
-
325
- this.ops = after.ops;
326
- }
321
+ const after = new RawCoMap(this.core) as this;
322
+
323
+ this.ops = after.ops;
324
+ }
327
325
  }