web/dev/upgrade-to-typescript-7

## What

Upgrade our typechecking to use Typescript 7 (aka `tsgo`).

Steps taken:

- Eliminate using ordinary maps for reference holders; the constructors are incompatible with `WeakMap` and TSGO doesn’t allow for it.
- Strengthen the typecheck for `CurrentBrand`; TSGO needed help understand what constraint applied
- Narrow the types supplied to `JSONifiable` so that `symbol` isn’t a valid key; `JSON.stringify()` elides symbol keys.
- Correct bad typing in `paths.js`; at the type level, `dist` is the *type* of `const foo = "dist"`, so a `typeof` key here passes muster.

## Why

    $ time npm run lint:types # (TSC)
    real: 0m18.45s

    $ time npm run lint:types # (TSGO)
    real: 0m0.8s
This commit is contained in:
Ken Sternberg
2026-03-04 15:37:47 -08:00
parent ce1237e03f
commit 6f352bc5b1
7 changed files with 120 additions and 37 deletions

110
web/package-lock.json generated
View File

@@ -56,6 +56,7 @@
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"@typescript/native-preview": "^7.0.0-dev.20260304.1",
"@vitest/browser": "^4.0.18",
"@vitest/browser-playwright": "^4.0.15",
"@webcomponents/webcomponentsjs": "^2.8.0",
@@ -4945,6 +4946,115 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@typescript/native-preview": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-Xj0ZeHEy+yJ/bIg6psPwl0POvBf1j5u7IZAXsUqgvgWbMIvdM9JOGmhpifcj6j28LcXM6GTvXUoXwlatxJ73Qg==",
"license": "Apache-2.0",
"bin": {
"tsgo": "bin/tsgo.js"
},
"optionalDependencies": {
"@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260304.1",
"@typescript/native-preview-darwin-x64": "7.0.0-dev.20260304.1",
"@typescript/native-preview-linux-arm": "7.0.0-dev.20260304.1",
"@typescript/native-preview-linux-arm64": "7.0.0-dev.20260304.1",
"@typescript/native-preview-linux-x64": "7.0.0-dev.20260304.1",
"@typescript/native-preview-win32-arm64": "7.0.0-dev.20260304.1",
"@typescript/native-preview-win32-x64": "7.0.0-dev.20260304.1"
}
},
"node_modules/@typescript/native-preview-darwin-arm64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-TnTUxYt+dShRSoeOldx7VlKoEG+bvPHnyPEBImlNc7c3WP0AHYyNHrNg6EbLbzkOorARtd06J3Vk+XYzkrRzZg==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@typescript/native-preview-darwin-x64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-1nwXX1zbyYI3sDKdaR8NsBdM7LmE0J6OzVtlWgEJ/8YR7oC2/HY6/SfShF3DHHcEOHOFxRLbkJ9zVTJJspWLCw==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@typescript/native-preview-linux-arm": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-TXZClCJVteK2f9gcI+I7o1Sxgq3qdMtraXOP9GZF8o0sKCLdDWENN8uORfZSeQv2qOJohcKvrrEz6LLSSngvEg==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@typescript/native-preview-linux-arm64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-cw+xqroXtsk/yVTKbelcPWMd6oZdET9kNWmigyc189KWwzOu2eq2EPXPQsrhEigq8O3j0xW0z3q2oqG+smOiXg==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@typescript/native-preview-linux-x64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-EXufnN4PG0HYBHYbHXQXXRXtaQKuKBT3e6nxPhKnwpBBgy2MgWDIxzroTLvI9+SllhbJQzHNZOWiB+SU+KdCNw==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@typescript/native-preview-win32-arm64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-Be9yyDDbT/PEdNlhG+NXT47fwuiIeN0+/9BkeRKkiLgzY8DqQIC9w5FRWmwAJ+9PVa2sKr5cjD1SpJDHGrPIrA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@typescript/native-preview-win32-x64": {
"version": "7.0.0-dev.20260304.1",
"resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260304.1.tgz",
"integrity": "sha512-lg/w+rZ9NIUoqSsk2TbtDsqyD9nW0/rhTMYd14RFP7vuNijLrTbl7GPiMhFtMxaqCSOFapwbql7/3lU4BKHB6g==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",

