Start consolidating shared types in the monorepo (#34062)

* Start consolidating shared types in the monorepo

* Iterate

* Simplify api-extractor

* Iterate

* Fix lockfile
This commit is contained in:
Michael Telatynski
2026-07-06 13:49:32 +00:00
committed by GitHub
parent b6cbc3a9d8
commit d4f72dfa69
29 changed files with 519 additions and 389 deletions
+1 -1
View File
@@ -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
@@ -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;
}
+1
View File
@@ -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:",
+2 -6
View File
@@ -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",
+3 -3
View File
@@ -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
}
/**
+5 -1
View File
@@ -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",
+4 -20
View File
@@ -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<string, boolean>;
setting_defaults: Record<string, unknown>;
map_style_url?: string;
features: Record<string, boolean>;
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<Config> = {
default_server_config: {},
+217
View File
@@ -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<Pick<ClientWellKnown, "m.homeserver" | "m.identity_server">>;
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<string, any>; // <SettingName, Value>
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<string, boolean>; // <FeatureName, EnabledBool>
/**
* 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<string, boolean>; // <HomeserverName, Enabled>
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;
}
+11
View File
@@ -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";
+1
View File
@@ -0,0 +1 @@
// Dummy file to make Node happy to import `shared-types` lib.
+20
View File
@@ -0,0 +1,20 @@
/*
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<JsonValue | JsonObject | JsonArray>;
/** Type representing a valid JSON object */
export interface JsonObject {
[key: string]: JsonObject | JsonArray | JsonValue;
}
/** Type representing a valid JSON document */
export type JsonDocument = JsonArray | JsonObject;
+43
View File
@@ -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;
};
+83
View File
@@ -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<Input extends object, Str extends string> = {
[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, D> = P extends any
? string extends keyof P
? P
: Pick<P, Exclude<keyof P, keyof D>> &
Partial<Pick<P, Extract<keyof P, keyof D>>> &
Partial<Pick<D, Exclude<keyof D, keyof P>>>
: 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<T, D> = {
[K in keyof T as K extends keyof D ? K : never]-?: SafeIndex<D, K> extends object
? NonNullable<T[K]> extends any[]
? NonNullable<T[K]>
: NonNullable<T[K]> extends object
? ResolveDefaults<NonNullable<T[K]>, SafeIndex<D, K>>
: NonNullable<T[K]>
: NonNullable<T[K]>;
} & {
[K in keyof T as K extends keyof D ? never : K]: T[K];
} & {};
type SafeIndex<D, K> = K extends keyof D ? D[K] : never;
/**
* Applies the `readonly` modifier to all fields of T and its object children.
*/
export type DeepReadonly<T> = T extends (infer R)[]
? DeepReadonlyArray<R>
: T extends Function
? T
: T extends object
? DeepReadonlyObject<T>
: T;
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
/**
* Like `Partial` but requires at least one property to be present.
*/
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & 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<Object, Item> = {
[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> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;
+20
View File
@@ -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:"
}
}
+13
View File
@@ -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"]
}