From ca3cf4004057f782a9f146ecf2c7efd607fdf15c Mon Sep 17 00:00:00 2001 From: Will Hunt <2072976+Half-Shot@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:44:38 +0100 Subject: [PATCH] Add a Module API for accessing storage helper functions. (#34284) * Add a Module API for accessing the Platform. * Drop overkill module test * drop unused sample file * fix import * Update module API * fix typo * fix typo again * and again * update api --- apps/web/src/modules/Api.ts | 2 ++ apps/web/src/modules/StorageHelperApi.test.ts | 32 +++++++++++++++++++ apps/web/src/modules/StorageHelperApi.ts | 15 +++++++++ .../module-api/element-web-module-api.api.md | 7 ++++ packages/module-api/src/api/index.ts | 6 ++++ packages/module-api/src/api/storage-helper.ts | 20 ++++++++++++ packages/module-api/src/index.ts | 1 + 7 files changed, 83 insertions(+) create mode 100644 apps/web/src/modules/StorageHelperApi.test.ts create mode 100644 apps/web/src/modules/StorageHelperApi.ts create mode 100644 packages/module-api/src/api/storage-helper.ts diff --git a/apps/web/src/modules/Api.ts b/apps/web/src/modules/Api.ts index 0bd3e247bd..df822131e1 100644 --- a/apps/web/src/modules/Api.ts +++ b/apps/web/src/modules/Api.ts @@ -34,6 +34,7 @@ import { WidgetLifecycleApi } from "./WidgetLifecycleApi.ts"; import { WidgetApi } from "./WidgetApi.ts"; import { CustomisationsApi } from "./customisationsApi.ts"; import { ComposerApi } from "./ComposerApi.ts"; +import { StorageHelperApi } from "./StorageHelperApi.ts"; import { SettingsApi } from "./SettingsApi.ts"; import defaultDispatcher from "../dispatcher/dispatcher.ts"; @@ -97,6 +98,7 @@ export class ModuleApi implements Api { public readonly client = new ClientApi(); public readonly stores = new StoresApi(); public readonly composer = new ComposerApi(defaultDispatcher); + public readonly storageHelper = new StorageHelperApi(); public readonly settings = new SettingsApi(); public createRoot(element: Element): Root { diff --git a/apps/web/src/modules/StorageHelperApi.test.ts b/apps/web/src/modules/StorageHelperApi.test.ts new file mode 100644 index 0000000000..d476accd95 --- /dev/null +++ b/apps/web/src/modules/StorageHelperApi.test.ts @@ -0,0 +1,32 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +// @vitest-environment happy-dom + +import { describe, it, expect, vi } from "vitest"; +import { mockPlatformPeg } from "test-utils"; + +import { StorageHelperApi } from "./StorageHelperApi"; + +describe("StorageHelperApi", () => { + it("should return the pickle key from the platform", async () => { + const mockPlatform = mockPlatformPeg(); + vi.spyOn(mockPlatform, "getPickleKey").mockResolvedValue("test-pickle-key"); + + const api = new StorageHelperApi(); + await expect(api.getPickleKey("@alice:example.org", "DEVICE123")).resolves.toBe("test-pickle-key"); + expect(mockPlatform.getPickleKey).toHaveBeenCalledWith("@alice:example.org", "DEVICE123"); + }); + + it("should return null if the platform has no pickle key stored", async () => { + const mockPlatform = mockPlatformPeg(); + vi.spyOn(mockPlatform, "getPickleKey").mockResolvedValue(null); + + const api = new StorageHelperApi(); + await expect(api.getPickleKey("@alice:example.org", "DEVICE123")).resolves.toBeNull(); + }); +}); diff --git a/apps/web/src/modules/StorageHelperApi.ts b/apps/web/src/modules/StorageHelperApi.ts new file mode 100644 index 0000000000..42753c6627 --- /dev/null +++ b/apps/web/src/modules/StorageHelperApi.ts @@ -0,0 +1,15 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { StorageHelperApi as IStorageHelperApi } from "@element-hq/element-web-module-api"; +import PlatformPeg from "../PlatformPeg"; + +export class StorageHelperApi implements IStorageHelperApi { + public async getPickleKey(userId: string, deviceId: string): Promise { + return PlatformPeg.get()!.getPickleKey(userId, deviceId); + } +} diff --git a/packages/module-api/element-web-module-api.api.md b/packages/module-api/element-web-module-api.api.md index 14bd20f0ea..d27bdc3315 100644 --- a/packages/module-api/element-web-module-api.api.md +++ b/packages/module-api/element-web-module-api.api.md @@ -63,6 +63,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx readonly rootNode: HTMLElement; // @alpha readonly settings: SettingsApi; + // @alpha + readonly storageHelper: StorageHelperApi; readonly stores: StoresApi; // @alpha readonly widget: WidgetApi; @@ -495,6 +497,11 @@ export interface SpacePanelItemProps { tooltip?: string; } +// @alpha +export interface StorageHelperApi { + getPickleKey(userId: string, deviceId: string): Promise; +} + // @public export interface StoresApi { roomListStore: RoomListStoreApi; diff --git a/packages/module-api/src/api/index.ts b/packages/module-api/src/api/index.ts index 9d1556d97f..151d385e16 100644 --- a/packages/module-api/src/api/index.ts +++ b/packages/module-api/src/api/index.ts @@ -24,6 +24,7 @@ import { type WidgetLifecycleApi } from "./widget-lifecycle.ts"; import { type WidgetApi } from "./widget.ts"; import { type CustomisationsApi } from "./customisations.ts"; import { type ComposerApi } from "./composer.ts"; +import { type StorageHelperApi } from "./storage-helper.ts"; import { type SettingsApi } from "./settings.ts"; /** @@ -167,6 +168,11 @@ export interface Api */ readonly composer: ComposerApi; + /** + * Allows modules to access storage helper functions. + * @alpha Subject to change. + */ + readonly storageHelper: StorageHelperApi; /** * Allows modules to read application settings. * @alpha Subject to change. diff --git a/packages/module-api/src/api/storage-helper.ts b/packages/module-api/src/api/storage-helper.ts new file mode 100644 index 0000000000..a9487ea762 --- /dev/null +++ b/packages/module-api/src/api/storage-helper.ts @@ -0,0 +1,20 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * API for accessing functions that help with storing things. + * @alpha Subject to change. + */ +export interface StorageHelperApi { + /** + * Gets a previously stored pickle key, used for encrypting crypto data. + * @param userId - the user ID for the user that the pickle key is for. + * @param deviceId - the device ID that the pickle key is for. + * @returns the previously stored pickle key, or null if no pickle key has been stored. + */ + getPickleKey(userId: string, deviceId: string): Promise; +} diff --git a/packages/module-api/src/index.ts b/packages/module-api/src/index.ts index 71058f0a79..64dee60878 100644 --- a/packages/module-api/src/index.ts +++ b/packages/module-api/src/index.ts @@ -35,6 +35,7 @@ export type * from "./api/widget-lifecycle"; export type * from "./api/widget"; export type * from "./api/customisations"; export { UIComponent } from "./api/customisations"; +export type * from "./api/storage-helper"; export type * from "./api/settings"; export * from "./api/watchable"; export type * from "./utils";