View File

@@ -131,6 +131,7 @@
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"@typescript/native-preview": "^7.0.0-dev.20260304.1",
"@vitest/browser": "^4.0.18",
"@vitest/browser-playwright": "^4.0.15",
"@webcomponents/webcomponentsjs": "^2.8.0",
@@ -258,7 +259,7 @@
"command": "lit-analyzer src"
},
"lint:types": {
"command": "tsc -p .",
"command": "tsgo -p .",
"env": {
"NODE_OPTIONS": "--max_old_space_size=8192"
},
@@ -290,7 +291,7 @@
}
},
"tsc": {
"command": "tsc -p .",
"command": "tsgo -p .",
"env": {
"NODE_OPTIONS": "--max_old_space_size=8192"
},

View File

@@ -50,7 +50,7 @@ export const NodeEnvironment = process.env.NODE_ENV || "development";
* @param {EnvRecord} input
* @param {Prefix} [prefix='import.meta.env.']
*
* @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}}
* @returns {{[K in keyof EnvRecord & string as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}}
*/
export function serializeEnvironmentVars(
input,

View File

@@ -31,7 +31,7 @@ export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(relative
*
* @runtime node
*/
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ (
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${typeof DistDirectoryName}`} */ (
resolve(PackageRoot, DistDirectoryName)
);

View File

@@ -1,34 +1,5 @@
import { type ContextControllerRegistryMap } from "#elements/types";
/**
* Check if the environment supports Symbol-keyed WeakMaps.
*
* @see {@link https://caniuse.com/mdn-javascript_builtins_weakmap_symbol_as_keys | Can I use}
*
* @todo Re-evaluate browser coverage after 2027-01-01
*/
function supportsSymbolKeyedWeakMap(): boolean {
const testKey = Symbol("test");
const wm = new WeakMap();
try {
wm.set(testKey, "value");
return wm.has(testKey);
} catch (_error) {
return false;
}
}
/**
* A constructor for either WeakMap or Map, depending on environment support.
*
* @remarks
*
* A preference for `WeakMap` is optional at the moment.
* However, if we ever support short-lived context controllers, such as
*/
const ContextControllerConstructor = supportsSymbolKeyedWeakMap() ? WeakMap : Map;
/**
* A registry of context controllers added to the Interface.
*
@@ -38,5 +9,4 @@ const ContextControllerConstructor = supportsSymbolKeyedWeakMap() ? WeakMap : Ma
*
* This is exported separately to avoid circular dependencies.
*/
export const ContextControllerRegistry =
new ContextControllerConstructor() as ContextControllerRegistryMap;
export const ContextControllerRegistry = new WeakMap() as ContextControllerRegistryMap;

View File

@@ -5,11 +5,13 @@ import { TabID } from "#flow/tabs/TabID";
import { ConsoleLogger } from "#logger/browser";
import { CurrentBrandFlags } from "@goauthentik/api";
const lockKey = "authentik-tab-locked";
const logger = ConsoleLogger.prefix("mtab/orchestrate");
export function multiTabOrchestrateLeave() {
if (!globalAK().brand.flags.flowsContinuousLogin) {
if (!(globalAK().brand.flags satisfies CurrentBrandFlags).flowsContinuousLogin) {
return;
}
Broadcast.shared.akExitTab();

View File

@@ -13,7 +13,7 @@
"target": "esnext",
"module": "preserve",
"moduleResolution": "bundler",
"baseUrl": ".",
"paths": { "*": ["./*"] },
"lib": ["DOM", "DOM.Iterable", "ESNext"],
// TODO: We should enable this when when we're ready to enforce it.
"noUncheckedIndexedAccess": false,