jazz-tools 0.8.44 → 0.8.45

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. package/.turbo/turbo-build.log +16 -10
  2. package/CHANGELOG.md +12 -0
  3. package/dist/chunk-5WLKIDKU.js +2844 -0
  4. package/dist/chunk-5WLKIDKU.js.map +1 -0
  5. package/dist/index.native.js +63 -0
  6. package/dist/index.native.js.map +1 -0
  7. package/dist/index.web.js +61 -0
  8. package/dist/index.web.js.map +1 -0
  9. package/package.json +9 -12
  10. package/src/coValues/account.ts +21 -9
  11. package/src/coValues/coFeed.ts +4 -3
  12. package/src/coValues/coList.ts +8 -5
  13. package/src/coValues/coMap.ts +6 -3
  14. package/src/coValues/deepLoading.ts +6 -12
  15. package/src/coValues/extensions/imageDef.ts +3 -1
  16. package/src/coValues/group.ts +8 -11
  17. package/src/coValues/inbox.ts +377 -0
  18. package/src/coValues/interfaces.ts +9 -6
  19. package/src/coValues/profile.ts +11 -0
  20. package/src/coValues/registeredSchemas.ts +12 -0
  21. package/src/exports.ts +21 -15
  22. package/src/implementation/anonymousJazzAgent.ts +6 -0
  23. package/src/implementation/createContext.ts +6 -7
  24. package/src/implementation/refs.ts +1 -1
  25. package/src/implementation/subscriptionScope.ts +1 -1
  26. package/src/index.native.ts +0 -1
  27. package/src/internal.ts +1 -9
  28. package/src/tests/account.test.ts +2 -2
  29. package/src/tests/coFeed.test.ts +0 -9
  30. package/src/tests/coMap.test.ts +2 -2
  31. package/src/tests/groupsAndAccounts.test.ts +1 -1
  32. package/src/tests/inbox.test.ts +256 -0
  33. package/src/tests/schemaUnion.test.ts +1 -1
  34. package/src/tests/utils.ts +14 -0
  35. package/tsup.config.ts +13 -0
  36. package/dist/native/coValues/account.js +0 -221
  37. package/dist/native/coValues/account.js.map +0 -1
  38. package/dist/native/coValues/coFeed.js +0 -571
  39. package/dist/native/coValues/coFeed.js.map +0 -1
  40. package/dist/native/coValues/coList.js +0 -404
  41. package/dist/native/coValues/coList.js.map +0 -1
  42. package/dist/native/coValues/coMap.js +0 -537
  43. package/dist/native/coValues/coMap.js.map +0 -1
  44. package/dist/native/coValues/deepLoading.js +0 -63
  45. package/dist/native/coValues/deepLoading.js.map +0 -1
  46. package/dist/native/coValues/extensions/imageDef.js +0 -42
  47. package/dist/native/coValues/extensions/imageDef.js.map +0 -1
  48. package/dist/native/coValues/group.js +0 -136
  49. package/dist/native/coValues/group.js.map +0 -1
  50. package/dist/native/coValues/interfaces.js +0 -135
  51. package/dist/native/coValues/interfaces.js.map +0 -1
  52. package/dist/native/coValues/schemaUnion.js +0 -89
  53. package/dist/native/coValues/schemaUnion.js.map +0 -1
  54. package/dist/native/exports.js +0 -5
  55. package/dist/native/exports.js.map +0 -1
  56. package/dist/native/implementation/createContext.js +0 -157
  57. package/dist/native/implementation/createContext.js.map +0 -1
  58. package/dist/native/implementation/devtoolsFormatters.js +0 -95
  59. package/dist/native/implementation/devtoolsFormatters.js.map +0 -1
  60. package/dist/native/implementation/errors.js +0 -2
  61. package/dist/native/implementation/errors.js.map +0 -1
  62. package/dist/native/implementation/inspect.js +0 -2
  63. package/dist/native/implementation/inspect.js.map +0 -1
  64. package/dist/native/implementation/refs.js +0 -115
  65. package/dist/native/implementation/refs.js.map +0 -1
  66. package/dist/native/implementation/schema.js +0 -96
  67. package/dist/native/implementation/schema.js.map +0 -1
  68. package/dist/native/implementation/subscriptionScope.js +0 -91
  69. package/dist/native/implementation/subscriptionScope.js.map +0 -1
  70. package/dist/native/implementation/symbols.js +0 -4
  71. package/dist/native/implementation/symbols.js.map +0 -1
  72. package/dist/native/index.native.js +0 -3
  73. package/dist/native/index.native.js.map +0 -1
  74. package/dist/native/internal.js +0 -18
  75. package/dist/native/internal.js.map +0 -1
  76. package/dist/native/lib/cache.js +0 -13
  77. package/dist/native/lib/cache.js.map +0 -1
  78. package/dist/native/lib/cache.test.js +0 -52
  79. package/dist/native/lib/cache.test.js.map +0 -1
  80. package/dist/web/coValues/account.js +0 -221
  81. package/dist/web/coValues/account.js.map +0 -1
  82. package/dist/web/coValues/coFeed.js +0 -571
  83. package/dist/web/coValues/coFeed.js.map +0 -1
  84. package/dist/web/coValues/coList.js +0 -404
  85. package/dist/web/coValues/coList.js.map +0 -1
  86. package/dist/web/coValues/coMap.js +0 -537
  87. package/dist/web/coValues/coMap.js.map +0 -1
  88. package/dist/web/coValues/deepLoading.js +0 -63
  89. package/dist/web/coValues/deepLoading.js.map +0 -1
  90. package/dist/web/coValues/extensions/imageDef.js +0 -42
  91. package/dist/web/coValues/extensions/imageDef.js.map +0 -1
  92. package/dist/web/coValues/group.js +0 -136
  93. package/dist/web/coValues/group.js.map +0 -1
  94. package/dist/web/coValues/interfaces.js +0 -135
  95. package/dist/web/coValues/interfaces.js.map +0 -1
  96. package/dist/web/coValues/schemaUnion.js +0 -89
  97. package/dist/web/coValues/schemaUnion.js.map +0 -1
  98. package/dist/web/exports.js +0 -5
  99. package/dist/web/exports.js.map +0 -1
  100. package/dist/web/implementation/createContext.js +0 -157
  101. package/dist/web/implementation/createContext.js.map +0 -1
  102. package/dist/web/implementation/devtoolsFormatters.js +0 -95
  103. package/dist/web/implementation/devtoolsFormatters.js.map +0 -1
  104. package/dist/web/implementation/errors.js +0 -2
  105. package/dist/web/implementation/errors.js.map +0 -1
  106. package/dist/web/implementation/inspect.js +0 -2
  107. package/dist/web/implementation/inspect.js.map +0 -1
  108. package/dist/web/implementation/refs.js +0 -115
  109. package/dist/web/implementation/refs.js.map +0 -1
  110. package/dist/web/implementation/schema.js +0 -96
  111. package/dist/web/implementation/schema.js.map +0 -1
  112. package/dist/web/implementation/subscriptionScope.js +0 -91
  113. package/dist/web/implementation/subscriptionScope.js.map +0 -1
  114. package/dist/web/implementation/symbols.js +0 -4
  115. package/dist/web/implementation/symbols.js.map +0 -1
  116. package/dist/web/index.web.js +0 -3
  117. package/dist/web/index.web.js.map +0 -1
  118. package/dist/web/internal.js +0 -18
  119. package/dist/web/internal.js.map +0 -1
  120. package/dist/web/lib/cache.js +0 -13
  121. package/dist/web/lib/cache.js.map +0 -1
  122. package/dist/web/lib/cache.test.js +0 -52
  123. package/dist/web/lib/cache.test.js.map +0 -1
