diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 55bd6f4f56..37ff6800eb 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -102,6 +102,7 @@ "mkdirp": "^3.0.0", "pacote": "^22.0.0", "rimraf": "^6.0.0", + "shared-types": "workspace:*", "tar": "^7.5.8", "typescript": "catalog:", "vitest": "catalog:" diff --git a/apps/desktop/src/build-config.ts b/apps/desktop/src/build-config.ts index b4d52865a3..5533e0b774 100644 --- a/apps/desktop/src/build-config.ts +++ b/apps/desktop/src/build-config.ts @@ -7,8 +7,9 @@ Please see LICENSE files in the repository root for full details. import path, { dirname } from "node:path"; import { fileURLToPath } from "node:url"; +import { type JsonObject } from "shared-types"; -import { type JsonObject, loadJsonFile } from "./utils.js"; +import { loadJsonFile } from "./utils.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); diff --git a/apps/desktop/src/config.ts b/apps/desktop/src/config.ts index 5f25066b63..09d3446300 100644 --- a/apps/desktop/src/config.ts +++ b/apps/desktop/src/config.ts @@ -7,27 +7,12 @@ Please see LICENSE files in the repository root for full details. import { app, dialog } from "electron"; import path from "node:path"; +import { type ResolveDefaults, type DesktopConfigJson, type JsonDocument } from "shared-types"; import { getAsarPath } from "./asar.js"; -import { type Json, loadJsonFile } from "./utils.js"; +import { loadJsonFile } from "./utils.js"; -export interface ConfigOptions { - brand: string; - help_url: string; - web_base_url: string; - modules?: string[]; - sentry?: { - dsn?: string; - environment?: string; - }; - update_base_url?: string; - - // homeserver props - default_is_url?: string; - default_hs_url?: string; - default_server_name?: string; - default_server_config?: object; -} +export type ConfigOptions = ResolveDefaults; const ConfigFilename = "config.json"; @@ -35,7 +20,7 @@ let config: ConfigOptions; const homeserverProps = ["default_is_url", "default_hs_url", "default_server_name", "default_server_config"] as const; -function loadLocalConfigFile(location: string | undefined): Json { +function loadLocalConfigFile(location: string | undefined): JsonDocument { if (location) { console.log("Loading local config: " + location); return loadJsonFile(location); @@ -50,9 +35,9 @@ const DEFAULTS = { brand: "Element", help_url: "https://element.io/help", web_base_url: "https://app.element.io/", -} satisfies ConfigOptions; +} satisfies DesktopConfigJson; -function applyDefaults(conf: ConfigOptions): void { +function applyDefaults(conf: DesktopConfigJson): asserts conf is ConfigOptions { for (const k in DEFAULTS) { const key = k as keyof typeof DEFAULTS; conf[key] ||= DEFAULTS[key]; @@ -71,7 +56,9 @@ export function loadConfig(localConfigPath: string | undefined): Promise { }); } -type JsonValue = null | string | number; -type JsonArray = Array; -export interface JsonObject { - [key: string]: JsonObject | JsonArray | JsonValue; -} -export type Json = JsonArray | JsonObject; - /** * Synchronously load a JSON file from the local filesystem. * Unlike `require`, will never execute any javascript in a loaded file. * @param paths - An array of path segments which will be joined using the system's path delimiter. */ -export function loadJsonFile(...paths: string[]): T { +export function loadJsonFile(...paths: string[]): T { const joinedPaths = path.join(...paths); if (!fs.existsSync(joinedPaths)) { diff --git a/apps/web/package.json b/apps/web/package.json index 1b61489422..9b8b51e64a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -217,6 +217,7 @@ "postcss-simple-vars": "7.0.1", "process": "^0.11.10", "semver": "^7.5.2", + "shared-types": "workspace:*", "source-map-loader": "^5.0.0", "stylelint": "^17.0.0", "stylelint-config-standard": "^40.0.0", diff --git a/apps/web/playwright/element-web-test.ts b/apps/web/playwright/element-web-test.ts index b77a027241..c1b66e551c 100644 --- a/apps/web/playwright/element-web-test.ts +++ b/apps/web/playwright/element-web-test.ts @@ -20,7 +20,6 @@ import { type ToMatchScreenshotOptions, } from "@element-hq/element-web-playwright-common"; -import type { IConfigOptions } from "../src/IConfigOptions"; import { type Credentials } from "./plugins/homeserver"; import { ElementAppPage } from "./pages/ElementAppPage"; import { Crypto } from "./pages/crypto"; @@ -32,11 +31,6 @@ import { type WorkerOptions, type Services, test as base } from "./services"; // See https://playwright.dev/docs/service-workers-experimental#how-to-enable process.env["PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS"] = "1"; -declare module "@element-hq/element-web-playwright-common" { - // Improve the type for the config fixture based on the real type - export interface Config extends Omit {} -} - export interface CredentialsWithDisplayName extends Credentials { displayName: string; } diff --git a/apps/web/src/@types/common.ts b/apps/web/src/@types/common.ts index bfb4f64c16..024e9cef86 100644 --- a/apps/web/src/@types/common.ts +++ b/apps/web/src/@types/common.ts @@ -10,55 +10,8 @@ import { type JSX, type JSXElementConstructor } from "react"; export type { NonEmptyArray, XOR, Writeable } from "matrix-js-sdk/src/matrix"; +export type * from "shared-types/lib/utils"; + export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor; export type { Leaves } from "matrix-web-i18n"; - -export type KeysStartingWith = { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - [P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X -}[keyof Input]; - -export type Defaultize = P extends any - ? string extends keyof P - ? P - : Pick> & - Partial>> & - Partial>> - : never; - -/* eslint-disable @typescript-eslint/no-unsafe-function-type */ -export type DeepReadonly = T extends (infer R)[] - ? DeepReadonlyArray - : T extends Function - ? T - : T extends object - ? DeepReadonlyObject - : T; -/* eslint-enable @typescript-eslint/no-unsafe-function-type */ - -interface DeepReadonlyArray extends ReadonlyArray> {} - -type DeepReadonlyObject = { - readonly [P in keyof T]: DeepReadonly; -}; - -export type AtLeastOne }> = Partial & U[keyof U]; - -/** - * Returns a union type of the keys of the input Object type whose values are assignable to the given Item type. - * Based on https://stackoverflow.com/a/57862073 - */ -export type Assignable = { - [Key in keyof Object]: Object[Key] extends Item ? Key : never; -}[keyof Object]; - -/** - * Like `Partial` but for applied to all nested objects. - * Based on https://dev.to/perennialautodidact/adventures-in-typescript-deeppartial-2f2a - */ -export type DeepPartial = T extends object - ? { - [P in keyof T]?: DeepPartial; - } - : T; diff --git a/apps/web/src/IConfigOptions.ts b/apps/web/src/IConfigOptions.ts index a3a6a32c91..16f28075ff 100644 --- a/apps/web/src/IConfigOptions.ts +++ b/apps/web/src/IConfigOptions.ts @@ -7,15 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { type IClientWellKnown } from "matrix-js-sdk/src/matrix"; +import { type ResolveDefaults, type WebConfigJson } from "shared-types"; import { type ValidatedServerConfig } from "./utils/ValidatedServerConfig"; - -// Convention decision: All config options are lower_snake_case -// We use an isolated file for the interface so we can mess around with the eslint options. - -/* eslint-disable camelcase */ -/* eslint @typescript-eslint/naming-convention: ["error", { "selector": "property", "format": ["snake_case"] } ] */ +import { type DEFAULTS } from "./SdkConfig.ts"; /** * Bug reports are enabled but must only be locally @@ -23,202 +18,14 @@ import { type ValidatedServerConfig } from "./utils/ValidatedServerConfig"; */ export const BugReportEndpointURLLocal = "local"; -// see element-web config.md for non-developer docs -export interface IConfigOptions { - // dev note: while true that this is arbitrary JSON, it's valuable to enforce that all - // config options are documented for "find all usages" sort of searching. - // [key: string]: any; - - // Properties of this interface are roughly grouped by their subject matter, such as - // "instance customisation", "login stuff", "branding", etc. Use blank lines to denote - // a logical separation of properties, but keep similar ones near each other. - - // Exactly one of the following must be supplied - default_server_config?: IClientWellKnown; // copy/paste of client well-known - default_server_name?: string; // domain to do well-known lookup on - default_hs_url?: string; // http url - - default_is_url?: string; // used in combination with default_hs_url, but for the identity server - - // This is intended to be overridden by app startup and not specified by the user - // This is also why it's allowed to have an interface that isn't snake_case +export interface ConfigOptions extends WebConfigJson { + /** + * This is not a real config field, we're just abusing the config structure to pass around a validated server config + */ validated_server_config?: ValidatedServerConfig; - - fallback_hs_url?: string; - - disable_custom_urls?: boolean; - disable_guests?: boolean; - disable_login_language_selector?: boolean; - disable_3pid_login?: boolean; - - brand: string; - branding: { - welcome_background_url: string | string[]; // chosen at random if array - logo_link_url: string; - auth_header_logo_url?: string; - auth_footer_links?: { text: string; url: string }[]; - }; - - force_verification?: boolean; // if true, users must verify new logins - - map_style_url?: string; // for location-shared maps - - embedded_pages?: { - welcome_url?: string; - home_url?: string; - login_for_welcome?: boolean; - }; - - permalink_prefix?: string; - - update_base_url?: string; - desktop_builds: { - available: boolean; - logo: string; // url - url: string; // download url - url_macos?: string; - url_win64?: string; - url_win64arm?: string; - url_linux?: string; - }; - mobile_builds: { - ios: string | null; // download url - android: string | null; // download url - fdroid: string | null; // download url - }; - - mobile_guide_toast?: boolean; - mobile_guide_app_variant?: "element" | "element-classic" | "element-pro"; - - default_theme?: "light" | "dark" | string; // custom themes are strings - default_country_code?: string; // ISO 3166 alpha2 country code - default_federate?: boolean; - default_device_display_name?: string; // for device naming on login+registration - - setting_defaults?: Record; // - - integrations_ui_url?: string; - integrations_rest_url?: string; - integrations_widgets_urls?: string[]; - default_widget_container_height?: number; // height in pixels - - show_labs_settings: boolean; - features?: Record; // - - /** - * Bug report endpoint URL. "local" means the logs should not be uploaded. - */ - bug_report_endpoint_url?: typeof BugReportEndpointURLLocal | string; // omission disables bug reporting - sentry?: { - dsn: string; - environment?: string; // "production", etc - }; - - widget_build_url?: string; // url called to replace jitsi/call widget creation - widget_build_url_ignore_dm?: boolean; - audio_stream_url?: string; - jitsi?: { - preferred_domain: string; - }; - jitsi_widget?: { - skip_built_in_welcome_screen?: boolean; - }; - voip?: { - obey_asserted_identity?: boolean; // MSC3086 - }; - element_call: { - guest_spa_url?: string; - use_exclusively?: boolean; - brand?: string; - }; - - logout_redirect_url?: string; - - sso_redirect_options?: ISsoRedirectOptions; - - custom_translations_url?: string; - - report_event?: { - admin_message_md: string; // message for how to contact the server owner when reporting an event - }; - - room_directory?: { - servers: string[]; - }; - - posthog?: { - project_api_key: string; - api_host: string; // hostname - }; - analytics_owner?: string; // defaults to `brand` - privacy_policy_url?: string; // location for cookie policy - - enable_presence_by_hs_url?: Record; // - - terms_and_conditions_links?: { url: string; text: string }[]; - help_url: string; - help_encryption_url: string; - help_key_storage_url: string; - - latex_maths_delims?: { - inline?: { - left?: string; - right?: string; - pattern?: { - tex?: string; - latex?: string; - }; - }; - display?: { - left?: string; - right?: string; - pattern?: { - tex?: string; - latex?: string; - }; - }; - }; - - sync_timeline_limit?: number; - dangerously_allow_unsafe_and_insecure_passwords?: boolean; // developer option - - user_notice?: { - title: string; - description: string; - show_once?: boolean; - }; - - feedback: { - existing_issues_url: string; - new_issue_url: string; - }; - - /** - * Configuration for OIDC issuers where a static client_id has been issued for the app. - * Otherwise dynamic client registration is attempted. - * The issuer URL must have a trailing `/`. - * OPTIONAL - */ - oidc_static_clients?: { - [issuer: string]: { client_id: string }; - }; - - /** - * Configuration for OIDC dynamic registration where a static OIDC client is not configured. - */ - oidc_metadata?: { - client_uri?: string; - logo_uri?: string; - tos_uri?: string; - policy_uri?: string; - contacts?: string[]; - }; - - modules?: string[]; } -export interface ISsoRedirectOptions { - immediate?: boolean; - on_welcome_page?: boolean; - on_login_page?: boolean; -} +/** + * Type representing the effective config.json structure after DEFAULTS has been merged in + */ +export type IConfigOptions = ResolveDefaults; diff --git a/apps/web/src/PosthogAnalytics.ts b/apps/web/src/PosthogAnalytics.ts index 814d840f19..54e14ce028 100644 --- a/apps/web/src/PosthogAnalytics.ts +++ b/apps/web/src/PosthogAnalytics.ts @@ -139,10 +139,10 @@ export class PosthogAnalytics { } public constructor(private readonly posthog: PostHog) { - const posthogConfig = SdkConfig.getObject("posthog"); - if (posthogConfig) { - this.posthog.init(posthogConfig.get("project_api_key"), { - api_host: posthogConfig.get("api_host"), + const posthogConfig = SdkConfig.get("posthog"); + if (posthogConfig?.project_api_key && posthogConfig?.api_host) { + this.posthog.init(posthogConfig.project_api_key, { + api_host: posthogConfig.api_host, autocapture: false, mask_all_text: true, mask_all_element_attributes: true, diff --git a/apps/web/src/SdkConfig.ts b/apps/web/src/SdkConfig.ts index 84b87e040c..76e4d47b39 100644 --- a/apps/web/src/SdkConfig.ts +++ b/apps/web/src/SdkConfig.ts @@ -8,15 +8,15 @@ Please see LICENSE files in the repository root for full details. */ import { mergeWith } from "lodash"; +import { type DeepReadonly } from "shared-types"; import { SnakedObject } from "./utils/SnakedObject"; -import { type IConfigOptions } from "./IConfigOptions"; +import { type IConfigOptions, type ConfigOptions } from "./IConfigOptions"; import { isObject, objectClone } from "./utils/objects"; -import { type DeepPartial, type DeepReadonly, type Defaultize } from "./@types/common"; import ElementDesktopLogoSvg from "../res/img/element-desktop-logo.svg"; // see element-web config.md for docs, or the IConfigOptions interface for dev docs -export const DEFAULTS: DeepReadonly = { +export const DEFAULTS = { brand: "Element", branding: { logo_link_url: "https://element.io", @@ -69,13 +69,13 @@ export const DEFAULTS: DeepReadonly = { android: "https://play.google.com/store/apps/details?id=im.vector.app", fdroid: "https://f-droid.org/repository/browse/?fdid=im.vector.app", }, -}; +} satisfies ConfigOptions; -export type ConfigOptions = Defaultize; +export type { ConfigOptions }; function mergeConfig( config: DeepReadonly, - changes: DeepReadonly>, + changes: DeepReadonly, ): DeepReadonly { // return { ...config, ...changes }; return mergeWith(objectClone(config), changes, (objValue, srcValue) => { @@ -141,7 +141,7 @@ export default class SdkConfig { SdkConfig.setInstance(mergeConfig(DEFAULTS, {})); // safe to cast - defaults will be applied } - public static add(cfg: DeepPartial): void { + public static add(cfg: DeepReadonly): void { SdkConfig.put(mergeConfig(SdkConfig.get(), cfg)); } } diff --git a/apps/web/src/models/Call.ts b/apps/web/src/models/Call.ts index 6cf95a0681..617bacfb2a 100644 --- a/apps/web/src/models/Call.ts +++ b/apps/web/src/models/Call.ts @@ -707,7 +707,11 @@ export class ElementCall extends Call { */ private static appendAnalyticsParams(params: URLSearchParams, client: MatrixClient): void { const posthogConfig = SdkConfig.get("posthog"); - if (!posthogConfig || PosthogAnalytics.instance.getAnonymity() === Anonymity.Disabled) { + if ( + !posthogConfig?.project_api_key || + !posthogConfig?.api_host || + PosthogAnalytics.instance.getAnonymity() === Anonymity.Disabled + ) { return; } @@ -725,7 +729,7 @@ export class ElementCall extends Call { // We gate passing sentry behind analytics consent as EC shares data automatically without user-consent, // unlike EW where data is shared upon an intentional user action (rageshake). const sentryConfig = SdkConfig.get("sentry"); - if (sentryConfig) { + if (sentryConfig?.dsn) { params.append("sentryDsn", sentryConfig.dsn); params.append("sentryEnvironment", sentryConfig.environment ?? ""); } diff --git a/apps/web/src/settings/Settings.tsx b/apps/web/src/settings/Settings.tsx index bd29b7484b..f379bead93 100644 --- a/apps/web/src/settings/Settings.tsx +++ b/apps/web/src/settings/Settings.tsx @@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details. import React, { type ReactNode } from "react"; import { STABLE_MSC4133_EXTENDED_PROFILES, UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix"; +import { type JsonDocument, type JsonValue } from "shared-types"; // Import these directly from shared-components to avoid circular deps import { _t, _td } from "@element-hq/web-shared-components"; @@ -44,7 +45,6 @@ import FallbackIceServerController from "./controllers/FallbackIceServerControll import { type IRightPanelForRoomStored } from "../stores/right-panel/RightPanelStoreIPanelState.ts"; import { type ILayoutSettings } from "../stores/widgets/WidgetLayoutStore.ts"; import { type ReleaseAnnouncementData } from "../stores/ReleaseAnnouncementStore.ts"; -import { type Json, type JsonValue } from "../@types/json.ts"; import { type RecentEmojiData } from "../emojipicker/recent.ts"; import { type Assignable } from "../@types/common.ts"; import { SortingAlgorithm } from "../stores/room-list-v3/skip-list/sorters/index.ts"; @@ -121,7 +121,7 @@ export const labGroupNames: Record = { [LabGroup.Ui]: _td("labs|group_ui"), }; -export type SettingValueType = Json | JsonValue | Record | Record[]; +export type SettingValueType = JsonDocument | JsonValue | Record | Record[]; export interface IBaseSetting { isFeature?: false | undefined; diff --git a/packages/module-api/api-extractor.json b/packages/module-api/api-extractor.json index e945efc023..dd2aafef65 100644 --- a/packages/module-api/api-extractor.json +++ b/packages/module-api/api-extractor.json @@ -67,7 +67,7 @@ * * "bundledPackages": [ "@my-company/*" ], */ - "bundledPackages": [], + "bundledPackages": ["shared-types"], /** * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files diff --git a/packages/module-api/element-web-module-api.api.md b/packages/module-api/element-web-module-api.api.md index c23218f60e..2318c1028a 100644 --- a/packages/module-api/element-web-module-api.api.md +++ b/packages/module-api/element-web-module-api.api.md @@ -125,8 +125,10 @@ export type ComposerApiTarget = { view: "thread"; }; +// Warning: (ae-forgotten-export) The symbol "WebConfigJson" needs to be exported by the entry point index.d.ts +// // @public -export interface Config { +export interface Config extends WebConfigJson { // (undocumented) brand: string; } diff --git a/packages/module-api/package.json b/packages/module-api/package.json index 1b6cea2a2f..ceaf85978f 100644 --- a/packages/module-api/package.json +++ b/packages/module-api/package.json @@ -46,6 +46,7 @@ "matrix-widget-api": "^1.17.0", "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", + "shared-types": "workspace:*", "typescript": "catalog:", "unplugin-dts": "catalog:", "vite": "catalog:", diff --git a/packages/module-api/project.json b/packages/module-api/project.json index 2b7e0be792..e7979345be 100644 --- a/packages/module-api/project.json +++ b/packages/module-api/project.json @@ -5,14 +5,10 @@ "targets": { "build": { "cache": true, - "executor": "nx:run-commands", "inputs": ["src"], "outputs": ["{projectRoot}/lib"], - "options": { - "commands": ["vite build", "api-extractor run"], - "parallel": false, - "cwd": "packages/module-api" - } + "command": "vite build", + "options": { "cwd": "packages/module-api" } }, "start": { "command": "vite build --watch", diff --git a/packages/module-api/src/api/config.ts b/packages/module-api/src/api/config.ts index 5f708b4983..84e3e4cdb1 100644 --- a/packages/module-api/src/api/config.ts +++ b/packages/module-api/src/api/config.ts @@ -5,16 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import { type WebConfigJson } from "shared-types"; + /** * The configuration for the application. * Should be extended via declaration merging. * @public */ -export interface Config { +export interface Config extends WebConfigJson { // The branding name of the application brand: string; - // Other config options are available but not specified in the types as that would make it difficult to change for element-web - // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md } /** diff --git a/packages/module-api/vite.config.ts b/packages/module-api/vite.config.ts index 305a482983..5e01e921b3 100644 --- a/packages/module-api/vite.config.ts +++ b/packages/module-api/vite.config.ts @@ -27,7 +27,11 @@ export default defineConfig({ sourcemap: true, }, plugins: [ - dts(), + dts({ + bundleTypes: { + configPath: "./api-extractor.json", + }, + }), externalGlobals({ // Reuse React from the host app react: "window.React", diff --git a/packages/playwright-common/src/index.ts b/packages/playwright-common/src/index.ts index abbee03316..8fbf36e212 100644 --- a/packages/playwright-common/src/index.ts +++ b/packages/playwright-common/src/index.ts @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { type Config as BaseConfig } from "@element-hq/element-web-module-api"; +import { type Config } from "@element-hq/element-web-module-api"; import { test as base } from "./fixtures/index.js"; import { routeConfigJson } from "./utils/config_json.js"; @@ -22,27 +22,11 @@ export { populateLocalStorageWithCredentials } from "./fixtures/user.js"; // See https://playwright.dev/docs/service-workers-experimental#how-to-enable process.env["PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS"] = "1"; -// We extend the Module API Config interface so that all modules -// which use declaration merging will have their config types correctly applied. -export interface Config extends BaseConfig { - default_server_config: { - "m.homeserver"?: { - base_url: string; - server_name?: string; - }; - "m.identity_server"?: { - base_url: string; - server_name?: string; - }; - }; - enable_presence_by_hs_url?: Record; - setting_defaults: Record; - map_style_url?: string; - features: Record; - modules?: string[]; -} +export type { Config }; // This is deliberately quite a minimal config.json, so that we can test that the default settings actually work. +// We use the Module API Config interface so that all modules +// which use declaration merging will have their config types correctly applied. export const CONFIG_JSON: Partial = { default_server_config: {}, diff --git a/packages/shared-types/lib/config.json.d.ts b/packages/shared-types/lib/config.json.d.ts new file mode 100644 index 0000000000..b9992ef5fb --- /dev/null +++ b/packages/shared-types/lib/config.json.d.ts @@ -0,0 +1,217 @@ +/* +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. +*/ + +import { DeepPartial } from "./utils"; +import { ClientWellKnown } from "./matrix"; + +// Convention decision: All config options are lower_snake_case +// see docs/config.md for non-developer docs + +/** + * Type describing the `config.json` format for Element Web + * All fields are optional here, consumers should validate that fields are present before assuming otherwise + */ +export interface WebConfigJson { + // dev note: while true that this is arbitrary JSON, it's valuable to enforce that all + // config options are documented for "find all usages" sort of searching. + + // Properties of this interface are roughly grouped by their subject matter, such as + // "instance customisation", "login stuff", "branding", etc. Use blank lines to denote + // a logical separation of properties, but keep similar ones near each other. + + // Exactly one of the following must be supplied + default_server_config?: DeepPartial>; + default_server_name?: string; // domain to do well-known lookup on + default_hs_url?: string; // http url + + default_is_url?: string; // used in combination with default_hs_url, but for the identity server + + fallback_hs_url?: string; + + disable_custom_urls?: boolean; + disable_guests?: boolean; + disable_login_language_selector?: boolean; + disable_3pid_login?: boolean; + + brand?: string; + branding?: { + welcome_background_url?: string | string[]; // chosen at random if array + logo_link_url?: string; + auth_header_logo_url?: string; + auth_footer_links?: { text: string; url: string }[]; + }; + + force_verification?: boolean; // if true, users must verify new logins + + map_style_url?: string; // for location-shared maps + + embedded_pages?: { + welcome_url?: string; + home_url?: string; + login_for_welcome?: boolean; + }; + + permalink_prefix?: string; + + desktop_builds?: { + available?: boolean; + logo?: string; // url + url?: string; // download url + url_macos?: string; + url_win64?: string; + url_win64arm?: string; + url_linux?: string; + }; + mobile_builds?: { + ios?: string; // download url + android?: string; // download url + fdroid?: string; // download url + }; + + mobile_guide_toast?: boolean; + mobile_guide_app_variant?: "element" | "element-classic" | "element-pro"; + + default_theme?: "light" | "dark" | string; // custom themes are strings + default_country_code?: string; // ISO 3166 alpha2 country code + default_federate?: boolean; + default_device_display_name?: string; // for device naming on login+registration + + setting_defaults?: Record; // + + integrations_ui_url?: string; + integrations_rest_url?: string; + integrations_widgets_urls?: string[]; + default_widget_container_height?: number; // height in pixels + + show_labs_settings?: boolean; + features?: Record; // + + /** + * Bug report endpoint URL. "local" means the logs should not be uploaded. + * Omission disables bug reporting + */ + bug_report_endpoint_url?: string; + sentry?: { + dsn?: string; + environment?: string; // "production", etc + }; + + widget_build_url?: string; // url called to replace jitsi/call widget creation + widget_build_url_ignore_dm?: boolean; + audio_stream_url?: string; + jitsi?: { + preferred_domain?: string; + }; + jitsi_widget?: { + skip_built_in_welcome_screen?: boolean; + }; + voip?: { + obey_asserted_identity?: boolean; // MSC3086 + }; + element_call?: { + guest_spa_url?: string; + use_exclusively?: boolean; + brand?: string; + }; + + logout_redirect_url?: string; + + sso_redirect_options?: { + immediate?: boolean; + on_welcome_page?: boolean; + on_login_page?: boolean; + }; + + custom_translations_url?: string; + + report_event?: { + admin_message_md?: string; // message for how to contact the server owner when reporting an event + }; + + room_directory?: { + servers?: string[]; + }; + + posthog?: { + project_api_key?: string; + api_host?: string; // hostname + }; + analytics_owner?: string; // defaults to `brand` + privacy_policy_url?: string; // location for cookie policy + + enable_presence_by_hs_url?: Record; // + + terms_and_conditions_links?: { url: string; text: string }[]; + help_url?: string; + help_encryption_url?: string; + help_key_storage_url?: string; + + latex_maths_delims?: { + inline?: { + left?: string; + right?: string; + pattern?: { + tex?: string; + latex?: string; + }; + }; + display?: { + left?: string; + right?: string; + pattern?: { + tex?: string; + latex?: string; + }; + }; + }; + + sync_timeline_limit?: number; + dangerously_allow_unsafe_and_insecure_passwords?: boolean; // developer option + + user_notice?: { + title?: string; + description?: string; + show_once?: boolean; + }; + + feedback?: { + existing_issues_url?: string; + new_issue_url?: string; + }; + + /** + * Configuration for OIDC issuers where a static client_id has been issued for the app. + * Otherwise dynamic client registration is attempted. + * The issuer URL must have a trailing `/`. + * OPTIONAL + */ + oidc_static_clients?: { + [issuer: string]: { client_id: string }; + }; + + /** + * Configuration for OIDC dynamic registration where a static OIDC client is not configured. + */ + oidc_metadata?: { + client_uri?: string; + logo_uri?: string; + tos_uri?: string; + policy_uri?: string; + contacts?: string[]; + }; + + modules?: string[]; +} + +/** + * Type describing the `config.json` format for Element Desktop, a superset of Element Web's config. + * All fields are optional here, consumers should validate that fields are present before assuming otherwise + */ +export interface DesktopConfigJson extends WebConfigJson { + web_base_url?: string; + update_base_url?: string; +} diff --git a/packages/shared-types/lib/index.d.ts b/packages/shared-types/lib/index.d.ts new file mode 100644 index 0000000000..d07922b4ab --- /dev/null +++ b/packages/shared-types/lib/index.d.ts @@ -0,0 +1,11 @@ +/* +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. +*/ + +export type * from "./config.json.d.ts"; +export type * from "./utils.d.ts"; +export type * from "./matrix.d.ts"; +export type * from "./json.d.ts"; diff --git a/packages/shared-types/lib/index.js b/packages/shared-types/lib/index.js new file mode 100644 index 0000000000..1be6a73d8c --- /dev/null +++ b/packages/shared-types/lib/index.js @@ -0,0 +1 @@ +// Dummy file to make Node happy to import `shared-types` lib. diff --git a/apps/web/src/@types/json.ts b/packages/shared-types/lib/json.d.ts similarity index 57% rename from apps/web/src/@types/json.ts rename to packages/shared-types/lib/json.d.ts index 33af49a19f..cf7255954a 100644 --- a/apps/web/src/@types/json.ts +++ b/packages/shared-types/lib/json.d.ts @@ -1,13 +1,20 @@ /* -Copyright 2024 New Vector Ltd. +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. */ +/** Type representing a valid JSON value */ export type JsonValue = null | string | number | boolean; + +/** Type representing a valid JSON array */ export type JsonArray = Array; + +/** Type representing a valid JSON object */ export interface JsonObject { [key: string]: JsonObject | JsonArray | JsonValue; } -export type Json = JsonArray | JsonObject; + +/** Type representing a valid JSON document */ +export type JsonDocument = JsonArray | JsonObject; diff --git a/packages/shared-types/lib/matrix.d.ts b/packages/shared-types/lib/matrix.d.ts new file mode 100644 index 0000000000..609f62a0dd --- /dev/null +++ b/packages/shared-types/lib/matrix.d.ts @@ -0,0 +1,43 @@ +/* +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. +*/ + +import { JsonDocument } from "./json"; + +/** + * As specified by https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient + */ +export type ClientWellKnown = { + /** + * Used by clients to discover homeserver information. + */ + "m.homeserver": { + /** + * The base URL for the homeserver for client-server connections. + */ + base_url: string; + /** + * This field is not part of the spec but supported by Element Web's config.json + * @deprecated - we should figure out whether we want to keep this or not. + */ + server_name?: string; + }; + /** + * Used by clients to discover identity server information. + */ + "m.identity_server"?: { + /** + * The base URL for the identity server for client-server connections. + */ + base_url: string; + }; +} & { + /** + * Other properties + * Application-dependent keys using Java package naming convention. + */ + [key: string]: JsonDocument; +}; diff --git a/packages/shared-types/lib/utils.d.ts b/packages/shared-types/lib/utils.d.ts new file mode 100644 index 0000000000..e68cf0bfba --- /dev/null +++ b/packages/shared-types/lib/utils.d.ts @@ -0,0 +1,83 @@ +/* +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. +*/ + +/** + * Returns a union type of the keys of the Input type whose names start with the given string Str. + */ +export type KeysStartingWith = { + [P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X +}[keyof Input]; + +/** + * Makes fields of T and its object children optional if they are defined in D. + * Useful for generating the input type for a given function if it applies an object of defaults. + */ +export type Defaultize = P extends any + ? string extends keyof P + ? P + : Pick> & + Partial>> & + Partial>> + : never; + +/** + * Makes fields of T and its object children non-optional if they are defined in D. + * Useful for generating a type which allows you to know which fields will be defined once you apply default values. + */ +export type ResolveDefaults = { + [K in keyof T as K extends keyof D ? K : never]-?: SafeIndex extends object + ? NonNullable extends any[] + ? NonNullable + : NonNullable extends object + ? ResolveDefaults, SafeIndex> + : NonNullable + : NonNullable; +} & { + [K in keyof T as K extends keyof D ? never : K]: T[K]; +} & {}; + +type SafeIndex = K extends keyof D ? D[K] : never; + +/** + * Applies the `readonly` modifier to all fields of T and its object children. + */ +export type DeepReadonly = T extends (infer R)[] + ? DeepReadonlyArray + : T extends Function + ? T + : T extends object + ? DeepReadonlyObject + : T; + +interface DeepReadonlyArray extends ReadonlyArray> {} + +type DeepReadonlyObject = { + readonly [P in keyof T]: DeepReadonly; +}; + +/** + * Like `Partial` but requires at least one property to be present. + */ +export type AtLeastOne }> = Partial & U[keyof U]; + +/** + * Returns a union type of the keys of the input Object type whose values are assignable to the given Item type. + * Based on https://stackoverflow.com/a/57862073 + */ +export type Assignable = { + [Key in keyof Object]: Object[Key] extends Item ? Key : never; +}[keyof Object]; + +/** + * Like `Partial` but for applied to all nested objects. + * Based on https://dev.to/perennialautodidact/adventures-in-typescript-deeppartial-2f2a + */ +export type DeepPartial = T extends object + ? { + [P in keyof T]?: DeepPartial; + } + : T; diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json new file mode 100644 index 0000000000..6fd62d3c30 --- /dev/null +++ b/packages/shared-types/package.json @@ -0,0 +1,20 @@ +{ + "name": "shared-types", + "type": "module", + "version": "0.0.0", + "private": true, + "description": "Shared types for Element Web & Desktop", + "author": "element-hq", + "license": "SEE LICENSE IN README.md", + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "lint:types": "tsc --noEmit" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/shared-types/tsconfig.json b/packages/shared-types/tsconfig.json new file mode 100644 index 0000000000..b58ee71b24 --- /dev/null +++ b/packages/shared-types/tsconfig.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "./lib", + "target": "esnext", + "lib": ["es2024"], + "strict": true, + "types": [], + "allowImportingTsExtensions": true, + "noEmit": true + }, + "include": ["lib"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f6fbc1bf4..f0fa71a38a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -241,9 +241,6 @@ catalogs: react-dom: specifier: ^19.0.0 version: 19.2.7 - typescript: - specifier: 6.0.3 - version: 6.0.3 unplugin-dts: specifier: 1.0.1 version: 1.0.1 @@ -284,6 +281,7 @@ overrides: protobufjs@7 <7.5.8: 7.6.1 '@protobufjs/utf8@1 <1.1.1': 1.1.1 yauzl: ^3.3.1 + typescript: 6.0.3 packageExtensionsChecksum: sha256-EMEi1vcyzQthk7O/0AcntvnHgJaKCoFBlzp6iX/qNYk= @@ -351,7 +349,7 @@ importers: specifier: 0.56.0 version: 0.56.0 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vitepress: specifier: ^1.6.4 @@ -492,11 +490,14 @@ importers: rimraf: specifier: ^6.0.0 version: 6.1.3 + shared-types: + specifier: workspace:* + version: link:../../packages/shared-types tar: specifier: ^7.5.8 version: 7.5.19 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vitest: specifier: 'catalog:' @@ -1033,6 +1034,9 @@ importers: semver: specifier: ^7.5.2 version: 7.8.5 + shared-types: + specifier: workspace:* + version: link:../../packages/shared-types source-map-loader: specifier: ^5.0.0 version: 5.0.0(webpack@5.108.3) @@ -1055,7 +1059,7 @@ importers: specifier: ^12.0.0 version: 12.0.4 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 util: specifier: ^0.12.5 @@ -1164,7 +1168,7 @@ importers: specifier: 'catalog:' version: 19.2.7 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vite: specifier: 'catalog:' @@ -1213,7 +1217,7 @@ importers: specifier: ^12.0.1 version: 12.0.4 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vite: specifier: 'catalog:' @@ -1235,7 +1239,7 @@ importers: specifier: 25.9.3 version: 25.9.3 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vite: specifier: 'catalog:' @@ -1293,7 +1297,7 @@ importers: specifier: 'catalog:' version: 19.2.7 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 vite: specifier: 'catalog:' @@ -1344,8 +1348,11 @@ importers: semver: specifier: ^7.6.3 version: 7.8.5 + shared-types: + specifier: workspace:* + version: link:../shared-types typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 unplugin-dts: specifier: 'catalog:' @@ -1400,7 +1407,7 @@ importers: specifier: ^4.17.12 version: 4.17.12 typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 packages/shared-components: @@ -1602,7 +1609,7 @@ importers: specifier: ^4.1.2 version: 4.1.3(typedoc@0.28.19(typescript@6.0.3)) typescript: - specifier: 'catalog:' + specifier: 6.0.3 version: 6.0.3 unplugin-dts: specifier: 'catalog:' @@ -1617,6 +1624,12 @@ importers: specifier: 'catalog:' version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.3)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.6)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.1.3(@types/node@25.9.3)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.16))(terser@5.48.0)(yaml@2.8.4)) + packages/shared-types: + devDependencies: + typescript: + specifier: 6.0.3 + version: 6.0.3 + packages: '@action-validator/cli@0.6.0': @@ -3383,7 +3396,7 @@ packages: '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0': resolution: {integrity: sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==} peerDependencies: - typescript: '>= 4.3.x' + typescript: 6.0.3 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: @@ -5573,7 +5586,7 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 storybook: ^10.4.6 - typescript: '>= 4.9.x' + typescript: 6.0.3 peerDependenciesMeta: '@types/react': optional: true @@ -6148,32 +6161,32 @@ packages: peerDependencies: '@typescript-eslint/parser': ^8.61.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/parser@8.62.1': resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/project-service@8.61.0': resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/project-service@8.61.1': resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/project-service@8.62.1': resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/scope-manager@8.61.0': resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} @@ -6191,26 +6204,26 @@ packages: resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/tsconfig-utils@8.61.1': resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/tsconfig-utils@8.62.1': resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/type-utils@8.61.0': resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/types@8.61.0': resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} @@ -6228,33 +6241,33 @@ packages: resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/typescript-estree@8.61.1': resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/typescript-estree@8.62.1': resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/utils@8.61.0': resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/utils@8.61.1': resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + typescript: 6.0.3 '@typescript-eslint/visitor-keys@8.61.0': resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} @@ -7670,7 +7683,7 @@ packages: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} peerDependencies: - typescript: '>=4.9.5' + typescript: 6.0.3 peerDependenciesMeta: typescript: optional: true @@ -7679,7 +7692,7 @@ packages: resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} engines: {node: '>=14'} peerDependencies: - typescript: '>=4.9.5' + typescript: 6.0.3 peerDependenciesMeta: typescript: optional: true @@ -8620,7 +8633,7 @@ packages: '@typescript-eslint/eslint-plugin': ^8.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 jest: '*' - typescript: '>=4.8.4 <7.0.0' + typescript: 6.0.3 peerDependenciesMeta: '@typescript-eslint/eslint-plugin': optional: true @@ -8655,7 +8668,7 @@ packages: eslint-plugin-react-hooks: '*' eslint-plugin-unicorn: <57 prettier: '*' - typescript: '*' + typescript: 6.0.3 eslint-plugin-n@17.24.0: resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} @@ -11966,7 +11979,7 @@ packages: react-docgen-typescript@2.4.0: resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} peerDependencies: - typescript: '>= 4.3.x' + typescript: 6.0.3 react-docgen@8.0.3: resolution: {integrity: sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==} @@ -13186,12 +13199,12 @@ packages: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.8.4' + typescript: 6.0.3 ts-declaration-location@1.0.7: resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} peerDependencies: - typescript: '>=4.0.0' + typescript: 6.0.3 ts-dedent@2.3.0: resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} @@ -13271,7 +13284,7 @@ packages: type-plus@8.0.0-beta.8: resolution: {integrity: sha512-egrpXQq2tV0abCf99+n4SCD/stT76qEwPBI1q7BqiVUe5pHWc+bm4vsOiNR84SmZTv4SoEl9UOZUdkEbS3POdw==} peerDependencies: - typescript: '>= 5.6.0' + typescript: 6.0.3 typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} @@ -13305,12 +13318,7 @@ packages: engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true + typescript: 6.0.3 typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} @@ -13428,7 +13436,7 @@ packages: esbuild: 0.27.4 rolldown: '*' rollup: '>=3' - typescript: '>=4' + typescript: 6.0.3 vite: '>=3' webpack: ^4 || ^5 peerDependenciesMeta: @@ -13730,7 +13738,7 @@ packages: vue@3.5.31: resolution: {integrity: sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==} peerDependencies: - typescript: '*' + typescript: 6.0.3 peerDependenciesMeta: typescript: optional: true @@ -14282,7 +14290,7 @@ snapshots: '@arcmantle/vite-plugin-import-css-sheet@1.0.14(patch_hash=8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747)': dependencies: lightningcss: 1.32.0 - typescript: 5.9.3 + typescript: 6.0.3 '@asamuzakjp/css-color@3.2.0': dependencies: @@ -16545,7 +16553,7 @@ snapshots: resolve: 1.22.12 semver: 7.7.4 source-map: 0.6.1 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - '@types/node' @@ -27603,8 +27611,6 @@ snapshots: typescript: 6.0.3 yaml: 2.8.4 - typescript@5.9.3: {} - typescript@6.0.3: {} ua-parser-js@1.0.40: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d0919bc43a..2c6c4c227f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -135,6 +135,8 @@ overrides: "@protobufjs/utf8@1 <1.1.1": 1.1.1 # Workaround for https://github.com/electron/electron/issues/51619 yauzl: "^3.3.1" + # Convince api-extractor to use an up to date Typescript version + typescript: "catalog:" minimumReleaseAgeExclude: - "matrix-js-sdk"