@meddleware/frontend-config 0.0.0-rename.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.0.1] - 2026-10-31
9
+
10
+ ### Added
11
+
12
+ - First release. Shared browserslist config, ESLint (`vueApp`), Stylelint, html-validate, PostCSS, Vite target, Playwright config,
13
+ axe helper (WCAG 2.2 AA gate, AAA reported), screenshot matrix and Lighthouse CI config, extracted from the per-repo copies in
14
+ the frontend repos.
package/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ BSD Zero Clause License
2
+
3
+ Copyright (c) 2026 MeddleWare
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # @meddleware/frontend-config
2
+
3
+ Shared frontend tooling for MeddleWare apps and libraries, so a repo's config files are one-liners and every repo is held to the
4
+ same accessibility and browser-support bar.
5
+
6
+ | Subpath | What it gives you |
7
+ | --- | --- |
8
+ | `/browserslist-config` | The browser support promise (below), as a browserslist shared config. |
9
+ | `/eslint` | `vueApp({ name, sui?, ignores?, controlComponents?, labelComponents?, extra? })` — Vue + TypeScript, `eslint-plugin-vuejs-accessibility`, `eslint-plugin-compat`, optionally the Sui boundary from `@meddleware/eslint-config`. |
10
+ | `/stylelint` | `stylelintConfig({ tokens?, compat?, browsers?, rules? })` — standard config, no hex, token-only colour and `z-index`, `@double-great/stylelint-a11y`, `stylelint-no-unsupported-browser-features`. |
11
+ | `/html-validate` | `htmlValidateConfig({ elements?, rules? })` — recommended + `html-validate:a11y` + Vue, with `@meddleware/ui` components described as the native element they render. |
12
+ | `/postcss` | Autoprefixer for the repo's browserslist. |
13
+ | `/vite` | `viteTarget()` — Vite `build.target` derived from the browserslist; `themeInit({ default })` — a Vite plugin that loads ui's `theme-init.js` first in `<head>` (the saved or default colour mode and the season applied before first paint, no inline script, no CSP hash) and adds `color-scheme` and the browser-chrome `theme-color` from the tokens. |
14
+ | `/playwright` | `definePlaywrightConfig({ port, kind?, command?, browsers?, extra? })`. |
15
+ | `/axe` | `expectAccessible(page, options)` — WCAG 2.2 AA gate; AAA as `aaa: 'gate'`, `'report'` or `'off'`. |
16
+ | `/look` | `looks()`, `applyLook(page, theme, season)`, `offline(page)`, `VIEWPORTS`, `THEMES`, `SEASONS` — the viewport × colour mode × season matrix. |
17
+ | `/shots` | `defineShots(states)` — one screenshot per state × look, for looking at the UI. |
18
+ | `/a11y` | `defineA11ySuite(states, { aaa })` — the sweep every app runs, one call: each state in every colour mode × season at WCAG 2.2 AA and AAA, a 320 px reflow, 200% text size, forced colours, reduced motion and a keyboard pass. `expectNoHorizontalScroll`, `expectNoRunningAnimations` and `expectKeyboardFocusVisible` are exported on their own. |
19
+ | `/vitest` | `inlineMeddleware` — the packages a test must process through Vite (they import `.css`/`.vue`), which also gives them the test's single copy of Vue; `installDialogShim()` — jsdom's missing `<dialog>` modality. |
20
+ | `/lighthouserc` | `lighthouseConfig({ port, paths?, … })` — Lighthouse CI (accessibility must score 100). |
21
+
22
+ ## Use
23
+
24
+ ```jsonc
25
+ // package.json
26
+ "browserslist": ["extends @meddleware/frontend-config/browserslist-config"]
27
+ ```
28
+
29
+ ```ts
30
+ // eslint.config.ts
31
+ import { vueApp } from '@meddleware/frontend-config/eslint'
32
+ export default vueApp({ name: 'treasury-ui', sui: true })
33
+ ```
34
+
35
+ ```js
36
+ // stylelint.config.js
37
+ import { stylelintConfig } from '@meddleware/frontend-config/stylelint'
38
+ export default stylelintConfig()
39
+ ```
40
+
41
+ ```js
42
+ // .htmlvalidate.mjs
43
+ import { htmlValidateConfig } from '@meddleware/frontend-config/html-validate'
44
+ export default htmlValidateConfig()
45
+ ```
46
+
47
+ ```js
48
+ // postcss.config.js
49
+ export { default } from '@meddleware/frontend-config/postcss'
50
+ ```
51
+
52
+ ```ts
53
+ // playwright.config.ts
54
+ import { definePlaywrightConfig } from '@meddleware/frontend-config/playwright'
55
+ export default definePlaywrightConfig({ port: 4281 })
56
+ ```
57
+
58
+ ```ts
59
+ // e2e/screens.spec.ts — run with `SHOTS=1 playwright test` (a screenshot run loads only the screens specs)
60
+ import { defineShots } from '@meddleware/frontend-config/shots'
61
+ defineShots({ overview: async (page) => { await page.goto('/') } })
62
+ ```
63
+
64
+ ```ts
65
+ // e2e/a11y.spec.ts
66
+ import { test } from '@playwright/test'
67
+ import { expectAccessible } from '@meddleware/frontend-config/axe'
68
+ import { applyLook, looks } from '@meddleware/frontend-config/look'
69
+ for (const { theme, season } of looks()) {
70
+ test(`home is accessible · ${theme} · ${season}`, async ({ page }) => {
71
+ await page.goto('/')
72
+ await applyLook(page, theme, season)
73
+ await expectAccessible(page)
74
+ })
75
+ }
76
+ ```
77
+
78
+ ## The support promise
79
+
80
+ `last 2` of Chrome, Chrome for Android, Edge, Firefox, Firefox for Android, Safari, iOS Safari and Samsung Internet, plus Firefox
81
+ ESR, minus dead browsers. It is deliberately **not** `last 2 versions`, which also selects Opera Mini, KaiOS, Baidu and QQ: every
82
+ app is a Vue SPA and the wallet apps need browser extensions, so those engines cannot run them, and fallbacks for them would cost
83
+ CSS and complexity for no working user. Features newer than the oldest browser in the list still need `@supports` or progressive
84
+ enhancement; the compat linters enforce that.
85
+
86
+ ## Accessibility bar
87
+
88
+ WCAG 2.2 AA is always a hard gate: `expectAccessible` fails on any `wcag2a`, `wcag2aa`, `wcag21a`, `wcag21aa`, `wcag22aa` or axe
89
+ best-practice violation. WCAG AAA (`wcag2aaa`, including enhanced contrast) is the target: `aaa: 'gate'` fails the run on it,
90
+ `'report'` (the default, while a repo still has findings) attaches it to the test as `aaa` annotations, `'off'` skips it. A repo
91
+ switches to `gate` in the release that clears its AAA findings. `@meddleware/design-tokens` already guarantees 7:1 for every
92
+ text and label role in every theme × season, so remaining AAA findings are local colours and sizes, not the palette.
93
+
94
+ ## Browsers in tests
95
+
96
+ `definePlaywrightConfig` runs Chromium locally and five projects in CI: Chromium, Firefox, WebKit, a Pixel 7 and an iPhone 14
97
+ viewport. `E2E_BROWSERS=all` runs all five locally (install them first: `npx playwright install chromium firefox webkit`).
98
+ `SHOTS=1` keeps a screenshot run on Chromium and runs only `screens.spec.ts` (which a normal run ignores): use `SHOTS=1 playwright test` with no file filter, since the tests are attributed to `shots.js`.
99
+
100
+ ## Mock wallet in Playwright (`/wallet`)
101
+
102
+ ```ts
103
+ import { connectMockWallet, exposeSigner, E2E_ADDRESS } from '@meddleware/frontend-config/wallet'
104
+ await exposeSigner(page, { signTransaction, signPersonalMessage }) // optional: sign in Node (call before page.goto)
105
+ await page.goto('/')
106
+ await connectMockWallet(page) // the app's wallet dialog → "Test Wallet"
107
+ ```
108
+
109
+ The page side is `@meddleware/wallet-adapter/testing` (`installE2eWallet`), registered by an app's e2e build.
110
+
111
+ ## Peers
112
+
113
+ `eslint` ^10 (required for `/eslint`), and optional peers per subpath: `stylelint` ^17, `html-validate` ^11, `postcss` ^8,
114
+ `@playwright/test` ~1.64. The lint plugins are dependencies of this package and are resolved from it, so a consumer needs none of
115
+ them.
package/dist/a11y.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /** @typedef {(page: import('@playwright/test').Page) => Promise<void>} State */
2
+ /**
3
+ * The page has no horizontal scroll: nothing is wider than the viewport (WCAG 1.4.10 reflow, and 1.4.4 at a larger text size).
4
+ * @param {import('@playwright/test').Page} page
5
+ */
6
+ export function expectNoHorizontalScroll(page: import("@playwright/test").Page): Promise<void>;
7
+ /**
8
+ * No animation is running: with `prefers-reduced-motion: reduce` the page holds still (WCAG 2.3.3).
9
+ * @param {import('@playwright/test').Page} page
10
+ */
11
+ export function expectNoRunningAnimations(page: import("@playwright/test").Page): Promise<void>;
12
+ /**
13
+ * Tab through the page: every stop is visible and shows a focus indicator (an outline or a shadow), and the tab order ends (no trap).
14
+ * @param {import('@playwright/test').Page} page
15
+ * @param {{ maxStops?: number }} [options]
16
+ */
17
+ export function expectKeyboardFocusVisible(page: import("@playwright/test").Page, { maxStops }?: {
18
+ maxStops?: number;
19
+ }): Promise<void>;
20
+ /**
21
+ * @param {Record<string, State>} states Opens each state of the app (navigates, mocks, opens the dialog).
22
+ * @param {{ aaa?: 'gate' | 'report' | 'off' }} [options] How WCAG AAA findings are treated (default `report`; a repo moves to `gate`
23
+ * in the release that clears them).
24
+ */
25
+ export function defineA11ySuite(states: Record<string, State>, { aaa }?: {
26
+ aaa?: "gate" | "report" | "off";
27
+ }): void;
28
+ export type State = (page: import("@playwright/test").Page) => Promise<void>;
package/dist/axe.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Fail on any WCAG 2.2 A/AA (or best-practice) violation. AAA findings: `aaa: 'gate'` fails on them, `'report'` (default)
3
+ * attaches them to the test as `aaa` annotations, `'off'` skips the AAA run.
4
+ * @param {import('@playwright/test').Page} page
5
+ * @param {{ include?: string, exclude?: string[], disableRules?: string[], aaa?: 'gate' | 'report' | 'off' }} [options]
6
+ */
7
+ export function expectAccessible(page: import("@playwright/test").Page, options?: {
8
+ include?: string;
9
+ exclude?: string[];
10
+ disableRules?: string[];
11
+ aaa?: "gate" | "report" | "off";
12
+ }): Promise<void>;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @param {object} options
3
+ * @param {string} options.name Prefix for the config object names (usually the repo name).
4
+ * @param {boolean} [options.sui] Append the Sui chain-access boundary (apps and tool libraries; not ui, tokens or sites).
5
+ * @param {string[]} [options.ignores] Extra global ignores (for example `docs/api/**`).
6
+ * @param {string[]} [options.controlComponents] Extra components that render a native form control.
7
+ * @param {string[]} [options.labelComponents] Extra components that render a label.
8
+ * @param {FlatConfig[]} [options.extra] Extra config objects, placed before the boundary.
9
+ * @returns {FlatConfig[]}
10
+ */
11
+ export function vueApp({ name, sui, ignores, controlComponents, labelComponents, extra }: {
12
+ name: string;
13
+ sui?: boolean | undefined;
14
+ ignores?: string[] | undefined;
15
+ controlComponents?: string[] | undefined;
16
+ labelComponents?: string[] | undefined;
17
+ extra?: FlatConfig[] | undefined;
18
+ }): FlatConfig[];
19
+ /** `@meddleware/ui` components that render a native control, so the label rules see through them. */
20
+ export const CONTROL_COMPONENTS: readonly string[];
21
+ /** `@meddleware/ui` components that render the `<label for>` for the control in their default slot. */
22
+ export const LABEL_COMPONENTS: readonly string[];
23
+ /**
24
+ * One flat-config object. Declared structurally (rather than as ESLint's own type) because ESLint's type refers to internal
25
+ * packages that TypeScript cannot name in a declaration file; ESLint accepts this shape.
26
+ */
27
+ export type FlatConfig = {
28
+ name?: string;
29
+ files?: (string | string[])[];
30
+ ignores?: string[];
31
+ rules?: Record<string, unknown>;
32
+ [key: string]: unknown;
33
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @param {object} [options]
3
+ * @param {Record<string, unknown>} [options.elements] Extra element descriptions.
4
+ * @param {Record<string, unknown>} [options.rules] Rule overrides.
5
+ * @returns {import('html-validate').ConfigData}
6
+ */
7
+ export function htmlValidateConfig({ elements, rules }?: {
8
+ elements?: Record<string, unknown> | undefined;
9
+ rules?: Record<string, unknown> | undefined;
10
+ }): import("html-validate").ConfigData;
11
+ /**
12
+ * `@meddleware/ui` components that render a single native element, described to the validator so content models and form
13
+ * rules see through them (keys are lowercased tag names).
14
+ */
15
+ export const UI_ELEMENTS: Readonly<{
16
+ uibutton: {
17
+ inherit: string;
18
+ };
19
+ uitoolbarbutton: {
20
+ inherit: string;
21
+ };
22
+ uiselect: {
23
+ inherit: string;
24
+ };
25
+ uinotice: {
26
+ inherit: string;
27
+ };
28
+ }>;
29
+ declare const _default: import("html-validate").ConfigData;
30
+ export default _default;
@@ -0,0 +1,6 @@
1
+ /** axe tags that gate a build: WCAG 2.0, 2.1 and 2.2 level A and AA, plus axe's best practices. */
2
+ export const AXE_TAGS_AA: readonly string[];
3
+ /** axe tags reported but never gating: WCAG AAA (enhanced contrast and so on). */
4
+ export const AXE_TAGS_AAA: readonly string[];
5
+ /** Playwright projects for the browsers the support promise names (engines: Chromium, Gecko, WebKit; two mobile viewports). */
6
+ export const BROWSER_PROJECTS: readonly string[];
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @param {object} options
3
+ * @param {number} options.port Port `vite preview` (or `vitepress preview`) serves on.
4
+ * @param {string[]} [options.paths] Routes to audit.
5
+ * @param {string} [options.startServerCommand] Command that serves the built site (default `npx vite preview`).
6
+ * @param {number} [options.maxScriptKb] Warn above this much JavaScript on a route.
7
+ * @param {number} [options.maxStylesheetKb] Warn above this much CSS on a route.
8
+ */
9
+ export function lighthouseConfig({ port, paths, startServerCommand, maxScriptKb, maxStylesheetKb }: {
10
+ port: number;
11
+ paths?: string[] | undefined;
12
+ startServerCommand?: string | undefined;
13
+ maxScriptKb?: number | undefined;
14
+ maxStylesheetKb?: number | undefined;
15
+ }): {
16
+ ci: {
17
+ collect: {
18
+ url: string[];
19
+ startServerCommand: string;
20
+ startServerReadyPattern: string;
21
+ numberOfRuns: number;
22
+ settings: {
23
+ chromeFlags: string;
24
+ onlyCategories: string[];
25
+ };
26
+ };
27
+ assert: {
28
+ assertions: {
29
+ 'categories:accessibility': (string | {
30
+ minScore: number;
31
+ })[];
32
+ 'categories:best-practices': (string | {
33
+ minScore: number;
34
+ })[];
35
+ 'categories:seo': (string | {
36
+ minScore: number;
37
+ })[];
38
+ 'categories:performance': (string | {
39
+ minScore: number;
40
+ })[];
41
+ 'resource-summary:script:size': (string | {
42
+ maxNumericValue: number;
43
+ })[];
44
+ 'resource-summary:stylesheet:size': (string | {
45
+ maxNumericValue: number;
46
+ })[];
47
+ };
48
+ };
49
+ upload: {
50
+ target: string;
51
+ outputDir: string;
52
+ };
53
+ };
54
+ };
package/dist/look.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ /** Desktop covers every theme × season; tablet and phone cover each theme with no season. */
2
+ export function looks(): {
3
+ viewport: keyof typeof VIEWPORTS;
4
+ theme: string;
5
+ season: string;
6
+ }[];
7
+ /**
8
+ * Refuse every request that leaves localhost, so a run needs no network. Failed fetches are expected and are not page errors.
9
+ * @param {import('@playwright/test').Page} page
10
+ */
11
+ export function offline(page: import("@playwright/test").Page): Promise<void>;
12
+ /**
13
+ * Put the page in a look. Uses the real colour-mode control when the page has one and is not behind a modal dialog (as a visitor would, so
14
+ * the control's own state matches the page); otherwise sets the attribute (galleries; VitePress, which themes with a `.dark` class).
15
+ * @param {import('@playwright/test').Page} page
16
+ * @param {string} theme `light` | `dark`
17
+ * @param {string} season `none` | a season name
18
+ */
19
+ export function applyLook(page: import("@playwright/test").Page, theme: string, season: string): Promise<void>;
20
+ export const VIEWPORTS: Readonly<{
21
+ phone: Readonly<{
22
+ width: 390;
23
+ height: 844;
24
+ }>;
25
+ tablet: Readonly<{
26
+ width: 768;
27
+ height: 1024;
28
+ }>;
29
+ desktop: Readonly<{
30
+ width: 1280;
31
+ height: 900;
32
+ }>;
33
+ }>;
34
+ export const THEMES: readonly string[];
35
+ export const SEASONS: readonly string[];
@@ -0,0 +1,21 @@
1
+ /** @typedef {'chromium' | 'all'} BrowserSet */
2
+ /**
3
+ * @param {object} options
4
+ * @param {number} options.port Preview server port (unique per repo, so repos can run side by side).
5
+ * @param {'app' | 'gallery' | 'vitepress'} [options.kind] What is served. `app`: `npm run build` then `vite preview`.
6
+ * `gallery`: `npm run build:gallery` then `vite preview --config vite.gallery.config.ts`. `vitepress`: `vitepress preview docs`
7
+ * (the site must already be built, so the repo's `test:e2e` script builds first).
8
+ * @param {string} [options.command] Full server command; replaces the one `kind` implies.
9
+ * @param {BrowserSet} [options.browsers] Browser set; default from E2E_BROWSERS, else `all` in CI and `chromium` otherwise.
10
+ * @param {string} [options.testDir]
11
+ * @param {import('@playwright/test').PlaywrightTestConfig} [options.extra] Merged over the defaults.
12
+ */
13
+ export function definePlaywrightConfig({ port, kind, command, browsers, testDir, extra }: {
14
+ port: number;
15
+ kind?: "app" | "gallery" | "vitepress" | undefined;
16
+ command?: string | undefined;
17
+ browsers?: BrowserSet | undefined;
18
+ testDir?: string | undefined;
19
+ extra?: import("playwright/test").PlaywrightTestConfig<{}, {}> | undefined;
20
+ }): import("playwright/test").PlaywrightTestConfig<{}, {}>;
21
+ export type BrowserSet = "chromium" | "all";
@@ -0,0 +1,5 @@
1
+ declare namespace _default {
2
+ let plugins: (import("postcss").Plugin & autoprefixer.ExportedAPI)[];
3
+ }
4
+ export default _default;
5
+ import autoprefixer from 'autoprefixer';
@@ -0,0 +1,10 @@
1
+ /** @typedef {(page: import('@playwright/test').Page) => Promise<void>} State */
2
+ /**
3
+ * Register one test per state × look. Call once, at the top level of an `e2e/screens.spec.ts`.
4
+ * @param {Record<string, State>} states
5
+ * @param {{ out?: string }} [options]
6
+ */
7
+ export function defineShots(states: Record<string, State>, options?: {
8
+ out?: string;
9
+ }): void;
10
+ export type State = (page: import("@playwright/test").Page) => Promise<void>;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @param {object} [options]
3
+ * @param {'error' | 'warning' | false} [options.tokens] Severity of the token-only rule. Default `error`; a repo with
4
+ * literals still to fix starts at `warning` and moves to `error` in the same release as the fixes.
5
+ * @param {'error' | 'warning'} [options.compat] Severity of unsupported-browser-feature findings. Default `error`.
6
+ * @param {string[]} [options.browsers] Browsers to check against. Default: the repo's browserslist (the shared support promise).
7
+ * @param {Record<string, unknown>} [options.rules] Rule overrides.
8
+ */
9
+ export function stylelintConfig({ tokens, compat, browsers, rules }?: {
10
+ tokens?: false | "error" | "warning" | undefined;
11
+ compat?: "error" | "warning" | undefined;
12
+ browsers?: string[] | undefined;
13
+ rules?: Record<string, unknown> | undefined;
14
+ }): {
15
+ extends: string[];
16
+ plugins: string[];
17
+ overrides: {
18
+ files: string[];
19
+ customSyntax: ((options?: Record<string, any>) => import("postcss").Syntax) & import("postcss").Syntax<import("postcss").Document | import("postcss").Root>;
20
+ }[];
21
+ rules: {
22
+ 'plugin/no-unsupported-browser-features': (boolean | {
23
+ browsers?: string[] | undefined;
24
+ severity: "error" | "warning";
25
+ ignorePartialSupport: boolean;
26
+ ignore: string[];
27
+ })[];
28
+ 'a11y/media-prefers-reduced-motion': null;
29
+ 'a11y/no-outline-none': boolean;
30
+ 'a11y/selector-pseudo-class-focus': null;
31
+ 'a11y/content-property-no-static-value': boolean;
32
+ 'a11y/font-size-is-readable': boolean;
33
+ 'a11y/no-obsolete-attribute': boolean;
34
+ 'a11y/no-obsolete-element': boolean;
35
+ 'a11y/no-text-align-justify': boolean;
36
+ 'scale-unlimited/declaration-strict-value'?: (string[] | {
37
+ ignoreValues: string[];
38
+ disableFix: boolean;
39
+ severity: "error" | "warning";
40
+ })[] | undefined;
41
+ 'custom-property-empty-line-before': null;
42
+ 'declaration-empty-line-before': null;
43
+ 'comment-empty-line-before': null;
44
+ 'rule-empty-line-before': null;
45
+ 'at-rule-empty-line-before': null;
46
+ 'no-descending-specificity': null;
47
+ 'selector-class-pattern': null;
48
+ 'declaration-block-single-line-max-declarations': null;
49
+ 'value-keyword-case': null;
50
+ 'selector-pseudo-class-no-unknown': (boolean | {
51
+ ignorePseudoClasses: string[];
52
+ })[];
53
+ 'custom-property-pattern': null;
54
+ 'property-no-vendor-prefix': null;
55
+ 'value-no-vendor-prefix': null;
56
+ 'color-no-hex': boolean;
57
+ };
58
+ };
59
+ declare namespace _default {
60
+ let _extends: string[];
61
+ export { _extends as extends };
62
+ export let plugins: string[];
63
+ export let overrides: {
64
+ files: string[];
65
+ customSyntax: ((options?: Record<string, any>) => import("postcss").Syntax) & import("postcss").Syntax<import("postcss").Document | import("postcss").Root>;
66
+ }[];
67
+ export let rules: {
68
+ 'plugin/no-unsupported-browser-features': (boolean | {
69
+ browsers?: string[] | undefined;
70
+ severity: "error" | "warning";
71
+ ignorePartialSupport: boolean;
72
+ ignore: string[];
73
+ })[];
74
+ 'a11y/media-prefers-reduced-motion': null;
75
+ 'a11y/no-outline-none': boolean;
76
+ 'a11y/selector-pseudo-class-focus': null;
77
+ 'a11y/content-property-no-static-value': boolean;
78
+ 'a11y/font-size-is-readable': boolean;
79
+ 'a11y/no-obsolete-attribute': boolean;
80
+ 'a11y/no-obsolete-element': boolean;
81
+ 'a11y/no-text-align-justify': boolean;
82
+ 'scale-unlimited/declaration-strict-value'?: (string[] | {
83
+ ignoreValues: string[];
84
+ disableFix: boolean;
85
+ severity: "error" | "warning";
86
+ })[] | undefined;
87
+ 'custom-property-empty-line-before': null;
88
+ 'declaration-empty-line-before': null;
89
+ 'comment-empty-line-before': null;
90
+ 'rule-empty-line-before': null;
91
+ 'at-rule-empty-line-before': null;
92
+ 'no-descending-specificity': null;
93
+ 'selector-class-pattern': null;
94
+ 'declaration-block-single-line-max-declarations': null;
95
+ 'value-keyword-case': null;
96
+ 'selector-pseudo-class-no-unknown': (boolean | {
97
+ ignorePseudoClasses: string[];
98
+ })[];
99
+ 'custom-property-pattern': null;
100
+ 'property-no-vendor-prefix': null;
101
+ 'value-no-vendor-prefix': null;
102
+ 'color-no-hex': boolean;
103
+ };
104
+ }
105
+ export default _default;
package/dist/vite.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ /** @param {string | string[]} [queries] Browserslist queries; default: the repo's own configuration. */
2
+ export function viteTarget(queries?: string | string[]): string[];
3
+ /**
4
+ * The colour mode and the browser chrome, before the first paint: a Vite plugin for an app's `index.html`.
5
+ *
6
+ * - Serves `@meddleware/ui`'s `theme-init.js` at `/theme-init.js` (dev) and emits it into the build, and loads it as the first,
7
+ * render-blocking script in `<head>`. It applies the saved (or default) colour mode and the season to `<html>` before anything
8
+ * paints, so a page never flashes the wrong theme while its JavaScript loads. It is a same-origin file, so the CSP needs no hash
9
+ * and no `'unsafe-inline'`.
10
+ * - Adds `<meta name="color-scheme" content="light dark">` (native controls and scrollbars follow the theme) and the browser-chrome
11
+ * `theme-color` for each colour mode, from the design tokens' page background.
12
+ *
13
+ * ```ts
14
+ * plugins: [vue(), themeInit({ default: 'dark' })]
15
+ * ```
16
+ *
17
+ * @param {object} [options]
18
+ * @param {'light' | 'dark' | 'system'} [options.default] The mode a first visit gets (a saved choice always wins). Default `system`.
19
+ * @param {string} [options.scriptPath] The script to serve (default: `@meddleware/ui/theme-init.js` as installed in the app).
20
+ * @param {{ light: string, dark: string }} [options.colors] The `theme-color` values (default: the tokens' page backgrounds).
21
+ * @returns {import('vite').Plugin}
22
+ */
23
+ export function themeInit({ default: mode, scriptPath, colors }?: {
24
+ default?: "light" | "dark" | "system" | undefined;
25
+ scriptPath?: string | undefined;
26
+ colors?: {
27
+ light: string;
28
+ dark: string;
29
+ } | undefined;
30
+ }): import("vite").Plugin;
31
+ /**
32
+ * The end-to-end build mode registers a mock wallet and a stub chain client in the app (`@meddleware/wallet-adapter/testing`), which must
33
+ * never reach a production build. The app's entry loads it only when `import.meta.env.MODE === 'e2e' && import.meta.env.VITE_E2E === '1'`
34
+ * (both static, so every other build drops the block); this makes `VITE_E2E=1` outside `--mode e2e` a build error instead of a silent no-op.
35
+ *
36
+ * ```ts
37
+ * export default defineConfig(({ mode }) => { assertE2eOnlyInE2eMode(mode, process.env); return { … } })
38
+ * ```
39
+ *
40
+ * @param {string} mode Vite's mode.
41
+ * @param {Record<string, string | undefined>} env The environment (default: the process's).
42
+ */
43
+ export function assertE2eOnlyInE2eMode(mode: string, env?: Record<string, string | undefined>): void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * jsdom does not implement `<dialog>` modality (`showModal`, `close`, `returnValue`). This minimal shim models the parts `UiDialog` relies on:
3
+ * the `open` attribute, `returnValue`, and a `close` event on closing. Call it once (`beforeAll(installDialogShim)`) in a jsdom test that
4
+ * renders a dialog; the browser's real behaviour (top layer, inert background) is checked by the Playwright suites, not jsdom.
5
+ */
6
+ export function installDialogShim(): void;
7
+ /**
8
+ * Packages to process through Vite in tests: the ones that import CSS or `.vue` files. Vitest matches these against the module's resolved
9
+ * file path (`…/node_modules/@meddleware/ui/dist/…`), not the package name, so the patterns match the path.
10
+ */
11
+ export const inlineMeddleware: RegExp[];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Connect the mock wallet as a visitor would: open the wallet dialog, choose the wallet, and wait for the dialog to close.
3
+ * @param {import('@playwright/test').Page} page
4
+ * @param {{ wallet?: string, trigger?: string | RegExp }} [options] The wallet's name in the picker; the name of the button that opens the dialog.
5
+ */
6
+ export function connectMockWallet(page: import("@playwright/test").Page, { wallet, trigger }?: {
7
+ wallet?: string;
8
+ trigger?: string | RegExp;
9
+ }): Promise<void>;
10
+ /**
11
+ * @typedef {object} NodeSigner
12
+ * @property {(transactionJson: string, chain: string) => Promise<{ bytes: string, signature: string }>} [signTransaction]
13
+ * Build and sign a transaction (`Transaction.from(json)`); resolves base64 bytes and signature.
14
+ * @property {(message: string) => Promise<{ signature: string }>} [signPersonalMessage] Sign a personal message (base64 bytes).
15
+ */
16
+ /**
17
+ * Let the page's mock wallet sign in this Node process. Call it before `page.goto`: the functions are exposed to every page load, and
18
+ * `installE2eWallet` uses them when they exist.
19
+ * @param {import('@playwright/test').Page} page
20
+ * @param {NodeSigner} signer
21
+ * @param {{ address?: string }} [options] The address the wallet connects with: the signer's own, so the transaction's sender and its signature agree.
22
+ */
23
+ export function exposeSigner(page: import("@playwright/test").Page, signer: NodeSigner, { address }?: {
24
+ address?: string;
25
+ }): Promise<void>;
26
+ /** The address the mock wallet connects with (`MOCK_ADDRESS` in wallet-adapter's testing module). */
27
+ export const E2E_ADDRESS: string;
28
+ export type NodeSigner = {
29
+ /**
30
+ * Build and sign a transaction (`Transaction.from(json)`); resolves base64 bytes and signature.
31
+ */
32
+ signTransaction?: ((transactionJson: string, chain: string) => Promise<{
33
+ bytes: string;
34
+ signature: string;
35
+ }>) | undefined;
36
+ /**
37
+ * Sign a personal message (base64 bytes).
38
+ */
39
+ signPersonalMessage?: ((message: string) => Promise<{
40
+ signature: string;
41
+ }>) | undefined;
42
+ };