jazz-tools 0.19.21 → 0.19.22
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.
- package/.turbo/turbo-build.log +47 -47
- package/CHANGELOG.md +15 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -1
- package/dist/media/{chunk-3LKBM3G3.js → chunk-IRL3KNPO.js} +2 -2
- package/dist/media/{chunk-3LKBM3G3.js.map → chunk-IRL3KNPO.js.map} +1 -1
- package/dist/media/create-image/react-native.d.ts +1 -1
- package/dist/media/create-image/react-native.d.ts.map +1 -1
- package/dist/media/index.browser.js +1 -1
- package/dist/media/index.js +1 -1
- package/dist/media/index.native.js +5 -5
- package/dist/media/index.native.js.map +1 -1
- package/dist/media/index.server.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/tools/exports.d.ts +1 -0
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/worker/JazzMessageChannel.d.ts +36 -0
- package/dist/worker/JazzMessageChannel.d.ts.map +1 -0
- package/dist/worker/index.d.ts +7 -1
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +28 -17
- package/dist/worker/index.js.map +1 -1
- package/package.json +4 -4
- package/src/media/create-image/react-native.ts +9 -7
- package/src/media/create-image-factory.test.ts +1 -1
- package/src/media/create-image-factory.ts +1 -1
- package/src/react-core/tests/testUtils.tsx +2 -2
- package/src/tools/exports.ts +5 -0
- package/src/tools/tests/testStorage.ts +2 -2
- package/src/worker/JazzMessageChannel.ts +73 -0
- package/src/worker/index.ts +36 -17
package/dist/worker/index.js
CHANGED
|
@@ -20,21 +20,26 @@ async function startWorker(options) {
|
|
|
20
20
|
} = options;
|
|
21
21
|
let node = void 0;
|
|
22
22
|
const peers = [];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
let wsPeer;
|
|
24
|
+
if (options.peer) {
|
|
25
|
+
peers.push(options.peer);
|
|
26
|
+
} else {
|
|
27
|
+
wsPeer = new WebSocketPeerWithReconnection({
|
|
28
|
+
peer: syncServer,
|
|
29
|
+
reconnectionTimeout: 100,
|
|
30
|
+
addPeer: (peer) => {
|
|
31
|
+
if (node) {
|
|
32
|
+
node.syncManager.addPeer(peer);
|
|
33
|
+
} else {
|
|
34
|
+
peers.push(peer);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
removePeer: () => {
|
|
38
|
+
},
|
|
39
|
+
WebSocketConstructor: options.WebSocket
|
|
40
|
+
});
|
|
41
|
+
wsPeer.enable();
|
|
42
|
+
}
|
|
38
43
|
if (!accountID) {
|
|
39
44
|
throw new Error("No accountID provided");
|
|
40
45
|
}
|
|
@@ -67,7 +72,7 @@ async function startWorker(options) {
|
|
|
67
72
|
const inbox = skipInboxLoad ? void 0 : await Inbox.load(account);
|
|
68
73
|
async function done() {
|
|
69
74
|
await context.account.$jazz.waitForAllCoValuesSync();
|
|
70
|
-
wsPeer
|
|
75
|
+
wsPeer?.disable();
|
|
71
76
|
context.done();
|
|
72
77
|
}
|
|
73
78
|
const inboxPublicApi = inbox ? {
|
|
@@ -93,11 +98,17 @@ async function startWorker(options) {
|
|
|
93
98
|
* Wait for the connection to the sync server to be established.
|
|
94
99
|
*
|
|
95
100
|
* If already connected, it will resolve immediately.
|
|
101
|
+
* Returns immediately if using a custom peer.
|
|
96
102
|
*/
|
|
97
103
|
waitForConnection() {
|
|
98
|
-
return wsPeer
|
|
104
|
+
return wsPeer?.waitUntilConnected() ?? Promise.resolve();
|
|
99
105
|
},
|
|
100
106
|
subscribeToConnectionChange(listener) {
|
|
107
|
+
if (!wsPeer) {
|
|
108
|
+
listener(true);
|
|
109
|
+
return () => {
|
|
110
|
+
};
|
|
111
|
+
}
|
|
101
112
|
wsPeer.subscribe(listener);
|
|
102
113
|
return () => {
|
|
103
114
|
wsPeer.unsubscribe(listener);
|
package/dist/worker/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/worker/index.ts"],"sourcesContent":["import {\n AgentSecret,\n CryptoProvider,\n LocalNode,\n Peer,\n StorageAPI,\n} from \"cojson\";\nimport {\n type AnyWebSocketConstructor,\n WebSocketPeerWithReconnection,\n} from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n Inbox,\n InstanceOfSchema,\n Loaded,\n createJazzContextFromExistingCredentials,\n MockSessionProvider,\n} from \"jazz-tools\";\n\ntype WorkerOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n accountID?: string;\n accountSecret?: string;\n syncServer?: string;\n WebSocket?: AnyWebSocketConstructor;\n AccountSchema?: S;\n crypto?: CryptoProvider;\n /**\n * If true, the inbox will not be loaded.\n */\n skipInboxLoad?: boolean;\n /**\n * If false, the worker will not set in the global account context\n */\n asActiveAccount?: boolean;\n storage?: StorageAPI;\n};\n\n/** @category Context Creation */\nexport async function startWorker<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: WorkerOptions<S>) {\n const {\n accountID = process.env.JAZZ_WORKER_ACCOUNT,\n accountSecret = process.env.JAZZ_WORKER_SECRET,\n syncServer = \"wss://cloud.jazz.tools\",\n AccountSchema = Account as unknown as S,\n skipInboxLoad = false,\n asActiveAccount = true,\n } = options;\n\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/worker/index.ts"],"sourcesContent":["import {\n AgentSecret,\n CryptoProvider,\n LocalNode,\n Peer,\n StorageAPI,\n} from \"cojson\";\nimport {\n type AnyWebSocketConstructor,\n WebSocketPeerWithReconnection,\n} from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n Inbox,\n InstanceOfSchema,\n Loaded,\n createJazzContextFromExistingCredentials,\n MockSessionProvider,\n} from \"jazz-tools\";\n\ntype WorkerOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n accountID?: string;\n accountSecret?: string;\n /**\n * A peer to connect to for synchronization.\n * If provided, syncServer is ignored.\n */\n peer?: Peer;\n syncServer?: string;\n WebSocket?: AnyWebSocketConstructor;\n AccountSchema?: S;\n crypto?: CryptoProvider;\n /**\n * If true, the inbox will not be loaded.\n */\n skipInboxLoad?: boolean;\n /**\n * If false, the worker will not set in the global account context\n */\n asActiveAccount?: boolean;\n storage?: StorageAPI;\n};\n\n/** @category Context Creation */\nexport async function startWorker<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: WorkerOptions<S>) {\n const {\n accountID = process.env.JAZZ_WORKER_ACCOUNT,\n accountSecret = process.env.JAZZ_WORKER_SECRET,\n syncServer = \"wss://cloud.jazz.tools\",\n AccountSchema = Account as unknown as S,\n skipInboxLoad = false,\n asActiveAccount = true,\n } = options;\n\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n // If a peer is provided directly, use it instead of WebSocket\n let wsPeer: WebSocketPeerWithReconnection | undefined;\n\n if (options.peer) {\n peers.push(options.peer);\n } else {\n wsPeer = new WebSocketPeerWithReconnection({\n peer: syncServer,\n reconnectionTimeout: 100,\n addPeer: (peer) => {\n if (node) {\n node.syncManager.addPeer(peer);\n } else {\n peers.push(peer);\n }\n },\n removePeer: () => {},\n WebSocketConstructor: options.WebSocket,\n });\n\n wsPeer.enable();\n }\n\n if (!accountID) {\n throw new Error(\"No accountID provided\");\n }\n if (!accountSecret) {\n throw new Error(\"No accountSecret provided\");\n }\n if (!accountID.startsWith(\"co_\")) {\n throw new Error(\"Invalid accountID\");\n }\n if (!accountSecret?.startsWith(\"sealerSecret_\")) {\n throw new Error(\"Invalid accountSecret\");\n }\n\n const context = await createJazzContextFromExistingCredentials({\n credentials: {\n accountID: accountID,\n secret: accountSecret as AgentSecret,\n },\n AccountSchema,\n sessionProvider: new MockSessionProvider(),\n peers,\n crypto: options.crypto ?? (await WasmCrypto.create()),\n asActiveAccount,\n storage: options.storage,\n });\n\n const account = context.account as InstanceOfSchema<S>;\n node = account.$jazz.localNode;\n\n if (!account.$jazz.refs.profile?.id) {\n throw new Error(\"Account has no profile\");\n }\n\n const inbox = skipInboxLoad ? undefined : await Inbox.load(account);\n\n async function done() {\n await context.account.$jazz.waitForAllCoValuesSync();\n\n wsPeer?.disable();\n context.done();\n }\n\n const inboxPublicApi = inbox\n ? {\n subscribe: inbox.subscribe.bind(inbox) as Inbox[\"subscribe\"],\n }\n : {\n subscribe: () => {},\n };\n\n return {\n /**\n * The worker account instance.\n */\n worker: context.account as Loaded<S>,\n experimental: {\n /**\n * API to subscribe to the inbox messages.\n *\n * More info on the Inbox API: https://jazz.tools/docs/react/server-side/inbox\n */\n inbox: inboxPublicApi,\n },\n /**\n * Wait for the connection to the sync server to be established.\n *\n * If already connected, it will resolve immediately.\n * Returns immediately if using a custom peer.\n */\n waitForConnection() {\n return wsPeer?.waitUntilConnected() ?? Promise.resolve();\n },\n subscribeToConnectionChange(listener: (connected: boolean) => void) {\n if (!wsPeer) {\n // For custom peers, immediately notify as connected\n listener(true);\n return () => {};\n }\n\n wsPeer.subscribe(listener);\n\n return () => {\n wsPeer.unsubscribe(listener);\n };\n },\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n *\n * @deprecated Use shutdownWorker\n */\n done,\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n */\n shutdownWorker() {\n return done();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
},
|
|
212
212
|
"type": "module",
|
|
213
213
|
"license": "MIT",
|
|
214
|
-
"version": "0.19.
|
|
214
|
+
"version": "0.19.22",
|
|
215
215
|
"dependencies": {
|
|
216
216
|
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
|
217
217
|
"@scure/base": "1.2.1",
|
|
@@ -228,9 +228,9 @@
|
|
|
228
228
|
"prosemirror-transform": "^1.9.0",
|
|
229
229
|
"use-sync-external-store": "^1.5.0",
|
|
230
230
|
"zod": "4.1.11",
|
|
231
|
-
"cojson": "0.19.
|
|
232
|
-
"cojson-storage-indexeddb": "0.19.
|
|
233
|
-
"cojson-transport-ws": "0.19.
|
|
231
|
+
"cojson": "0.19.22",
|
|
232
|
+
"cojson-storage-indexeddb": "0.19.22",
|
|
233
|
+
"cojson-transport-ws": "0.19.22"
|
|
234
234
|
},
|
|
235
235
|
"devDependencies": {
|
|
236
236
|
"@scure/bip39": "^1.3.0",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { NativeModules } from "react-native";
|
|
2
1
|
import type ImageResizerType from "@bam.tech/react-native-image-resizer";
|
|
3
2
|
import type ImageManipulatorType from "expo-image-manipulator";
|
|
4
3
|
import type { Account, Group } from "jazz-tools";
|
|
5
|
-
import { FileStream } from "jazz-tools";
|
|
4
|
+
import { co, type FileStream } from "jazz-tools";
|
|
6
5
|
import { Image } from "react-native";
|
|
7
6
|
import { createImageFactory } from "../create-image-factory";
|
|
8
7
|
|
|
@@ -108,7 +107,8 @@ async function getPlaceholderBase64(filePath: string): Promise<string> {
|
|
|
108
107
|
);
|
|
109
108
|
}
|
|
110
109
|
|
|
111
|
-
|
|
110
|
+
// Convert base64 to data URL
|
|
111
|
+
return "data:image/png;base64," + base64;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -147,7 +147,7 @@ async function resize(
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function getMimeType(filePath: string): Promise<string> {
|
|
150
|
+
async function getMimeType(filePath: string): Promise<string> {
|
|
151
151
|
return fetch(filePath)
|
|
152
152
|
.then((res) => res.blob())
|
|
153
153
|
.then((blob) => blob.type);
|
|
@@ -167,9 +167,11 @@ export async function createFileStreamFromSource(
|
|
|
167
167
|
const blob = await fetch(filePath).then((res) => res.blob());
|
|
168
168
|
const arrayBuffer = await toArrayBuffer(blob);
|
|
169
169
|
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
return co
|
|
171
|
+
.fileStream()
|
|
172
|
+
.createFromArrayBuffer(arrayBuffer, blob.type, undefined, {
|
|
173
|
+
owner,
|
|
174
|
+
});
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
// TODO: look for more efficient way to do this as React Native hasn't blob.arrayBuffer()
|
|
@@ -162,8 +162,8 @@ describe("createImage", async () => {
|
|
|
162
162
|
expect(image).toBeDefined();
|
|
163
163
|
expect(image.originalSize).toEqual([1920, 400]);
|
|
164
164
|
expect(image.placeholderDataURL).not.toBeDefined();
|
|
165
|
-
|
|
166
165
|
expect(image[`256x53`]).toBeDefined();
|
|
166
|
+
expect(image[`512x107`]).toBeDefined();
|
|
167
167
|
expect(image[`1024x213`]).toBeDefined();
|
|
168
168
|
expect(image[`2048x427`]).not.toBeDefined();
|
|
169
169
|
|
|
@@ -154,7 +154,7 @@ async function createImage<TSourceType, TResizeOutput>(
|
|
|
154
154
|
if (options?.progressive) {
|
|
155
155
|
imageCoValue.$jazz.set("progressive", true);
|
|
156
156
|
|
|
157
|
-
const resizes = ([256, 1024, 2048] as const).filter(
|
|
157
|
+
const resizes = ([256, 512, 1024, 2048] as const).filter(
|
|
158
158
|
(s) =>
|
|
159
159
|
s <
|
|
160
160
|
Math.max(imageCoValue.originalSize[0], imageCoValue.originalSize[1]),
|
package/src/tools/exports.ts
CHANGED
|
@@ -144,3 +144,8 @@ export * from "./ssr/index.js";
|
|
|
144
144
|
export { captureStack } from "./subscribe/errorReporting.js";
|
|
145
145
|
|
|
146
146
|
export * as jazzConfig from "./config.js";
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
JazzMessageChannel as experimental_JazzMessageChannel,
|
|
150
|
+
type JazzMessageChannelExposeOptions,
|
|
151
|
+
} from "../worker/JazzMessageChannel.js";
|
|
@@ -50,8 +50,8 @@ export async function createAsyncStorage({ filename }: { filename?: string }) {
|
|
|
50
50
|
new LibSQLSqliteAsyncDriver(getDbPath(filename)),
|
|
51
51
|
);
|
|
52
52
|
|
|
53
|
-
onTestFinished(() => {
|
|
54
|
-
storage.close();
|
|
53
|
+
onTestFinished(async () => {
|
|
54
|
+
await storage.close();
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
return storage;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { CojsonMessageChannel, type Peer } from "cojson";
|
|
2
|
+
import type {
|
|
3
|
+
WaitForConnectionOptions,
|
|
4
|
+
ExposeOptions,
|
|
5
|
+
PostMessageTarget,
|
|
6
|
+
} from "cojson/src/CojsonMessageChannel/types.js";
|
|
7
|
+
import { Account, AnonymousJazzAgent } from "jazz-tools";
|
|
8
|
+
import { activeAccountContext } from "../tools/implementation/activeAccountContext.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for JazzMessageChannel.expose()
|
|
12
|
+
*/
|
|
13
|
+
export interface JazzMessageChannelExposeOptions extends ExposeOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The account or anonymous agent to use for the connection.
|
|
16
|
+
* If not provided, falls back to the active account context.
|
|
17
|
+
*/
|
|
18
|
+
loadAs?: Account | AnonymousJazzAgent;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* JazzMessageChannel provides a high-level API for creating Jazz connections
|
|
23
|
+
* via the MessageChannel API. It wraps cojson's CojsonMessageChannel and
|
|
24
|
+
* automatically manages the node connection.
|
|
25
|
+
*/
|
|
26
|
+
export class JazzMessageChannel {
|
|
27
|
+
/**
|
|
28
|
+
* Expose a Jazz connection to a target.
|
|
29
|
+
* This is the host-side API, typically called from the main thread.
|
|
30
|
+
*
|
|
31
|
+
* @param target - Any object with a postMessage method (Worker, Window, etc.)
|
|
32
|
+
* @param opts - Configuration options including the account to use
|
|
33
|
+
* @returns A promise that resolves once the connection is established
|
|
34
|
+
*/
|
|
35
|
+
static async expose(
|
|
36
|
+
target: PostMessageTarget,
|
|
37
|
+
opts: JazzMessageChannelExposeOptions = {},
|
|
38
|
+
): Promise<void> {
|
|
39
|
+
const { loadAs, ...cojsonOpts } = opts;
|
|
40
|
+
|
|
41
|
+
// Get account from loadAs or fall back to active account context
|
|
42
|
+
const accountOrAgent = loadAs ?? activeAccountContext.maybeGet();
|
|
43
|
+
|
|
44
|
+
if (!accountOrAgent) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"No account provided and no active account context available",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const node =
|
|
51
|
+
accountOrAgent instanceof AnonymousJazzAgent
|
|
52
|
+
? accountOrAgent.node
|
|
53
|
+
: accountOrAgent.$jazz.localNode;
|
|
54
|
+
|
|
55
|
+
const peer = await CojsonMessageChannel.expose(target, cojsonOpts);
|
|
56
|
+
|
|
57
|
+
node.syncManager.addPeer(peer);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Accept an incoming Jazz connection.
|
|
62
|
+
* Same as cojson CojsonMessageChannel.waitForConnection().
|
|
63
|
+
*/
|
|
64
|
+
static waitForConnection(opts?: WaitForConnectionOptions): Promise<Peer> {
|
|
65
|
+
return CojsonMessageChannel.waitForConnection(opts);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Re-export types for convenience
|
|
70
|
+
export type {
|
|
71
|
+
WaitForConnectionOptions,
|
|
72
|
+
AcceptFromPortOptions,
|
|
73
|
+
} from "cojson/src/CojsonMessageChannel/types.js";
|
package/src/worker/index.ts
CHANGED
|
@@ -29,6 +29,11 @@ type WorkerOptions<
|
|
|
29
29
|
> = {
|
|
30
30
|
accountID?: string;
|
|
31
31
|
accountSecret?: string;
|
|
32
|
+
/**
|
|
33
|
+
* A peer to connect to for synchronization.
|
|
34
|
+
* If provided, syncServer is ignored.
|
|
35
|
+
*/
|
|
36
|
+
peer?: Peer;
|
|
32
37
|
syncServer?: string;
|
|
33
38
|
WebSocket?: AnyWebSocketConstructor;
|
|
34
39
|
AccountSchema?: S;
|
|
@@ -63,21 +68,28 @@ export async function startWorker<
|
|
|
63
68
|
|
|
64
69
|
const peers: Peer[] = [];
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
// If a peer is provided directly, use it instead of WebSocket
|
|
72
|
+
let wsPeer: WebSocketPeerWithReconnection | undefined;
|
|
73
|
+
|
|
74
|
+
if (options.peer) {
|
|
75
|
+
peers.push(options.peer);
|
|
76
|
+
} else {
|
|
77
|
+
wsPeer = new WebSocketPeerWithReconnection({
|
|
78
|
+
peer: syncServer,
|
|
79
|
+
reconnectionTimeout: 100,
|
|
80
|
+
addPeer: (peer) => {
|
|
81
|
+
if (node) {
|
|
82
|
+
node.syncManager.addPeer(peer);
|
|
83
|
+
} else {
|
|
84
|
+
peers.push(peer);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
removePeer: () => {},
|
|
88
|
+
WebSocketConstructor: options.WebSocket,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
wsPeer.enable();
|
|
92
|
+
}
|
|
81
93
|
|
|
82
94
|
if (!accountID) {
|
|
83
95
|
throw new Error("No accountID provided");
|
|
@@ -117,7 +129,7 @@ export async function startWorker<
|
|
|
117
129
|
async function done() {
|
|
118
130
|
await context.account.$jazz.waitForAllCoValuesSync();
|
|
119
131
|
|
|
120
|
-
wsPeer
|
|
132
|
+
wsPeer?.disable();
|
|
121
133
|
context.done();
|
|
122
134
|
}
|
|
123
135
|
|
|
@@ -146,11 +158,18 @@ export async function startWorker<
|
|
|
146
158
|
* Wait for the connection to the sync server to be established.
|
|
147
159
|
*
|
|
148
160
|
* If already connected, it will resolve immediately.
|
|
161
|
+
* Returns immediately if using a custom peer.
|
|
149
162
|
*/
|
|
150
163
|
waitForConnection() {
|
|
151
|
-
return wsPeer
|
|
164
|
+
return wsPeer?.waitUntilConnected() ?? Promise.resolve();
|
|
152
165
|
},
|
|
153
166
|
subscribeToConnectionChange(listener: (connected: boolean) => void) {
|
|
167
|
+
if (!wsPeer) {
|
|
168
|
+
// For custom peers, immediately notify as connected
|
|
169
|
+
listener(true);
|
|
170
|
+
return () => {};
|
|
171
|
+
}
|
|
172
|
+
|
|
154
173
|
wsPeer.subscribe(listener);
|
|
155
174
|
|
|
156
175
|
return () => {
|