cojson 0.19.22 → 0.20.1

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 (223) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +66 -0
  3. package/dist/PeerState.d.ts +6 -1
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +18 -3
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/coValueContentMessage.d.ts +0 -2
  8. package/dist/coValueContentMessage.d.ts.map +1 -1
  9. package/dist/coValueContentMessage.js +0 -8
  10. package/dist/coValueContentMessage.js.map +1 -1
  11. package/dist/coValueCore/SessionMap.d.ts +4 -2
  12. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  13. package/dist/coValueCore/SessionMap.js +30 -0
  14. package/dist/coValueCore/SessionMap.js.map +1 -1
  15. package/dist/coValueCore/coValueCore.d.ts +70 -5
  16. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  17. package/dist/coValueCore/coValueCore.js +302 -31
  18. package/dist/coValueCore/coValueCore.js.map +1 -1
  19. package/dist/coValueCore/verifiedState.d.ts +6 -1
  20. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  21. package/dist/coValueCore/verifiedState.js +9 -0
  22. package/dist/coValueCore/verifiedState.js.map +1 -1
  23. package/dist/coValues/coList.d.ts +4 -2
  24. package/dist/coValues/coList.d.ts.map +1 -1
  25. package/dist/coValues/coList.js +3 -0
  26. package/dist/coValues/coList.js.map +1 -1
  27. package/dist/coValues/group.d.ts.map +1 -1
  28. package/dist/coValues/group.js +3 -6
  29. package/dist/coValues/group.js.map +1 -1
  30. package/dist/config.d.ts +2 -8
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +4 -12
  33. package/dist/config.js.map +1 -1
  34. package/dist/crypto/NapiCrypto.d.ts +1 -2
  35. package/dist/crypto/NapiCrypto.d.ts.map +1 -1
  36. package/dist/crypto/NapiCrypto.js +19 -4
  37. package/dist/crypto/NapiCrypto.js.map +1 -1
  38. package/dist/crypto/RNCrypto.d.ts.map +1 -1
  39. package/dist/crypto/RNCrypto.js +19 -4
  40. package/dist/crypto/RNCrypto.js.map +1 -1
  41. package/dist/crypto/WasmCrypto.d.ts +11 -4
  42. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  43. package/dist/crypto/WasmCrypto.js +52 -10
  44. package/dist/crypto/WasmCrypto.js.map +1 -1
  45. package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
  46. package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
  47. package/dist/crypto/WasmCryptoEdge.js +4 -1
  48. package/dist/crypto/WasmCryptoEdge.js.map +1 -1
  49. package/dist/crypto/crypto.d.ts +3 -3
  50. package/dist/crypto/crypto.d.ts.map +1 -1
  51. package/dist/crypto/crypto.js +6 -1
  52. package/dist/crypto/crypto.js.map +1 -1
  53. package/dist/exports.d.ts +5 -5
  54. package/dist/exports.d.ts.map +1 -1
  55. package/dist/exports.js +4 -3
  56. package/dist/exports.js.map +1 -1
  57. package/dist/ids.d.ts +4 -1
  58. package/dist/ids.d.ts.map +1 -1
  59. package/dist/ids.js +4 -0
  60. package/dist/ids.js.map +1 -1
  61. package/dist/knownState.d.ts +2 -0
  62. package/dist/knownState.d.ts.map +1 -1
  63. package/dist/localNode.d.ts +12 -0
  64. package/dist/localNode.d.ts.map +1 -1
  65. package/dist/localNode.js +14 -0
  66. package/dist/localNode.js.map +1 -1
  67. package/dist/platformUtils.d.ts +3 -0
  68. package/dist/platformUtils.d.ts.map +1 -0
  69. package/dist/platformUtils.js +24 -0
  70. package/dist/platformUtils.js.map +1 -0
  71. package/dist/queue/LinkedList.d.ts +9 -3
  72. package/dist/queue/LinkedList.d.ts.map +1 -1
  73. package/dist/queue/LinkedList.js +30 -1
  74. package/dist/queue/LinkedList.js.map +1 -1
  75. package/dist/queue/OutgoingLoadQueue.d.ts +95 -0
  76. package/dist/queue/OutgoingLoadQueue.d.ts.map +1 -0
  77. package/dist/queue/OutgoingLoadQueue.js +240 -0
  78. package/dist/queue/OutgoingLoadQueue.js.map +1 -0
  79. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
  80. package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
  81. package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
  82. package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
  83. package/dist/storage/sqlite/client.d.ts +3 -0
  84. package/dist/storage/sqlite/client.d.ts.map +1 -1
  85. package/dist/storage/sqlite/client.js +44 -0
  86. package/dist/storage/sqlite/client.js.map +1 -1
  87. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
  88. package/dist/storage/sqlite/sqliteMigrations.js +7 -0
  89. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
  90. package/dist/storage/sqliteAsync/client.d.ts +3 -0
  91. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  92. package/dist/storage/sqliteAsync/client.js +42 -0
  93. package/dist/storage/sqliteAsync/client.js.map +1 -1
  94. package/dist/storage/storageAsync.d.ts +7 -0
  95. package/dist/storage/storageAsync.d.ts.map +1 -1
  96. package/dist/storage/storageAsync.js +48 -0
  97. package/dist/storage/storageAsync.js.map +1 -1
  98. package/dist/storage/storageSync.d.ts +6 -0
  99. package/dist/storage/storageSync.d.ts.map +1 -1
  100. package/dist/storage/storageSync.js +42 -0
  101. package/dist/storage/storageSync.js.map +1 -1
  102. package/dist/storage/types.d.ts +59 -0
  103. package/dist/storage/types.d.ts.map +1 -1
  104. package/dist/storage/types.js +12 -1
  105. package/dist/storage/types.js.map +1 -1
  106. package/dist/sync.d.ts.map +1 -1
  107. package/dist/sync.js +66 -43
  108. package/dist/sync.js.map +1 -1
  109. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
  110. package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
  111. package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
  112. package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
  113. package/dist/tests/GarbageCollector.test.js +5 -6
  114. package/dist/tests/GarbageCollector.test.js.map +1 -1
  115. package/dist/tests/LinkedList.test.js +90 -0
  116. package/dist/tests/LinkedList.test.js.map +1 -1
  117. package/dist/tests/OutgoingLoadQueue.test.d.ts +2 -0
  118. package/dist/tests/OutgoingLoadQueue.test.d.ts.map +1 -0
  119. package/dist/tests/OutgoingLoadQueue.test.js +814 -0
  120. package/dist/tests/OutgoingLoadQueue.test.js.map +1 -0
  121. package/dist/tests/StorageApiAsync.test.js +484 -152
  122. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  123. package/dist/tests/StorageApiSync.test.js +505 -136
  124. package/dist/tests/StorageApiSync.test.js.map +1 -1
  125. package/dist/tests/WasmCrypto.test.js +6 -3
  126. package/dist/tests/WasmCrypto.test.js.map +1 -1
  127. package/dist/tests/coValueCore.loadFromStorage.test.js +3 -0
  128. package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
  129. package/dist/tests/coValueCore.test.js +34 -13
  130. package/dist/tests/coValueCore.test.js.map +1 -1
  131. package/dist/tests/coreWasm.test.js +127 -4
  132. package/dist/tests/coreWasm.test.js.map +1 -1
  133. package/dist/tests/crypto.test.js +89 -93
  134. package/dist/tests/crypto.test.js.map +1 -1
  135. package/dist/tests/deleteCoValue.test.d.ts +2 -0
  136. package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
  137. package/dist/tests/deleteCoValue.test.js +313 -0
  138. package/dist/tests/deleteCoValue.test.js.map +1 -0
  139. package/dist/tests/group.removeMember.test.js +18 -30
  140. package/dist/tests/group.removeMember.test.js.map +1 -1
  141. package/dist/tests/knownState.lazyLoading.test.js +3 -0
  142. package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
  143. package/dist/tests/sync.concurrentLoad.test.d.ts +2 -0
  144. package/dist/tests/sync.concurrentLoad.test.d.ts.map +1 -0
  145. package/dist/tests/sync.concurrentLoad.test.js +481 -0
  146. package/dist/tests/sync.concurrentLoad.test.js.map +1 -0
  147. package/dist/tests/sync.deleted.test.d.ts +2 -0
  148. package/dist/tests/sync.deleted.test.d.ts.map +1 -0
  149. package/dist/tests/sync.deleted.test.js +214 -0
  150. package/dist/tests/sync.deleted.test.js.map +1 -0
  151. package/dist/tests/sync.mesh.test.js +3 -2
  152. package/dist/tests/sync.mesh.test.js.map +1 -1
  153. package/dist/tests/sync.storage.test.js +4 -3
  154. package/dist/tests/sync.storage.test.js.map +1 -1
  155. package/dist/tests/sync.test.js +3 -2
  156. package/dist/tests/sync.test.js.map +1 -1
  157. package/dist/tests/testStorage.d.ts +3 -0
  158. package/dist/tests/testStorage.d.ts.map +1 -1
  159. package/dist/tests/testStorage.js +17 -1
  160. package/dist/tests/testStorage.js.map +1 -1
  161. package/dist/tests/testUtils.d.ts +7 -3
  162. package/dist/tests/testUtils.d.ts.map +1 -1
  163. package/dist/tests/testUtils.js +19 -4
  164. package/dist/tests/testUtils.js.map +1 -1
  165. package/package.json +6 -16
  166. package/src/PeerState.ts +26 -3
  167. package/src/coValueContentMessage.ts +0 -14
  168. package/src/coValueCore/SessionMap.ts +43 -1
  169. package/src/coValueCore/coValueCore.ts +415 -27
  170. package/src/coValueCore/verifiedState.ts +26 -3
  171. package/src/coValues/coList.ts +9 -3
  172. package/src/coValues/group.ts +5 -6
  173. package/src/config.ts +4 -13
  174. package/src/crypto/NapiCrypto.ts +29 -13
  175. package/src/crypto/RNCrypto.ts +29 -11
  176. package/src/crypto/WasmCrypto.ts +67 -20
  177. package/src/crypto/WasmCryptoEdge.ts +5 -1
  178. package/src/crypto/crypto.ts +16 -4
  179. package/src/exports.ts +4 -2
  180. package/src/ids.ts +11 -1
  181. package/src/localNode.ts +15 -0
  182. package/src/platformUtils.ts +26 -0
  183. package/src/queue/LinkedList.ts +34 -4
  184. package/src/queue/OutgoingLoadQueue.ts +307 -0
  185. package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
  186. package/src/storage/sqlite/client.ts +77 -0
  187. package/src/storage/sqlite/sqliteMigrations.ts +7 -0
  188. package/src/storage/sqliteAsync/client.ts +75 -0
  189. package/src/storage/storageAsync.ts +62 -0
  190. package/src/storage/storageSync.ts +58 -0
  191. package/src/storage/types.ts +69 -0
  192. package/src/sync.ts +78 -46
  193. package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
  194. package/src/tests/GarbageCollector.test.ts +6 -10
  195. package/src/tests/LinkedList.test.ts +111 -0
  196. package/src/tests/OutgoingLoadQueue.test.ts +1129 -0
  197. package/src/tests/StorageApiAsync.test.ts +572 -162
  198. package/src/tests/StorageApiSync.test.ts +580 -143
  199. package/src/tests/WasmCrypto.test.ts +8 -3
  200. package/src/tests/coValueCore.loadFromStorage.test.ts +6 -0
  201. package/src/tests/coValueCore.test.ts +49 -14
  202. package/src/tests/coreWasm.test.ts +319 -10
  203. package/src/tests/crypto.test.ts +141 -150
  204. package/src/tests/deleteCoValue.test.ts +528 -0
  205. package/src/tests/group.removeMember.test.ts +35 -35
  206. package/src/tests/knownState.lazyLoading.test.ts +6 -0
  207. package/src/tests/sync.concurrentLoad.test.ts +650 -0
  208. package/src/tests/sync.deleted.test.ts +294 -0
  209. package/src/tests/sync.mesh.test.ts +5 -2
  210. package/src/tests/sync.storage.test.ts +6 -3
  211. package/src/tests/sync.test.ts +5 -2
  212. package/src/tests/testStorage.ts +31 -2
  213. package/src/tests/testUtils.ts +31 -10
  214. package/dist/crypto/PureJSCrypto.d.ts +0 -77
  215. package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
  216. package/dist/crypto/PureJSCrypto.js +0 -236
  217. package/dist/crypto/PureJSCrypto.js.map +0 -1
  218. package/dist/tests/PureJSCrypto.test.d.ts +0 -2
  219. package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
  220. package/dist/tests/PureJSCrypto.test.js +0 -145
  221. package/dist/tests/PureJSCrypto.test.js.map +0 -1
  222. package/src/crypto/PureJSCrypto.ts +0 -429
  223. package/src/tests/PureJSCrypto.test.ts +0 -217
