{
  "version": 3,
  "sources": ["../src/useSync.ts"],
  "sourcesContent": ["import { atom, transact } from '@tldraw/state'\nimport {\n\tClientWebSocketAdapter,\n\tTLCustomMessageHandler,\n\tTLObjectStoreAccess,\n\tTLPersistentClientSocket,\n\tTLPresenceMode,\n\tTLRemoteSyncError,\n\tTLSocketClientSentEvent,\n\tTLSocketServerSentEvent,\n\tTLSyncClient,\n\tTLSyncErrorCloseEventReason,\n} from '@tldraw/sync-core'\nimport { useEffect } from 'react'\nimport {\n\tEditor,\n\tTAB_ID,\n\tTLAssetStore,\n\tTLPresenceStateInfo,\n\tTLRecord,\n\tTLStore,\n\tTLStoreSchemaOptions,\n\tTLStoreWithStatus,\n\tTLThemes,\n\tTLUser,\n\tTLUserStore,\n\tUserRecordType,\n\tcomputed,\n\tcreateCachedUserResolve,\n\tcreatePresenceStateDerivation,\n\tregisterColorsFromThemes,\n\tregisterFontsFromThemes,\n\tresolveThemes,\n\tcreateTLStore,\n\tcreateUserId,\n\tdefaultUserPreferences,\n\tdefaultUserStore,\n\tgetDefaultUserPresence,\n\tgetUserPreferences,\n\tuniqueId,\n\tuseEvent,\n\tuseReactiveEvent,\n\tuseRefState,\n\tuseTLSchemaFromUtils,\n\tuseValue,\n} from 'tldraw'\n\nconst MULTIPLAYER_EVENT_NAME = 'multiplayer.client'\n\nconst defaultCustomMessageHandler: TLCustomMessageHandler = () => {}\n\n// Keyed by `reason`, which is an arbitrary string off the server's close event, so a Map rather\n// than an object literal: a reason of 'constructor' or '__proto__' would find a prototype member.\nconst SYNC_ERROR_EVENT_NAMES = new Map<string, string>([\n\t[TLSyncErrorCloseEventReason.NOT_FOUND, 'room-not-found'],\n\t[TLSyncErrorCloseEventReason.FORBIDDEN, 'forbidden'],\n\t[TLSyncErrorCloseEventReason.NOT_AUTHENTICATED, 'not-authenticated'],\n\t[TLSyncErrorCloseEventReason.RATE_LIMITED, 'rate-limited'],\n])\n\ntype SyncSocket = TLPersistentClientSocket<\n\tTLSocketClientSentEvent<TLRecord>,\n\tTLSocketServerSentEvent<TLRecord>\n>\n\n// The socket reports 'error' but the collaboration status only knows 'offline'.\nfunction toCollaborationStatus(status: SyncSocket['connectionStatus']) {\n\treturn status === 'error' ? 'offline' : status\n}\n\n/**\n * A store wrapper specifically for remote collaboration that excludes local-only states.\n * This type represents a tldraw store that is synchronized with a remote multiplayer server.\n *\n * Unlike the base TLStoreWithStatus, this excludes 'synced-local' and 'not-synced' states\n * since remote stores are always either loading, connected to a server, or in an error state.\n *\n * @example\n * ```tsx\n * function MyCollaborativeApp() {\n *   const store: RemoteTLStoreWithStatus = useSync({\n *     uri: 'wss://myserver.com/sync/room-123',\n *     assets: myAssetStore\n *   })\n *\n *   if (store.status === 'loading') {\n *     return <div>Connecting to multiplayer session...</div>\n *   }\n *\n *   if (store.status === 'error') {\n *     return <div>Connection failed: {store.error.message}</div>\n *   }\n *\n *   // store.status === 'synced-remote'\n *   return <Tldraw store={store.store} />\n * }\n * ```\n *\n * @public\n */\nexport type RemoteTLStoreWithStatus =\n\t| Exclude<\n\t\t\tTLStoreWithStatus,\n\t\t\t{ status: 'synced-local' } | { status: 'not-synced' } | { status: 'synced-remote' }\n\t  >\n\t| (Extract<TLStoreWithStatus, { status: 'synced-remote' }> & {\n\t\t\t/**\n\t\t\t * Write access for object-store lane record types (e.g. comments), as granted by the\n\t\t\t * server for this session. Independent of the canvas read-only state, so a session can\n\t\t\t * be allowed to comment without being allowed to edit. Defaults to `'write'`.\n\t\t\t */\n\t\t\treadonly objectAccess: TLObjectStoreAccess\n\t  })\n\n/**\n * Creates a reactive store synchronized with a multiplayer server for real-time collaboration.\n *\n * This hook manages the complete lifecycle of a collaborative tldraw session, including\n * WebSocket connection establishment, state synchronization, user presence, and error handling.\n * The returned store can be passed directly to the Tldraw component to enable multiplayer features.\n *\n * The store progresses through multiple states:\n * - `loading`: Establishing connection and synchronizing initial state\n * - `synced-remote`: Successfully connected and actively synchronizing changes\n * - `error`: Connection failed or synchronization error occurred\n *\n * For optimal performance with media assets, provide an `assets` store that implements\n * external blob storage. Without this, large images and videos will be stored inline\n * as base64, causing performance issues during serialization.\n *\n * @param opts - Configuration options for multiplayer synchronization\n *   - `uri` - WebSocket server URI (string or async function returning URI)\n *   - `assets` - Asset store for blob storage (required for production use)\n *   - `users` - User store for identity, presence and attribution\n *   - `getUserPresence` - Optional function to customize presence data\n *   - `onCustomMessageReceived` - Handler for custom socket messages\n *   - `roomId` - Room identifier for analytics (internal use)\n *   - `onMount` - Callback when editor mounts (internal use)\n *   - `trackAnalyticsEvent` - Analytics event tracker (internal use)\n *\n * @returns A reactive store wrapper with connection status and synchronized store\n *\n * @example\n * ```tsx\n * // Basic multiplayer setup\n * function CollaborativeApp() {\n *   const store = useSync({\n *     uri: 'wss://myserver.com/sync/room-123',\n *     assets: myAssetStore,\n *     users: {\n *       currentUser: computed('current-user', () => ({\n *         id: createUserId('user-1'),\n *         name: 'Alice',\n *         color: '#ff0000',\n *         meta: {},\n *       })),\n *     }\n *   })\n *\n *   if (store.status === 'loading') {\n *     return <div>Connecting to collaboration session...</div>\n *   }\n *\n *   if (store.status === 'error') {\n *     return <div>Failed to connect: {store.error.message}</div>\n *   }\n *\n *   return <Tldraw store={store.store} />\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Dynamic authentication with user store\n * function AuthenticatedApp() {\n *   const store = useSync({\n *     uri: async () => {\n *       const token = await getAuthToken()\n *       return `wss://myserver.com/sync/room-123?token=${token}`\n *     },\n *     assets: authenticatedAssetStore,\n *     users: myUserStore,\n *     getUserPresence: (store, user) => {\n *       return {\n *         userId: user.id,\n *         userName: user.name,\n *         cursor: getCurrentCursor(store)\n *       }\n *     }\n *   })\n *\n *   return <Tldraw store={store.store} />\n * }\n * ```\n *\n * @public\n */\nexport function useSync(opts: UseSyncOptions & TLStoreSchemaOptions): RemoteTLStoreWithStatus {\n\tconst [state, setState] = useRefState<{\n\t\treadyClient?: TLSyncClient<TLRecord, TLStore>\n\t\terror?: Error\n\t\tobjectAccess?: TLObjectStoreAccess\n\t} | null>(null)\n\tconst {\n\t\turi,\n\t\troomId = 'default',\n\t\tassets,\n\t\tusers: _users,\n\t\tonMount,\n\t\tconnect,\n\t\ttrackAnalyticsEvent: track,\n\t\tgetUserPresence: _getUserPresence,\n\t\tonCustomMessageReceived: _onCustomMessageReceived,\n\t\tthemes,\n\t\t...schemaOpts\n\t} = opts\n\n\t// This line will throw a type error if we add any new options to the useSync hook but we don't destructure them\n\t// This is required because otherwise the useTLSchemaFromUtils might return a new schema on every render if the newly-added option\n\t// is allowed to be unstable\n\tconst __never__: never = 0 as any as keyof Omit<typeof schemaOpts, keyof TLStoreSchemaOptions>\n\n\tconst resolvedThemes = resolveThemes(themes)\n\tregisterColorsFromThemes(resolvedThemes)\n\tregisterFontsFromThemes(resolvedThemes)\n\tconst schema = useTLSchemaFromUtils(schemaOpts)\n\n\tconst getUserPresence = useReactiveEvent(\n\t\t(_getUserPresence ?? getDefaultUserPresence) as typeof getDefaultUserPresence\n\t)\n\tconst onCustomMessageReceived = useEvent(_onCustomMessageReceived ?? defaultCustomMessageHandler)\n\n\tuseEffect(() => {\n\t\tconst storeId = uniqueId()\n\n\t\tconst currentUserSignal = _users?.currentUser ?? defaultUserStore.currentUser\n\t\tconst users: Required<TLUserStore> = {\n\t\t\tcurrentUser: currentUserSignal,\n\t\t\tresolve:\n\t\t\t\t_users?.resolve ??\n\t\t\t\tcreateCachedUserResolve((userId) => {\n\t\t\t\t\tconst id = createUserId(userId)\n\t\t\t\t\tconst current = currentUserSignal.get()\n\t\t\t\t\tif (current && current.id === id) return current\n\t\t\t\t\t// Only the default user store falls back to presence records for other users.\n\t\t\t\t\tif (_users) return null\n\t\t\t\t\tconst match = store.query\n\t\t\t\t\t\t.records('instance_presence')\n\t\t\t\t\t\t.get()\n\t\t\t\t\t\t.find((p) => p.userId === id)\n\t\t\t\t\treturn match\n\t\t\t\t\t\t? UserRecordType.create({ id, name: match.userName, color: match.color })\n\t\t\t\t\t\t: null\n\t\t\t\t}),\n\t\t}\n\n\t\t// This always returns a non-null user for presence display, falling back\n\t\t// to anonymous user preferences. The store receives the raw `users` object\n\t\t// (where currentUser may return null), so attribution via\n\t\t// getAttributionUserId() correctly returns null for anonymous sessions.\n\t\tconst currentUser = computed<TLUser>('currentUser', () => {\n\t\t\tconst user = users.currentUser.get()\n\t\t\tif (user) return user\n\t\t\tconst prefs = getUserPreferences()\n\t\t\treturn UserRecordType.create({\n\t\t\t\tid: createUserId(prefs.id),\n\t\t\t\tname: prefs.name ?? '',\n\t\t\t\tcolor: prefs.color ?? defaultUserPreferences.color,\n\t\t\t})\n\t\t})\n\n\t\tlet socket: SyncSocket\n\t\tif (connect) {\n\t\t\tif (uri) {\n\t\t\t\tthrow new Error('uri and connect cannot be used together')\n\t\t\t}\n\n\t\t\tsocket = connect({ sessionId: TAB_ID, storeId }) as SyncSocket\n\t\t} else if (uri) {\n\t\t\tsocket = new ClientWebSocketAdapter(async () => {\n\t\t\t\t// set sessionId as a query param on the uri\n\t\t\t\tconst withParams = new URL(typeof uri === 'string' ? uri : await uri())\n\t\t\t\tfor (const param of ['sessionId', 'storeId']) {\n\t\t\t\t\tif (withParams.searchParams.has(param)) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`useSync. \"${param}\" is a reserved query param name. Please use a different name`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twithParams.searchParams.set('sessionId', TAB_ID)\n\t\t\t\twithParams.searchParams.set('storeId', storeId)\n\t\t\t\treturn withParams.toString()\n\t\t\t})\n\t\t} else {\n\t\t\tthrow new Error('uri or connect must be provided')\n\t\t}\n\n\t\tlet didCancel = false\n\n\t\tconst collaborationStatusSignal = atom(\n\t\t\t'collaboration status',\n\t\t\ttoCollaborationStatus(socket.connectionStatus)\n\t\t)\n\t\tconst unsubscribeFromConnectionStatus = socket.onStatusChange(() => {\n\t\t\tcollaborationStatusSignal.set(toCollaborationStatus(socket.connectionStatus))\n\t\t})\n\n\t\tconst syncMode = atom('sync mode', 'readwrite' as 'readonly' | 'readwrite')\n\n\t\tconst store = createTLStore({\n\t\t\tid: storeId,\n\t\t\tschema,\n\t\t\tassets,\n\t\t\tusers,\n\t\t\tonMount,\n\t\t\tcollaboration: {\n\t\t\t\tstatus: collaborationStatusSignal,\n\t\t\t\tmode: syncMode,\n\t\t\t},\n\t\t})\n\n\t\tconst presence = createPresenceStateDerivation(currentUser, {\n\t\t\tgetUserPresence,\n\t\t})(store)\n\n\t\t// Every connected session \u2014 each tab, window, or device \u2014 pushes its\n\t\t// presence on connect, so the store holds one instance_presence record per\n\t\t// *other* session in the room, including the user's own other tabs. (The\n\t\t// server never echoes a session its own record.) So an empty set means\n\t\t// we're genuinely the only session and can throttle to solo; any other\n\t\t// session \u2014 another user, or just another tab of our own \u2014 keeps us at the\n\t\t// full sync rate so edits propagate without the solo-mode lag.\n\t\tconst otherSessions = store.query.ids('instance_presence')\n\n\t\tconst presenceMode = computed<TLPresenceMode>('presenceMode', () => {\n\t\t\treturn otherSessions.get().size === 0 ? 'solo' : 'full'\n\t\t})\n\n\t\tconst client = new TLSyncClient<TLRecord, TLStore>({\n\t\t\tstore,\n\t\t\tsocket,\n\t\t\tdidCancel: () => didCancel,\n\t\t\tonLoad(client) {\n\t\t\t\ttrack?.(MULTIPLAYER_EVENT_NAME, { name: 'load', roomId })\n\t\t\t\t// Keep objectAccess (onAfterConnect always runs first for this client) but drop the\n\t\t\t\t// rest: an `error` left behind by a previous client (e.g. NOT_FOUND on the old uri)\n\t\t\t\t// would otherwise keep the hook reporting status 'error' for the new, healthy room.\n\t\t\t\tsetState((prev) => ({ readyClient: client, objectAccess: prev?.objectAccess }))\n\t\t\t},\n\t\t\tonSyncError(reason) {\n\t\t\t\tconsole.error('sync error', reason)\n\n\t\t\t\ttrack?.(MULTIPLAYER_EVENT_NAME, {\n\t\t\t\t\tname: SYNC_ERROR_EVENT_NAMES.get(reason) ?? 'sync-error:' + reason,\n\t\t\t\t\troomId,\n\t\t\t\t})\n\t\t\t\tsetState({ error: new TLRemoteSyncError(reason) })\n\t\t\t\tsocket.close()\n\t\t\t},\n\t\t\tonAfterConnect(_, { isReadonly, objectAccess }) {\n\t\t\t\t// Object-lane (comment) write access is decided per session by the server and can\n\t\t\t\t// change on reconnect (e.g. when the file's share tier changes), so surface it on\n\t\t\t\t// the returned status for consumers that gate comment UI. This fires before the\n\t\t\t\t// first onLoad, hence the merge in both directions.\n\t\t\t\tsetState((prev) => ({ ...prev, objectAccess }))\n\t\t\t\ttransact(() => {\n\t\t\t\t\tsyncMode.set(isReadonly ? 'readonly' : 'readwrite')\n\t\t\t\t\t// if the server crashes and loses all data it can return an empty document\n\t\t\t\t\t// when it comes back up. This is a safety check to make sure that if something like\n\t\t\t\t\t// that happens, it won't render the app broken and require a restart. The user will\n\t\t\t\t\t// most likely lose all their changes though since they'll have been working with pages\n\t\t\t\t\t// that won't exist. There's certainly something we can do to make this better.\n\t\t\t\t\t// but the likelihood of this happening is very low and maybe not worth caring about beyond this.\n\t\t\t\t\tstore.ensureStoreIsUsable()\n\t\t\t\t})\n\t\t\t},\n\t\t\tonCustomMessageReceived,\n\t\t\tpresence,\n\t\t\tpresenceMode,\n\t\t})\n\n\t\treturn () => {\n\t\t\tdidCancel = true\n\t\t\tunsubscribeFromConnectionStatus()\n\t\t\tclient.close()\n\t\t\tsocket.close()\n\t\t}\n\t}, [\n\t\tassets,\n\t\tonMount,\n\t\tconnect,\n\t\t_users,\n\t\troomId,\n\t\tschema,\n\t\tsetState,\n\t\ttrack,\n\t\turi,\n\t\tgetUserPresence,\n\t\tonCustomMessageReceived,\n\t])\n\n\treturn useValue<RemoteTLStoreWithStatus>(\n\t\t'remote synced store',\n\t\t() => {\n\t\t\tif (!state) return { status: 'loading' }\n\t\t\tif (state.error) return { status: 'error', error: state.error }\n\t\t\tif (!state.readyClient) return { status: 'loading' }\n\t\t\treturn {\n\t\t\t\tstatus: 'synced-remote',\n\t\t\t\tconnectionStatus: toCollaborationStatus(state.readyClient.socket.connectionStatus),\n\t\t\t\tstore: state.readyClient.store,\n\t\t\t\tobjectAccess: state.objectAccess ?? 'write',\n\t\t\t}\n\t\t},\n\t\t[state]\n\t)\n}\n\n/**\n * Configuration options for the {@link useSync} hook to establish multiplayer collaboration.\n *\n * This interface defines the required and optional settings for connecting to a multiplayer\n * server, managing user presence, handling assets, and customizing the collaboration experience.\n *\n * @example\n * ```tsx\n * const syncOptions: UseSyncOptions = {\n *   uri: 'wss://myserver.com/sync/room-123',\n *   assets: myAssetStore,\n *   users: {\n *     currentUser: myCurrentUserSignal,\n *   },\n *   getUserPresence: (store, user) => ({\n *     userId: user.id,\n *     userName: user.name,\n *     cursor: getCursorPosition()\n *   })\n * }\n * ```\n *\n * @public\n */\nexport interface UseSyncOptionsBase {\n\t/**\n\t * Named theme definitions. When provided, custom color names are automatically\n\t * registered before the store is constructed so persisted data with those\n\t * colors passes validation on load.\n\t */\n\tthemes?: Partial<TLThemes>\n\n\t/**\n\t * Asset store implementation for handling file uploads and storage.\n\t *\n\t * Required for production applications to handle images, videos, and other\n\t * media efficiently. Without an asset store, files are stored inline as\n\t * base64, which causes performance issues with large files.\n\t *\n\t * The asset store must implement upload (for new files) and resolve\n\t * (for displaying existing files) methods. For prototyping, you can use\n\t * {@link @tldraw/editor#inlineBase64AssetStore} but this is not recommended for production.\n\t *\n\t * @example\n\t * ```ts\n\t * const myAssetStore: TLAssetStore = {\n\t *   upload: async (asset, file) => {\n\t *     const url = await uploadToCloudStorage(file)\n\t *     return { src: url }\n\t *   },\n\t *   resolve: (asset, context) => {\n\t *     return getOptimizedUrl(asset.src, context)\n\t *   }\n\t * }\n\t * ```\n\t */\n\tassets: TLAssetStore\n\n\t/**\n\t * User store for identity, presence and attribution.\n\t *\n\t * Both methods return reactive {@link @tldraw/state#Signal | Signals}.\n\t * `currentUser` provides the current user's identity (used for\n\t * both presence broadcasting and shape attribution) and optionally\n\t * `resolve(userId)` looks up other users by ID. If not provided,\n\t * a default implementation backed by localStorage user preferences is\n\t * used, with `resolve` falling back to presence records in the store.\n\t */\n\tusers?: TLUserStore\n\n\t/**\n\t * Handler for receiving custom messages sent through the multiplayer connection.\n\t *\n\t * Use this to implement custom communication channels between clients beyond\n\t * the standard shape and presence synchronization. Messages are sent using\n\t * the TLSyncClient's sendMessage method.\n\t *\n\t * @param data - The custom message data received from another client\n\t *\n\t * @example\n\t * ```ts\n\t * onCustomMessageReceived: (data) => {\n\t *   if (data.type === 'chat') {\n\t *     displayChatMessage(data.message, data.userId)\n\t *   }\n\t * }\n\t * ```\n\t */\n\tonCustomMessageReceived?(data: any): void\n\n\t/** @internal */\n\tonMount?(editor: Editor): void\n\t/** @internal used for analytics only, we should refactor this away */\n\troomId?: string\n\t/** @internal */\n\ttrackAnalyticsEvent?(name: string, data: { [key: string]: any }): void\n\n\t/**\n\t * A reactive function that returns a {@link @tldraw/tlschema#TLInstancePresence} object.\n\t *\n\t * This function is called reactively whenever the store state changes and\n\t * determines what presence information to broadcast to other clients. The\n\t * result is synchronized across all connected clients for displaying cursors,\n\t * selections, and other collaborative indicators.\n\t *\n\t * If not provided, uses the default implementation which includes standard\n\t * cursor position and selection state. Custom implementations allow you to\n\t * add additional presence data like current tool, view state, or custom status.\n\t *\n\t * See {@link @tldraw/tlschema#getDefaultUserPresence} for\n\t * the default implementation of this function.\n\t *\n\t * @param store - The current TLStore\n\t * @param user - The current user information\n\t * @returns Presence state to broadcast to other clients, or null to hide presence\n\t *\n\t * @example\n\t * ```ts\n\t * getUserPresence: (store, user) => {\n\t *   return {\n\t *     userId: user.id,\n\t *     userName: user.name,\n\t *     cursor: { x: 100, y: 200 },\n\t *     currentTool: 'select',\n\t *     isActive: true\n\t *   }\n\t * }\n\t * ```\n\t */\n\tgetUserPresence?(store: TLStore, user: TLUser): TLPresenceStateInfo | null\n}\n\n/** @public */\nexport interface UseSyncOptionsWithUri extends UseSyncOptionsBase {\n\t/**\n\t * The WebSocket URI of the multiplayer server for real-time synchronization.\n\t *\n\t * Must include the protocol (wss:// for secure, ws:// for local development).\n\t * HTTP/HTTPS URLs will be automatically upgraded to WebSocket connections.\n\t *\n\t * Can be a static string or a function that returns a URI (useful for dynamic\n\t * authentication tokens or room routing). The function is called on each\n\t * connection attempt, allowing for token refresh and dynamic routing.\n\t *\n\t * Reserved query parameters `sessionId` and `storeId` are automatically added\n\t * by the sync system and should not be included in your URI.\n\t *\n\t * @example\n\t * ```ts\n\t * // Static URI\n\t * uri: 'wss://myserver.com/sync/room-123'\n\t *\n\t * // Dynamic URI with authentication\n\t * uri: async () => {\n\t *   const token = await getAuthToken()\n\t *   return `wss://myserver.com/sync/room-123?token=${token}`\n\t * }\n\t * ```\n\t */\n\turi: string | (() => string | Promise<string>)\n\tconnect?: never\n}\n\n/** @public */\nexport interface UseSyncOptionsWithConnectFn extends UseSyncOptionsBase {\n\t/**\n\t * Create a connection to the server. Mostly you should use {@link UseSyncOptionsWithUri.uri}\n\t * instead, but this is useful if you want to use a custom transport to connect to the server,\n\t * instead of our default websocket-based transport.\n\t */\n\tconnect: UseSyncConnectFn\n\turi?: never\n}\n\n/** @public */\nexport type UseSyncConnectFn = (query: {\n\tsessionId: string\n\tstoreId: string\n}) => TLPersistentClientSocket\n\n/**\n * Options for the {@link useSync} hook.\n * @public\n */\nexport type UseSyncOptions = UseSyncOptionsWithUri | UseSyncOptionsWithConnectFn\n"],
  "mappings": "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",
  "names": ["client"]
}
