Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ jobs:
run: npm ci

- name: Install Chromium
if: ${{ hashFiles('vitest.test.browser.config.ts', 'playwright.config.*', 'tests/verify-hydration.test.ts') != '' }}
run: npx playwright-core install chromium
if: ${{ hashFiles('vitest.test.browser.config.ts', 'playwright.config.*') != '' }}
run: npx playwright install chromium

- name: Format check
run: npm run fmt --if-present -- --check
Expand All @@ -52,8 +52,6 @@ jobs:

- name: Test
run: npm run test:coverage
env:
ASKR_BROWSER_CHANNEL: playwright

- name: Type checks
run: |
Expand Down
39 changes: 15 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ unless you opt out with `--no-skills`.
- `askr doctor [--cwd <dir>] [--workspace <pattern>]... [--json]`
- `askr repair [--cwd <dir>] [--workspace <pattern>]... [--json]`
- `askr check [--cwd <dir>] [--workspace <pattern>]... [--json]`
- `askr database validate|generate [--database <name>] [--json]`
- `askr database migration create|status|plan|apply|resolve ...`
- `askr skills list`
- `askr skills install [--cwd <dir>] [--force]`
- `askr skills sync [--cwd <dir>]`
- `askr ssg --config <path> --output <dir> [--incremental]`
- `askr verify-hydration [--output ./dist] [--route <path>]...`
- `askr openapi [--entry ./src/api.ts] [--output ./openapi.yml] [--check]`
- `askr outdated [packages...] [--workspace <glob>] [--tag <tag>] [--json]`
- `askr update [packages...] [--workspace <glob>] [--tag <tag>] [--json]`
Expand All @@ -74,10 +75,8 @@ askr analyze --json --check

All diagnostics include a stable rule ID and workspace-relative source
location. The analyzer distinguishes canonical Askr imports from unrelated
same-named functions and recommends `<For>` only when a `.map()` result is
rendered directly as JSX children, so ordinary data transforms remain valid.
It reports eager `<For>`/`<Show>`/`<Case>` controls behind changing ternaries
while accepting conditionally mounted components with their own render scope.
same-named functions and only recommends `<For>` for state-backed reactive JSX
collections, so static transforms with `.map()` remain valid.

By default it transactionally applies only mechanical route-parameter and
plain-JSON JSX configuration fixes. `--check` is read-only for CI. Semantic
Expand All @@ -99,7 +98,17 @@ askr check
and static-analysis inspection. `repair` transactionally applies only safe
mechanical fixes and reports remaining semantic work. `check` requires clean
analysis before running the project's declared lint, typecheck, test, and build
scripts in order. Generated projects expose that final gate as `npm run check`.
scripts in order. When `database/index.ts` exists, `check` first delegates
database validation to the project's installed `@askrjs/orm` tooling.
Generated projects expose that final gate as `npm run check`.

## Database tooling

`askr database ...` is a lazy front end. The CLI resolves
`@askrjs/orm/tooling` from the target project and delegates the complete
command, so schema generation and migration semantics always match the
project's installed ORM version. See the
[database command reference](./docs/database.md).

See the [project guardrails reference](./docs/guardrails.md) for command and JSON
contracts.
Expand Down Expand Up @@ -141,24 +150,6 @@ owned files so stale chunks and previous output paths are removed. Full and
incremental builds publish through a sibling staging directory, so route output,
metadata, assets, and sitemap artifacts change together or not at all.

## Hydration verification

`askr verify-hydration` builds SSG output, serves the generated route set, and
loads every successful metadata route in a real headless browser both with and
without JavaScript. It compares normalized tag-and-child topology under `#app`
after hydration, so text, classes, and mutable ARIA state do not create noise
while nodes migrating into the wrong sibling container fail with an actionable
static-versus-hydrated path diff.

```bash
askr verify-hydration
askr verify-hydration --route / --route /docs
askr verify-hydration --no-build --output ./dist
```

See the [hydration verification reference](./docs/verify-hydration.md) for
browser installation, timeout, route, and root-selector options.

## OpenAPI artifacts

`askr openapi` loads a TypeScript module whose default export exposes
Expand Down
31 changes: 5 additions & 26 deletions benchmarks/analyze-budget-reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ const ANALYZE_BUDGETS_MS: Readonly<Record<string, number>> = {
"50-file workspace": 100,
"250-file workspace": 250,
"5-workspace monorepo with 250 files": 300,
"diagnostic-heavy registered-rule sweep": 5,
"deep cyclic wrapper graph through barrels": 6,
"deep cyclic wrapper full analysis": 100,
"lifecycle cleanup matching": 5,
"realistic shipped startkit workspace": 100,
};

interface BenchmarkMeta {
Expand All @@ -21,35 +16,19 @@ interface BenchmarkMeta {
export class AnalyzeBudgetReporter implements Reporter {
onTestRunEnd(testModules: readonly TestModule[]): void {
const failures: string[] = [];
const measurements: string[] = [];
const seen = new Set<string>();
for (const testModule of testModules) {
for (const test of testModule.children.allTests()) {
const meta = test.meta() as BenchmarkMeta;
if (!meta.benchmark) continue;
seen.add(test.name);
const budget = ANALYZE_BUDGETS_MS[test.name];
if (budget === undefined) {
failures.push(`${test.name}: missing performance budget`);
continue;
}
if (budget === undefined) continue;
const meta = test.meta() as BenchmarkMeta;
const mean = meta.result?.mean;
if (mean === undefined || !Number.isFinite(mean) || mean <= 0) {
if (!meta.benchmark || mean === undefined) {
failures.push(`${test.name}: missing benchmark result`);
} else {
measurements.push(`${test.name}: ${mean.toFixed(2)} ms / ${budget} ms`);
if (mean > budget) {
failures.push(`${test.name}: mean ${mean.toFixed(1)} ms exceeds ${budget} ms`);
}
} else if (mean > budget) {
failures.push(`${test.name}: mean ${mean.toFixed(1)} ms exceeds ${budget} ms`);
}
}
}
for (const name of Object.keys(ANALYZE_BUDGETS_MS)) {
if (!seen.has(name)) failures.push(`${name}: stale performance budget`);
}
if (measurements.length > 0) {
console.log(`Analyzer performance budgets:\n${measurements.join("\n")}`);
}
if (failures.length > 0) {
throw new Error(`Analyzer performance budget failed:\n${failures.join("\n")}`);
}
Expand Down
53 changes: 0 additions & 53 deletions benchmarks/analyze-workloads.ts

This file was deleted.

Loading