@@ -14,12 +14,16 @@ import {
14
14
  Signature,
15
15
  SignerID,
16
16
  } from "../crypto/crypto.js";
17
- import { RawCoID, SessionID, TransactionID } from "../ids.js";
17
+ import {
18
+ isDeleteSessionID,
19
+ RawCoID,
20
+ SessionID,
21
+ TransactionID,
22
+ } from "../ids.js";
18
23
  import { Stringified } from "../jsonStringify.js";
19
24
  import { JsonObject, JsonValue } from "../jsonValue.js";
20
25
  import { PermissionsDef as RulesetDef } from "../permissions.js";
21
26
  import { NewContentMessage } from "../sync.js";
22
- import { TryAddTransactionsError } from "./coValueCore.js";
23
27
  import { SessionMap } from "./SessionMap.js";
24
28
  import { ControlledAccountOrAgent } from "../coValues/account.js";
25
29
  import {
@@ -35,10 +39,19 @@ export type CoValueHeader = {
35
39
  } & CoValueUniqueness;
36
40
 
37
41
  export type CoValueUniqueness = {
38
- uniqueness: JsonValue;
42
+ uniqueness: Uniqueness;
39
43
  createdAt?: `2${string}` | null;
40
44
  };
41
45
 
46
+ export type Uniqueness =
47
+ | string
48
+ | boolean
49
+ | null
50
+ | undefined
51
+ | {
52
+ [key: string]: string;
53
+ };
54
+
42
55
  export type PrivateTransaction = {
43
56
  privacy: "private";
44
57
  madeAt: number;
@@ -63,6 +76,7 @@ export class VerifiedState {
63
76
  public lastAccessed: number | undefined;
64
77
  public branchSourceId?: RawCoID;
65
78
  public branchName?: string;
79
+ private isDeleted: boolean = false;
66
80
 
67
81
  constructor(
68
82
  id: RawCoID,
@@ -87,6 +101,11 @@ export class VerifiedState {
87
101
  );
88
102
  }
89
103
 
104
+ markAsDeleted() {
105
+ this.isDeleted = true;
106
+ this.sessions.markAsDeleted();
107
+ }
108
+
90
109
  tryAddTransactions(
91
110
  sessionID: SessionID,
92
111
  signerID: SignerID | undefined,
@@ -199,6 +218,10 @@ export class VerifiedState {
199
218
  const sessionSent = knownState?.sessions;
200
219
 
201
220
  for (const [sessionID, log] of this.sessions.sessions) {
221
+ if (this.isDeleted && !isDeleteSessionID(sessionID)) {
222
+ continue;
223
+ }
224
+
202
225
  const startFrom = sessionSent?.[sessionID] ?? 0;
203
226
 
204
227
  let currentSessionSize = 0;
@@ -59,14 +59,14 @@ export class RawCoList<
59
59
  afterStart: OpID[] = [];
60
60
  beforeEnd: OpID[] = [];
61
61
  insertions: {
62
- [sessionID: SessionID]: {
62
+ [sessionID: SessionIndex]: {
63
63
  [txIdx: number]: {
64
64
  [changeIdx: number]: InsertionEntry<Item>;
65
65
  };
66
66
  };
67
67
  } = {};
68
68
  deletionsByInsertion: {
69
- [deletedSessionID: SessionID]: {
69
+ [deletedSessionID: SessionIndex]: {
70
70
  [deletedTxIdx: number]: {
71
71
  [deletedChangeIdx: number]: DeletionEntry[];
72
72
  };
@@ -378,6 +378,10 @@ export class RawCoList<
378
378
  return arr;
379
379
  }
380
380
 
381
+ length() {
382
+ return this.entries().length;
383
+ }
384
+
381
385
  /** @internal */
382
386
  entriesUncached(): {
383
387
  value: Item;
@@ -700,7 +704,9 @@ export class RawCoList<
700
704
  }
701
705
  }
702
706
 
703
- function getSessionIndex(txID: TransactionID): SessionID {
707
+ type SessionIndex = SessionID | `${SessionID}_branch_${RawCoID}`;
708
+
709
+ function getSessionIndex(txID: TransactionID): SessionIndex {
704
710
  if (txID.branch) {
705
711
  return `${txID.sessionID}_branch_${txID.branch}`;
706
712
  }
@@ -1203,12 +1203,11 @@ export class RawGroup<
1203
1203
 
1204
1204
  this.set(memberKey, "revoked", "trusting");
1205
1205
 
1206
- // TODO: removeMember fails silently. Uncomment this will be a breaking change
1207
- // if (this.get(memberKey) !== "revoked") {
1208
- // throw new Error(
1209
- // `Failed to revoke role to ${memberKey} (role of current account is ${this.myRole()})`,
1210
- // );
1211
- // }
1206
+ if (this.get(memberKey) !== "revoked") {
1207
+ throw new Error(
1208
+ `Failed to revoke role to ${memberKey} (role of current account is ${this.myRole()})`,
1209
+ );
1210
+ }
1212
1211
  }
1213
1212
 
1214
1213
  /**
package/src/config.ts CHANGED
@@ -7,25 +7,17 @@
7
7
  **/
8
8
  export const TRANSACTION_CONFIG = {
9
9
  MAX_RECOMMENDED_TX_SIZE: 100 * 1024,
10
- /**
11
- * Messages larger than this will be rejected when creating a transaction.
12
- * The current limit is set at 1MB because that's the limit imposed by Cloudflare to Websocket messages.
13
- */
14
- MAX_TX_SIZE_BYTES: 1 * 1024 * 1024,
15
10
  };
16
11
 
17
12
  export function setMaxRecommendedTxSize(size: number) {
18
13
  TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE = size;
19
14
  }
20
15
 
21
- export function setMaxTxSizeBytes(size: number) {
22
- TRANSACTION_CONFIG.MAX_TX_SIZE_BYTES = size;
23
- }
24
-
25
16
  export const CO_VALUE_LOADING_CONFIG = {
26
17
  MAX_RETRIES: 1,
27
- TIMEOUT: 30_000,
18
+ TIMEOUT: 60_000,
28
19
  RETRY_DELAY: 3000,
20
+ MAX_IN_FLIGHT_LOADS_PER_PEER: 1000,
29
21
  };
30
22
 
31
23
  export function setCoValueLoadingMaxRetries(maxRetries: number) {
@@ -63,13 +55,12 @@ export function setGarbageCollectorInterval(interval: number) {
63
55
 
64
56
  export const WEBSOCKET_CONFIG = {
65
57
  MAX_OUTGOING_MESSAGES_CHUNK_BYTES: 25_000,
66
- OUTGOING_MESSAGES_CHUNK_DELAY: 5,
67
58
  };
68
59
 
69
60
  export function setMaxOutgoingMessagesChunkBytes(bytes: number) {
70
61
  WEBSOCKET_CONFIG.MAX_OUTGOING_MESSAGES_CHUNK_BYTES = bytes;
71
62
  }
72
63
 
73
- export function setOutgoingMessagesChunkDelay(delay: number) {
74
- WEBSOCKET_CONFIG.OUTGOING_MESSAGES_CHUNK_DELAY = delay;
64
+ export function setMaxInFlightLoadsPerPeer(limit: number) {
65
+ CO_VALUE_LOADING_CONFIG.MAX_IN_FLIGHT_LOADS_PER_PEER = limit;
75
66
  }
@@ -19,7 +19,6 @@ import { RawCoID, SessionID, TransactionID } from "../ids.js";
19
19
  import { Stringified, stableStringify } from "../jsonStringify.js";
20
20
  import { JsonObject, JsonValue } from "../jsonValue.js";
21
21
  import { logger } from "../logger.js";
22
- import { PureJSCrypto } from "./PureJSCrypto.js";
23
22
  import {
24
23
  CryptoProvider,
25
24
  Encrypted,
@@ -58,7 +57,7 @@ export class NapiCrypto extends CryptoProvider<Blake3State> {
58
57
  super();
59
58
  }
60
59
 
61
- static async create(): Promise<NapiCrypto | PureJSCrypto | WasmCrypto> {
60
+ static async create(): Promise<NapiCrypto | WasmCrypto> {
62
61
  return new NapiCrypto();
63
62
  }
64
63
 
@@ -190,11 +189,24 @@ class SessionLogAdapter {
190
189
  newSignature: Signature,
191
190
  skipVerify: boolean,
192
191
  ): void {
193
- this.sessionLog.tryAdd(
194
- transactions.map((tx) => stableStringify(tx)),
195
- newSignature,
196
- skipVerify,
197
- );
192
+ // Use direct calls instead of JSON.stringify for better performance
193
+ for (const tx of transactions) {
194
+ if (tx.privacy === "private") {
195
+ this.sessionLog.addExistingPrivateTransaction(
196
+ tx.encryptedChanges,
197
+ tx.keyUsed,
198
+ tx.madeAt,
199
+ tx.meta,
200
+ );
201
+ } else {
202
+ this.sessionLog.addExistingTrustingTransaction(
203
+ tx.changes,
204
+ tx.madeAt,
205
+ tx.meta,
206
+ );
207
+ }
208
+ }
209
+ this.sessionLog.commitTransactions(newSignature, skipVerify);
198
210
  }
199
211
 
200
212
  addNewPrivateTransaction(
@@ -206,12 +218,14 @@ class SessionLogAdapter {
206
218
  meta: JsonObject | undefined,
207
219
  ): { signature: Signature; transaction: PrivateTransaction } {
208
220
  const output = this.sessionLog.addNewPrivateTransaction(
209
- stableStringify(changes),
221
+ // We can avoid stableStringify because it will be encrypted.
222
+ JSON.stringify(changes),
210
223
  signerAgent.currentSignerSecret(),
211
224
  keySecret,
212
225
  keyID,
213
226
  madeAt,
214
- meta ? stableStringify(meta) : undefined,
227
+ // We can avoid stableStringify because it will be encrypted.
228
+ meta ? JSON.stringify(meta) : undefined,
215
229
  );
216
230
  const parsedOutput = JSON.parse(output);
217
231
  const transaction: PrivateTransaction = {
@@ -230,8 +244,10 @@ class SessionLogAdapter {
230
244
  madeAt: number,
231
245
  meta: JsonObject | undefined,
232
246
  ): { signature: Signature; transaction: TrustingTransaction } {
233
- const stringifiedChanges = stableStringify(changes);
234
- const stringifiedMeta = meta ? stableStringify(meta) : undefined;
247
+ // We can avoid stableStringify because the changes will be in a string format already.
248
+ const stringifiedChanges = JSON.stringify(changes);
249
+ // We can avoid stableStringify because the meta will be in a string format already.
250
+ const stringifiedMeta = meta ? JSON.stringify(meta) : undefined;
235
251
  const output = this.sessionLog.addNewTrustingTransaction(
236
252
  stringifiedChanges,
237
253
  signerAgent.currentSignerSecret(),
@@ -241,8 +257,8 @@ class SessionLogAdapter {
241
257
  const transaction: TrustingTransaction = {
242
258
  privacy: "trusting",
243
259
  madeAt,
244
- changes: stringifiedChanges,
245
- meta: stringifiedMeta,
260
+ changes: stringifiedChanges as Stringified<JsonValue[]>,
261
+ meta: stringifiedMeta as Stringified<JsonObject> | undefined,
246
262
  };
247
263
  return { signature: output as Signature, transaction };
248
264
  }
@@ -47,6 +47,7 @@ import {
47
47
  Blake3Hasher,
48
48
  SessionLog,
49
49
  } from "cojson-core-rn";
50
+ import { WasmCrypto } from "./WasmCrypto.js";
50
51
 
51
52
  type Blake3State = Blake3Hasher;
52
53
 
@@ -221,11 +222,24 @@ class SessionLogAdapter implements SessionLogImpl {
221
222
  newSignature: Signature,
222
223
  skipVerify: boolean,
223
224
  ): void {
224
- this.sessionLog.tryAdd(
225
- transactions.map((tx) => stableStringify(tx)),
226
- newSignature,
227
- skipVerify,
228
- );
225
+ // Use direct calls instead of JSON.stringify for better performance
226
+ for (const tx of transactions) {
227
+ if (tx.privacy === "private") {
228
+ this.sessionLog.addExistingPrivateTransaction(
229
+ tx.encryptedChanges,
230
+ tx.keyUsed,
231
+ tx.madeAt,
232
+ tx.meta,
233
+ );
234
+ } else {
235
+ this.sessionLog.addExistingTrustingTransaction(
236
+ tx.changes,
237
+ tx.madeAt,
238
+ tx.meta,
239
+ );
240
+ }
241
+ }
242
+ this.sessionLog.commitTransactions(newSignature, skipVerify);
229
243
  }
230
244
 
231
245
  addNewPrivateTransaction(
@@ -237,12 +251,14 @@ class SessionLogAdapter implements SessionLogImpl {
237
251
  meta: JsonObject | undefined,
238
252
  ) {
239
253
  const output = this.sessionLog.addNewPrivateTransaction(
240
- stableStringify(changes),
254
+ // We can avoid stableStringify because it will be encrypted.
255
+ JSON.stringify(changes),
241
256
  signerAgent.currentSignerSecret(),
242
257
  keySecret,
243
258
  keyID,
244
259
  madeAt,
245
- meta ? stableStringify(meta) : undefined,
260
+ // We can avoid stableStringify because it will be encrypted.
261
+ meta ? JSON.stringify(meta) : undefined,
246
262
  );
247
263
  const parsedOutput = JSON.parse(output);
248
264
  const transaction: PrivateTransaction = {
@@ -261,8 +277,10 @@ class SessionLogAdapter implements SessionLogImpl {
261
277
  madeAt: number,
262
278
  meta: JsonObject | undefined,
263
279
  ) {
264
- const stringifiedChanges = stableStringify(changes);
265
- const stringifiedMeta = meta ? stableStringify(meta) : undefined;
280
+ // We can avoid stableStringify because the changes will be in a string format already.
281
+ const stringifiedChanges = JSON.stringify(changes);
282
+ // We can avoid stableStringify because the meta will be in a string format already.
283
+ const stringifiedMeta = meta ? JSON.stringify(meta) : undefined;
266
284
  const output = this.sessionLog.addNewTrustingTransaction(
267
285
  stringifiedChanges,
268
286
  signerAgent.currentSignerSecret(),
@@ -272,8 +290,8 @@ class SessionLogAdapter implements SessionLogImpl {
272
290
  const transaction: TrustingTransaction = {
273
291
  privacy: "trusting",
274
292
  madeAt,
275
- changes: stringifiedChanges,
276
- meta: stringifiedMeta,
293
+ changes: stringifiedChanges as Stringified<JsonValue[]>,
294
+ meta: stringifiedMeta as Stringified<JsonObject> | undefined,
277
295
  };
278
296
  return { signature: output as Signature, transaction };
279
297
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  SessionLog,
3
3
  initialize,
4
+ initializeSync,
4
5
  Blake3Hasher,
5
6
  blake3HashOnce,
6
7
  blake3HashOnceWithContext,
@@ -20,7 +21,6 @@ import { RawCoID, SessionID, TransactionID } from "../ids.js";
20
21
  import { Stringified, stableStringify } from "../jsonStringify.js";
21
22
  import { JsonObject, JsonValue } from "../jsonValue.js";
22
23
  import { logger } from "../logger.js";
23
- import { PureJSCrypto } from "./PureJSCrypto.js";
24
24
  import {
25
25
  CryptoProvider,
26
26
  Encrypted,
@@ -41,10 +41,34 @@ import {
41
41
  Transaction,
42
42
  TrustingTransaction,
43
43
  } from "../coValueCore/verifiedState.js";
44
+ import { isCloudflare, isEvalAllowed } from "../platformUtils.js";
44
45
 
45
46
  type Blake3State = Blake3Hasher;
46
47
 
47
48
  let wasmInit = initialize;
49
+ let wasmInitSync = initializeSync;
50
+
51
+ const wasmCryptoErrorMessage = (
52
+ e: unknown,
53
+ ) => `Critical Error: Failed to load WASM module
54
+
55
+ ${!isEvalAllowed() ? `You need to add \`import "jazz-tools/load-edge-wasm";\` on top of your entry module to make Jazz work with ${isCloudflare() ? "Cloudflare workers" : "this runtime"}` : (e as Error).message}
56
+
57
+ A native crypto module is required for Jazz to work. See https://jazz.tools/docs/react/reference/performance#use-the-best-crypto-implementation-for-your-platform for possible alternatives.`;
58
+
59
+ /**
60
+ * Initializes the WasmCrypto module. This function can be used to initialize the WasmCrypto module in a worker or a browser.
61
+ * if you are using SSR and you want to initialize WASM crypto asynchronously you can use this function.
62
+ * @returns A promise that resolves when the WasmCrypto module is successfully initialized.
63
+ */
64
+ export async function initWasmCrypto() {
65
+ try {
66
+ await wasmInit();
67
+ } catch (e) {
68
+ throw new Error(wasmCryptoErrorMessage(e), { cause: e });
69
+ }
70
+ }
71
+
48
72
  /**
49
73
  * WebAssembly implementation of the CryptoProvider interface using cojson-core-wasm.
50
74
  * This provides the primary implementation using WebAssembly for optimal performance, offering:
@@ -54,7 +78,7 @@ let wasmInit = initialize;
54
78
  * - Hashing (BLAKE3)
55
79
  */
56
80
  export class WasmCrypto extends CryptoProvider<Blake3State> {
57
- private constructor() {
81
+ protected constructor() {
58
82
  super();
59
83
  }
60
84
 
@@ -62,17 +86,23 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
62
86
  wasmInit = value;
63
87
  }
64
88
 
65
- static async create(): Promise<WasmCrypto | PureJSCrypto> {
89
+ static setInitSync(value: typeof initializeSync) {
90
+ wasmInitSync = value;
91
+ }
92
+
93
+ static createSync(): WasmCrypto {
66
94
  try {
67
- await wasmInit();
95
+ wasmInitSync();
68
96
  } catch (e) {
69
- logger.warn(
70
- "Failed to initialize WasmCrypto, falling back to PureJSCrypto",
71
- { err: e },
72
- );
73
- return new PureJSCrypto();
97
+ throw new Error(wasmCryptoErrorMessage(e), { cause: e });
74
98
  }
99
+ return new WasmCrypto();
100
+ }
75
101
 
102
+ // TODO: Remove this method and use createSync instead, this is not necessary since we can use createSync in the browser and in the worker.
103
+ // @deprecated
104
+ static async create(): Promise<WasmCrypto> {
105
+ await initWasmCrypto();
76
106
  return new WasmCrypto();
77
107
  }
78
108
 
@@ -204,11 +234,24 @@ class SessionLogAdapter {
204
234
  newSignature: Signature,
205
235
  skipVerify: boolean,
206
236
  ): void {
207
- this.sessionLog.tryAdd(
208
- transactions.map((tx) => stableStringify(tx)),
209
- newSignature,
210
- skipVerify,
211
- );
237
+ // Use direct calls instead of JSON.stringify for better performance
238
+ for (const tx of transactions) {
239
+ if (tx.privacy === "private") {
240
+ this.sessionLog.addExistingPrivateTransaction(
241
+ tx.encryptedChanges,
242
+ tx.keyUsed,
243
+ tx.madeAt,
244
+ tx.meta,
245
+ );
246
+ } else {
247
+ this.sessionLog.addExistingTrustingTransaction(
248
+ tx.changes,
249
+ tx.madeAt,
250
+ tx.meta,
251
+ );
252
+ }
253
+ }
254
+ this.sessionLog.commitTransactions(newSignature, skipVerify);
212
255
  }
213
256
 
214
257
  addNewPrivateTransaction(
@@ -220,12 +263,14 @@ class SessionLogAdapter {
220
263
  meta: JsonObject | undefined,
221
264
  ): { signature: Signature; transaction: PrivateTransaction } {
222
265
  const output = this.sessionLog.addNewPrivateTransaction(
223
- stableStringify(changes),
266
+ // We can avoid stableStringify because it will be encrypted.
267
+ JSON.stringify(changes),
224
268
  signerAgent.currentSignerSecret(),
225
269
  keySecret,
226
270
  keyID,
227
271
  madeAt,
228
- meta ? stableStringify(meta) : undefined,
272
+ // We can avoid stableStringify because it will be encrypted.
273
+ meta ? JSON.stringify(meta) : undefined,
229
274
  );
230
275
  const parsedOutput = JSON.parse(output);
231
276
  const transaction: PrivateTransaction = {
@@ -244,8 +289,10 @@ class SessionLogAdapter {
244
289
  madeAt: number,
245
290
  meta: JsonObject | undefined,
246
291
  ): { signature: Signature; transaction: TrustingTransaction } {
247
- const stringifiedChanges = stableStringify(changes);
248
- const stringifiedMeta = meta ? stableStringify(meta) : undefined;
292
+ // We can avoid stableStringify because the changes will be in a string format already.
293
+ const stringifiedChanges = JSON.stringify(changes);
294
+ // We can avoid stableStringify because the meta will be in a string format already.
295
+ const stringifiedMeta = meta ? JSON.stringify(meta) : undefined;
249
296
  const output = this.sessionLog.addNewTrustingTransaction(
250
297
  stringifiedChanges,
251
298
  signerAgent.currentSignerSecret(),
@@ -255,8 +302,8 @@ class SessionLogAdapter {
255
302
  const transaction: TrustingTransaction = {
256
303
  privacy: "trusting",
257
304
  madeAt,
258
- changes: stringifiedChanges,
259
- meta: stringifiedMeta,
305
+ changes: stringifiedChanges as Stringified<JsonValue[]>,
306
+ meta: stringifiedMeta as Stringified<JsonObject> | undefined,
260
307
  };
261
308
  return { signature: output as Signature, transaction };
262
309
  }
@@ -1,4 +1,4 @@
1
- import { initialize } from "cojson-core-wasm/edge-lite";
1
+ import { initialize, initializeSync } from "cojson-core-wasm/edge-lite";
2
2
  import { WasmCrypto } from "./WasmCrypto.js";
3
3
 
4
4
  /**
@@ -12,3 +12,7 @@ import { WasmCrypto } from "./WasmCrypto.js";
12
12
  export const init = async () => {
13
13
  return initialize();
14
14
  };
15
+
16
+ export const initSync = () => {
17
+ return initializeSync();
18
+ };
@@ -1,7 +1,13 @@
1
1
  import { base58 } from "@scure/base";
2
2
  import { ControlledAccountOrAgent, RawAccountID } from "../coValues/account.js";
3
- import { AgentID, RawCoID, TransactionID } from "../ids.js";
4
- import { SessionID } from "../ids.js";
3
+ import {
4
+ AgentID,
5
+ RawCoID,
6
+ TransactionID,
7
+ SessionID,
8
+ ActiveSessionID,
9
+ DeleteSessionID,
10
+ } from "../ids.js";
5
11
  import { Stringified, parseJSON, stableStringify } from "../jsonStringify.js";
6
12
  import { JsonObject, JsonValue } from "../jsonValue.js";
7
13
  import { logger } from "../logger.js";
@@ -246,8 +252,14 @@ export abstract class CryptoProvider<Blake3State = any> {
246
252
  )}`;
247
253
  }
248
254
 
249
- newRandomSessionID(accountID: RawAccountID | AgentID): SessionID {
250
- return `${accountID}_session_z${base58.encode(this.randomBytes(8))}`;
255
+ newRandomSessionID(accountID: RawAccountID | AgentID): ActiveSessionID {
256
+ const randomPart = base58.encode(this.randomBytes(8));
257
+ return `${accountID}_session_z${randomPart}`;
258
+ }
259
+
260
+ newDeleteSessionID(accountID: RawAccountID | AgentID): DeleteSessionID {
261
+ const randomPart = base58.encode(this.randomBytes(7));
262
+ return `${accountID}_session_d${randomPart}$`;
251
263
  }
252
264
 
253
265
  abstract createSessionLog(
package/src/exports.ts CHANGED
@@ -77,6 +77,7 @@ import { emptyKnownState } from "./knownState.js";
77
77
  import {
78
78
  getContentMessageSize,
79
79
  getTransactionSize,
80
+ knownStateFromContent,
80
81
  } from "./coValueContentMessage.js";
81
82
  import { getDependedOnCoValuesFromRawData } from "./coValueCore/utils.js";
82
83
  import {
@@ -87,8 +88,8 @@ import {
87
88
  setCoValueLoadingRetryDelay,
88
89
  setCoValueLoadingTimeout,
89
90
  setIncomingMessagesTimeBudget,
91
+ setMaxInFlightLoadsPerPeer,
90
92
  setMaxOutgoingMessagesChunkBytes,
91
- setOutgoingMessagesChunkDelay,
92
93
  setMaxRecommendedTxSize,
93
94
  } from "./config.js";
94
95
  import { LogLevel, logger } from "./logger.js";
@@ -102,6 +103,7 @@ type Value = JsonValue | AnyRawCoValue;
102
103
  export { PriorityBasedMessageQueue } from "./queue/PriorityBasedMessageQueue.js";
103
104
  /** @hidden */
104
105
  export const cojsonInternals = {
106
+ knownStateFromContent,
105
107
  connectedPeers,
106
108
  rawCoIDtoBytes,
107
109
  rawCoIDfromBytes,
@@ -140,7 +142,7 @@ export const cojsonInternals = {
140
142
  canBeBranched,
141
143
  WEBSOCKET_CONFIG,
142
144
  setMaxOutgoingMessagesChunkBytes,
143
- setOutgoingMessagesChunkDelay,
145
+ setMaxInFlightLoadsPerPeer,
144
146
  };
145
147
 
146
148
  export {
package/src/ids.ts CHANGED
@@ -36,7 +36,17 @@ export function isAgentID(id: unknown): id is AgentID {
36
36
  );
37
37
  }
38
38
 
39
- export type SessionID = `${RawAccountID | AgentID}_session_z${string}`;
39
+ export type ActiveSessionID = `${RawAccountID | AgentID}_session_z${string}`;
40
+ export type DeleteSessionID = `${RawAccountID | AgentID}_session_d${string}$`;
41
+ export type SessionID = ActiveSessionID | DeleteSessionID;
42
+
43
+ const CHAR_DOLLAR = "$".charCodeAt(0);
44
+
45
+ export function isDeleteSessionID(
46
+ sessionID: SessionID,
47
+ ): sessionID is DeleteSessionID {
48
+ return sessionID.charCodeAt(sessionID.length - 1) === CHAR_DOLLAR;
49
+ }
40
50
 
41
51
  export function isParentGroupReference(
42
52
  key: string,
package/src/localNode.ts CHANGED
@@ -101,6 +101,21 @@ export class LocalNode {
101
101
  this.syncManager.removeStorage();
102
102
  }
103
103
 
104
+ /**
105
+ * Enable background erasure of deleted coValues (space reclamation).
106
+ *
107
+ * Deleted coValues are immediately blocked from syncing via tombstones; this feature
108
+ * only reclaims local storage space by deleting historical content while preserving
109
+ * the tombstone (header + delete session).
110
+ *
111
+ * This is opt-in and affects only the currently configured storage (if any)
112
+ *
113
+ * @category 3. Low-level
114
+ */
115
+ enableDeletedCoValuesErasure() {
116
+ this.storage?.enableDeletedCoValuesErasure();
117
+ }
118
+
104
119
  hasCoValue(id: RawCoID) {
105
120
  const coValue = this.coValues.get(id);
106
121
 
@@ -0,0 +1,26 @@
1
+ export function isCloudflare() {
2
+ if (
3
+ // @ts-ignore
4
+ typeof navigator !== "undefined" &&
5
+ // @ts-ignore
6
+ navigator?.userAgent?.includes("Cloudflare")
7
+ ) {
8
+ return true;
9
+ }
10
+
11
+ return false;
12
+ }
13
+
14
+ export const isEvalAllowed = () => {
15
+ if (isCloudflare()) {
16
+ return false;
17
+ }
18
+
19
+ try {
20
+ const F = Function;
21
+ new F("");
22
+ return true;
23
+ } catch (_) {
24
+ return false;
25
+ }
26
+ };