@mandujs/core 0.54.32 → 0.55.0-beta.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/README.md +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
package/src/a11y/fix-hints.ts
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Curated fix hints for the most common axe-core rules we see in the
|
|
3
|
-
* wild during framework dogfooding. Each hint is a single actionable
|
|
4
|
-
* sentence — long enough to be useful, short enough to fit in a CI
|
|
5
|
-
* table cell. When a rule is not in this map the runner simply omits
|
|
6
|
-
* `fixHint` and callers fall back to axe's `helpUrl` link.
|
|
7
|
-
*
|
|
8
|
-
* Rule ids mirror axe-core's canonical list:
|
|
9
|
-
* https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
|
|
10
|
-
*/
|
|
11
|
-
export const AXE_RULE_FIX_HINTS: Record<string, string> = {
|
|
12
|
-
"color-contrast":
|
|
13
|
-
"Increase foreground/background contrast to meet WCAG AA 4.5:1 for normal text (3:1 for large).",
|
|
14
|
-
"image-alt":
|
|
15
|
-
"Add an `alt` attribute to every <img>. Decorative images should use `alt=\"\"`.",
|
|
16
|
-
"label":
|
|
17
|
-
"Associate every form control with a <label for=\"id\"> or wrap it in <label>.",
|
|
18
|
-
"link-name":
|
|
19
|
-
"Ensure <a> elements contain accessible text (visible, aria-label, or aria-labelledby).",
|
|
20
|
-
"button-name":
|
|
21
|
-
"Give every <button> an accessible name: visible text, aria-label, or aria-labelledby.",
|
|
22
|
-
"document-title":
|
|
23
|
-
"Render a non-empty <title> inside <head>. Mandu's `metadata.title` exports auto-populate this.",
|
|
24
|
-
"html-has-lang":
|
|
25
|
-
"Set `lang` on <html>. In Mandu, configure via `metadata.lang` or the root layout.",
|
|
26
|
-
"html-lang-valid":
|
|
27
|
-
"Use a BCP-47 language code (`en`, `ko`, `en-US`) — case-sensitive region subtag matters.",
|
|
28
|
-
"landmark-one-main":
|
|
29
|
-
"Wrap primary content in exactly one <main> landmark per page.",
|
|
30
|
-
"region":
|
|
31
|
-
"Place all content inside a landmark (<header>, <main>, <nav>, <footer>, or role=\"region\").",
|
|
32
|
-
"duplicate-id":
|
|
33
|
-
"Every `id` must be unique in the DOM. Check island + SSR output for collisions.",
|
|
34
|
-
"duplicate-id-active":
|
|
35
|
-
"Focusable elements must have unique ids — screen readers cannot resolve duplicates.",
|
|
36
|
-
"duplicate-id-aria":
|
|
37
|
-
"ids referenced by aria-* attributes must be unique (one target per reference).",
|
|
38
|
-
"meta-viewport":
|
|
39
|
-
"Do not disable user scaling: avoid `user-scalable=no` or `maximum-scale<2` in <meta name=viewport>.",
|
|
40
|
-
"aria-valid-attr":
|
|
41
|
-
"Remove unknown aria-* attributes. Check for typos (`aria-labeledby` → `aria-labelledby`).",
|
|
42
|
-
"aria-valid-attr-value":
|
|
43
|
-
"aria-* attribute values must match the allowed set for that attribute.",
|
|
44
|
-
"aria-required-attr":
|
|
45
|
-
"The ARIA role you used requires additional attributes (e.g. role=\"slider\" needs aria-valuenow).",
|
|
46
|
-
"aria-roles":
|
|
47
|
-
"Use a valid ARIA role. Custom roles (`role=\"card\"`) are ignored by screen readers.",
|
|
48
|
-
"list":
|
|
49
|
-
"<ul>/<ol> must contain only <li> children (plus script/template). Wrap other content inside <li>.",
|
|
50
|
-
"listitem":
|
|
51
|
-
"<li> must have a parent <ul>, <ol>, or <menu>.",
|
|
52
|
-
"heading-order":
|
|
53
|
-
"Headings must increase by one level at a time — don't skip from <h2> to <h4>.",
|
|
54
|
-
"empty-heading":
|
|
55
|
-
"Remove empty heading tags or add text content — they confuse screen-reader navigation.",
|
|
56
|
-
"tabindex":
|
|
57
|
-
"Avoid tabindex values greater than 0 — they break the natural focus order.",
|
|
58
|
-
"frame-title":
|
|
59
|
-
"Give every <iframe> a `title` attribute describing its contents.",
|
|
60
|
-
"object-alt":
|
|
61
|
-
"Provide fallback text for <object> via its text content or `aria-label`.",
|
|
62
|
-
"video-caption":
|
|
63
|
-
"Every <video> must have at least one <track kind=\"captions\">.",
|
|
64
|
-
"bypass":
|
|
65
|
-
"Include a skip-link or a landmark so keyboard users can bypass repeated blocks.",
|
|
66
|
-
} as const;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Return the fix hint for a rule id, or `undefined` when we don't have
|
|
70
|
-
* one. Kept as a function (rather than direct map access) so the
|
|
71
|
-
* lookup layer can evolve (e.g. add localization) without rippling
|
|
72
|
-
* through call sites.
|
|
73
|
-
*/
|
|
74
|
-
export function getFixHint(ruleId: string): string | undefined {
|
|
75
|
-
return AXE_RULE_FIX_HINTS[ruleId];
|
|
76
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Curated fix hints for the most common axe-core rules we see in the
|
|
3
|
+
* wild during framework dogfooding. Each hint is a single actionable
|
|
4
|
+
* sentence — long enough to be useful, short enough to fit in a CI
|
|
5
|
+
* table cell. When a rule is not in this map the runner simply omits
|
|
6
|
+
* `fixHint` and callers fall back to axe's `helpUrl` link.
|
|
7
|
+
*
|
|
8
|
+
* Rule ids mirror axe-core's canonical list:
|
|
9
|
+
* https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
|
|
10
|
+
*/
|
|
11
|
+
export const AXE_RULE_FIX_HINTS: Record<string, string> = {
|
|
12
|
+
"color-contrast":
|
|
13
|
+
"Increase foreground/background contrast to meet WCAG AA 4.5:1 for normal text (3:1 for large).",
|
|
14
|
+
"image-alt":
|
|
15
|
+
"Add an `alt` attribute to every <img>. Decorative images should use `alt=\"\"`.",
|
|
16
|
+
"label":
|
|
17
|
+
"Associate every form control with a <label for=\"id\"> or wrap it in <label>.",
|
|
18
|
+
"link-name":
|
|
19
|
+
"Ensure <a> elements contain accessible text (visible, aria-label, or aria-labelledby).",
|
|
20
|
+
"button-name":
|
|
21
|
+
"Give every <button> an accessible name: visible text, aria-label, or aria-labelledby.",
|
|
22
|
+
"document-title":
|
|
23
|
+
"Render a non-empty <title> inside <head>. Mandu's `metadata.title` exports auto-populate this.",
|
|
24
|
+
"html-has-lang":
|
|
25
|
+
"Set `lang` on <html>. In Mandu, configure via `metadata.lang` or the root layout.",
|
|
26
|
+
"html-lang-valid":
|
|
27
|
+
"Use a BCP-47 language code (`en`, `ko`, `en-US`) — case-sensitive region subtag matters.",
|
|
28
|
+
"landmark-one-main":
|
|
29
|
+
"Wrap primary content in exactly one <main> landmark per page.",
|
|
30
|
+
"region":
|
|
31
|
+
"Place all content inside a landmark (<header>, <main>, <nav>, <footer>, or role=\"region\").",
|
|
32
|
+
"duplicate-id":
|
|
33
|
+
"Every `id` must be unique in the DOM. Check island + SSR output for collisions.",
|
|
34
|
+
"duplicate-id-active":
|
|
35
|
+
"Focusable elements must have unique ids — screen readers cannot resolve duplicates.",
|
|
36
|
+
"duplicate-id-aria":
|
|
37
|
+
"ids referenced by aria-* attributes must be unique (one target per reference).",
|
|
38
|
+
"meta-viewport":
|
|
39
|
+
"Do not disable user scaling: avoid `user-scalable=no` or `maximum-scale<2` in <meta name=viewport>.",
|
|
40
|
+
"aria-valid-attr":
|
|
41
|
+
"Remove unknown aria-* attributes. Check for typos (`aria-labeledby` → `aria-labelledby`).",
|
|
42
|
+
"aria-valid-attr-value":
|
|
43
|
+
"aria-* attribute values must match the allowed set for that attribute.",
|
|
44
|
+
"aria-required-attr":
|
|
45
|
+
"The ARIA role you used requires additional attributes (e.g. role=\"slider\" needs aria-valuenow).",
|
|
46
|
+
"aria-roles":
|
|
47
|
+
"Use a valid ARIA role. Custom roles (`role=\"card\"`) are ignored by screen readers.",
|
|
48
|
+
"list":
|
|
49
|
+
"<ul>/<ol> must contain only <li> children (plus script/template). Wrap other content inside <li>.",
|
|
50
|
+
"listitem":
|
|
51
|
+
"<li> must have a parent <ul>, <ol>, or <menu>.",
|
|
52
|
+
"heading-order":
|
|
53
|
+
"Headings must increase by one level at a time — don't skip from <h2> to <h4>.",
|
|
54
|
+
"empty-heading":
|
|
55
|
+
"Remove empty heading tags or add text content — they confuse screen-reader navigation.",
|
|
56
|
+
"tabindex":
|
|
57
|
+
"Avoid tabindex values greater than 0 — they break the natural focus order.",
|
|
58
|
+
"frame-title":
|
|
59
|
+
"Give every <iframe> a `title` attribute describing its contents.",
|
|
60
|
+
"object-alt":
|
|
61
|
+
"Provide fallback text for <object> via its text content or `aria-label`.",
|
|
62
|
+
"video-caption":
|
|
63
|
+
"Every <video> must have at least one <track kind=\"captions\">.",
|
|
64
|
+
"bypass":
|
|
65
|
+
"Include a skip-link or a landmark so keyboard users can bypass repeated blocks.",
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Return the fix hint for a rule id, or `undefined` when we don't have
|
|
70
|
+
* one. Kept as a function (rather than direct map access) so the
|
|
71
|
+
* lookup layer can evolve (e.g. add localization) without rippling
|
|
72
|
+
* through call sites.
|
|
73
|
+
*/
|
|
74
|
+
export function getFixHint(ruleId: string): string | undefined {
|
|
75
|
+
return AXE_RULE_FIX_HINTS[ruleId];
|
|
76
|
+
}
|
package/src/a11y/index.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @mandujs/core/a11y — public surface.
|
|
3
|
-
*
|
|
4
|
-
* Accessibility audit runner (Phase 18.χ). Consumers typically reach
|
|
5
|
-
* for `runAudit` + `formatAuditReport`; the type exports are there so
|
|
6
|
-
* CI tooling can build typed gates on top of the report shape.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { runAudit, formatAuditReport } from "./run-audit";
|
|
10
|
-
export { AXE_RULE_FIX_HINTS, getFixHint } from "./fix-hints";
|
|
11
|
-
export { AUDIT_IMPACT_ORDER, impactAtLeast } from "./types";
|
|
12
|
-
export type {
|
|
13
|
-
AuditImpact,
|
|
14
|
-
AuditNode,
|
|
15
|
-
AuditViolation,
|
|
16
|
-
AuditReport,
|
|
17
|
-
RunAuditOptions,
|
|
18
|
-
} from "./types";
|
|
1
|
+
/**
|
|
2
|
+
* @mandujs/core/a11y — public surface.
|
|
3
|
+
*
|
|
4
|
+
* Accessibility audit runner (Phase 18.χ). Consumers typically reach
|
|
5
|
+
* for `runAudit` + `formatAuditReport`; the type exports are there so
|
|
6
|
+
* CI tooling can build typed gates on top of the report shape.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { runAudit, formatAuditReport } from "./run-audit";
|
|
10
|
+
export { AXE_RULE_FIX_HINTS, getFixHint } from "./fix-hints";
|
|
11
|
+
export { AUDIT_IMPACT_ORDER, impactAtLeast } from "./types";
|
|
12
|
+
export type {
|
|
13
|
+
AuditImpact,
|
|
14
|
+
AuditNode,
|
|
15
|
+
AuditViolation,
|
|
16
|
+
AuditReport,
|
|
17
|
+
RunAuditOptions,
|
|
18
|
+
} from "./types";
|
package/src/a11y/run-audit.ts
CHANGED
|
@@ -69,11 +69,11 @@ interface DomProvider {
|
|
|
69
69
|
fromHtml(html: string, url: string): Promise<{ window: unknown; dispose: () => Promise<void> }>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
const DEFAULT_MAX_FILES = 500;
|
|
73
|
-
const DEFAULT_MIN_IMPACT: AuditImpact = "minor";
|
|
74
|
-
const AXE_CORE_MODULE = "axe-core";
|
|
75
|
-
const JSDOM_MODULE = "jsdom";
|
|
76
|
-
const HAPPY_DOM_MODULE = "happy-dom";
|
|
72
|
+
const DEFAULT_MAX_FILES = 500;
|
|
73
|
+
const DEFAULT_MIN_IMPACT: AuditImpact = "minor";
|
|
74
|
+
const AXE_CORE_MODULE = "axe-core";
|
|
75
|
+
const JSDOM_MODULE = "jsdom";
|
|
76
|
+
const HAPPY_DOM_MODULE = "happy-dom";
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Zero every entry in an impact-count record. Returned by value so
|
|
@@ -103,10 +103,10 @@ async function resolveAxe(options: RunAuditOptions): Promise<AxeLike | null> {
|
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
|
-
|
|
107
|
-
if (options.axeLoader) return tryLoad(options.axeLoader);
|
|
108
|
-
return tryLoad(() => import(AXE_CORE_MODULE));
|
|
109
|
-
}
|
|
106
|
+
|
|
107
|
+
if (options.axeLoader) return tryLoad(options.axeLoader);
|
|
108
|
+
return tryLoad(() => import(AXE_CORE_MODULE));
|
|
109
|
+
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Resolve a DOM provider. Prefers jsdom; falls back to HappyDOM via
|
|
@@ -128,9 +128,9 @@ async function resolveDomProvider(options: RunAuditOptions): Promise<DomProvider
|
|
|
128
128
|
return null;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// Preferred path — jsdom.
|
|
132
|
-
try {
|
|
133
|
-
const jsdom = await import(JSDOM_MODULE);
|
|
131
|
+
// Preferred path — jsdom.
|
|
132
|
+
try {
|
|
133
|
+
const jsdom = await import(JSDOM_MODULE);
|
|
134
134
|
const JSDOMCtor = (jsdom as { JSDOM?: new (html: string, opts?: unknown) => unknown }).JSDOM;
|
|
135
135
|
if (JSDOMCtor) {
|
|
136
136
|
return {
|
|
@@ -154,9 +154,9 @@ async function resolveDomProvider(options: RunAuditOptions): Promise<DomProvider
|
|
|
154
154
|
// jsdom not installed — fall through to HappyDOM.
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
// Fallback path — HappyDOM.
|
|
158
|
-
try {
|
|
159
|
-
const happy = await import(HAPPY_DOM_MODULE);
|
|
157
|
+
// Fallback path — HappyDOM.
|
|
158
|
+
try {
|
|
159
|
+
const happy = await import(HAPPY_DOM_MODULE);
|
|
160
160
|
const WindowCtor = (happy as { Window?: new (opts?: { url?: string; innerWidth?: number }) => unknown }).Window;
|
|
161
161
|
if (WindowCtor) {
|
|
162
162
|
return {
|
package/src/a11y/types.ts
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @mandujs/core/a11y — audit result types.
|
|
3
|
-
*
|
|
4
|
-
* Phase 18.χ introduces a framework-level accessibility guardrail that
|
|
5
|
-
* runs axe-core against prerendered HTML and aggregates WCAG violations
|
|
6
|
-
* into a structured report. axe-core and jsdom are **optional peer
|
|
7
|
-
* dependencies** — we never install them for the user. When absent, the
|
|
8
|
-
* runner degrades gracefully to an `"axe-missing"` outcome instead of
|
|
9
|
-
* throwing or bundling ~1 MB of rules into every consumer.
|
|
10
|
-
*
|
|
11
|
-
* Severity mapping matches axe-core's `impact` scale verbatim so CI
|
|
12
|
-
* gates using `--audit-fail-on=<impact>` speak the same vocabulary as
|
|
13
|
-
* the axe documentation:
|
|
14
|
-
*
|
|
15
|
-
* - `minor` — nice-to-fix cosmetic a11y issue
|
|
16
|
-
* - `moderate` — noticeable UX degradation
|
|
17
|
-
* - `serious` — significant barrier for users with disabilities
|
|
18
|
-
* - `critical` — blocks assistive-tech users outright (default gate)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** axe-core impact scale (lowest → highest). */
|
|
22
|
-
export type AuditImpact = "minor" | "moderate" | "serious" | "critical";
|
|
23
|
-
|
|
24
|
-
/** Canonical ordering used by severity comparisons. */
|
|
25
|
-
export const AUDIT_IMPACT_ORDER: readonly AuditImpact[] = [
|
|
26
|
-
"minor",
|
|
27
|
-
"moderate",
|
|
28
|
-
"serious",
|
|
29
|
-
"critical",
|
|
30
|
-
] as const;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Return `true` when `candidate` is at least as severe as `threshold`.
|
|
34
|
-
* Used by both the runner (severity filter) and the CLI gate
|
|
35
|
-
* (`--audit-fail-on`).
|
|
36
|
-
*/
|
|
37
|
-
export function impactAtLeast(candidate: AuditImpact | null | undefined, threshold: AuditImpact): boolean {
|
|
38
|
-
if (!candidate) return false;
|
|
39
|
-
const ci = AUDIT_IMPACT_ORDER.indexOf(candidate);
|
|
40
|
-
const ti = AUDIT_IMPACT_ORDER.indexOf(threshold);
|
|
41
|
-
return ci >= 0 && ti >= 0 && ci >= ti;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Per-node failure detail. Keeps payloads small — we intentionally
|
|
45
|
-
* discard axe's full `any`/`all`/`none` check trees and keep only the
|
|
46
|
-
* fields humans act on (selector + failure summary). */
|
|
47
|
-
export interface AuditNode {
|
|
48
|
-
/** CSS selector chain axe-core emits (e.g. `html > body > div#root`). */
|
|
49
|
-
target: string;
|
|
50
|
-
/** axe's `failureSummary` — already-localized multi-line description. */
|
|
51
|
-
failureSummary: string;
|
|
52
|
-
/** Raw HTML snippet for the offending node (truncated to 300 chars). */
|
|
53
|
-
html?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** One WCAG violation emitted by axe-core, scoped to a single HTML file. */
|
|
57
|
-
export interface AuditViolation {
|
|
58
|
-
/** Source HTML file (absolute path). */
|
|
59
|
-
file: string;
|
|
60
|
-
/** axe-core rule id, e.g. `color-contrast`, `label`, `image-alt`. */
|
|
61
|
-
rule: string;
|
|
62
|
-
/** Severity — may be `null` when axe fails to classify (rare). */
|
|
63
|
-
impact: AuditImpact | null;
|
|
64
|
-
/** One-line human-readable rule summary (`node.help`). */
|
|
65
|
-
help: string;
|
|
66
|
-
/** URL to axe-core's documentation for this rule. */
|
|
67
|
-
helpUrl?: string;
|
|
68
|
-
/** Offending DOM nodes. Capped at 10 to keep reports consumable. */
|
|
69
|
-
nodes: AuditNode[];
|
|
70
|
-
/** Phase 18.χ hint — short actionable fix recipe when we recognize the rule. */
|
|
71
|
-
fixHint?: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Audit outcome. The three arms are mutually exclusive:
|
|
76
|
-
*
|
|
77
|
-
* - `ok` — runner executed, zero violations ≥ minImpact.
|
|
78
|
-
* - `violations` — runner executed, at least one violation fired.
|
|
79
|
-
* - `axe-missing` — optional dep not installed; runner was a no-op.
|
|
80
|
-
*
|
|
81
|
-
* The `filesScanned` counter is always present so callers can print a
|
|
82
|
-
* meaningful summary ("audited 12 files, 0 violations") regardless of
|
|
83
|
-
* outcome.
|
|
84
|
-
*/
|
|
85
|
-
export interface AuditReport {
|
|
86
|
-
outcome: "ok" | "violations" | "axe-missing";
|
|
87
|
-
/** Number of HTML files actually fed to axe-core (0 when dep missing). */
|
|
88
|
-
filesScanned: number;
|
|
89
|
-
/** Aggregated violations across every file. Empty when `outcome !== "violations"`. */
|
|
90
|
-
violations: AuditViolation[];
|
|
91
|
-
/** Count of violations at each impact level. */
|
|
92
|
-
impactCounts: Record<AuditImpact, number>;
|
|
93
|
-
/** Effective severity filter applied during this run. */
|
|
94
|
-
minImpact: AuditImpact;
|
|
95
|
-
/** Optional human-readable note (e.g. why the runner skipped). */
|
|
96
|
-
note?: string;
|
|
97
|
-
/** Elapsed wallclock ms. Zero when runner was a no-op. */
|
|
98
|
-
durationMs: number;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Options accepted by `runAudit`. */
|
|
102
|
-
export interface RunAuditOptions {
|
|
103
|
-
/**
|
|
104
|
-
* Minimum severity to include in the report. Violations below this
|
|
105
|
-
* threshold are dropped at aggregation time. Default `"minor"`.
|
|
106
|
-
*/
|
|
107
|
-
minImpact?: AuditImpact;
|
|
108
|
-
/**
|
|
109
|
-
* Cap on files to audit. Prevents catastrophic CI runs on projects
|
|
110
|
-
* that accidentally prerender thousands of routes. Default `500`.
|
|
111
|
-
*/
|
|
112
|
-
maxFiles?: number;
|
|
113
|
-
/**
|
|
114
|
-
* Override for axe-core module resolution. When undefined, the runner
|
|
115
|
-
* uses dynamic `import("axe-core")`. Tests inject a fixture instead
|
|
116
|
-
* of shimming the module resolver.
|
|
117
|
-
*/
|
|
118
|
-
axeLoader?: () => Promise<unknown>;
|
|
119
|
-
/**
|
|
120
|
-
* Override for jsdom module resolution. Same contract as `axeLoader`;
|
|
121
|
-
* returning `null` forces the runner to fall back to HappyDOM which is
|
|
122
|
-
* already a transitive test-time dep for Mandu.
|
|
123
|
-
*/
|
|
124
|
-
domLoader?: () => Promise<unknown>;
|
|
125
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @mandujs/core/a11y — audit result types.
|
|
3
|
+
*
|
|
4
|
+
* Phase 18.χ introduces a framework-level accessibility guardrail that
|
|
5
|
+
* runs axe-core against prerendered HTML and aggregates WCAG violations
|
|
6
|
+
* into a structured report. axe-core and jsdom are **optional peer
|
|
7
|
+
* dependencies** — we never install them for the user. When absent, the
|
|
8
|
+
* runner degrades gracefully to an `"axe-missing"` outcome instead of
|
|
9
|
+
* throwing or bundling ~1 MB of rules into every consumer.
|
|
10
|
+
*
|
|
11
|
+
* Severity mapping matches axe-core's `impact` scale verbatim so CI
|
|
12
|
+
* gates using `--audit-fail-on=<impact>` speak the same vocabulary as
|
|
13
|
+
* the axe documentation:
|
|
14
|
+
*
|
|
15
|
+
* - `minor` — nice-to-fix cosmetic a11y issue
|
|
16
|
+
* - `moderate` — noticeable UX degradation
|
|
17
|
+
* - `serious` — significant barrier for users with disabilities
|
|
18
|
+
* - `critical` — blocks assistive-tech users outright (default gate)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** axe-core impact scale (lowest → highest). */
|
|
22
|
+
export type AuditImpact = "minor" | "moderate" | "serious" | "critical";
|
|
23
|
+
|
|
24
|
+
/** Canonical ordering used by severity comparisons. */
|
|
25
|
+
export const AUDIT_IMPACT_ORDER: readonly AuditImpact[] = [
|
|
26
|
+
"minor",
|
|
27
|
+
"moderate",
|
|
28
|
+
"serious",
|
|
29
|
+
"critical",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return `true` when `candidate` is at least as severe as `threshold`.
|
|
34
|
+
* Used by both the runner (severity filter) and the CLI gate
|
|
35
|
+
* (`--audit-fail-on`).
|
|
36
|
+
*/
|
|
37
|
+
export function impactAtLeast(candidate: AuditImpact | null | undefined, threshold: AuditImpact): boolean {
|
|
38
|
+
if (!candidate) return false;
|
|
39
|
+
const ci = AUDIT_IMPACT_ORDER.indexOf(candidate);
|
|
40
|
+
const ti = AUDIT_IMPACT_ORDER.indexOf(threshold);
|
|
41
|
+
return ci >= 0 && ti >= 0 && ci >= ti;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Per-node failure detail. Keeps payloads small — we intentionally
|
|
45
|
+
* discard axe's full `any`/`all`/`none` check trees and keep only the
|
|
46
|
+
* fields humans act on (selector + failure summary). */
|
|
47
|
+
export interface AuditNode {
|
|
48
|
+
/** CSS selector chain axe-core emits (e.g. `html > body > div#root`). */
|
|
49
|
+
target: string;
|
|
50
|
+
/** axe's `failureSummary` — already-localized multi-line description. */
|
|
51
|
+
failureSummary: string;
|
|
52
|
+
/** Raw HTML snippet for the offending node (truncated to 300 chars). */
|
|
53
|
+
html?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One WCAG violation emitted by axe-core, scoped to a single HTML file. */
|
|
57
|
+
export interface AuditViolation {
|
|
58
|
+
/** Source HTML file (absolute path). */
|
|
59
|
+
file: string;
|
|
60
|
+
/** axe-core rule id, e.g. `color-contrast`, `label`, `image-alt`. */
|
|
61
|
+
rule: string;
|
|
62
|
+
/** Severity — may be `null` when axe fails to classify (rare). */
|
|
63
|
+
impact: AuditImpact | null;
|
|
64
|
+
/** One-line human-readable rule summary (`node.help`). */
|
|
65
|
+
help: string;
|
|
66
|
+
/** URL to axe-core's documentation for this rule. */
|
|
67
|
+
helpUrl?: string;
|
|
68
|
+
/** Offending DOM nodes. Capped at 10 to keep reports consumable. */
|
|
69
|
+
nodes: AuditNode[];
|
|
70
|
+
/** Phase 18.χ hint — short actionable fix recipe when we recognize the rule. */
|
|
71
|
+
fixHint?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Audit outcome. The three arms are mutually exclusive:
|
|
76
|
+
*
|
|
77
|
+
* - `ok` — runner executed, zero violations ≥ minImpact.
|
|
78
|
+
* - `violations` — runner executed, at least one violation fired.
|
|
79
|
+
* - `axe-missing` — optional dep not installed; runner was a no-op.
|
|
80
|
+
*
|
|
81
|
+
* The `filesScanned` counter is always present so callers can print a
|
|
82
|
+
* meaningful summary ("audited 12 files, 0 violations") regardless of
|
|
83
|
+
* outcome.
|
|
84
|
+
*/
|
|
85
|
+
export interface AuditReport {
|
|
86
|
+
outcome: "ok" | "violations" | "axe-missing";
|
|
87
|
+
/** Number of HTML files actually fed to axe-core (0 when dep missing). */
|
|
88
|
+
filesScanned: number;
|
|
89
|
+
/** Aggregated violations across every file. Empty when `outcome !== "violations"`. */
|
|
90
|
+
violations: AuditViolation[];
|
|
91
|
+
/** Count of violations at each impact level. */
|
|
92
|
+
impactCounts: Record<AuditImpact, number>;
|
|
93
|
+
/** Effective severity filter applied during this run. */
|
|
94
|
+
minImpact: AuditImpact;
|
|
95
|
+
/** Optional human-readable note (e.g. why the runner skipped). */
|
|
96
|
+
note?: string;
|
|
97
|
+
/** Elapsed wallclock ms. Zero when runner was a no-op. */
|
|
98
|
+
durationMs: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Options accepted by `runAudit`. */
|
|
102
|
+
export interface RunAuditOptions {
|
|
103
|
+
/**
|
|
104
|
+
* Minimum severity to include in the report. Violations below this
|
|
105
|
+
* threshold are dropped at aggregation time. Default `"minor"`.
|
|
106
|
+
*/
|
|
107
|
+
minImpact?: AuditImpact;
|
|
108
|
+
/**
|
|
109
|
+
* Cap on files to audit. Prevents catastrophic CI runs on projects
|
|
110
|
+
* that accidentally prerender thousands of routes. Default `500`.
|
|
111
|
+
*/
|
|
112
|
+
maxFiles?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Override for axe-core module resolution. When undefined, the runner
|
|
115
|
+
* uses dynamic `import("axe-core")`. Tests inject a fixture instead
|
|
116
|
+
* of shimming the module resolver.
|
|
117
|
+
*/
|
|
118
|
+
axeLoader?: () => Promise<unknown>;
|
|
119
|
+
/**
|
|
120
|
+
* Override for jsdom module resolution. Same contract as `axeLoader`;
|
|
121
|
+
* returning `null` forces the runner to fall back to HappyDOM which is
|
|
122
|
+
* already a transitive test-time dep for Mandu.
|
|
123
|
+
*/
|
|
124
|
+
domLoader?: () => Promise<unknown>;
|
|
125
|
+
}
|