diff --git a/.repos/effect b/.repos/effect index 7f524e25c..9a91b1926 160000 --- a/.repos/effect +++ b/.repos/effect @@ -1 +1 @@ -Subproject commit 7f524e25cfb49a71ac39b0f44278d08d40dda5de +Subproject commit 9a91b1926a5fbfaffd302a7a41a94d4d53a1154e diff --git a/biome.json b/biome.json index 24886b8b5..af4692848 100644 --- a/biome.json +++ b/biome.json @@ -15,7 +15,9 @@ "!**/region-iso-3166-codes.ts", "!**/mockServiceWorker.js", "!**/package.json", - "!**/.repos" + "!**/.repos", + "!**/CHANGELOG.md", + "!!**/@dedot/api/chaintypes/substrate/tx.d.ts" ] }, "formatter": { @@ -37,7 +39,7 @@ "rules": { "preset": "recommended", "style": { - "noNonNullAssertion": "off", + "noNonNullAssertion": "error", "noParameterAssign": "error", "useAsConstAssertion": "error", "useDefaultParameterLast": "error", @@ -59,14 +61,19 @@ }, "correctness": { "useJsxKeyInIterable": "off", - "useUniqueElementIds": "off" + "useUniqueElementIds": "off", + "noComponentHookFactories": "off" }, "complexity": { "noForEach": "off", "useIndexOf": "off" }, "nursery": { - "useReactCompiler": "error" + "useReactCompiler": "error", + "useExhaustiveSwitchCases": "error", + "noFloatingPromises": "error", + "noMisusedPromises": "error", + "noUselessTypeConversion": "error" } }, "domains": { @@ -76,6 +83,26 @@ } }, "overrides": [ + { + "includes": ["packages/widget/src/**"], + "linter": { + "rules": { + "nursery": { + "noUnsafeTypeAssertion": "error" + } + } + } + }, + { + "includes": ["packages/widget/tests/**"], + "linter": { + "rules": { + "style": { + "noNonNullAssertion": "off" + } + } + } + }, { "includes": [ "packages/widget/src/**", diff --git a/package.json b/package.json index 5c4a8fc1a..72b2888f9 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,6 @@ ], "author": "Petar Todorovic (https://github.com)", "license": "MIT", - "devEngines": { - "packageManager": { - "name": "pnpm", - "version": "12" - } - }, "scripts": { "dev": "mise run dev --", "build": "mise run build --", diff --git a/packages/examples/with-angular/package.json b/packages/examples/with-angular/package.json index 809c00e03..a1c06f248 100644 --- a/packages/examples/with-angular/package.json +++ b/packages/examples/with-angular/package.json @@ -21,6 +21,7 @@ "tslib": "catalog:" }, "devDependencies": { + "@types/node": "catalog:", "@angular/build": "catalog:", "@angular/cli": "catalog:", "@angular/compiler-cli": "catalog:", diff --git a/packages/examples/with-vite-bundled/package.json b/packages/examples/with-vite-bundled/package.json index 93bd85fbe..1dc54493c 100644 --- a/packages/examples/with-vite-bundled/package.json +++ b/packages/examples/with-vite-bundled/package.json @@ -14,6 +14,7 @@ "@stakekit/widget": "workspace:*" }, "devDependencies": { + "@types/node": "catalog:", "@typescript/native": "catalog:", "vite": "catalog:" } diff --git a/packages/examples/with-vite-bundled/src/main.ts b/packages/examples/with-vite-bundled/src/main.ts index 9d5d5c3ff..b32c1382f 100644 --- a/packages/examples/with-vite-bundled/src/main.ts +++ b/packages/examples/with-vite-bundled/src/main.ts @@ -2,8 +2,11 @@ import "./style.css"; import "@stakekit/widget/style.css"; import { darkTheme, renderSKWidget } from "@stakekit/widget/bundle"; +const container = document.querySelector("#app"); +if (!container) throw new Error("Missing #app container"); + renderSKWidget({ - container: document.querySelector("#app")!, + container, apiKey: import.meta.env.VITE_API_KEY, theme: darkTheme, }); diff --git a/packages/examples/with-vite/package.json b/packages/examples/with-vite/package.json index c1b24fc58..ebb414fd9 100644 --- a/packages/examples/with-vite/package.json +++ b/packages/examples/with-vite/package.json @@ -16,6 +16,7 @@ "react-dom": "catalog:" }, "devDependencies": { + "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@typescript/native": "catalog:", diff --git a/packages/examples/with-vite/src/main.tsx b/packages/examples/with-vite/src/main.tsx index 95e2bdc2c..a6e452fa0 100644 --- a/packages/examples/with-vite/src/main.tsx +++ b/packages/examples/with-vite/src/main.tsx @@ -2,7 +2,10 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; -ReactDOM.createRoot(document.getElementById("root")!).render( +const root = document.getElementById("root"); +if (!root) throw new Error("Missing #root element"); + +ReactDOM.createRoot(root).render( diff --git a/packages/widget/package.json b/packages/widget/package.json index e9b8c021c..76e4ca2fd 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -134,6 +134,7 @@ "@tronweb3/tronwallet-adapter-bitkeep": "catalog:", "@tronweb3/tronwallet-adapter-ledger": "catalog:", "@tronweb3/tronwallet-adapter-tronlink": "catalog:", + "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@typescript/native": "catalog:", diff --git a/packages/widget/playground/panel.tsx b/packages/widget/playground/panel.tsx index 98c902551..4fe825dd2 100644 --- a/packages/widget/playground/panel.tsx +++ b/packages/widget/playground/panel.tsx @@ -40,7 +40,7 @@ const Choice = ({ value, }: { readonly disabled?: boolean; - readonly hint?: string; + readonly hint?: string | undefined; readonly label: string; readonly onChange: (value: Value) => void; readonly options: ReadonlyArray; @@ -75,7 +75,7 @@ const Toggle = ({ }: { readonly checked: boolean; readonly disabled?: boolean; - readonly hint?: string; + readonly hint?: string | undefined; readonly label: string; readonly onChange: (checked: boolean) => void; }) => ( diff --git a/packages/widget/playground/playground-config.ts b/packages/widget/playground/playground-config.ts index 5c5709af2..22f2f1aab 100644 --- a/packages/widget/playground/playground-config.ts +++ b/packages/widget/playground/playground-config.ts @@ -45,7 +45,7 @@ export type PlaygroundConfig = typeof PlaygroundConfigSchema.Type; export const defaultPlaygroundConfig: PlaygroundConfig = { wallet: "builtin", scriptedAddress: scriptedWalletAccount, - trackingLog: true, + trackingLog: false, variant: "default", theme: "dark", language: "auto", diff --git a/packages/widget/scripts/architecture/check-architecture.ts b/packages/widget/scripts/architecture/check-architecture.ts index cd70482ee..efab11b94 100644 --- a/packages/widget/scripts/architecture/check-architecture.ts +++ b/packages/widget/scripts/architecture/check-architecture.ts @@ -25,7 +25,6 @@ const { options: configuredOptions = {}, ...ruleSet } = const cruiseOutput = await cruise(["src"], { ...configuredOptions, baseDir: widgetRoot, - outputType: undefined, ruleSet, validate: true, }); diff --git a/packages/widget/scripts/architecture/dependency-cruiser.config.mts b/packages/widget/scripts/architecture/dependency-cruiser.config.mts index 294e2f40f..9a3165327 100644 --- a/packages/widget/scripts/architecture/dependency-cruiser.config.mts +++ b/packages/widget/scripts/architecture/dependency-cruiser.config.mts @@ -271,10 +271,11 @@ const compileCollection = ( sameOwnerInterfacePath: `^${declaration.root}/$1/${interfacePattern}\\.ts$`, interfacePaths, interfaceImporters, - parentInterfacePath: - declaration.parent === undefined - ? undefined - : `^${declaration.parent}/${interfacePattern}\\.ts$`, + ...(declaration.parent === undefined + ? {} + : { + parentInterfacePath: `^${declaration.parent}/${interfacePattern}\\.ts$`, + }), }; }; diff --git a/packages/widget/scripts/generated-api/generated-schema-safety.ts b/packages/widget/scripts/generated-api/generated-schema-safety.ts index d33813bd8..50e99830b 100644 --- a/packages/widget/scripts/generated-api/generated-schema-safety.ts +++ b/packages/widget/scripts/generated-api/generated-schema-safety.ts @@ -97,6 +97,8 @@ const schemaMemberName = (expression: ts.Expression): string | undefined => { ) { return current.expression.name.text; } + + return undefined; }; const isEmptyStruct = (expression: ts.Expression): boolean => { diff --git a/packages/widget/scripts/smoke/check-production-host-consumption.ts b/packages/widget/scripts/smoke/check-production-host-consumption.ts index 2c4287948..8453868c3 100644 --- a/packages/widget/scripts/smoke/check-production-host-consumption.ts +++ b/packages/widget/scripts/smoke/check-production-host-consumption.ts @@ -311,12 +311,16 @@ const assertEncryptWorkerStarts = async ( name: string; }) => Worker) : undefined; - if (scriptUrl === null && InlineWorker === undefined) { + // Code here runs in the page: a named helper would be compiled to + // `__name(...)`, which the page does not define. + const worker = + InlineWorker === undefined + ? scriptUrl !== null && + new Worker(scriptUrl, { name: "zama-fhe-encrypt", type: "module" }) + : new InlineWorker({ name: "zama-fhe-encrypt" }); + if (worker === false) { return `${chunk} does not export an inline worker constructor`; } - const worker = InlineWorker - ? new InlineWorker({ name: "zama-fhe-encrypt" }) - : new Worker(scriptUrl!, { name: "zama-fhe-encrypt", type: "module" }); const { promise, resolve } = Promise.withResolvers(); worker.addEventListener("message", (event) => { if (event.data === "zama-encrypt-worker-ready") resolve("ready"); diff --git a/packages/widget/src/app/composition/providers/theme-wrapper.tsx b/packages/widget/src/app/composition/providers/theme-wrapper.tsx index 6757e6d7f..c513154fc 100644 --- a/packages/widget/src/app/composition/providers/theme-wrapper.tsx +++ b/packages/widget/src/app/composition/providers/theme-wrapper.tsx @@ -1,4 +1,5 @@ import { assignInlineVars } from "@vanilla-extract/dynamic"; +import { Predicate } from "effect"; import type { PropsWithChildren } from "react"; import { useWidgetConfig } from "../../../features/widget-configuration/index"; import type { WidgetConfig } from "../../../services/config/widget-config-model"; @@ -6,7 +7,10 @@ import { mergeDeep } from "../../../shared/effect/merge-deep"; import { vars } from "../../../shared/styles/theme/contract.css"; import { id, rootSelector } from "../../../shared/styles/theme/ids"; import { lightTheme } from "../../../shared/styles/theme/themes"; -import { getFineryThemeOverrides } from "../../../shared/styles/theme/variant-overrides/finery"; +import { + type FineryBaseTheme, + getFineryThemeOverrides, +} from "../../../shared/styles/theme/variant-overrides/finery"; import { portoThemeOverrides } from "../../../shared/styles/theme/variant-overrides/porto"; import { utilaThemeOverrides } from "../../../shared/styles/theme/variant-overrides/utila"; import type { RecursivePartial } from "../../../shared/types/utils"; @@ -15,7 +19,7 @@ export const getThemeOverrides = ({ baseTheme, variant, }: { - baseTheme: typeof lightTheme; + baseTheme: FineryBaseTheme; variant: WidgetConfig["variant"]; }): RecursivePartial => { if (variant === "utila") { @@ -33,6 +37,25 @@ export const getThemeOverrides = ({ return {}; }; +/** + * CSS variable assignments for a merged theme, read through the theme + * contract: each contract leaf (a `var(--…)` reference) takes the merged value + * at the same path. Values off the contract are not emitted. + */ +const themeVarEntries = ( + contract: unknown, + tokens: unknown +): ReadonlyArray => { + if (typeof contract === "string") { + return typeof tokens === "string" ? [[contract, tokens]] : []; + } + if (!Predicate.isObject(contract) || !Predicate.isObject(tokens)) return []; + + return Object.entries(contract).flatMap(([key, node]) => + themeVarEntries(node, tokens[key]) + ); +}; + /** Theme variables for a host theme and variant, read from props. */ export const ThemeStyles = ({ children, @@ -42,19 +65,21 @@ export const ThemeStyles = ({ readonly theme: WidgetConfig["theme"]; readonly variant: WidgetConfig["variant"]; }>) => { - const baseTheme = mergeDeep(lightTheme, theme); const overrides = getThemeOverrides({ - baseTheme, + baseTheme: { + color: { primary: theme?.color?.primary ?? lightTheme.color.primary }, + }, variant, }); const finalTheme = mergeDeep(lightTheme, theme, overrides); + const themeVars = Object.fromEntries(themeVarEntries(vars, finalTheme)); return (