jazz-tools 0.19.12 → 0.19.14
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 +44 -44
- package/CHANGELOG.md +18 -0
- package/dist/browser/createBrowserContext.d.ts +1 -5
- package/dist/browser/createBrowserContext.d.ts.map +1 -1
- package/dist/browser/index.js +124 -47
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts +12 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts +2 -0
- package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts +6 -0
- package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts.map +1 -0
- package/dist/browser/provideBrowserLockSession/index.d.ts +4 -0
- package/dist/browser/provideBrowserLockSession/index.d.ts.map +1 -0
- package/dist/{chunk-AGF4HEDH.js → chunk-GAPMDNJY.js} +437 -82
- package/dist/chunk-GAPMDNJY.js.map +1 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/react-native/index.js +41 -12
- package/dist/react-native/index.js.map +1 -1
- package/dist/react-native-core/ReactNativeSessionProvider.d.ts +11 -0
- package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -0
- package/dist/react-native-core/index.js +41 -12
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/platform.d.ts +2 -8
- package/dist/react-native-core/platform.d.ts.map +1 -1
- package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts +2 -0
- package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts.map +1 -0
- package/dist/testing.js +4 -3
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/coFeed.d.ts +2 -2
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/coList.d.ts +1 -2
- package/dist/tools/coValues/coList.d.ts.map +1 -1
- package/dist/tools/coValues/coMap.d.ts.map +1 -1
- package/dist/tools/coValues/coVector.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts +5 -1
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts +2 -1
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/exports.d.ts +2 -2
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/createContext.d.ts +21 -11
- package/dist/tools/implementation/createContext.d.ts.map +1 -1
- package/dist/tools/implementation/schema.d.ts +14 -6
- package/dist/tools/implementation/schema.d.ts.map +1 -1
- package/dist/tools/implementation/schemaUtils.d.ts +1 -1
- package/dist/tools/implementation/schemaUtils.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts +99 -0
- package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts.map +1 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +11 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +11 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +15 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +10 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +9 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +13 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +10 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts +12 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
- package/dist/tools/internal.d.ts +1 -0
- package/dist/tools/internal.d.ts.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/tests/schema.withPermissions.test.d.ts +2 -0
- package/dist/tools/tests/schema.withPermissions.test.d.ts.map +1 -0
- package/dist/worker/index.js +2 -2
- package/dist/worker/index.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/createBrowserContext.ts +3 -62
- package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +406 -0
- package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +132 -0
- package/src/browser/provideBrowserLockSession/SessionIDStorage.ts +33 -0
- package/src/browser/provideBrowserLockSession/index.ts +11 -0
- package/src/react-native-core/ReactNativeSessionProvider.ts +52 -0
- package/src/react-native-core/platform.ts +5 -30
- package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +124 -0
- package/src/tools/coValues/account.ts +4 -0
- package/src/tools/coValues/coFeed.ts +8 -3
- package/src/tools/coValues/coList.ts +6 -3
- package/src/tools/coValues/coMap.ts +10 -0
- package/src/tools/coValues/coVector.ts +2 -1
- package/src/tools/coValues/group.ts +6 -4
- package/src/tools/coValues/interfaces.ts +19 -7
- package/src/tools/exports.ts +3 -1
- package/src/tools/implementation/createContext.ts +43 -15
- package/src/tools/implementation/schema.ts +23 -13
- package/src/tools/implementation/schemaUtils.ts +1 -1
- package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +105 -4
- package/src/tools/implementation/zodSchema/schemaPermissions.ts +188 -0
- package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +46 -3
- package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +46 -3
- package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +50 -13
- package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +14 -0
- package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +24 -1
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +51 -4
- package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +25 -1
- package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +21 -1
- package/src/tools/implementation/zodSchema/unionUtils.ts +72 -20
- package/src/tools/internal.ts +1 -0
- package/src/tools/testing.ts +3 -1
- package/src/tools/tests/ContextManager.test.ts +2 -1
- package/src/tools/tests/coPlainText.test.ts +2 -2
- package/src/tools/tests/createContext.test.ts +79 -1
- package/src/tools/tests/deepLoading.test.ts +25 -2
- package/src/tools/tests/schema.resolved.test.ts +10 -0
- package/src/tools/tests/schema.withPermissions.test.ts +859 -0
- package/src/tools/tests/utils.ts +2 -2
- package/src/worker/index.ts +2 -2
- package/dist/chunk-AGF4HEDH.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/tools/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EACL,OAAO,EACP,YAAY,EACZ,KAAK,kBAAkB,EAEvB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,2BAA2B,EAC3B,2BAA2B,EAM5B,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/tools/testing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.withPermissions.test.d.ts","sourceRoot":"","sources":["../../../src/tools/tests/schema.withPermissions.test.ts"],"names":[],"mappings":""}
|
package/dist/worker/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Account,
|
|
8
8
|
Inbox,
|
|
9
9
|
createJazzContextFromExistingCredentials,
|
|
10
|
-
|
|
10
|
+
MockSessionProvider
|
|
11
11
|
} from "jazz-tools";
|
|
12
12
|
async function startWorker(options) {
|
|
13
13
|
const {
|
|
@@ -53,7 +53,7 @@ async function startWorker(options) {
|
|
|
53
53
|
secret: accountSecret
|
|
54
54
|
},
|
|
55
55
|
AccountSchema,
|
|
56
|
-
sessionProvider:
|
|
56
|
+
sessionProvider: new MockSessionProvider(),
|
|
57
57
|
peers,
|
|
58
58
|
crypto: options.crypto ?? await WasmCrypto.create(),
|
|
59
59
|
asActiveAccount,
|
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
|
|
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 const 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 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 */\n waitForConnection() {\n return wsPeer.waitUntilConnected();\n },\n subscribeToConnectionChange(listener: (connected: boolean) => void) {\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":";AAOA;AAAA,EAEE;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EAIA;AAAA,EAGA;AAAA,EACA;AAAA,OACK;AAyBP,eAAsB,YAIpB,SAA2B;AAC3B,QAAM;AAAA,IACJ,YAAY,QAAQ,IAAI;AAAA,IACxB,gBAAgB,QAAQ,IAAI;AAAA,IAC5B,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB,IAAI;AAEJ,MAAI,OAA8B;AAElC,QAAM,QAAgB,CAAC;AAEvB,QAAM,SAAS,IAAI,8BAA8B;AAAA,IAC/C,MAAM;AAAA,IACN,qBAAqB;AAAA,IACrB,SAAS,CAAC,SAAS;AACjB,UAAI,MAAM;AACR,aAAK,YAAY,QAAQ,IAAI;AAAA,MAC/B,OAAO;AACL,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,IACA,YAAY,MAAM;AAAA,IAAC;AAAA,IACnB,sBAAsB,QAAQ;AAAA,EAChC,CAAC;AAED,SAAO,OAAO;AAEd,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC;AACA,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,MAAI,CAAC,UAAU,WAAW,KAAK,GAAG;AAChC,UAAM,IAAI,MAAM,mBAAmB;AAAA,EACrC;AACA,MAAI,CAAC,eAAe,WAAW,eAAe,GAAG;AAC/C,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC;AAEA,QAAM,UAAU,MAAM,yCAAyC;AAAA,IAC7D,aAAa;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA;AAAA,IACA,iBAAiB,IAAI,oBAAoB;AAAA,IACzC;AAAA,IACA,QAAQ,QAAQ,UAAW,MAAM,WAAW,OAAO;AAAA,IACnD;AAAA,IACA,SAAS,QAAQ;AAAA,EACnB,CAAC;AAED,QAAM,UAAU,QAAQ;AACxB,SAAO,QAAQ,MAAM;AAErB,MAAI,CAAC,QAAQ,MAAM,KAAK,SAAS,IAAI;AACnC,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,QAAM,QAAQ,gBAAgB,SAAY,MAAM,MAAM,KAAK,OAAO;AAElE,iBAAe,OAAO;AACpB,UAAM,QAAQ,QAAQ,MAAM,uBAAuB;AAEnD,WAAO,QAAQ;AACf,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,iBAAiB,QACnB;AAAA,IACE,WAAW,MAAM,UAAU,KAAK,KAAK;AAAA,EACvC,IACA;AAAA,IACE,WAAW,MAAM;AAAA,IAAC;AAAA,EACpB;AAEJ,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,QAAQ,QAAQ;AAAA,IAChB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMZ,OAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,oBAAoB;AAClB,aAAO,OAAO,mBAAmB;AAAA,IACnC;AAAA,IACA,4BAA4B,UAAwC;AAClE,aAAO,UAAU,QAAQ;AAEzB,aAAO,MAAM;AACX,eAAO,YAAY,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,iBAAiB;AACf,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
},
|
|
206
206
|
"type": "module",
|
|
207
207
|
"license": "MIT",
|
|
208
|
-
"version": "0.19.
|
|
208
|
+
"version": "0.19.14",
|
|
209
209
|
"dependencies": {
|
|
210
210
|
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
|
211
211
|
"@scure/base": "1.2.1",
|
|
@@ -222,9 +222,9 @@
|
|
|
222
222
|
"prosemirror-transform": "^1.9.0",
|
|
223
223
|
"use-sync-external-store": "^1.5.0",
|
|
224
224
|
"zod": "4.1.11",
|
|
225
|
-
"cojson": "0.19.
|
|
226
|
-
"cojson-storage-indexeddb": "0.19.
|
|
227
|
-
"cojson-transport-ws": "0.19.
|
|
225
|
+
"cojson": "0.19.14",
|
|
226
|
+
"cojson-storage-indexeddb": "0.19.14",
|
|
227
|
+
"cojson-transport-ws": "0.19.14"
|
|
228
228
|
},
|
|
229
229
|
"devDependencies": {
|
|
230
230
|
"@scure/bip39": "^1.3.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LocalNode, Peer
|
|
1
|
+
import { LocalNode, Peer } from "cojson";
|
|
2
2
|
import { getIndexedDBStorage } from "cojson-storage-indexeddb";
|
|
3
3
|
import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
|
|
4
4
|
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import { createJazzContext } from "jazz-tools";
|
|
24
24
|
import { StorageConfig, getStorageOptions } from "./storageOptions.js";
|
|
25
25
|
import { setupInspector } from "./utils/export-account-inspector.js";
|
|
26
|
+
import { getBrowserLockSessionProvider } from "./provideBrowserLockSession/index.js";
|
|
26
27
|
|
|
27
28
|
setupInspector();
|
|
28
29
|
|
|
@@ -210,7 +211,7 @@ export async function createJazzBrowserContext<
|
|
|
210
211
|
crypto,
|
|
211
212
|
defaultProfileName: options.defaultProfileName,
|
|
212
213
|
AccountSchema: options.AccountSchema,
|
|
213
|
-
sessionProvider:
|
|
214
|
+
sessionProvider: getBrowserLockSessionProvider(),
|
|
214
215
|
authSecretStorage: options.authSecretStorage,
|
|
215
216
|
});
|
|
216
217
|
|
|
@@ -240,66 +241,6 @@ export type SessionProvider = (
|
|
|
240
241
|
accountID: ID<Account> | AgentID,
|
|
241
242
|
) => Promise<SessionID>;
|
|
242
243
|
|
|
243
|
-
export function provideBrowserLockSession(
|
|
244
|
-
accountID: ID<Account> | AgentID,
|
|
245
|
-
crypto: CryptoProvider,
|
|
246
|
-
) {
|
|
247
|
-
if (typeof navigator === "undefined" || !navigator.locks?.request) {
|
|
248
|
-
// Fallback to random session ID for each tab session
|
|
249
|
-
return Promise.resolve({
|
|
250
|
-
sessionID: crypto.newRandomSessionID(accountID as RawAccountID | AgentID),
|
|
251
|
-
sessionDone: () => {},
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
let sessionDone!: () => void;
|
|
256
|
-
const donePromise = new Promise<void>((resolve) => {
|
|
257
|
-
sessionDone = resolve;
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
let resolveSession: (sessionID: SessionID) => void;
|
|
261
|
-
const sessionPromise = new Promise<SessionID>((resolve) => {
|
|
262
|
-
resolveSession = resolve;
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
void (async function () {
|
|
266
|
-
for (let idx = 0; idx < 100; idx++) {
|
|
267
|
-
// To work better around StrictMode
|
|
268
|
-
for (let retry = 0; retry < 2; retry++) {
|
|
269
|
-
// console.debug("Trying to get lock", accountID + "_" + idx);
|
|
270
|
-
const sessionFinishedOrNoLock = await navigator.locks.request(
|
|
271
|
-
accountID + "_" + idx,
|
|
272
|
-
{ ifAvailable: true },
|
|
273
|
-
async (lock) => {
|
|
274
|
-
if (!lock) return "noLock";
|
|
275
|
-
|
|
276
|
-
const sessionID =
|
|
277
|
-
localStorage.getItem(accountID + "_" + idx) ||
|
|
278
|
-
crypto.newRandomSessionID(accountID as RawAccountID | AgentID);
|
|
279
|
-
localStorage.setItem(accountID + "_" + idx, sessionID);
|
|
280
|
-
|
|
281
|
-
resolveSession(sessionID as SessionID);
|
|
282
|
-
|
|
283
|
-
await donePromise;
|
|
284
|
-
console.log("Done with lock", accountID + "_" + idx, sessionID);
|
|
285
|
-
return "sessionFinished";
|
|
286
|
-
},
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
if (sessionFinishedOrNoLock === "sessionFinished") {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
throw new Error("Couldn't get lock on session after 100x2 tries");
|
|
295
|
-
})();
|
|
296
|
-
|
|
297
|
-
return sessionPromise.then((sessionID) => ({
|
|
298
|
-
sessionID,
|
|
299
|
-
sessionDone,
|
|
300
|
-
}));
|
|
301
|
-
}
|
|
302
|
-
|
|
303
244
|
/** @category Invite Links */
|
|
304
245
|
export function createInviteLink<C extends CoValue>(
|
|
305
246
|
value: C,
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
|
4
|
+
import { SessionID } from "cojson";
|
|
5
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
6
|
+
import { BrowserSessionProvider } from "./BrowserSessionProvider.js";
|
|
7
|
+
import { SessionIDStorage } from "./SessionIDStorage.js";
|
|
8
|
+
import { createJazzTestAccount } from "jazz-tools/testing";
|
|
9
|
+
import type { CryptoProvider } from "jazz-tools";
|
|
10
|
+
|
|
11
|
+
const Crypto = await WasmCrypto.create();
|
|
12
|
+
|
|
13
|
+
// Mock navigator.locks
|
|
14
|
+
interface LockInfo {
|
|
15
|
+
mode: "exclusive" | "shared";
|
|
16
|
+
release: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const mockLocks = new Map<string, LockInfo>();
|
|
20
|
+
|
|
21
|
+
function isLockAvailable(
|
|
22
|
+
name: string,
|
|
23
|
+
requestedMode: "exclusive" | "shared",
|
|
24
|
+
): boolean {
|
|
25
|
+
const existingLock = mockLocks.get(name);
|
|
26
|
+
if (!existingLock) {
|
|
27
|
+
return true; // No existing lock
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Exclusive locks block everything
|
|
31
|
+
if (existingLock.mode === "exclusive" || requestedMode === "exclusive") {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Shared locks can coexist with other shared locks
|
|
36
|
+
if (existingLock.mode === "shared" && requestedMode === "shared") {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createMockLock(
|
|
44
|
+
name: string,
|
|
45
|
+
mode: "exclusive" | "shared",
|
|
46
|
+
): {
|
|
47
|
+
lock: { name: string } | null;
|
|
48
|
+
release: () => void;
|
|
49
|
+
} {
|
|
50
|
+
if (!isLockAvailable(name, mode)) {
|
|
51
|
+
return { lock: null, release: () => {} };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Create new lock
|
|
55
|
+
const lockInfo: LockInfo = {
|
|
56
|
+
mode,
|
|
57
|
+
release: () => {
|
|
58
|
+
mockLocks.delete(name);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
mockLocks.set(name, lockInfo);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
lock: { name },
|
|
66
|
+
release: lockInfo.release,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
vi.stubGlobal("navigator", {
|
|
71
|
+
locks: {
|
|
72
|
+
request: vi.fn(
|
|
73
|
+
async (
|
|
74
|
+
name: string,
|
|
75
|
+
options: { mode?: "exclusive" | "shared"; ifAvailable?: boolean },
|
|
76
|
+
callback: (lock: { name: string } | null) => Promise<void> | void,
|
|
77
|
+
) => {
|
|
78
|
+
const mode = options?.mode || "exclusive";
|
|
79
|
+
const ifAvailable = options?.ifAvailable || false;
|
|
80
|
+
|
|
81
|
+
if (ifAvailable) {
|
|
82
|
+
const { lock } = createMockLock(name, mode);
|
|
83
|
+
if (!lock) {
|
|
84
|
+
// Lock not available, call callback with null and return immediately
|
|
85
|
+
await callback(null);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Lock available, call callback with lock
|
|
89
|
+
// The lock is held until the promise returned from callback resolves
|
|
90
|
+
const callbackPromise = callback(lock);
|
|
91
|
+
const result = await callbackPromise;
|
|
92
|
+
// Release lock after callback promise resolves
|
|
93
|
+
mockLocks.get(name)?.release();
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// For non-ifAvailable locks, wait until available
|
|
98
|
+
// In a real implementation, this would wait, but for tests we assume immediate availability
|
|
99
|
+
const { lock, release } = createMockLock(name, mode);
|
|
100
|
+
if (!lock) {
|
|
101
|
+
// This shouldn't happen in tests for non-ifAvailable locks
|
|
102
|
+
// But handle it gracefully
|
|
103
|
+
await callback(null);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const callbackPromise = callback(lock);
|
|
109
|
+
const result = await callbackPromise;
|
|
110
|
+
return result;
|
|
111
|
+
} finally {
|
|
112
|
+
release();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("BrowserSessionProvider", () => {
|
|
120
|
+
let sessionProvider: BrowserSessionProvider;
|
|
121
|
+
let account: Awaited<ReturnType<typeof createJazzTestAccount>>;
|
|
122
|
+
|
|
123
|
+
beforeEach(async () => {
|
|
124
|
+
// Clear localStorage
|
|
125
|
+
localStorage.clear();
|
|
126
|
+
|
|
127
|
+
// Clear mock locks
|
|
128
|
+
mockLocks.clear();
|
|
129
|
+
|
|
130
|
+
// Create new session provider instance
|
|
131
|
+
sessionProvider = new BrowserSessionProvider();
|
|
132
|
+
|
|
133
|
+
// Create test account
|
|
134
|
+
account = await createJazzTestAccount({
|
|
135
|
+
isCurrentActiveAccount: true,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("acquireSession", () => {
|
|
140
|
+
test("creates new session when none exists", async () => {
|
|
141
|
+
const accountID = account.$jazz.id;
|
|
142
|
+
|
|
143
|
+
// Verify no sessions exist
|
|
144
|
+
const existingSessionsBefore =
|
|
145
|
+
SessionIDStorage.getSessionsList(accountID);
|
|
146
|
+
expect(existingSessionsBefore).toEqual([]);
|
|
147
|
+
|
|
148
|
+
// Acquire session
|
|
149
|
+
const result = await sessionProvider.acquireSession(
|
|
150
|
+
accountID,
|
|
151
|
+
Crypto as CryptoProvider,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Verify a new session ID is generated
|
|
155
|
+
expect(result.sessionID).toBeDefined();
|
|
156
|
+
|
|
157
|
+
// Verify the session is stored in localStorage
|
|
158
|
+
const storedSessions = SessionIDStorage.getSessionsList(accountID);
|
|
159
|
+
expect(storedSessions).toHaveLength(1);
|
|
160
|
+
expect(storedSessions[0]).toBe(result.sessionID);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("returns existing session when available", async () => {
|
|
164
|
+
const accountID = account.$jazz.id;
|
|
165
|
+
const existingSessionID = "existing-session-id" as SessionID;
|
|
166
|
+
|
|
167
|
+
// Pre-populate localStorage with a session ID
|
|
168
|
+
SessionIDStorage.storeSessionID(accountID, existingSessionID, 0);
|
|
169
|
+
|
|
170
|
+
// Verify session exists before calling acquireSession
|
|
171
|
+
const sessionsBefore = SessionIDStorage.getSessionsList(accountID);
|
|
172
|
+
expect(sessionsBefore).toHaveLength(1);
|
|
173
|
+
expect(sessionsBefore[0]).toBe(existingSessionID);
|
|
174
|
+
|
|
175
|
+
// Acquire session
|
|
176
|
+
const result = await sessionProvider.acquireSession(
|
|
177
|
+
accountID,
|
|
178
|
+
Crypto as CryptoProvider,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// Verify the existing session ID is returned (not a new one)
|
|
182
|
+
expect(result.sessionID).toBe(existingSessionID);
|
|
183
|
+
|
|
184
|
+
// Verify no new session is created (same value still in store)
|
|
185
|
+
const sessionsAfter = SessionIDStorage.getSessionsList(accountID);
|
|
186
|
+
expect(sessionsAfter).toHaveLength(1);
|
|
187
|
+
expect(sessionsAfter[0]).toBe(existingSessionID);
|
|
188
|
+
expect(sessionsAfter[0]).toBe(result.sessionID);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("handles multiple sessions in list - skips locked sessions and returns next available", async () => {
|
|
192
|
+
const accountID = account.$jazz.id;
|
|
193
|
+
const session1 = "session-1" as SessionID;
|
|
194
|
+
const session2 = "session-2" as SessionID;
|
|
195
|
+
const session3 = "session-3" as SessionID;
|
|
196
|
+
|
|
197
|
+
// Pre-populate localStorage with multiple sessions
|
|
198
|
+
SessionIDStorage.storeSessionID(accountID, session1, 0);
|
|
199
|
+
SessionIDStorage.storeSessionID(accountID, session2, 1);
|
|
200
|
+
SessionIDStorage.storeSessionID(accountID, session3, 2);
|
|
201
|
+
|
|
202
|
+
// Verify sessions are stored
|
|
203
|
+
const sessionsBefore = SessionIDStorage.getSessionsList(accountID);
|
|
204
|
+
expect(sessionsBefore).toHaveLength(3);
|
|
205
|
+
|
|
206
|
+
// Lock the first session (index 0) by manually adding it to mockLocks
|
|
207
|
+
const lockName = `load_session_${session1}`;
|
|
208
|
+
mockLocks.set(lockName, {
|
|
209
|
+
mode: "exclusive",
|
|
210
|
+
release: () => {
|
|
211
|
+
mockLocks.delete(lockName);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Acquire session - should skip locked session1 and return session2
|
|
216
|
+
const result = await sessionProvider.acquireSession(
|
|
217
|
+
accountID,
|
|
218
|
+
Crypto as CryptoProvider,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Verify it returned session2 (next available, not session1 which is locked)
|
|
222
|
+
expect(result.sessionID).toBe(session2);
|
|
223
|
+
|
|
224
|
+
// Verify the returned session is from the existing list, not a new one
|
|
225
|
+
const allSessions = SessionIDStorage.getSessionsList(accountID);
|
|
226
|
+
expect(allSessions).toContain(result.sessionID);
|
|
227
|
+
expect([session1, session2, session3]).toContain(result.sessionID);
|
|
228
|
+
|
|
229
|
+
// Clean up the held lock
|
|
230
|
+
mockLocks.delete(lockName);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("creates new session when all existing sessions are locked", async () => {
|
|
234
|
+
const accountID = account.$jazz.id;
|
|
235
|
+
const session1 = "session-1" as SessionID;
|
|
236
|
+
const session2 = "session-2" as SessionID;
|
|
237
|
+
|
|
238
|
+
// Pre-populate localStorage with sessions
|
|
239
|
+
SessionIDStorage.storeSessionID(accountID, session1, 0);
|
|
240
|
+
SessionIDStorage.storeSessionID(accountID, session2, 1);
|
|
241
|
+
|
|
242
|
+
// Verify sessions are stored
|
|
243
|
+
const sessionsBefore = SessionIDStorage.getSessionsList(accountID);
|
|
244
|
+
expect(sessionsBefore).toHaveLength(2);
|
|
245
|
+
|
|
246
|
+
// Lock all existing sessions by manually adding them to mockLocks
|
|
247
|
+
const lock1Name = `load_session_${session1}`;
|
|
248
|
+
const lock2Name = `load_session_${session2}`;
|
|
249
|
+
mockLocks.set(lock1Name, {
|
|
250
|
+
mode: "exclusive",
|
|
251
|
+
release: () => {
|
|
252
|
+
mockLocks.delete(lock1Name);
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
mockLocks.set(lock2Name, {
|
|
256
|
+
mode: "exclusive",
|
|
257
|
+
release: () => {
|
|
258
|
+
mockLocks.delete(lock2Name);
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Acquire session - should create a new one since all existing are locked
|
|
263
|
+
const result = await sessionProvider.acquireSession(
|
|
264
|
+
accountID,
|
|
265
|
+
Crypto as CryptoProvider,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// Verify a new session is created (not one of the existing locked ones)
|
|
269
|
+
expect(result.sessionID).not.toBe(session1);
|
|
270
|
+
expect(result.sessionID).not.toBe(session2);
|
|
271
|
+
|
|
272
|
+
// Verify the new session is added to localStorage
|
|
273
|
+
const sessionsAfter = SessionIDStorage.getSessionsList(accountID);
|
|
274
|
+
expect(sessionsAfter).toHaveLength(3);
|
|
275
|
+
expect(sessionsAfter).toContain(result.sessionID);
|
|
276
|
+
|
|
277
|
+
// Clean up held locks
|
|
278
|
+
mockLocks.delete(lock1Name);
|
|
279
|
+
mockLocks.delete(lock2Name);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("releases lock when sessionDone is called", async () => {
|
|
283
|
+
const accountID = account.$jazz.id;
|
|
284
|
+
|
|
285
|
+
// Acquire a session
|
|
286
|
+
const result = await sessionProvider.acquireSession(
|
|
287
|
+
accountID,
|
|
288
|
+
Crypto as CryptoProvider,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const sessionID = result.sessionID;
|
|
292
|
+
|
|
293
|
+
// Call sessionDone to release the lock
|
|
294
|
+
result.sessionDone();
|
|
295
|
+
|
|
296
|
+
// Wait for async lock release
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
298
|
+
|
|
299
|
+
// Acquire a session
|
|
300
|
+
const result2 = await sessionProvider.acquireSession(
|
|
301
|
+
accountID,
|
|
302
|
+
Crypto as CryptoProvider,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
expect(result2.sessionID).toBe(sessionID);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe("persistSession", () => {
|
|
310
|
+
test("stores session ID correctly", async () => {
|
|
311
|
+
const accountID = account.$jazz.id;
|
|
312
|
+
const sessionID = "test-session-id" as SessionID;
|
|
313
|
+
|
|
314
|
+
// Verify no sessions exist before
|
|
315
|
+
const sessionsBefore = SessionIDStorage.getSessionsList(accountID);
|
|
316
|
+
expect(sessionsBefore).toEqual([]);
|
|
317
|
+
|
|
318
|
+
// Persist session
|
|
319
|
+
const result = await sessionProvider.persistSession(accountID, sessionID);
|
|
320
|
+
|
|
321
|
+
// Verify the session ID is stored in localStorage at index 0
|
|
322
|
+
const storedSessions = SessionIDStorage.getSessionsList(accountID);
|
|
323
|
+
expect(storedSessions).toHaveLength(1);
|
|
324
|
+
expect(storedSessions[0]).toBe(sessionID);
|
|
325
|
+
|
|
326
|
+
// Verify sessionDone callback is provided
|
|
327
|
+
expect(typeof result.sessionDone).toBe("function");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("adds to sessions list properly", async () => {
|
|
331
|
+
const accountID = account.$jazz.id;
|
|
332
|
+
const initialSessionID = "initial-session-id" as SessionID;
|
|
333
|
+
const newSessionID = "new-session-id" as SessionID;
|
|
334
|
+
|
|
335
|
+
// Pre-populate localStorage with one session (index 0)
|
|
336
|
+
SessionIDStorage.storeSessionID(accountID, initialSessionID, 0);
|
|
337
|
+
|
|
338
|
+
// Verify initial session is stored
|
|
339
|
+
const sessionsBefore = SessionIDStorage.getSessionsList(accountID);
|
|
340
|
+
expect(sessionsBefore).toHaveLength(1);
|
|
341
|
+
expect(sessionsBefore[0]).toBe(initialSessionID);
|
|
342
|
+
|
|
343
|
+
// Persist a new session ID
|
|
344
|
+
await sessionProvider.persistSession(accountID, newSessionID);
|
|
345
|
+
|
|
346
|
+
// Verify the new session is stored at index 1
|
|
347
|
+
const sessionsAfter = SessionIDStorage.getSessionsList(accountID);
|
|
348
|
+
expect(sessionsAfter).toHaveLength(2);
|
|
349
|
+
expect(sessionsAfter[0]).toBe(initialSessionID);
|
|
350
|
+
expect(sessionsAfter[1]).toBe(newSessionID);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("locks session when persisting", async () => {
|
|
354
|
+
const accountID = account.$jazz.id;
|
|
355
|
+
const sessionID = "persisted-session-id" as SessionID;
|
|
356
|
+
|
|
357
|
+
// Persist session - this should acquire a lock on the session
|
|
358
|
+
const { sessionDone } = await sessionProvider.persistSession(
|
|
359
|
+
accountID,
|
|
360
|
+
sessionID,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// Verify the session is locked by checking mockLocks directly
|
|
364
|
+
// The lock should be held until sessionDone is called
|
|
365
|
+
const lockName = `load_session_${sessionID}`;
|
|
366
|
+
expect(mockLocks.has(lockName)).toBe(true);
|
|
367
|
+
|
|
368
|
+
// Also verify we can't acquire the lock while it's held
|
|
369
|
+
// (This tests the isLockAvailable function)
|
|
370
|
+
expect(isLockAvailable(lockName, "exclusive")).toBe(false);
|
|
371
|
+
|
|
372
|
+
// Clean up by releasing the lock
|
|
373
|
+
sessionDone();
|
|
374
|
+
|
|
375
|
+
// After releasing, the lock should be removed
|
|
376
|
+
// Note: The lock might be removed asynchronously, so we check after a small delay
|
|
377
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
378
|
+
expect(mockLocks.has(lockName)).toBe(false);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("releases lock when sessionDone is called", async () => {
|
|
382
|
+
const accountID = account.$jazz.id;
|
|
383
|
+
const sessionID = "session-to-release" as SessionID;
|
|
384
|
+
|
|
385
|
+
// Persist a session
|
|
386
|
+
const { sessionDone } = await sessionProvider.persistSession(
|
|
387
|
+
accountID,
|
|
388
|
+
sessionID,
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
// Call sessionDone to release the lock
|
|
392
|
+
sessionDone();
|
|
393
|
+
|
|
394
|
+
// Wait for async lock release
|
|
395
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
396
|
+
|
|
397
|
+
// Acquire a session
|
|
398
|
+
const result = await sessionProvider.acquireSession(
|
|
399
|
+
accountID,
|
|
400
|
+
Crypto as CryptoProvider,
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
expect(result.sessionID).toBe(sessionID);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
});
|