{"version":3,"sources":["jsdelivr-header.js","/npm/@netless/synced-store@2.0.9/src/utils.ts","/npm/@netless/synced-store@2.0.9/src/refine.ts","/npm/@netless/synced-store@2.0.9/src/storage.ts","/npm/@netless/synced-store@2.0.9/src/synced-store.ts","/npm/@netless/synced-store@2.0.9/src/invisible-plugin.ts","/npm/@netless/synced-store@2.0.9/src/create-plugin.ts"],"names":[],"mappings":"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","file":"/npm/@netless/synced-store@2.0.9/dist/synced-store.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@netless/synced-store@2.0.9/dist/synced-store.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","declare module \"white-web-sdk\" {\r\n  export function isRoom(displayer: Displayer): displayer is Room;\r\n}\r\n\r\nexport const isObject = <O>(obj?: O): obj is O =>\r\n  typeof obj === \"object\" && obj !== null;\r\n\r\nexport const ensureObject = (obj: any, method: string): void => {\r\n  if (!isObject(obj)) {\r\n    throw new TypeError(\r\n      `[SyncedStore]: ${method} expects an object, got ${typeof obj}`\r\n    );\r\n  }\r\n};\r\n\r\nconst objHas = Object.prototype.hasOwnProperty;\r\n\r\nexport const has = <O, K extends string>(\r\n  obj: O,\r\n  key: K\r\n): obj is O & Record<K, Required<O>[Extract<K, keyof O>]> => {\r\n  return objHas.call(obj, key);\r\n};\r\n\r\nexport const plainObjectKeys = Object.keys as <T>(\r\n  o: T\r\n) => Array<Extract<keyof T, string>>;\r\n\r\nexport const toJS = <O>(obj: O): O => {\r\n  return isObject(obj) ? JSON.parse(JSON.stringify(obj)) : obj;\r\n};\r\n","import type { Diff, DiffOne } from \"./typings\";\r\n\r\nimport { genUID } from \"side-effect-manager\";\r\nimport { has, isObject, plainObjectKeys } from \"./utils\";\r\n\r\nconst REFINE_KEY = \"__IsReFiNe\";\r\n\r\nexport type RefineValue<TValue = any> = {\r\n  k: string;\r\n  v: TValue;\r\n  [REFINE_KEY]: 1;\r\n};\r\n\r\nexport type MaybeRefineValue<TValue = any> = TValue | RefineValue<TValue>;\r\n\r\nexport type RefineState<TState = any> = {\r\n  [K in keyof TState]: MaybeRefineValue<TState[K]>;\r\n};\r\n\r\nexport type ExtractRawValue<TValue> = TValue extends RefineValue<\r\n  infer TRefineValue\r\n>\r\n  ? TRefineValue\r\n  : TValue;\r\n\r\nexport function isRefineValue<TValue>(\r\n  value: MaybeRefineValue<TValue>\r\n): value is RefineValue<TValue> {\r\n  return isObject(value) && (value as RefineValue<TValue>)[REFINE_KEY] === 1;\r\n}\r\n\r\nexport function isMaybeRefineValue<TValue>(\r\n  value: MaybeRefineValue<TValue>\r\n): value is MaybeRefineValue<TValue> {\r\n  return !isObject(value) || (value as RefineValue<TValue>)[REFINE_KEY] === 1;\r\n}\r\n\r\nexport function makeRefineValue<TValue>(\r\n  value: TValue,\r\n  key: string = genUID()\r\n): RefineValue<TValue> {\r\n  return { [REFINE_KEY]: 1, k: key, v: value };\r\n}\r\n\r\nexport class Refine<TState = any> {\r\n  public state: TState;\r\n\r\n  public constructor(refState?: RefineState<TState>, defaultState?: TState) {\r\n    this.state = {} as TState;\r\n    this.replaceState(refState || defaultState || ({} as TState));\r\n  }\r\n\r\n  public replaceState(state: RefineState<TState>): Diff<TState> | null {\r\n    if (!isObject(state)) {\r\n      throw new Error(\"[SyncedStore] replaceState: state must be an object\");\r\n    }\r\n    const diff = {} as Diff<TState>;\r\n    let hasDiff = false;\r\n    const allKeys = new Set([\r\n      ...plainObjectKeys(this.state),\r\n      ...plainObjectKeys(state),\r\n    ]);\r\n    allKeys.forEach(key => {\r\n      const diffOne = this.setValue(key, state[key]);\r\n      if (diffOne) {\r\n        hasDiff = true;\r\n        diff[key] = diffOne;\r\n      }\r\n    });\r\n    return hasDiff ? diff : null;\r\n  }\r\n\r\n  public toRefState(): RefineState<TState> {\r\n    return plainObjectKeys(this.state).reduce((refState, key) => {\r\n      refState[key] = this.toRefValue(this.state[key]);\r\n      return refState;\r\n    }, {} as RefineState<TState>);\r\n  }\r\n\r\n  public setValue<TKey extends Extract<keyof TState, string>>(\r\n    key: TKey,\r\n    maybeRefValue: MaybeRefineValue<TState[TKey]> | undefined\r\n  ): DiffOne<TState[TKey]> | null {\r\n    if (isObject(maybeRefValue)) {\r\n      const refValue = this.ensureRefValue(maybeRefValue);\r\n      if (this.state[key] !== refValue.v) {\r\n        const oldValue = this.deleteRefKey(key);\r\n        this.state[key] = refValue.v;\r\n        return { oldValue, newValue: refValue.v };\r\n      }\r\n    } else if (typeof maybeRefValue === \"undefined\") {\r\n      if (has(this.state, key)) {\r\n        const oldValue = this.deleteRefKey(key);\r\n        delete this.state[key];\r\n        return { oldValue, newValue: undefined as unknown as TState[TKey] };\r\n      }\r\n    } else {\r\n      const value = maybeRefValue as TState[TKey];\r\n      if (this.state[key] !== value) {\r\n        const oldValue = this.deleteRefKey(key);\r\n        this.state[key] = value;\r\n        return { oldValue, newValue: value };\r\n      }\r\n    }\r\n    return null;\r\n  }\r\n\r\n  public toRefValue<TKey extends Extract<keyof TState, string>>(\r\n    maybeRefValue: MaybeRefineValue<TState[TKey]>\r\n  ): RefineValue<TState[TKey]> {\r\n    return isObject(maybeRefValue)\r\n      ? this.ensureRefValue(maybeRefValue)\r\n      : maybeRefValue;\r\n  }\r\n\r\n  public ensureRefValue<TValue>(\r\n    value: MaybeRefineValue<TValue>\r\n  ): RefineValue<TValue> {\r\n    if (isRefineValue(value)) {\r\n      this.refMap.set(value.v, value);\r\n      return value;\r\n    }\r\n    let refValue = this.refMap.get(value);\r\n    if (!refValue) {\r\n      refValue = makeRefineValue(value, this.genKey());\r\n      this.refMap.set(value, refValue);\r\n    }\r\n    this.refKeys.add(refValue.k);\r\n    return refValue;\r\n  }\r\n\r\n  private refMap: Map<any, RefineValue<any>> = new Map();\r\n\r\n  private refKeys = new Set<string>();\r\n  private genKey = (): string => {\r\n    let key: string;\r\n    do {\r\n      key = genUID();\r\n    } while (this.refKeys.has(key));\r\n    this.refKeys.add(key);\r\n    return key;\r\n  };\r\n\r\n  private deleteRefKey<TKey extends Extract<keyof TState, string>>(\r\n    key: TKey\r\n  ): TState[TKey] {\r\n    const value = this.state[key];\r\n    const refValue = this.refMap.get(value);\r\n    if (refValue) {\r\n      this.refKeys.delete(refValue.k);\r\n    }\r\n    return value;\r\n  }\r\n}\r\n","import type {\r\n  RemitterDisposer,\r\n  RemitterEventNames,\r\n  RemitterListener,\r\n} from \"remitter\";\r\nimport type { SideEffectDisposer } from \"side-effect-manager\";\r\nimport type { ReadonlyVal } from \"value-enhancer\";\r\nimport type { AkkoObjectUpdatedListener, InvisiblePlugin } from \"white-web-sdk\";\r\nimport type { RefineState } from \"./refine\";\r\nimport type { Diff } from \"./typings\";\r\n\r\nimport { Remitter } from \"remitter\";\r\nimport { SideEffectManager } from \"side-effect-manager\";\r\nimport { subscribe } from \"value-enhancer\";\r\nimport {\r\n  RoomPhase,\r\n  listenUpdated,\r\n  reaction,\r\n  toJS,\r\n  unlistenUpdated,\r\n} from \"white-web-sdk\";\r\nimport { Refine } from \"./refine\";\r\nimport { isObject, plainObjectKeys } from \"./utils\";\r\n\r\nexport const STORAGE_NS = \"_WM-StOrAgE_\";\r\n\r\nexport const MAIN_STORAGE = \"_WM-MaIn-StOrAgE_\";\r\n\r\nexport interface StorageEventData<TState> {\r\n  stateChanged: Diff<TState>;\r\n  disconnected: void;\r\n}\r\n\r\nexport interface StorageConfig<TState = any> {\r\n  namespace?: string;\r\n  plugin$: ReadonlyVal<InvisiblePlugin<any, any> | null>;\r\n  isWritable$: ReadonlyVal<boolean>;\r\n  defaultState?: TState;\r\n}\r\n\r\nexport class Storage<TState extends Record<string, any> = any> {\r\n  public readonly namespace: string;\r\n  public defaultState: Readonly<TState>;\r\n  private _plugin$: StorageConfig[\"plugin$\"];\r\n  private _isWritable$: StorageConfig[\"isWritable$\"];\r\n  private _refine: Refine<TState>;\r\n  private _sideEffect = new SideEffectManager();\r\n  private _events = new Remitter<StorageEventData<TState>>();\r\n\r\n  public constructor({\r\n    plugin$,\r\n    isWritable$,\r\n    namespace = MAIN_STORAGE,\r\n    defaultState = {} as TState,\r\n  }: StorageConfig<TState>) {\r\n    if (defaultState && !isObject(defaultState)) {\r\n      throw new Error(\r\n        `Default state for Storage ${namespace} is not an object.`\r\n      );\r\n    }\r\n\r\n    this.on = this._events.on.bind(this._events);\r\n    this.off = this._events.off.bind(this._events);\r\n\r\n    const getRawState = (): RefineState<TState> =>\r\n      plugin$.value?.attributes[STORAGE_NS]?.[namespace];\r\n\r\n    this.namespace = namespace;\r\n    this.defaultState = defaultState;\r\n    this._plugin$ = plugin$;\r\n    this._isWritable$ = isWritable$;\r\n    this._refine = new Refine(toJS(getRawState()), defaultState);\r\n\r\n    const onDiff = (diff: Diff<TState> | null): void => {\r\n      if (diff) {\r\n        this._events.emit(\"stateChanged\", diff);\r\n      }\r\n    };\r\n\r\n    const ensureInitState = (\r\n      plugin: InvisiblePlugin<any, any>\r\n    ): SideEffectDisposer => {\r\n      return subscribe(isWritable$, isWritable => {\r\n        if (isWritable) {\r\n          if (!isObject(plugin.attributes[STORAGE_NS])) {\r\n            plugin.updateAttributes([STORAGE_NS], {});\r\n          }\r\n          if (!isObject(toJS(getRawState()))) {\r\n            plugin.updateAttributes(\r\n              [STORAGE_NS, namespace],\r\n              this._refine.toRefState()\r\n            );\r\n          }\r\n        }\r\n      });\r\n    };\r\n\r\n    const listenNamespaceProps = (\r\n      rawState: RefineState<TState>\r\n    ): SideEffectDisposer => {\r\n      const handler: AkkoObjectUpdatedListener<TState> = actions => {\r\n        if (actions.length <= 0) return;\r\n        const diff = {} as Diff<TState>;\r\n        let hasDiff = false;\r\n        for (let i = 0; i < actions.length; i++) {\r\n          const action = actions[i];\r\n          const key = action.key as Extract<keyof TState, string>;\r\n          if (key === STORAGE_NS) {\r\n            continue;\r\n          }\r\n\r\n          const value = toJS(action.value);\r\n          const diffOne = this._refine.setValue(key, value);\r\n          if (diffOne) {\r\n            hasDiff = true;\r\n            diff[key] = diffOne;\r\n          }\r\n        }\r\n        if (hasDiff) {\r\n          onDiff(diff);\r\n        }\r\n      };\r\n      listenUpdated(rawState, handler);\r\n      return () => unlistenUpdated(rawState, handler);\r\n    };\r\n\r\n    const listenNamespace = (): SideEffectDisposer => {\r\n      let propsDisposer: SideEffectDisposer | undefined;\r\n      const reactionDisposer = reaction(\r\n        getRawState,\r\n        () => {\r\n          const rawState = getRawState();\r\n          if (rawState) {\r\n            onDiff(this._refine.replaceState(toJS(rawState)));\r\n            propsDisposer?.();\r\n            propsDisposer = listenNamespaceProps(rawState);\r\n          }\r\n        },\r\n        { fireImmediately: true }\r\n      );\r\n      return () => {\r\n        reactionDisposer();\r\n        propsDisposer?.();\r\n      };\r\n    };\r\n\r\n    const listenStorageChange = (\r\n      plugin: InvisiblePlugin<any, any>\r\n    ): SideEffectDisposer => {\r\n      let disposer = listenNamespace();\r\n\r\n      const handler = async (phase: RoomPhase): Promise<void> => {\r\n        if (phase === RoomPhase.Connected) {\r\n          disposer();\r\n          disposer = listenNamespace();\r\n        }\r\n      };\r\n      plugin.displayer.callbacks.on(\"onPhaseChanged\", handler);\r\n\r\n      return () => {\r\n        plugin.displayer.callbacks.off(\"onPhaseChanged\", handler);\r\n        disposer();\r\n      };\r\n    };\r\n\r\n    this._sideEffect.addDisposer(\r\n      subscribe(this._plugin$, plugin => {\r\n        const disposers: SideEffectDisposer[] = [];\r\n        this._sideEffect.flush(\"plugin-init\");\r\n        if (plugin) {\r\n          const rawState = toJS(getRawState());\r\n          if (isObject(rawState)) {\r\n            onDiff(this._refine.replaceState(rawState));\r\n          } else {\r\n            disposers.push(ensureInitState(plugin));\r\n          }\r\n          disposers.push(listenStorageChange(plugin));\r\n        } else {\r\n          onDiff(this._refine.replaceState(defaultState));\r\n        }\r\n        if (disposers.length > 0) {\r\n          this._sideEffect.addDisposer(disposers, \"plugin-init\");\r\n        }\r\n      })\r\n    );\r\n  }\r\n\r\n  public get isWritable(): boolean {\r\n    return this._isWritable$.value;\r\n  }\r\n\r\n  public get state(): Readonly<TState> {\r\n    return this._refine.state;\r\n  }\r\n\r\n  public setState(state: Partial<TState>): void {\r\n    const plugin = this._requireAccess(\"setState\");\r\n\r\n    if (!this.isWritable) {\r\n      throw new Error(\r\n        `Cannot setState on Storage \"${this.namespace}\" without writable access`\r\n      );\r\n    }\r\n\r\n    const keys = plainObjectKeys(state);\r\n    for (let i = 0; i < keys.length; i++) {\r\n      const key = keys[i];\r\n      const value = state[key];\r\n      plugin.updateAttributes(\r\n        [STORAGE_NS, this.namespace, key],\r\n        isObject(value) ? this._refine.ensureRefValue(value) : value\r\n      );\r\n    }\r\n  }\r\n\r\n  /** reset storage state to default state */\r\n  public resetState(): void {\r\n    const plugin = this._requireAccess(\"resetState\");\r\n    plugin.updateAttributes([STORAGE_NS, this.namespace], this.defaultState);\r\n  }\r\n\r\n  /** delete synced storage data and disconnect from synced storage */\r\n  public deleteStorage(): void {\r\n    const plugin = this._requireAccess(\"deleteStorage\");\r\n    this.disconnect();\r\n    plugin.updateAttributes([STORAGE_NS, this.namespace], undefined);\r\n  }\r\n\r\n  /**\r\n   * Add a listener to the eventName.\r\n   */\r\n  public on: <TEventName extends RemitterEventNames<StorageEventData<TState>>>(\r\n    eventName: TEventName,\r\n    listener: RemitterListener<StorageEventData<TState>, TEventName>\r\n  ) => RemitterDisposer;\r\n\r\n  /**\r\n   * Remove a listener from the eventName.\r\n   */\r\n  public off: <TEventName extends RemitterEventNames<StorageEventData<TState>>>(\r\n    eventName: TEventName,\r\n    listener: RemitterListener<StorageEventData<TState>, TEventName>\r\n  ) => boolean;\r\n\r\n  /** Disconnect from synced storage and release listeners */\r\n  public disconnect(): void {\r\n    this._disconnected = true;\r\n    this._sideEffect.flushAll();\r\n    this._events.emit(\"disconnected\");\r\n    this._events.dispose();\r\n  }\r\n\r\n  public get disconnected(): boolean {\r\n    return this._disconnected;\r\n  }\r\n\r\n  private _disconnected = false;\r\n\r\n  private _requireAccess(method: string): InvisiblePlugin<any, any> {\r\n    if (this._disconnected) {\r\n      throw new Error(\r\n        `Cannot call ${method} on destroyed Storage '${this.namespace}'.`\r\n      );\r\n    }\r\n\r\n    const plugin = this._plugin$.value;\r\n    if (!plugin) {\r\n      throw new Error(\r\n        `[SyncedStore]: cannot call '${method}' on Storage '${this.namespace}' because plugin is not initialized.`\r\n      );\r\n    }\r\n\r\n    if (!this.isWritable) {\r\n      throw new Error(\r\n        `[SyncedStore]: cannot call '${method}' on Storage '${this.namespace}' without writable permission`\r\n      );\r\n    }\r\n\r\n    return plugin;\r\n  }\r\n}\r\n","import type { ReadonlyVal } from \"value-enhancer\";\r\nimport type {\r\n  Displayer,\r\n  InvisiblePlugin,\r\n  MagixEventListenerOptions,\r\n  Room,\r\n  EventListener as WhiteEventListener,\r\n} from \"white-web-sdk\";\r\nimport type {\r\n  MagixEventHandler,\r\n  MagixEventListenerDisposer,\r\n  MagixEventTypes,\r\n} from \"./typings\";\r\n\r\nimport { SideEffectManager, genUID } from \"side-effect-manager\";\r\nimport { combine, reaction } from \"value-enhancer\";\r\nimport { isRoom } from \"white-web-sdk\";\r\nimport { Storage } from \"./storage\";\r\n\r\nexport class SyncedStore<TEventData extends Record<string, any> = any> {\r\n  public readonly displayer: Displayer;\r\n  public readonly _plugin$: ReadonlyVal<InvisiblePlugin<any, any> | null>;\r\n\r\n  private readonly _isPluginWritable$: ReadonlyVal<boolean>;\r\n  private readonly _isRoomWritable$: ReadonlyVal<boolean>;\r\n  private readonly _sideEffect = new SideEffectManager();\r\n  private readonly _room: Room | null;\r\n\r\n  public constructor(\r\n    displayer: Displayer,\r\n    invisiblePlugin$: ReadonlyVal<InvisiblePlugin<any, any> | null>,\r\n    isRoomWritable$: ReadonlyVal<boolean>\r\n  ) {\r\n    this.displayer = displayer;\r\n    this._plugin$ = invisiblePlugin$;\r\n    this._isRoomWritable$ = isRoomWritable$;\r\n    const room = isRoom(displayer) ? displayer : null;\r\n    this._room = room;\r\n\r\n    this._isPluginWritable$ = combine(\r\n      [this._plugin$, this._isRoomWritable$],\r\n      ([plugin, isRoomWritable]) => plugin !== null && isRoomWritable\r\n    );\r\n  }\r\n\r\n  public connectStorage<TState extends Record<string, unknown> = any>(\r\n    namespace?: string,\r\n    defaultState?: TState\r\n  ): Storage<TState> {\r\n    const storage = new Storage({\r\n      plugin$: this._plugin$,\r\n      isWritable$: this._isPluginWritable$,\r\n      namespace,\r\n      defaultState,\r\n    });\r\n    const disconnectDisposerID = this._sideEffect.addDisposer(() =>\r\n      storage.disconnect()\r\n    );\r\n    const disposerID = this._sideEffect.addDisposer(\r\n      storage.on(\"disconnected\", () => {\r\n        this._sideEffect.remove(disconnectDisposerID);\r\n        this._sideEffect.flush(disposerID);\r\n      })\r\n    );\r\n    return storage;\r\n  }\r\n\r\n  public get isPluginWritable(): boolean {\r\n    return this._isPluginWritable$.value;\r\n  }\r\n\r\n  public addPluginWritableChangeListener(\r\n    listener: (isWritable: boolean) => void\r\n  ): () => void {\r\n    return reaction(this._isPluginWritable$, listener);\r\n  }\r\n\r\n  public get isRoomWritable(): boolean {\r\n    return this._isRoomWritable$.value;\r\n  }\r\n\r\n  public async setRoomWritable(isWritable: boolean): Promise<void> {\r\n    if (!this._room) {\r\n      throw new Error(\"[SyncedStore]: cannot set room writable in replay mode\");\r\n    }\r\n    await this._room.setWritable(isWritable);\r\n  }\r\n\r\n  public addRoomWritableChangeListener(\r\n    listener: (isWritable: boolean) => void\r\n  ): () => void {\r\n    return reaction(this._isRoomWritable$, listener);\r\n  }\r\n\r\n  /** Dispatch events to other clients (and self). */\r\n  public dispatchEvent<\r\n    TEvent extends MagixEventTypes<TEventData> = MagixEventTypes<TEventData>,\r\n  >(event: TEvent, payload: TEventData[TEvent]): void {\r\n    if (!this._room) {\r\n      throw new Error(\"[SyncedStore] cannot dispatch event in replay mode\");\r\n    }\r\n    this._room.dispatchMagixEvent(event, payload);\r\n  }\r\n\r\n  /** Listen to events from others clients (and self messages). */\r\n  public addEventListener<\r\n    TEvent extends MagixEventTypes<TEventData> = MagixEventTypes<TEventData>,\r\n  >(\r\n    event: TEvent,\r\n    listener: MagixEventHandler<TEventData, TEvent>,\r\n    options?: MagixEventListenerOptions | undefined\r\n  ): MagixEventListenerDisposer {\r\n    this.displayer.addMagixEventListener(\r\n      event,\r\n      listener as WhiteEventListener,\r\n      options\r\n    );\r\n    return () =>\r\n      this.displayer.removeMagixEventListener(\r\n        event,\r\n        listener as WhiteEventListener\r\n      );\r\n  }\r\n\r\n  /** Remove a Magix event listener. */\r\n  public removeEventListener<\r\n    TEvent extends MagixEventTypes<TEventData> = MagixEventTypes<TEventData>,\r\n  >(event: TEvent, listener?: MagixEventHandler<TEventData, TEvent>): void {\r\n    return this.displayer.removeMagixEventListener(\r\n      event,\r\n      listener as WhiteEventListener\r\n    );\r\n  }\r\n\r\n  /** Wait for all sync operations being pushed to server. */\r\n  public nextFrame(): Promise<void> {\r\n    return new Promise(resolve => {\r\n      if (isRoom(this.displayer)) {\r\n        const uid = genUID();\r\n        const channel = \"SyncedStoreNextFrame\";\r\n        const handler: WhiteEventListener = ev => {\r\n          if (ev.payload === uid) {\r\n            this.displayer.removeMagixEventListener(channel, handler);\r\n            resolve();\r\n          }\r\n        };\r\n        this.displayer.addMagixEventListener(channel, handler, {\r\n          fireSelfEventAfterCommit: true,\r\n        });\r\n        try {\r\n          this.displayer.dispatchMagixEvent(channel, uid);\r\n        } catch (error) {\r\n          console.warn(error);\r\n          this.displayer.removeMagixEventListener(channel, handler);\r\n          resolve();\r\n        }\r\n      } else {\r\n        resolve();\r\n      }\r\n    });\r\n  }\r\n\r\n  /** @deprecated Use `dispose()` instead. */\r\n  public destroy(): void {\r\n    this.dispose();\r\n  }\r\n\r\n  public dispose(): void {\r\n    this._sideEffect.flushAll();\r\n    this._isPluginWritable$.dispose();\r\n  }\r\n}\r\n","import type { ReadonlyVal } from \"value-enhancer\";\r\nimport type { Displayer, InvisiblePluginContext } from \"white-web-sdk\";\r\n\r\nimport { setValue, subscribe, unsubscribe, val } from \"value-enhancer\";\r\nimport { InvisiblePlugin, RoomPhase, isRoom } from \"white-web-sdk\";\r\nimport { STORAGE_NS } from \"./storage\";\r\nimport { SyncedStore } from \"./synced-store\";\r\n\r\nexport class SyncedStorePlugin extends InvisiblePlugin<any, any> {\r\n  public static readonly kind: string = \"SyncedStore\";\r\n  public static readonly invisiblePlugins = new Map<\r\n    Displayer,\r\n    ReadonlyVal<InvisiblePlugin<any, any> | null>\r\n  >();\r\n\r\n  public static async init<TEventData extends Record<string, any> = any>(\r\n    displayer: Displayer\r\n  ): Promise<SyncedStore<TEventData>> {\r\n    const isRoomWritable$ = val(false);\r\n    const updateRoomWritable = () =>\r\n      setValue(isRoomWritable$, isRoom(displayer) && displayer.isWritable);\r\n    updateRoomWritable();\r\n    displayer.callbacks.on(\"onEnableWriteNowChanged\", updateRoomWritable);\r\n\r\n    const invisiblePlugin$ = val<InvisiblePlugin<any, any> | null>(\r\n      displayer.getInvisiblePlugin(SyncedStorePlugin.kind)\r\n    );\r\n    SyncedStorePlugin.invisiblePlugins.set(displayer, invisiblePlugin$);\r\n\r\n    const createSyncedStore = async (\r\n      isRoomWritable: boolean\r\n    ): Promise<void> => {\r\n      if (isRoomWritable && isRoom(displayer)) {\r\n        try {\r\n          const plugin = await displayer.createInvisiblePlugin(\r\n            SyncedStorePlugin,\r\n            { [STORAGE_NS]: {} }\r\n          );\r\n          setValue(invisiblePlugin$, plugin);\r\n          unsubscribe(isRoomWritable$, createSyncedStore);\r\n        } catch (e) {\r\n          // could be error if multiple users create plugin at the same time\r\n          await new Promise(resolve => setTimeout(resolve, 200));\r\n          if (!displayer.getInvisiblePlugin(SyncedStorePlugin.kind)) {\r\n            console.error(e);\r\n          }\r\n        }\r\n      }\r\n    };\r\n\r\n    const removeWritableListener = (plugin: SyncedStorePlugin | null): void => {\r\n      if (plugin) {\r\n        unsubscribe(isRoomWritable$, createSyncedStore);\r\n        unsubscribe(invisiblePlugin$, removeWritableListener);\r\n      }\r\n    };\r\n\r\n    if (!invisiblePlugin$.value && isRoom(displayer)) {\r\n      subscribe(isRoomWritable$, createSyncedStore);\r\n      subscribe(invisiblePlugin$, removeWritableListener);\r\n    }\r\n\r\n    const syncedStore = new SyncedStore(\r\n      displayer,\r\n      invisiblePlugin$,\r\n      isRoomWritable$\r\n    );\r\n\r\n    const onPhaseChanged = (phase: RoomPhase): void => {\r\n      if (phase === RoomPhase.Disconnected) {\r\n        displayer.callbacks.off(\"onPhaseChanged\", onPhaseChanged);\r\n        displayer.callbacks.off(\"onEnableWriteNowChanged\", updateRoomWritable);\r\n        isRoomWritable$.dispose();\r\n        invisiblePlugin$.dispose();\r\n        SyncedStorePlugin.invisiblePlugins.delete(displayer);\r\n      }\r\n    };\r\n    displayer.callbacks.on(\"onPhaseChanged\", onPhaseChanged);\r\n\r\n    return syncedStore;\r\n  }\r\n\r\n  public static onCreate(plugin: SyncedStorePlugin): void {\r\n    const invisiblePlugin$ = SyncedStorePlugin.invisiblePlugins.get(\r\n      plugin.displayer\r\n    );\r\n    invisiblePlugin$ && setValue(invisiblePlugin$, plugin);\r\n  }\r\n\r\n  public constructor(context: InvisiblePluginContext) {\r\n    super(context);\r\n    const invisiblePlugin$ = SyncedStorePlugin.invisiblePlugins.get(\r\n      this.displayer\r\n    );\r\n    invisiblePlugin$ && setValue(invisiblePlugin$, this);\r\n  }\r\n}\r\n","import type { InvisiblePluginContext } from \"white-web-sdk\";\r\nimport { SyncedStorePlugin } from \"./invisible-plugin\";\r\n\r\nexport function createPlugin(\r\n  kind: string = SyncedStorePlugin.kind\r\n): new (context: InvisiblePluginContext) => SyncedStorePlugin {\r\n  return class SyncedStoreCustomPlugin extends SyncedStorePlugin {\r\n    static override readonly kind = kind;\r\n  };\r\n}\r\n"]}