{"version":3,"file":"useJwtStore-B1DPLYgs.chunk.mjs","sources":["../node_modules/zustand/esm/vanilla.mjs","../node_modules/zustand/esm/react.mjs","../src/utils/createResolvablePromise.ts","../src/stores/useWhiteboardConfigStore.ts","../node_modules/zustand/esm/middleware.mjs","../src/stores/useCollaborationStore.ts","../src/stores/useJwtStore.ts"],"sourcesContent":["const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);\n\nexport { createStore };\n","import React from 'react';\nimport { createStore } from 'zustand/vanilla';\n\nconst identity = (arg) => arg;\nfunction useStore(api, selector = identity) {\n const slice = React.useSyncExternalStore(\n api.subscribe,\n React.useCallback(() => selector(api.getState()), [api, selector]),\n React.useCallback(() => selector(api.getInitialState()), [api, selector])\n );\n React.useDebugValue(slice);\n return slice;\n}\nconst createImpl = (createState) => {\n const api = createStore(createState);\n const useBoundStore = (selector) => useStore(api, selector);\n Object.assign(useBoundStore, api);\n return useBoundStore;\n};\nconst create = ((createState) => createState ? createImpl(createState) : createImpl);\n\nexport { create, useStore };\n","/**\n * SPDX-FileCopyrightText: 2020 Excalidraw\n * SPDX-License-Identifier: MIT\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport const createResolvablePromise = () => {\n\tlet resolve!: any\n\tlet reject!: any\n\tconst promise = new Promise((_resolve, _reject) => {\n\t\tresolve = _resolve\n\t\treject = _reject\n\t})\n\t;(promise as any).resolve = resolve\n\t;(promise as any).reject = reject\n\treturn promise as ResolvablePromise\n}\n","/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport { create } from 'zustand'\nimport { createResolvablePromise } from '../utils/createResolvablePromise'\nimport type { ExcalidrawInitialDataState } from '@excalidraw/excalidraw/types/types'\n\ntype InitialDataPromise = ReturnType\n\ninterface WhiteboardConfigState {\n\t// Core state\n\tfileId: number\n\tfileName: string\n\tpublicSharingToken: string | null\n\tisReadOnly: boolean // Single source of truth for read-only state, determined by JWT\n\tisEmbedded: boolean\n\tinitialDataPromise: InitialDataPromise\n\tpendingInitialDataPromises: InitialDataPromise[]\n\tcollabBackendUrl: string // URL of the collaboration backend server\n\tisVersionPreview: boolean\n\tversionSource: string | null\n\tfileVersion: string | null\n\n\t// UI state\n\tzenModeEnabled: boolean\n\tgridModeEnabled: boolean\n\n\t// Core actions\n\tsetConfig: (\n\t\tconfig: Partial<\n\t\t\tPick<\n\t\t\t\tWhiteboardConfigState,\n\t\t\t\t| 'fileId'\n\t\t\t\t| 'fileName'\n\t\t\t\t| 'publicSharingToken'\n\t\t\t\t| 'isEmbedded'\n\t\t\t\t| 'collabBackendUrl'\n\t\t\t\t| 'isVersionPreview'\n\t\t\t\t| 'versionSource'\n\t\t\t\t| 'fileVersion'\n\t\t\t>\n\t\t>,\n\t) => void\n\tresolveInitialData: (data: ExcalidrawInitialDataState) => void\n\tresetInitialDataPromise: () => void\n\tresetStore: () => void // Reset the entire store state\n\n\t// UI actions\n\tsetZenModeEnabled: (enabled: boolean) => void\n\tsetGridModeEnabled: (enabled: boolean) => void\n\n\t// Permission actions\n\tsetReadOnly: (readOnly: boolean) => void\n}\n\n// Create the store without persistence\nexport const useWhiteboardConfigStore = create()((set, get) => ({\n\t// Core state\n\tfileId: 0,\n\tfileName: '',\n\tpublicSharingToken: null,\n\tisReadOnly: false,\n\tisEmbedded: false,\n\tinitialDataPromise: createResolvablePromise(),\n\tpendingInitialDataPromises: [],\n\tcollabBackendUrl: '', // Will be initialized from initial state\n\tisVersionPreview: false,\n\tversionSource: null,\n\tfileVersion: null,\n\n\t// UI state\n\tzenModeEnabled: false,\n\tgridModeEnabled: false,\n\n\t// Core actions\n\tsetConfig: (config: Partial>) => {\n\t\tset(config)\n\t},\n\n\tresolveInitialData: (data: ExcalidrawInitialDataState) => {\n\t\tconst { initialDataPromise, pendingInitialDataPromises } = get()\n\t\tpendingInitialDataPromises.forEach((promise) => {\n\t\t\tpromise.resolve(data)\n\t\t})\n\t\tinitialDataPromise.resolve(data)\n\t\tif (pendingInitialDataPromises.length > 0) {\n\t\t\tset({ pendingInitialDataPromises: [] })\n\t\t}\n\t},\n\n\tresetInitialDataPromise: () =>\n\t\tset((state) => ({\n\t\t\tpendingInitialDataPromises: [\n\t\t\t\t...state.pendingInitialDataPromises,\n\t\t\t\tstate.initialDataPromise,\n\t\t\t],\n\t\t\tinitialDataPromise: createResolvablePromise(),\n\t\t})),\n\n\t// Reset the entire store to its initial state\n\tresetStore: () => {\n\t\t// Keep the current fileId, fileName, and other config values\n\t\tconst { fileId, fileName, publicSharingToken, isEmbedded, collabBackendUrl } = get()\n\t\tset({\n\t\t\t// Preserve these values\n\t\t\tfileId,\n\t\t\tfileName,\n\t\t\tpublicSharingToken,\n\t\t\tisEmbedded,\n\t\t\tcollabBackendUrl,\n\t\t\tisVersionPreview: false,\n\t\t\tversionSource: null,\n\t\t\tfileVersion: null,\n\t\t\t// Reset these values\n\t\t\tisReadOnly: false,\n\t\t\tinitialDataPromise: createResolvablePromise(),\n\t\t\tpendingInitialDataPromises: [],\n\t\t\tzenModeEnabled: false,\n\t\t\tgridModeEnabled: false,\n\t\t})\n\t},\n\n\t// UI actions\n\tsetZenModeEnabled: (enabled: boolean) => set({ zenModeEnabled: enabled }),\n\n\tsetGridModeEnabled: (enabled: boolean) => set({ gridModeEnabled: enabled }),\n\n\t// Permission actions\n\tsetReadOnly: (readOnly: boolean) => {\n\t\tif (get().isVersionPreview) {\n\t\t\tset({ isReadOnly: true })\n\t\t\treturn\n\t\t}\n\t\tset({ isReadOnly: readOnly })\n\t},\n}))\n\ntype WhiteboardTestHooks = {\n\twhiteboardConfigStore?: typeof useWhiteboardConfigStore\n\tblockInitialData?: boolean\n\tpendingInitialData?: ExcalidrawInitialDataState | null\n\toriginalResolveInitialData?: (data: ExcalidrawInitialDataState) => void\n\trestoreResolveInitialData?: () => void\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__whiteboardTest?: boolean\n\t\t__whiteboardTestHooks?: WhiteboardTestHooks\n\t}\n}\n\nconst attachTestHooks = () => {\n\tif (typeof window === 'undefined' || !window.__whiteboardTest) {\n\t\treturn\n\t}\n\n\twindow.__whiteboardTestHooks = window.__whiteboardTestHooks || {}\n\tconst hooks = window.__whiteboardTestHooks\n\thooks.whiteboardConfigStore = useWhiteboardConfigStore\n\n\tif (!hooks.blockInitialData || hooks.originalResolveInitialData) {\n\t\treturn\n\t}\n\n\thooks.pendingInitialData = null\n\thooks.originalResolveInitialData = useWhiteboardConfigStore.getState().resolveInitialData\n\tuseWhiteboardConfigStore.setState({\n\t\tresolveInitialData: (data: ExcalidrawInitialDataState) => {\n\t\t\thooks.pendingInitialData = data\n\t\t},\n\t})\n\thooks.restoreResolveInitialData = () => {\n\t\tconst original = hooks.originalResolveInitialData\n\t\tif (!original) {\n\t\t\treturn\n\t\t}\n\t\tuseWhiteboardConfigStore.setState({ resolveInitialData: original })\n\t}\n}\n\nattachTestHooks()\n","const reduxImpl = (reducer, initial) => (set, _get, api) => {\n api.dispatch = (action) => {\n set((state) => reducer(state, action), false, action);\n return action;\n };\n api.dispatchFromDevtools = true;\n return { dispatch: (...args) => api.dispatch(...args), ...initial };\n};\nconst redux = reduxImpl;\n\nconst trackedConnections = /* @__PURE__ */ new Map();\nconst getTrackedConnectionState = (name) => {\n const api = trackedConnections.get(name);\n if (!api) return {};\n return Object.fromEntries(\n Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])\n );\n};\nconst extractConnectionInformation = (store, extensionConnector, options) => {\n if (store === void 0) {\n return {\n type: \"untracked\",\n connection: extensionConnector.connect(options)\n };\n }\n const existingConnection = trackedConnections.get(options.name);\n if (existingConnection) {\n return { type: \"tracked\", store, ...existingConnection };\n }\n const newConnection = {\n connection: extensionConnector.connect(options),\n stores: {}\n };\n trackedConnections.set(options.name, newConnection);\n return { type: \"tracked\", store, ...newConnection };\n};\nconst removeStoreFromTrackedConnections = (name, store) => {\n if (store === void 0) return;\n const connectionInfo = trackedConnections.get(name);\n if (!connectionInfo) return;\n delete connectionInfo.stores[store];\n if (Object.keys(connectionInfo.stores).length === 0) {\n trackedConnections.delete(name);\n }\n};\nconst findCallerName = (stack) => {\n var _a, _b;\n if (!stack) return void 0;\n const traceLines = stack.split(\"\\n\");\n const apiSetStateLineIndex = traceLines.findIndex(\n (traceLine) => traceLine.includes(\"api.setState\")\n );\n if (apiSetStateLineIndex < 0) return void 0;\n const callerLine = ((_a = traceLines[apiSetStateLineIndex + 1]) == null ? void 0 : _a.trim()) || \"\";\n return (_b = /.+ (.+) .+/.exec(callerLine)) == null ? void 0 : _b[1];\n};\nconst devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {\n const { enabled, anonymousActionType, store, ...options } = devtoolsOptions;\n let extensionConnector;\n try {\n extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") && window.__REDUX_DEVTOOLS_EXTENSION__;\n } catch (e) {\n }\n if (!extensionConnector) {\n return fn(set, get, api);\n }\n const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options);\n let isRecording = true;\n api.setState = ((state, replace, nameOrAction) => {\n const r = set(state, replace);\n if (!isRecording) return r;\n const action = nameOrAction === void 0 ? {\n type: anonymousActionType || findCallerName(new Error().stack) || \"anonymous\"\n } : typeof nameOrAction === \"string\" ? { type: nameOrAction } : nameOrAction;\n if (store === void 0) {\n connection == null ? void 0 : connection.send(action, get());\n return r;\n }\n connection == null ? void 0 : connection.send(\n {\n ...action,\n type: `${store}/${action.type}`\n },\n {\n ...getTrackedConnectionState(options.name),\n [store]: api.getState()\n }\n );\n return r;\n });\n api.devtools = {\n cleanup: () => {\n if (connection && typeof connection.unsubscribe === \"function\") {\n connection.unsubscribe();\n }\n removeStoreFromTrackedConnections(options.name, store);\n }\n };\n const setStateFromDevtools = (...a) => {\n const originalIsRecording = isRecording;\n isRecording = false;\n set(...a);\n isRecording = originalIsRecording;\n };\n const initialState = fn(api.setState, get, api);\n if (connectionInformation.type === \"untracked\") {\n connection == null ? void 0 : connection.init(initialState);\n } else {\n connectionInformation.stores[connectionInformation.store] = api;\n connection == null ? void 0 : connection.init(\n Object.fromEntries(\n Object.entries(connectionInformation.stores).map(([key, store2]) => [\n key,\n key === connectionInformation.store ? initialState : store2.getState()\n ])\n )\n );\n }\n if (api.dispatchFromDevtools && typeof api.dispatch === \"function\") {\n let didWarnAboutReservedActionType = false;\n const originalDispatch = api.dispatch;\n api.dispatch = (...args) => {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && args[0].type === \"__setState\" && !didWarnAboutReservedActionType) {\n console.warn(\n '[zustand devtools middleware] \"__setState\" action type is reserved to set state from the devtools. Avoid using it.'\n );\n didWarnAboutReservedActionType = true;\n }\n originalDispatch(...args);\n };\n }\n connection.subscribe((message) => {\n var _a;\n switch (message.type) {\n case \"ACTION\":\n if (typeof message.payload !== \"string\") {\n console.error(\n \"[zustand devtools middleware] Unsupported action format\"\n );\n return;\n }\n return parseJsonThen(\n message.payload,\n (action) => {\n if (action.type === \"__setState\") {\n if (store === void 0) {\n setStateFromDevtools(action.state);\n return;\n }\n if (Object.keys(action.state).length !== 1) {\n console.error(\n `\n [zustand devtools middleware] Unsupported __setState action format.\n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n `\n );\n }\n const stateFromDevtools = action.state[store];\n if (stateFromDevtools === void 0 || stateFromDevtools === null) {\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {\n setStateFromDevtools(stateFromDevtools);\n }\n return;\n }\n if (!api.dispatchFromDevtools) return;\n if (typeof api.dispatch !== \"function\") return;\n api.dispatch(action);\n }\n );\n case \"DISPATCH\":\n switch (message.payload.type) {\n case \"RESET\":\n setStateFromDevtools(initialState);\n if (store === void 0) {\n return connection == null ? void 0 : connection.init(api.getState());\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"COMMIT\":\n if (store === void 0) {\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"ROLLBACK\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n setStateFromDevtools(state[store]);\n connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n });\n case \"JUMP_TO_STATE\":\n case \"JUMP_TO_ACTION\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) {\n setStateFromDevtools(state[store]);\n }\n });\n case \"IMPORT_STATE\": {\n const { nextLiftedState } = message.payload;\n const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;\n if (!lastComputedState) return;\n if (store === void 0) {\n setStateFromDevtools(lastComputedState);\n } else {\n setStateFromDevtools(lastComputedState[store]);\n }\n connection == null ? void 0 : connection.send(\n null,\n // FIXME no-any\n nextLiftedState\n );\n return;\n }\n case \"PAUSE_RECORDING\":\n return isRecording = !isRecording;\n }\n return;\n }\n });\n return initialState;\n};\nconst devtools = devtoolsImpl;\nconst parseJsonThen = (stringified, fn) => {\n let parsed;\n try {\n parsed = JSON.parse(stringified);\n } catch (e) {\n console.error(\n \"[zustand devtools middleware] Could not parse the received json\",\n e\n );\n }\n if (parsed !== void 0) fn(parsed);\n};\n\nconst subscribeWithSelectorImpl = (fn) => (set, get, api) => {\n const origSubscribe = api.subscribe;\n api.subscribe = ((selector, optListener, options) => {\n let listener = selector;\n if (optListener) {\n const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;\n let currentSlice = selector(api.getState());\n listener = (state) => {\n const nextSlice = selector(state);\n if (!equalityFn(currentSlice, nextSlice)) {\n const previousSlice = currentSlice;\n optListener(currentSlice = nextSlice, previousSlice);\n }\n };\n if (options == null ? void 0 : options.fireImmediately) {\n optListener(currentSlice, currentSlice);\n }\n }\n return origSubscribe(listener);\n });\n const initialState = fn(set, get, api);\n return initialState;\n};\nconst subscribeWithSelector = subscribeWithSelectorImpl;\n\nfunction combine(initialState, create) {\n return (...args) => Object.assign({}, initialState, create(...args));\n}\n\nfunction createJSONStorage(getStorage, options) {\n let storage;\n try {\n storage = getStorage();\n } catch (e) {\n return;\n }\n const persistStorage = {\n getItem: (name) => {\n var _a;\n const parse = (str2) => {\n if (str2 === null) {\n return null;\n }\n return JSON.parse(str2, options == null ? void 0 : options.reviver);\n };\n const str = (_a = storage.getItem(name)) != null ? _a : null;\n if (str instanceof Promise) {\n return str.then(parse);\n }\n return parse(str);\n },\n setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)),\n removeItem: (name) => storage.removeItem(name)\n };\n return persistStorage;\n}\nconst toThenable = (fn) => (input) => {\n try {\n const result = fn(input);\n if (result instanceof Promise) {\n return result;\n }\n return {\n then(onFulfilled) {\n return toThenable(onFulfilled)(result);\n },\n catch(_onRejected) {\n return this;\n }\n };\n } catch (e) {\n return {\n then(_onFulfilled) {\n return this;\n },\n catch(onRejected) {\n return toThenable(onRejected)(e);\n }\n };\n }\n};\nconst persistImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n storage: createJSONStorage(() => localStorage),\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n let hydrationVersion = 0;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage = options.storage;\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const setItem = () => {\n const state = options.partialize({ ...get() });\n return storage.setItem(options.name, {\n state,\n version: options.version\n });\n };\n const savedSetState = api.setState;\n api.setState = (state, replace) => {\n savedSetState(state, replace);\n return setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n return setItem();\n },\n get,\n api\n );\n api.getInitialState = () => configResult;\n let stateFromStorage;\n const hydrate = () => {\n var _a, _b;\n if (!storage) return;\n const currentVersion = ++hydrationVersion;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => {\n var _a2;\n return cb((_a2 = get()) != null ? _a2 : configResult);\n });\n const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n const migration = options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n );\n if (migration instanceof Promise) {\n return migration.then((result) => [true, result]);\n }\n return [true, migration];\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return [false, deserializedStorageValue.state];\n }\n }\n return [false, void 0];\n }).then((migrationResult) => {\n var _a2;\n if (currentVersion !== hydrationVersion) {\n return;\n }\n const [migrated, migratedState] = migrationResult;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n if (migrated) {\n return setItem();\n }\n }).then(() => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);\n stateFromStorage = get();\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).catch((e) => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n });\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.storage) {\n storage = newOptions.storage;\n }\n },\n clearStorage: () => {\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => hydrate(),\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n if (!options.skipHydration) {\n hydrate();\n }\n return stateFromStorage || configResult;\n};\nconst persist = persistImpl;\n\nfunction ssrSafe(config, isSSR = typeof window === \"undefined\") {\n return (set, get, api) => {\n if (!isSSR) {\n return config(set, get, api);\n }\n const ssrSet = () => {\n throw new Error(\"Cannot set state of Zustand store in SSR\");\n };\n api.setState = ssrSet;\n return config(ssrSet, get, api);\n };\n}\n\nexport { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector, ssrSafe as unstable_ssrSafe };\n","/**\n * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/* eslint-disable no-console */\n\nimport { create } from 'zustand'\nimport type { CollaborationSocket } from '../types/collaboration'\nimport type { Voting } from '../types/voting'\n\nexport type CollaborationConnectionStatus = 'online' | 'offline' | 'connecting' | 'reconnecting'\n\nexport type AuthErrorType = 'jwt_secret_mismatch' | 'token_expired' | 'unauthorized' | null\n\ninterface AuthErrorState {\n\ttype: AuthErrorType\n\tmessage: string | null\n\tconsecutiveFailures: number\n\tlastFailureTime: number | null\n\tisPersistent: boolean // True when we've detected a persistent auth issue (likely JWT secret mismatch)\n}\n\ninterface CollaborationStore {\n\tstatus: CollaborationConnectionStatus\n\tsocket: CollaborationSocket | null\n\tisDedicatedSyncer: boolean // Is this client responsible for syncing to server/broadcasting?\n\tauthError: AuthErrorState\n\tfollowedUserId: string | null // User ID being followed for viewport synchronization\n\tisInRoom: boolean // Whether the socket has joined the current room\n\n\t// Presentation state\n\tpresenterId: string | null // User ID of current presenter\n\tisPresentationMode: boolean // Whether presentation mode is active in the room\n\tisPresenting: boolean // Whether current user is presenting\n\tpresentationStartTime: number | null // When presentation started\n\tautoFollowPresenter: boolean // Whether to automatically follow presenter (can be disabled by user)\n\n\tvotings: Voting[]\n\n\t// Actions\n\tsetStatus: (status: CollaborationConnectionStatus) => void\n\tsetSocket: (socket: CollaborationSocket | null) => void\n\tsetDedicatedSyncer: (isSyncer: boolean) => void\n\tsetIsInRoom: (inRoom: boolean) => void\n\tsetAuthError: (error: Partial) => void\n\tincrementAuthFailure: (errorType: AuthErrorType, message: string) => void\n\tclearAuthError: () => void\n\tresetStore: () => void\n\n\t// Presentation actions\n\tsetPresentationState: (state: {\n\t\tpresenterId?: string | null\n\t\tisPresentationMode?: boolean\n\t\tisPresenting?: boolean\n\t\tpresentationStartTime?: number | null\n\t}) => void\n\tsetAutoFollowPresenter: (autoFollow: boolean) => void\n\n\t// Voting actions\n\taddVoting: (voting: Voting) => void\n\tupdateVoting: (voting: Voting) => void\n\tsetVotings: (votings: Voting[]) => void\n}\n\nconst initialAuthErrorState: AuthErrorState = {\n\ttype: null,\n\tmessage: null,\n\tconsecutiveFailures: 0,\n\tlastFailureTime: null,\n\tisPersistent: false,\n}\n\nconst initialState: Omit = {\n\tstatus: 'offline',\n\tsocket: null,\n\tisDedicatedSyncer: false,\n\tauthError: initialAuthErrorState,\n\tfollowedUserId: null,\n\tisInRoom: false,\n\n\t// Presentation initial state\n\tpresenterId: null,\n\tisPresentationMode: false,\n\tisPresenting: false,\n\tpresentationStartTime: null,\n\tautoFollowPresenter: true,\n\n\tvotings: [],\n}\n\n// Constants for auth failure detection\nconst MAX_AUTH_FAILURES = 3\nconst PERSISTENT_FAILURE_THRESHOLD = 5 * 60 * 1000 // 5 minutes\n\nexport const useCollaborationStore = create()((set) => ({\n\t...initialState,\n\n\tsetStatus: (status) => set((state) => (state.status === status ? {} : { status })),\n\tsetSocket: (socket) => set({ socket }),\n\tsetDedicatedSyncer: (isSyncer) => set({ isDedicatedSyncer: isSyncer }),\n\tsetIsInRoom: (inRoom) => set({ isInRoom: inRoom }),\n\n\tsetAuthError: (error) => set((state) => ({\n\t\tauthError: { ...state.authError, ...error },\n\t})),\n\n\tincrementAuthFailure: (errorType, message) => set((state) => {\n\t\tconst now = Date.now()\n\t\tconst newFailureCount = state.authError.consecutiveFailures + 1\n\n\t\t// Determine if this is a persistent issue (likely JWT secret mismatch)\n\t\tconst isPersistent = newFailureCount >= MAX_AUTH_FAILURES\n\t\t\t&& (state.authError.lastFailureTime === null\n\t\t\t || now - state.authError.lastFailureTime < PERSISTENT_FAILURE_THRESHOLD)\n\n\t\treturn {\n\t\t\tauthError: {\n\t\t\t\ttype: errorType,\n\t\t\t\tmessage,\n\t\t\t\tconsecutiveFailures: newFailureCount,\n\t\t\t\tlastFailureTime: now,\n\t\t\t\tisPersistent,\n\t\t\t},\n\t\t}\n\t}),\n\n\tclearAuthError: () => set({ authError: initialAuthErrorState }),\n\n\tresetStore: () => set(initialState),\n\n\t// Presentation actions\n\tsetPresentationState: (state) => set((currentState) => ({\n\t\t...currentState,\n\t\t...state,\n\t})),\n\n\tsetAutoFollowPresenter: (autoFollow) => set({ autoFollowPresenter: autoFollow }),\n\n\t// Voting actions\n\taddVoting: (voting) => set((state) => ({\n\t\tvotings: [...state.votings, voting],\n\t})),\n\n\tupdateVoting: (voting) => set((state) => ({\n\t\tvotings: state.votings.map(v => v.uuid === voting.uuid ? voting : v),\n\t})),\n\n\tsetVotings: (votings) => set({ votings }),\n}))\n\ntype WhiteboardTestHooks = {\n\tcollaborationStore?: typeof useCollaborationStore\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__whiteboardTest?: boolean\n\t\t__whiteboardTestHooks?: WhiteboardTestHooks & Record\n\t}\n}\n\nconst attachTestHooks = () => {\n\tif (typeof window === 'undefined' || !window.__whiteboardTest) {\n\t\treturn\n\t}\n\n\twindow.__whiteboardTestHooks = window.__whiteboardTestHooks || {}\n\twindow.__whiteboardTestHooks.collaborationStore = useCollaborationStore\n}\n\nattachTestHooks()\n","/**\n * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/* eslint-disable no-console */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand/middleware'\nimport axios from '@nextcloud/axios'\nimport { generateUrl } from '@nextcloud/router'\nimport { useWhiteboardConfigStore } from './useWhiteboardConfigStore'\nimport { useCollaborationStore } from './useCollaborationStore'\n\nconst TOKEN_REFRESH_BUFFER = 90\n\nfunction isTokenFormatValid(token: string): boolean {\n\tconst parts = token.split('.')\n\treturn parts.length === 3 && !!parts[0] && !!parts[1] && !!parts[2]\n}\n\ninterface JwtPayload {\n\tuserid: string\n\tfileId: number\n\tisFileReadOnly: boolean\n\tuser: {\n\t\tid: string\n\t\tname: string\n\t}\n\tiat: number\n\texp: number\n}\n\ninterface JWTStore {\n\ttokens: Record\n\ttokenExpiries: Record\n\tautoRefreshTimers: Record\n\trefreshPromise: Promise | null\n\n\tgetJWT: () => Promise\n\trefreshJWT: () => Promise\n\texecuteWithJWT: (apiCall: (token: string) => Promise) => Promise\n\tisTokenExpired: (roomId: string) => boolean\n\tparseJwt: (token: string) => JwtPayload | null\n\tvalidateJwtIntegrity: (token: string) => boolean\n\tsetupAutoRefresh: (roomId: string) => void\n\tclearAutoRefresh: (roomId: string) => void\n\tclearTokens: () => void\n}\n\nexport const useJWTStore = create()(\n\tpersist(\n\t\t(set, get) => ({\n\t\t\ttokens: {},\n\t\t\ttokenExpiries: {},\n\t\t\tautoRefreshTimers: {},\n\t\t\trefreshPromise: null,\n\t\t\tparseJwt: (token) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (!isTokenFormatValid(token)) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Invalid token format')\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\tconst base64Url = token.split('.')[1]\n\t\t\t\t\tconst base64 = base64Url\n\t\t\t\t\t\t.replace(/-/g, '+')\n\t\t\t\t\t\t.replace(/_/g, '/')\n\t\t\t\t\tconst jsonPayload = decodeURIComponent(\n\t\t\t\t\t\tatob(base64)\n\t\t\t\t\t\t\t.split('')\n\t\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\t\t\t'%'\n\t\t\t\t\t\t\t\t\t+ ('00' + c.charCodeAt(0).toString(16)).slice(\n\t\t\t\t\t\t\t\t\t\t-2,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(''),\n\t\t\t\t\t)\n\n\t\t\t\t\tconst payload = JSON.parse(jsonPayload)\n\n\t\t\t\t\tif (!payload || typeof payload !== 'object') {\n\t\t\t\t\t\tconsole.error('[JWTStore] Invalid token payload structure')\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!payload.exp || !payload.iat || !payload.userid || payload.fileId === undefined) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Token missing required fields', payload)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\tconst now = Math.floor(Date.now() / 1000)\n\n\t\t\t\t\tif (payload.exp < now) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Token has expired')\n\t\t\t\t\t}\n\n\t\t\t\t\treturn payload\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.error('[JWTStore] Error parsing JWT:', e)\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tvalidateJwtIntegrity: (token) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (!isTokenFormatValid(token)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\tconst payload = get().parseJwt(token)\n\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\t// Additional integrity checks\n\t\t\t\t\tconst now = Math.floor(Date.now() / 1000)\n\n\t\t\t\t\t// Check if token is not expired\n\t\t\t\t\tif (payload.exp < now) {\n\t\t\t\t\t\tconsole.warn('[JWTStore] Token validation failed: expired')\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if token was issued in the past (not future)\n\t\t\t\t\tif (payload.iat > now + 60) { // Allow 60 seconds clock skew\n\t\t\t\t\t\tconsole.warn('[JWTStore] Token validation failed: issued in future')\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check required fields are present and valid\n\t\t\t\t\tif (!payload.userid || typeof payload.userid !== 'string') {\n\t\t\t\t\t\tconsole.warn('[JWTStore] Token validation failed: invalid userid')\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\tif (payload.fileId === undefined || typeof payload.fileId !== 'number') {\n\t\t\t\t\t\tconsole.warn('[JWTStore] Token validation failed: invalid fileId')\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.error('[JWTStore] Error validating JWT integrity:', e)\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tisTokenExpired: (roomId) => {\n\t\t\t\tconst { tokens, tokenExpiries } = get()\n\t\t\t\tconst token = tokens[roomId]\n\t\t\t\tconst expiry = tokenExpiries[roomId]\n\n\t\t\t\tif (!token || !expiry) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\tconst now = Math.floor(Date.now() / 1000)\n\t\t\t\treturn now >= expiry - TOKEN_REFRESH_BUFFER\n\t\t\t},\n\n\t\t\tgetJWT: async () => {\n\t\t\t\tconst { fileId } = useWhiteboardConfigStore.getState()\n\n\t\t\t\t// Check if we have a persistent auth error - if so, don't try to get/refresh tokens\n\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\n\t\t\t\tif (authError.isPersistent) {\n\t\t\t\t\tconsole.warn('[JWTStore] Persistent auth error detected, working in local-only mode')\n\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\treturn null\n\t\t\t\t}\n\n\t\t\t\tconst { tokens, isTokenExpired, setupAutoRefresh, validateJwtIntegrity } = get()\n\t\t\t\tconst token = tokens[fileId]\n\t\t\t\tconst fileIdStr = String(fileId)\n\n\t\t\t\tif (token) {\n\t\t\t\t\t// Use comprehensive validation instead of just parsing\n\t\t\t\t\tconst isValid = validateJwtIntegrity(token)\n\n\t\t\t\t\tif (!isValid) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Stored token failed integrity validation')\n\t\t\t\t\t\tconsole.log('[JWTStore] Clearing invalid token and refreshing')\n\n\t\t\t\t\t\t// Clear the invalid token immediately\n\t\t\t\t\t\tset((state) => {\n\t\t\t\t\t\t\tconst newTokens = { ...state.tokens }\n\t\t\t\t\t\t\tconst newExpiries = { ...state.tokenExpiries }\n\t\t\t\t\t\t\tdelete newTokens[fileId]\n\t\t\t\t\t\t\tdelete newExpiries[fileId]\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\ttokens: newTokens,\n\t\t\t\t\t\t\t\ttokenExpiries: newExpiries,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t// This could indicate JWT secret mismatch\n\t\t\t\t\t\tuseCollaborationStore.getState().incrementAuthFailure(\n\t\t\t\t\t\t\t'jwt_secret_mismatch',\n\t\t\t\t\t\t\t'Stored JWT token failed integrity validation',\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tconsole.warn('[JWTStore] Invalid token cleared, enforcing read-only mode until refresh completes')\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\t// Continue to refresh flow below\n\t\t\t\t\t} else if (!isTokenExpired(fileIdStr)) {\n\t\t\t\t\t\t// Token is valid and not expired, parse it to get payload\n\t\t\t\t\t\tconst payload = get().parseJwt(token)\n\n\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\tconsole.error('[JWTStore] Failed to parse valid token')\n\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\t} else if (payload.fileId === fileId) {\n\t\t\t\t\t\t\tsetupAutoRefresh(fileIdStr)\n\n\t\t\t\t\t\t\tif (payload.isFileReadOnly !== undefined) {\n\t\t\t\t\t\t\t\tconsole.log(`[JWTStore] Setting permissions from valid token: ${payload.isFileReadOnly ? 'read-only' : 'write'} access`)\n\t\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(payload.isFileReadOnly)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn token\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.error(`[JWTStore] Stored token is for fileId ${payload.fileId}, but requested fileId is ${fileId}`)\n\t\t\t\t\t\t\t// Clear mismatched token\n\t\t\t\t\t\t\tset((state) => {\n\t\t\t\t\t\t\t\tconst newTokens = { ...state.tokens }\n\t\t\t\t\t\t\t\tconst newExpiries = { ...state.tokenExpiries }\n\t\t\t\t\t\t\t\tdelete newTokens[fileId]\n\t\t\t\t\t\t\t\tdelete newExpiries[fileId]\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\ttokens: newTokens,\n\t\t\t\t\t\t\t\t\ttokenExpiries: newExpiries,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log('[JWTStore] Token is expired, clearing and refreshing')\n\t\t\t\t\t\t// Clear expired token to force refresh\n\t\t\t\t\t\tset((state) => {\n\t\t\t\t\t\t\tconst newTokens = { ...state.tokens }\n\t\t\t\t\t\t\tconst newExpiries = { ...state.tokenExpiries }\n\t\t\t\t\t\t\tdelete newTokens[fileId]\n\t\t\t\t\t\t\tdelete newExpiries[fileId]\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\ttokens: newTokens,\n\t\t\t\t\t\t\t\ttokenExpiries: newExpiries,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\tconsole.warn('[JWTStore] Expired token cleared, enforcing read-only mode until refresh completes')\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (get().refreshPromise) {\n\t\t\t\t\tconsole.log('[JWTStore] Token refresh already in progress, waiting for it to complete')\n\t\t\t\t\treturn get().refreshPromise\n\t\t\t\t}\n\n\t\t\t\tconst refreshPromise = get().refreshJWT()\n\t\t\t\tset({ refreshPromise })\n\n\t\t\t\ttry {\n\t\t\t\t\tconst newToken = await refreshPromise\n\t\t\t\t\tif (newToken) {\n\n\t\t\t\t\t\tsetupAutoRefresh(fileIdStr)\n\t\t\t\t\t}\n\t\t\t\t\treturn newToken\n\t\t\t\t} finally {\n\n\t\t\t\t\tset({ refreshPromise: null })\n\t\t\t\t}\n\t\t\t},\n\n\t\t\trefreshJWT: async () => {\n\t\t\t\tconst { fileId, publicSharingToken }\n\t\t\t\t\t= useWhiteboardConfigStore.getState()\n\n\t\t\t\t// Check if we have a persistent auth error - if so, don't try to refresh\n\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\n\t\t\t\tif (authError.isPersistent) {\n\t\t\t\t\tconsole.warn('[JWTStore] Persistent auth error detected, skipping token refresh')\n\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\treturn null\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconsole.log(`[JWTStore] Refreshing JWT for room ${fileId}`)\n\n\t\t\t\t\tconst baseUrl = generateUrl(\n\t\t\t\t\t\t`apps/whiteboard/${fileId}/token`,\n\t\t\t\t\t)\n\t\t\t\t\tconst url = publicSharingToken\n\t\t\t\t\t\t? `${baseUrl}?publicSharingToken=${encodeURIComponent(publicSharingToken)}`\n\t\t\t\t\t\t: baseUrl\n\n\t\t\t\t\tconst response = await axios.get(url, {\n\t\t\t\t\t\twithCredentials: true,\n\t\t\t\t\t})\n\t\t\t\t\tconst { token } = response.data\n\n\t\t\t\t\tif (!token) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t'[JWTStore] No token received from server',\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate the token integrity\n\t\t\t\t\tconst isValid = get().validateJwtIntegrity(token)\n\t\t\t\t\tif (!isValid) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Refreshed token failed integrity validation')\n\n\t\t\t\t\t\t// This strongly suggests JWT secret mismatch between PHP and Node.js\n\t\t\t\t\t\tuseCollaborationStore.getState().incrementAuthFailure(\n\t\t\t\t\t\t\t'jwt_secret_mismatch',\n\t\t\t\t\t\t\t'Refreshed JWT token failed integrity validation - possible secret mismatch',\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Set read-only mode immediately\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\tconst payload = get().parseJwt(token)\n\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\tconsole.error('[JWTStore] Failed to parse validated token')\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate that the token is for the correct file\n\t\t\t\t\tif (payload.fileId !== fileId) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t`[JWTStore] Token fileId (${payload.fileId}) doesn't match requested fileId (${fileId})`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\n\t\t\t\t\t// Store the token and its expiry\n\t\t\t\t\tset((state) => ({\n\t\t\t\t\t\ttokens: {\n\t\t\t\t\t\t\t...state.tokens,\n\t\t\t\t\t\t\t[fileId]: token,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttokenExpiries: {\n\t\t\t\t\t\t\t...state.tokenExpiries,\n\t\t\t\t\t\t\t[fileId]: payload.exp,\n\t\t\t\t\t\t},\n\t\t\t\t\t}))\n\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[JWTStore] JWT refreshed for room ${fileId}, expires at ${new Date(payload.exp * 1000).toISOString()}`,\n\t\t\t\t\t)\n\n\t\t\t\t\t// Only clear auth errors if this was not a JWT secret mismatch\n\t\t\t\t\t// If it was a JWT secret mismatch, the new token will also fail validation\n\t\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\t\t\t\t\tif (authError.type !== 'jwt_secret_mismatch') {\n\t\t\t\t\t\tuseCollaborationStore.getState().clearAuthError()\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update read-only state based on the new JWT\n\t\t\t\t\tif (payload.isFileReadOnly !== undefined) {\n\t\t\t\t\t\tconsole.log(`[JWTStore] JWT indicates ${payload.isFileReadOnly ? 'read-only' : 'write'} access`)\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(payload.isFileReadOnly)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn token\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[JWTStore] Error refreshing JWT:', error)\n\n\t\t\t\t\t// Track authentication failures for better error detection\n\t\t\t\t\tif (error instanceof Error && 'response' in error && error.response) {\n\t\t\t\t\t\tconst status = (error.response as any).status\n\t\t\t\t\t\tif (status === 401 || status === 403) {\n\t\t\t\t\t\t\tconsole.warn('[JWTStore] Authentication error during token refresh')\n\t\t\t\t\t\t\tuseCollaborationStore.getState().incrementAuthFailure(\n\t\t\t\t\t\t\t\t'unauthorized',\n\t\t\t\t\t\t\t\t'Authentication failed during token refresh',\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Log network errors but continue with the same error handling\n\t\t\t\t\tif (error instanceof Error\n\t\t\t\t\t\t&& (error.message.includes('Network Error')\n\t\t\t\t\t\t || error.message.includes('Failed to fetch')\n\t\t\t\t\t\t || error.message.includes('timeout'))) {\n\t\t\t\t\t\tconsole.log('[JWTStore] Network error detected during token refresh')\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return existing token if available, with proper validation\n\t\t\t\t\tconst existingToken = get().tokens[fileId]\n\t\t\t\t\tif (existingToken) {\n\t\t\t\t\t\t// Always validate the token before using it after a refresh failure\n\t\t\t\t\t\tconst payload = get().parseJwt(existingToken)\n\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\tconsole.warn('[JWTStore] Invalid token after refresh failure, enforcing read-only mode')\n\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Token is structurally valid, apply its permissions\n\t\t\t\t\t\t\tif (payload.isFileReadOnly !== undefined) {\n\t\t\t\t\t\t\t\tconsole.log(`[JWTStore] Setting permissions from fallback token: ${payload.isFileReadOnly ? 'read-only' : 'write'} access`)\n\t\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(payload.isFileReadOnly)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.log('[JWTStore] Using existing token after refresh failure')\n\t\t\t\t\t\treturn existingToken\n\t\t\t\t\t}\n\n\t\t\t\t\t// No token available, default to read-only mode\n\t\t\t\t\tconsole.warn('[JWTStore] No token available after refresh failure, enforcing read-only mode')\n\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t},\n\n\t\t\texecuteWithJWT: async (apiCall) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst token = await get().getJWT()\n\n\t\t\t\t\tif (!token) {\n\t\t\t\t\t\t// Default to read-only mode if no token is available\n\t\t\t\t\t\tconsole.warn('[JWTStore] No token available for API call, enforcing read-only mode')\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\tthrow new Error('Failed to obtain JWT token')\n\t\t\t\t\t}\n\n\t\t\t\t\tconst result = await apiCall(token)\n\n\t\t\t\t\t// Only clear auth errors if this was not a JWT secret mismatch\n\t\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\t\t\t\t\tif (authError.type !== 'jwt_secret_mismatch') {\n\t\t\t\t\t\tuseCollaborationStore.getState().clearAuthError()\n\t\t\t\t\t}\n\n\t\t\t\t\treturn result\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Handle authentication errors (401/403)\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t&& 'response' in error\n\t\t\t\t\t\t&& error.response\n\t\t\t\t\t\t&& (error.response as any).status !== undefined\n\t\t\t\t\t\t&& ((error.response as any).status === 401\n\t\t\t\t\t\t\t|| (error.response as any).status === 403)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Authentication error indicates the token is invalid\n\t\t\t\t\t\t// Set read-only mode as a security precaution\n\t\t\t\t\t\tconsole.warn('[JWTStore] Authentication error, enforcing read-only mode')\n\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\n\t\t\t\t\t\t// Track authentication failure for better error detection\n\t\t\t\t\t\tconst status = (error.response as any).status\n\t\t\t\t\t\tconst errorType = status === 401 ? 'token_expired' : 'unauthorized'\n\t\t\t\t\t\tuseCollaborationStore.getState().incrementAuthFailure(\n\t\t\t\t\t\t\terrorType,\n\t\t\t\t\t\t\t`Authentication failed with status ${status}`,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Check if we have a persistent auth error before attempting refresh\n\t\t\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\t\t\t\t\t\tif (authError.isPersistent) {\n\t\t\t\t\t\t\tconsole.warn('[JWTStore] Persistent auth error detected, not attempting token refresh')\n\t\t\t\t\t\t\tuseWhiteboardConfigStore.getState().setReadOnly(true)\n\t\t\t\t\t\t\tthrow new Error('Persistent authentication error - working in local-only mode')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Always attempt to refresh the token regardless of connection state\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t'[JWTStore] Token expired or invalid, refreshing and retrying...',\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// If a refresh is already in progress, wait for it to complete\n\t\t\t\t\t\tif (get().refreshPromise) {\n\t\t\t\t\t\t\tconsole.log('[JWTStore] Token refresh already in progress, waiting for it to complete')\n\t\t\t\t\t\t\tconst newToken = await get().refreshPromise\n\t\t\t\t\t\t\tif (!newToken) {\n\t\t\t\t\t\t\t\tthrow new Error('Failed to refresh JWT token')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn await apiCall(newToken)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Start a new refresh\n\t\t\t\t\t\tconst refreshPromise = get().refreshJWT()\n\t\t\t\t\t\tset({ refreshPromise })\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst newToken = await refreshPromise\n\t\t\t\t\t\t\tif (!newToken) {\n\t\t\t\t\t\t\t\tthrow new Error('Failed to refresh JWT token')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn await apiCall(newToken)\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t// Clear the promise when done\n\t\t\t\t\t\t\tset({ refreshPromise: null })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tsetupAutoRefresh: (roomId) => {\n\t\t\t\tconst { autoRefreshTimers, tokenExpiries, refreshJWT } = get()\n\n\t\t\t\t// Clear any existing timer for this room\n\t\t\t\tif (autoRefreshTimers[roomId]) {\n\t\t\t\t\tclearTimeout(autoRefreshTimers[roomId])\n\t\t\t\t}\n\n\t\t\t\t// Get expiry time for this token\n\t\t\t\tconst expiry = tokenExpiries[roomId]\n\t\t\t\tif (!expiry) {\n\t\t\t\t\tconsole.log(`[JWTStore] No expiry found for room ${roomId}, skipping auto-refresh setup`)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Calculate time until refresh (expiry - buffer)\n\t\t\t\tconst now = Math.floor(Date.now() / 1000)\n\t\t\t\tconst timeUntilRefresh = Math.max(0, (expiry - TOKEN_REFRESH_BUFFER - now) * 1000)\n\n\t\t\t\tconsole.log(`[JWTStore] Setting up auto-refresh for room ${roomId} in ${Math.round(timeUntilRefresh / 1000)} seconds (at ${new Date(Date.now() + timeUntilRefresh).toISOString()})`)\n\n\t\t\t\t// Set up the timer\n\t\t\t\tconst timerId = window.setTimeout(async () => {\n\t\t\t\t\tconsole.log(`[JWTStore] Auto-refresh timer triggered for room ${roomId}`)\n\n\t\t\t\t\t// Check if we have a persistent auth error before auto-refreshing\n\t\t\t\t\tconst { authError } = useCollaborationStore.getState()\n\t\t\t\t\tif (authError.isPersistent) {\n\t\t\t\t\t\tconsole.warn('[JWTStore] Persistent auth error detected, skipping auto-refresh')\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tawait refreshJWT()\n\n\t\t\t\t\t// After refresh, set up the next auto-refresh\n\t\t\t\t\tget().setupAutoRefresh(roomId)\n\t\t\t\t}, timeUntilRefresh)\n\n\t\t\t\t// Store the timer ID\n\t\t\t\tset((state) => ({\n\t\t\t\t\tautoRefreshTimers: {\n\t\t\t\t\t\t...state.autoRefreshTimers,\n\t\t\t\t\t\t[roomId]: timerId,\n\t\t\t\t\t},\n\t\t\t\t}))\n\t\t\t},\n\n\t\t\tclearAutoRefresh: (roomId) => {\n\t\t\t\tconst { autoRefreshTimers } = get()\n\n\t\t\t\t// Clear the timer if it exists\n\t\t\t\tif (autoRefreshTimers[roomId]) {\n\t\t\t\t\tclearTimeout(autoRefreshTimers[roomId])\n\n\t\t\t\t\t// Remove the timer from state\n\t\t\t\t\tset((state) => {\n\t\t\t\t\t\tconst newTimers = { ...state.autoRefreshTimers }\n\t\t\t\t\t\tdelete newTimers[roomId]\n\t\t\t\t\t\treturn { autoRefreshTimers: newTimers }\n\t\t\t\t\t})\n\n\t\t\t\t\tconsole.log(`[JWTStore] Cleared auto-refresh timer for room ${roomId}`)\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tclearTokens: () => {\n\t\t\t\tconst { autoRefreshTimers } = get()\n\n\t\t\t\t// Clear all auto-refresh timers\n\t\t\t\tObject.values(autoRefreshTimers).forEach(timerId => {\n\t\t\t\t\tclearTimeout(timerId)\n\t\t\t\t})\n\n\t\t\t\t// Reset the store state\n\t\t\t\tset({\n\t\t\t\t\ttokens: {},\n\t\t\t\t\ttokenExpiries: {},\n\t\t\t\t\tautoRefreshTimers: {},\n\t\t\t\t})\n\n\t\t\t\tconsole.log('[JWTStore] All tokens and timers cleared')\n\t\t\t},\n\t\t}),\n\t\t{\n\t\t\tname: 'jwt-storage',\n\t\t\tstorage: createJSONStorage(() => localStorage),\n\t\t\tversion: 2, // Increment this to invalidate old stored tokens\n\t\t\tpartialize: (state) => ({\n\t\t\t\ttokens: state.tokens,\n\t\t\t\ttokenExpiries: state.tokenExpiries,\n\t\t\t\t// Don't persist timer IDs or refresh promises as they're not valid across sessions\n\t\t\t\tautoRefreshTimers: {},\n\t\t\t\trefreshPromise: null,\n\t\t\t}),\n\t\t\tmigrate: (persistedState: any, version: number) => {\n\t\t\t\t// Clear tokens if version changes\n\t\t\t\tif (version < 2) {\n\t\t\t\t\tconsole.log('[JWTStore] Storage version changed, clearing old tokens')\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttokens: {},\n\t\t\t\t\t\ttokenExpiries: {},\n\t\t\t\t\t\tautoRefreshTimers: {},\n\t\t\t\t\t\trefreshPromise: null,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn persistedState\n\t\t\t},\n\t\t},\n\t),\n)\n"],"names":["createStoreImpl","createState","state","listeners","setState","partial","replace","nextState","previousState","listener","getState","api","initialState","createStore","identity","arg","useStore","selector","slice","React","createImpl","useBoundStore","create","createResolvablePromise","resolve","reject","promise","_resolve","_reject","useWhiteboardConfigStore","set","get","config","data","initialDataPromise","pendingInitialDataPromises","fileId","fileName","publicSharingToken","isEmbedded","collabBackendUrl","enabled","readOnly","attachTestHooks","hooks","original","createJSONStorage","getStorage","options","storage","name","_a","parse","str2","str","newValue","toThenable","fn","input","result","onFulfilled","_onRejected","_onFulfilled","onRejected","persistImpl","baseOptions","persistedState","currentState","hasHydrated","hydrationVersion","hydrationListeners","finishHydrationListeners","args","setItem","savedSetState","configResult","stateFromStorage","hydrate","_b","currentVersion","cb","_a2","postRehydrationCallback","deserializedStorageValue","migration","migrationResult","migrated","migratedState","e","newOptions","persist","initialAuthErrorState","MAX_AUTH_FAILURES","PERSISTENT_FAILURE_THRESHOLD","useCollaborationStore","status","socket","isSyncer","inRoom","error","errorType","message","now","newFailureCount","isPersistent","autoFollow","voting","v","votings","TOKEN_REFRESH_BUFFER","isTokenFormatValid","token","parts","useJWTStore","base64","jsonPayload","c","payload","roomId","tokens","tokenExpiries","expiry","authError","isTokenExpired","setupAutoRefresh","validateJwtIntegrity","fileIdStr","newTokens","newExpiries","refreshPromise","newToken","baseUrl","generateUrl","url","response","axios","existingToken","apiCall","autoRefreshTimers","refreshJWT","timeUntilRefresh","timerId","newTimers","version"],"mappings":"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","x_google_ignoreList":[0,1,4]}