@@ -1,17 +1,18 @@
1
1
  import type { CojsonInternalTypes, RawCoValue } from "cojson";
2
2
  import { RawAccount } from "cojson";
3
+ import { AnonymousJazzAgent } from "../implementation/anonymousJazzAgent.js";
3
4
  import type { DeeplyLoaded, DepthsIn } from "../internal.js";
4
5
  import {
5
- Account,
6
- AnonymousJazzAgent,
7
- Group,
8
6
  Ref,
9
7
  SubscriptionScope,
10
8
  inspect,
11
9
  subscriptionsScopes,
12
10
  } from "../internal.js";
13
11
  import { coValuesCache } from "../lib/cache.js";
12
+ import { type Account } from "./account.js";
14
13
  import { fulfillsDepth } from "./deepLoading.js";
14
+ import { type Group } from "./group.js";
15
+ import { RegisteredSchemas } from "./registeredSchemas.js";
15
16
 
16
17
  /** @category Abstract interfaces */
17
18
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -85,8 +86,8 @@ export class CoValueBase implements CoValue {
85
86
  get _owner(): Account | Group {
86
87
  const owner =
87
88
  this._raw.group instanceof RawAccount
88
- ? Account.fromRaw(this._raw.group)
89
- : Group.fromRaw(this._raw.group);
89
+ ? RegisteredSchemas["Account"].fromRaw(this._raw.group)
90
+ : RegisteredSchemas["Group"].fromRaw(this._raw.group);
90
91
 
91
92
  const subScope = subscriptionsScopes.get(this);
92
93
  if (subScope) {
@@ -102,7 +103,9 @@ export class CoValueBase implements CoValue {
102
103
  const rawAccount = this._raw.core.node.account;
103
104
 
104
105
  if (rawAccount instanceof RawAccount) {
105
- return coValuesCache.get(rawAccount, () => Account.fromRaw(rawAccount));
106
+ return coValuesCache.get(rawAccount, () =>
107
+ RegisteredSchemas["Account"].fromRaw(rawAccount),
108
+ );
106
109
  }
107
110
 
108
111
  return new AnonymousJazzAgent(this._raw.core.node);
@@ -0,0 +1,11 @@
1
+ import { CoID } from "cojson";
2
+ import { co } from "../internal.js";
3
+ import { CoMap } from "./coMap.js";
4
+ import { InboxInvite, InboxRoot } from "./inbox.js";
5
+
6
+ /** @category Identity & Permissions */
7
+ export class Profile extends CoMap {
8
+ name = co.string;
9
+ inbox = co.optional.json<CoID<InboxRoot>>();
10
+ inboxInvite = co.optional.json<InboxInvite>();
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { Account } from "./account.js";
2
+ import type { CoMap } from "./coMap.js";
3
+ import type { Group } from "./group.js";
4
+
5
+ /**
6
+ * Regisering schemas into this Record to avoid circular dependencies.
7
+ */
8
+ export const RegisteredSchemas = {} as {
9
+ Account: typeof Account;
10
+ Group: typeof Group;
11
+ CoMap: typeof CoMap;
12
+ };
package/src/exports.ts CHANGED
@@ -12,25 +12,31 @@ export type { CoValue, ID } from "./internal.js";
12
12
 
13
13
  export { Encoders, co } from "./internal.js";
14
14
 
15
+ export {
16
+ Inbox,
17
+ InboxSender,
18
+ } from "./coValues/inbox.js";
19
+
15
20
  export {
16
21
  Account,
17
- FileStream,
18
- BinaryCoStream,
19
- CoList,
20
- CoMap,
21
- CoFeed,
22
- CoStream,
23
- CoValueBase,
24
- Group,
25
- ImageDefinition,
26
- Profile,
27
22
  isControlledAccount,
28
- SchemaUnion,
29
23
  type AccountClass,
30
- type CoMapInit,
31
- type CoValueClass,
32
- } from "./internal.js";
33
- export type { DeeplyLoaded, DepthsIn } from "./internal.js";
24
+ } from "./coValues/account.js";
25
+ export { Group } from "./coValues/group.js";
26
+ export {
27
+ CoStream,
28
+ CoFeed,
29
+ FileStream,
30
+ BinaryCoStream,
31
+ } from "./coValues/coFeed.js";
32
+ export { CoList } from "./coValues/coList.js";
33
+ export { CoMap, type CoMapInit } from "./coValues/coMap.js";
34
+ export { CoValueBase } from "./coValues/interfaces.js";
35
+ export { ImageDefinition } from "./coValues/extensions/imageDef.js";
36
+ export { Profile } from "./coValues/profile.js";
37
+ export { SchemaUnion } from "./coValues/schemaUnion.js";
38
+
39
+ export type { CoValueClass, DeeplyLoaded, DepthsIn } from "./internal.js";
34
40
 
35
41
  export {
36
42
  createCoValueObservable,
@@ -0,0 +1,6 @@
1
+ import { LocalNode } from "cojson";
2
+
3
+ export class AnonymousJazzAgent {
4
+ _type = "Anonymous" as const;
5
+ constructor(public node: LocalNode) {}
6
+ }
@@ -9,7 +9,10 @@ import {
9
9
  RawAccountID,
10
10
  SessionID,
11
11
  } from "cojson";
12
- import { Account, AccountClass, ID } from "../internal.js";
12
+ import { type Account, type AccountClass } from "../coValues/account.js";
13
+ import { RegisteredSchemas } from "../coValues/registeredSchemas.js";
14
+ import type { ID } from "../internal.js";
15
+ import { AnonymousJazzAgent } from "./anonymousJazzAgent.js";
13
16
 
14
17
  export type Credentials = {
15
18
  accountID: ID<Account>;
@@ -136,7 +139,8 @@ export async function createJazzContext<Acc extends Account>(
136
139
 
137
140
  const { auth, sessionProvider, peersToLoadFrom, crypto } = options;
138
141
  const AccountSchema =
139
- options.AccountSchema ?? (Account as unknown as AccountClass<Acc>);
142
+ options.AccountSchema ??
143
+ (RegisteredSchemas["Account"] as unknown as AccountClass<Acc>);
140
144
  let authResult: AuthResult;
141
145
  try {
142
146
  authResult = await auth.start(crypto);
@@ -243,11 +247,6 @@ export async function createJazzContext<Acc extends Account>(
243
247
  }
244
248
  }
245
249
 
246
- export class AnonymousJazzAgent {
247
- _type = "Anonymous" as const;
248
- constructor(public node: LocalNode) {}
249
- }
250
-
251
250
  export async function createAnonymousJazzContext({
252
251
  peersToLoadFrom,
253
252
  crypto,
@@ -1,6 +1,6 @@
1
1
  import type { CoID, RawCoValue } from "cojson";
2
+ import { type Account } from "../coValues/account.js";
2
3
  import type {
3
- Account,
4
4
  AnonymousJazzAgent,
5
5
  CoValue,
6
6
  ID,
@@ -1,6 +1,6 @@
1
1
  import type { RawCoValue } from "cojson";
2
+ import { type Account } from "../coValues/account.js";
2
3
  import type {
3
- Account,
4
4
  AnonymousJazzAgent,
5
5
  CoValue,
6
6
  CoValueClass,
@@ -2,6 +2,5 @@ export * from "./exports.js";
2
2
 
3
3
  export {
4
4
  MAX_RECOMMENDED_TX_SIZE,
5
- PureJSCrypto,
6
5
  cojsonInternals,
7
6
  } from "cojson/native";
package/src/internal.ts CHANGED
@@ -2,21 +2,13 @@ export * from "./implementation/symbols.js";
2
2
  export * from "./implementation/inspect.js";
3
3
  export * from "./coValues/interfaces.js";
4
4
 
5
- export * from "./coValues/coMap.js";
6
- export * from "./coValues/account.js";
7
- export * from "./coValues/coList.js";
8
- export * from "./coValues/coFeed.js";
9
- export * from "./coValues/schemaUnion.js";
10
- export * from "./coValues/group.js";
11
-
12
5
  export * from "./implementation/errors.js";
6
+ export * from "./implementation/anonymousJazzAgent.js";
13
7
  export * from "./implementation/refs.js";
14
8
  export * from "./implementation/schema.js";
15
9
  export * from "./implementation/subscriptionScope.js";
16
10
  export * from "./coValues/deepLoading.js";
17
11
 
18
- export * from "./coValues/extensions/imageDef.js";
19
-
20
12
  export * from "./implementation/createContext.js";
21
13
 
22
14
  import "./implementation/devtoolsFormatters.js";
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from "vitest";
2
- import { CoMap, co } from "../internal";
3
- import { setupTwoNodes } from "./utils";
2
+ import { CoMap, co } from "../exports.js";
3
+ import { setupTwoNodes } from "./utils.js";
4
4
 
5
5
  test("waitForAllCoValuesSync should resolve when all the values are synced", async () => {
6
6
  class TestMap extends CoMap {
@@ -192,15 +192,6 @@ describe("CoFeed resolution", async () => {
192
192
  const queue = new cojsonInternals.Channel();
193
193
 
194
194
  TestStream.subscribe(stream.id, meOnSecondPeer, [], (subscribedStream) => {
195
- console.log("subscribedStream[me.id]", subscribedStream[me.id]);
196
- console.log(
197
- "subscribedStream[me.id]?.value?.[me.id]?.value",
198
- subscribedStream[me.id]?.value?.[me.id]?.value,
199
- );
200
- console.log(
201
- "subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value",
202
- subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value,
203
- );
204
195
  void queue.push(subscribedStream);
205
196
  });
206
197
 
@@ -1,5 +1,6 @@
1
1
  import { connectedPeers } from "cojson/src/streamUtils.ts";
2
2
  import { describe, expect, expectTypeOf, test } from "vitest";
3
+ import { Group, randomSessionProvider } from "../exports.js";
3
4
  import {
4
5
  Account,
5
6
  CoMap,
@@ -11,8 +12,7 @@ import {
11
12
  fixedCredentialsAuth,
12
13
  isControlledAccount,
13
14
  } from "../index.web.js";
14
- import { Group, randomSessionProvider } from "../internal.js";
15
- import { loadCoValueOrFail, setupTwoNodes } from "./utils.js";
15
+ import { setupTwoNodes } from "./utils.js";
16
16
 
17
17
  const Crypto = await WasmCrypto.create();
18
18
 
@@ -1,7 +1,7 @@
1
1
  import { RawGroup } from "cojson";
2
2
  import { describe, expect, test } from "vitest";
3
3
  import { Account, CoMap, Group, WasmCrypto, co } from "../index.web.js";
4
- import { setupTwoNodes, waitFor } from "./utils.js";
4
+ import { setupTwoNodes } from "./utils.js";
5
5
 
6
6
  const Crypto = await WasmCrypto.create();
7
7
 
@@ -0,0 +1,256 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { CoMap } from "../coValues/coMap";
3
+ import { Group } from "../coValues/group";
4
+ import { Inbox, InboxSender } from "../coValues/inbox";
5
+ import { co } from "../internal";
6
+ import { setupTwoNodes, waitFor } from "./utils";
7
+
8
+ class Message extends CoMap {
9
+ text = co.string;
10
+ }
11
+
12
+ describe("Inbox", () => {
13
+ it("should create inbox and allow message exchange between accounts", async () => {
14
+ const { clientAccount: sender, serverAccount: receiver } =
15
+ await setupTwoNodes();
16
+
17
+ const receiverInbox = await Inbox.load(receiver);
18
+
19
+ // Create a message from sender
20
+ const message = Message.create(
21
+ { text: "Hello" },
22
+ {
23
+ owner: Group.create({ owner: sender }),
24
+ },
25
+ );
26
+
27
+ // Setup inbox sender
28
+ const inboxSender = await InboxSender.load(receiver.id, sender);
29
+ inboxSender.sendMessage(message);
30
+
31
+ // Track received messages
32
+ const receivedMessages: Message[] = [];
33
+ let senderAccountID: unknown = undefined;
34
+
35
+ // Subscribe to inbox messages
36
+ const unsubscribe = receiverInbox.subscribe(
37
+ Message,
38
+ async (message, id) => {
39
+ senderAccountID = id;
40
+ receivedMessages.push(message);
41
+ },
42
+ );
43
+
44
+ // Wait for message to be received
45
+ await waitFor(() => receivedMessages.length === 1);
46
+
47
+ expect(receivedMessages.length).toBe(1);
48
+ expect(receivedMessages[0]?.text).toBe("Hello");
49
+ expect(senderAccountID).toBe(sender.id);
50
+
51
+ unsubscribe();
52
+ });
53
+
54
+ it("should return the result of the message", async () => {
55
+ const { clientAccount: sender, serverAccount: receiver } =
56
+ await setupTwoNodes();
57
+
58
+ const receiverInbox = await Inbox.load(receiver);
59
+
60
+ // Create a message from sender
61
+ const message = Message.create(
62
+ { text: "Hello" },
63
+ {
64
+ owner: Group.create({ owner: sender }),
65
+ },
66
+ );
67
+
68
+ const unsubscribe = receiverInbox.subscribe(Message, async (message) => {
69
+ return Message.create(
70
+ { text: "Responded from the inbox" },
71
+ { owner: message._owner },
72
+ );
73
+ });
74
+
75
+ // Setup inbox sender
76
+ const inboxSender = await InboxSender.load<Message, Message>(
77
+ receiver.id,
78
+ sender,
79
+ );
80
+ const resultId = await inboxSender.sendMessage(message);
81
+
82
+ const result = await Message.load(resultId, receiver, {});
83
+ expect(result?.text).toBe("Responded from the inbox");
84
+
85
+ unsubscribe();
86
+ });
87
+
88
+ it("should return the undefined if the subscription returns undefined", async () => {
89
+ const { clientAccount: sender, serverAccount: receiver } =
90
+ await setupTwoNodes();
91
+
92
+ const receiverInbox = await Inbox.load(receiver);
93
+
94
+ // Create a message from sender
95
+ const message = Message.create(
96
+ { text: "Hello" },
97
+ {
98
+ owner: Group.create({ owner: sender }),
99
+ },
100
+ );
101
+
102
+ const unsubscribe = receiverInbox.subscribe(Message, async (message) => {});
103
+
104
+ // Setup inbox sender
105
+ const inboxSender = await InboxSender.load<Message>(receiver.id, sender);
106
+ const result = await inboxSender.sendMessage(message);
107
+
108
+ expect(result).toBeUndefined();
109
+
110
+ unsubscribe();
111
+ });
112
+
113
+ it("should reject if the subscription throws an error", async () => {
114
+ const { clientAccount: sender, serverAccount: receiver } =
115
+ await setupTwoNodes();
116
+
117
+ const receiverInbox = await Inbox.load(receiver);
118
+
119
+ // Create a message from sender
120
+ const message = Message.create(
121
+ { text: "Hello" },
122
+ {
123
+ owner: Group.create({ owner: sender }),
124
+ },
125
+ );
126
+
127
+ const unsubscribe = receiverInbox.subscribe(Message, async () => {
128
+ return Promise.reject(new Error("Failed"));
129
+ });
130
+
131
+ // Setup inbox sender
132
+ const inboxSender = await InboxSender.load<Message>(receiver.id, sender);
133
+
134
+ await expect(inboxSender.sendMessage(message)).rejects.toThrow(
135
+ "Error: Failed",
136
+ );
137
+
138
+ unsubscribe();
139
+ });
140
+
141
+ it("should mark messages as processed", async () => {
142
+ const { clientAccount: sender, serverAccount: receiver } =
143
+ await setupTwoNodes();
144
+
145
+ const receiverInbox = await Inbox.load(receiver);
146
+
147
+ // Create a message from sender
148
+ const message = Message.create(
149
+ { text: "Hello" },
150
+ {
151
+ owner: Group.create({ owner: sender }),
152
+ },
153
+ );
154
+
155
+ // Setup inbox sender
156
+ const inboxSender = await InboxSender.load(receiver.id, sender);
157
+ inboxSender.sendMessage(message);
158
+
159
+ // Track received messages
160
+ const receivedMessages: Message[] = [];
161
+
162
+ // Subscribe to inbox messages
163
+ const unsubscribe = receiverInbox.subscribe(Message, async (message) => {
164
+ receivedMessages.push(message);
165
+ });
166
+
167
+ // Wait for message to be received
168
+ await waitFor(() => receivedMessages.length === 1);
169
+
170
+ inboxSender.sendMessage(message);
171
+
172
+ await waitFor(() => receivedMessages.length === 2);
173
+
174
+ expect(receivedMessages.length).toBe(2);
175
+ expect(receivedMessages[0]?.text).toBe("Hello");
176
+ expect(receivedMessages[1]?.text).toBe("Hello");
177
+
178
+ unsubscribe();
179
+ });
180
+
181
+ it("should unsubscribe correctly", async () => {
182
+ const { clientAccount: sender, serverAccount: receiver } =
183
+ await setupTwoNodes();
184
+
185
+ const receiverInbox = await Inbox.load(receiver);
186
+
187
+ // Create a message from sender
188
+ const message = Message.create(
189
+ { text: "Hello" },
190
+ {
191
+ owner: Group.create({ owner: sender }),
192
+ },
193
+ );
194
+
195
+ // Setup inbox sender
196
+ const inboxSender = await InboxSender.load(receiver.id, sender);
197
+ inboxSender.sendMessage(message);
198
+
199
+ // Track received messages
200
+ const receivedMessages: Message[] = [];
201
+
202
+ // Subscribe to inbox messages
203
+ const unsubscribe = receiverInbox.subscribe(Message, async (message) => {
204
+ receivedMessages.push(message);
205
+ });
206
+
207
+ // Wait for message to be received
208
+ await waitFor(() => receivedMessages.length === 1);
209
+
210
+ unsubscribe();
211
+
212
+ inboxSender.sendMessage(message);
213
+
214
+ await new Promise((resolve) => setTimeout(resolve, 200));
215
+
216
+ expect(receivedMessages.length).toBe(1);
217
+ expect(receivedMessages[0]?.text).toBe("Hello");
218
+ });
219
+
220
+ it("should retry failed messages", async () => {
221
+ const { clientAccount: sender, serverAccount: receiver } =
222
+ await setupTwoNodes();
223
+
224
+ const receiverInbox = await Inbox.load(receiver);
225
+
226
+ // Create a message from sender
227
+ const message = Message.create(
228
+ { text: "Hello" },
229
+ {
230
+ owner: Group.create({ owner: sender }),
231
+ },
232
+ );
233
+
234
+ // Setup inbox sender
235
+ const inboxSender = await InboxSender.load(receiver.id, sender);
236
+ const promise = inboxSender.sendMessage(message);
237
+
238
+ let failures = 0;
239
+
240
+ // Subscribe to inbox messages
241
+ const unsubscribe = receiverInbox.subscribe(
242
+ Message,
243
+ async () => {
244
+ failures++;
245
+ throw new Error("Failed");
246
+ },
247
+ { retries: 2 },
248
+ );
249
+
250
+ await expect(promise).rejects.toThrow();
251
+ expect(failures).toBe(3);
252
+ const [failed] = Object.values(receiverInbox.failed.items).flat();
253
+ expect(failed?.value.errors.length).toBe(3);
254
+ unsubscribe();
255
+ });
256
+ });
@@ -7,7 +7,7 @@ import {
7
7
  co,
8
8
  loadCoValue,
9
9
  subscribeToCoValue,
10
- } from "../internal.js";
10
+ } from "../exports.js";
11
11
 
12
12
  class BaseWidget extends CoMap {
13
13
  type = co.string;
@@ -54,12 +54,26 @@ export async function setupTwoNodes() {
54
54
  peersToLoadFrom: [serverAsPeer],
55
55
  crypto: Crypto,
56
56
  creationProps: { name: "Client" },
57
+ migration: async (rawAccount, _node, creationProps) => {
58
+ const account = new Account({
59
+ fromRaw: rawAccount,
60
+ });
61
+
62
+ await account.migrate?.(creationProps);
63
+ },
57
64
  });
58
65
 
59
66
  const server = await LocalNode.withNewlyCreatedAccount({
60
67
  peersToLoadFrom: [clientAsPeer],
61
68
  crypto: Crypto,
62
69
  creationProps: { name: "Server" },
70
+ migration: async (rawAccount, _node, creationProps) => {
71
+ const account = new Account({
72
+ fromRaw: rawAccount,
73
+ });
74
+
75
+ await account.migrate?.(creationProps);
76
+ },
63
77
  });
64
78
 
65
79
  return {
package/tsup.config.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ "index.web": "src/index.web.ts",
6
+ "index.native": "src/index.native.ts",
7
+ },
8
+ format: ["esm"],
9
+ dts: false,
10
+ sourcemap: true,
11
+ clean: true,
12
+ minify: false,
13
+ });