@dfosco/storyboard-core 3.1.2 → 3.3.0

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.
Files changed (59) hide show
  1. package/dist/storyboard-ui.css +1 -0
  2. package/dist/storyboard-ui.js +26298 -0
  3. package/dist/storyboard-ui.js.map +1 -0
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +24 -19
  6. package/scaffold/manifest.json +35 -0
  7. package/scaffold/scripts/link.sh +26 -0
  8. package/scaffold/scripts/unlink.sh +10 -0
  9. package/scaffold/skills/create/SKILL.md +501 -0
  10. package/scaffold/skills/storyboard/SKILL.md +360 -0
  11. package/scaffold/skills/update-storyboard/SKILL.md +16 -0
  12. package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
  13. package/scaffold/skills/vitest/GENERATION.md +5 -0
  14. package/scaffold/skills/vitest/SKILL.md +52 -0
  15. package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
  16. package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
  17. package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
  18. package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
  19. package/scaffold/skills/vitest/references/core-cli.md +166 -0
  20. package/scaffold/skills/vitest/references/core-config.md +174 -0
  21. package/scaffold/skills/vitest/references/core-describe.md +193 -0
  22. package/scaffold/skills/vitest/references/core-expect.md +219 -0
  23. package/scaffold/skills/vitest/references/core-hooks.md +244 -0
  24. package/scaffold/skills/vitest/references/core-test-api.md +233 -0
  25. package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
  26. package/scaffold/skills/vitest/references/features-context.md +238 -0
  27. package/scaffold/skills/vitest/references/features-coverage.md +207 -0
  28. package/scaffold/skills/vitest/references/features-filtering.md +211 -0
  29. package/scaffold/skills/vitest/references/features-mocking.md +265 -0
  30. package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
  31. package/scaffold/skills/worktree/SKILL.md +51 -0
  32. package/scaffold/storyboard.config.json +26 -0
  33. package/scaffold/svelte.config.js +1 -0
  34. package/scaffold/toolbar.config.json +4 -0
  35. package/src/ActionMenuButton.svelte +1 -1
  36. package/src/CanvasCreateMenu.svelte +1 -1
  37. package/src/CoreUIBar.svelte +20 -9
  38. package/src/CreateMenuButton.svelte +1 -1
  39. package/src/InspectorPanel.svelte +144 -49
  40. package/src/SidePanel.svelte +10 -10
  41. package/src/commandActions.js +1 -1
  42. package/src/comments/index.js +0 -3
  43. package/src/devtools.js +4 -1
  44. package/src/index.js +5 -2
  45. package/src/inspector/highlighter.js +3 -4
  46. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  47. package/src/mountStoryboardCore.js +223 -0
  48. package/src/scaffold.js +100 -0
  49. package/src/stores/themeStore.ts +29 -8
  50. package/src/styles/tailwind.css +16 -0
  51. package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
  52. package/src/ui-entry.js +30 -0
  53. package/src/vite/server-plugin.js +8 -24
  54. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
  55. package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
  56. package/src/workshop/features/createFlow/index.js +0 -1
  57. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
  58. package/src/workshop/features/createPrototype/index.js +0 -1
  59. /package/{core-ui.config.json → toolbar.config.json} +0 -0
@@ -0,0 +1,360 @@
1
+ ---
2
+ name: storyboard
3
+ description: Storyboard data structuring and management system. Use when creating flow data, setting up storyboard data, creating data objects, or structuring data for a prototype page.
4
+ metadata:
5
+ author: Daniel Fosco
6
+ version: "2026.3.09"
7
+ ---
8
+
9
+ # Storyboard
10
+
11
+ > Triggered by: when building a new page or route in storyboard, "create flow data", "set up storyboard data", "create data objects", "create new page", "create new route", when structuring data for a prototype page
12
+
13
+ ## What This Does
14
+
15
+ Guides the creation of data objects, flow files, and record collections for pages being built. Determines what data should be externalized into the Storyboard data system vs. hardcoded in the component.
16
+
17
+ ## Data File Types
18
+
19
+ Storyboard uses **suffix-based naming** for data files. Files can live anywhere in the repo — a Vite plugin discovers them automatically at dev/build time.
20
+
21
+ | Suffix | Purpose | Example |
22
+ |--------|---------|---------|
23
+ | `.flow.json` | Page data context | `default.flow.json` |
24
+ | `.object.json` | Reusable data fragment | `jane-doe.object.json` |
25
+ | `.record.json` | Parameterized collection (array with `id` per entry) | `posts.record.json` |
26
+
27
+ **Rules:**
28
+ - Every name+suffix must be unique across the entire repo (build fails on duplicates)
29
+ - Files can be organized into any subdirectory structure
30
+ - JSONC (comments) is supported — use `.jsonc` extension if preferred
31
+ - `$ref` and `$global` use **name-based** references (not paths): `{ "$ref": "jane-doe" }` finds `jane-doe.object.json` anywhere
32
+
33
+ ## When This Applies
34
+
35
+ - After the primer-builder skill has identified the page structure and components (Steps 1–3)
36
+ - Replaces the primer-builder's Step 4 (Plan Data) with a more structured approach
37
+ - When refactoring an existing page to use flow data
38
+ - When creating dynamic route pages with records
39
+
40
+ ## Navigation Anti-Pattern
41
+
42
+ **DO NOT use React Router's `<Link>` component for internal navigation.** Use plain `<a href="...">` tags instead.
43
+
44
+ The storyboard hash preserver intercepts `<a>` clicks to preserve URL hash params and handle client-side routing. React Router `<Link>` bypasses this by calling `navigate()` directly, which creates a duplicate browser history entry (one from `<Link>`, one from the hash preserver's click handler). This causes a "double back" bug where the user must press back twice to actually navigate back.
45
+
46
+ ```jsx
47
+ // ❌ BAD — creates double history entries
48
+ <Link to="/Overview">Overview</Link>
49
+
50
+ // ✅ GOOD — intercepted by hash preserver for client-side navigation
51
+ <a href="/Overview">Overview</a>
52
+ ```
53
+
54
+ ## The Core Rule: What Goes in Data vs. What's Hardcoded
55
+
56
+ ### Externalize as data objects
57
+
58
+ **Content and business model data** — anything that represents "what" the page displays:
59
+
60
+ - User profiles (name, avatar, bio, role)
61
+ - Repository / project metadata (name, description, language, stars, forks)
62
+ - Issue / PR items (title, state, author, labels, timestamps)
63
+ - Organization info (name, avatar, member count)
64
+ - Lists of entities (repos, issues, teams, packages, people)
65
+
66
+ **Navigation** — always externalize because it has repeated elements with many labels that benefit from easy bulk adjustment:
67
+
68
+ - Top navigation tabs (label, icon, counter, url, current state)
69
+ - Sidebar filter lists
70
+ - Settings section navigation
71
+ - Breadcrumb paths
72
+
73
+ **Important**: This will more usually be slotted in existing Template components from the repository, not generated code.
74
+
75
+ ### Hardcode in the component
76
+
77
+ **UI chrome and microcopy** — anything that describes "how" the page works:
78
+
79
+ - Button labels: `"New repository"`, `"Save changes"`, `"Cancel"`
80
+ - Placeholder text: `"Find a repository..."`
81
+ - Section headings that are structural: `"Pinned repositories"`, `"Activity"`
82
+ - Empty state messages: `"No results found"`
83
+ - Filter dropdown labels: `"Type"`, `"Language"`, `"Sort"`
84
+ - Dropdown option labels: `"All"`, `"Public"`, `"Private"`
85
+ - Static instructional text
86
+
87
+ ### Gray area — use judgment
88
+
89
+ - **Counters on tabs** — externalize if they represent real data counts; hardcode if decorative
90
+ - **Metadata labels** (e.g., "Updated 3 days ago") — externalize the date, hardcode the label format
91
+ - **Card/list item structure** — externalize the items array, hardcode the card template
92
+
93
+ ## Workflow
94
+
95
+ ### Step 1: Inventory the data needs
96
+
97
+ From the page description (primer-builder Step 1), list every piece of dynamic content:
98
+
99
+ ```
100
+ Navigation:
101
+ - topnav: 9 tabs with icons, labels, counters, active state
102
+ - sidenav: 10 filter options with active state
103
+
104
+ Content:
105
+ - org name, avatar
106
+ - 6 repository items, each with: name, description, language, stars, updated date
107
+ - filter query string
108
+ ```
109
+
110
+ ### Step 2: Design the data objects
111
+
112
+ Create one object file per logical entity:
113
+
114
+ | Data type | File name pattern | Example |
115
+ |-----------|------------------|---------|
116
+ | User/person | `{name}.object.json` | `jane-doe.object.json` |
117
+ | Navigation | `{context}-navigation.object.json` | `org-navigation.object.json` |
118
+ | Entity list | `{entity-plural}.object.json` | `repositories.object.json` |
119
+ | Org/team | `{org-name}.object.json` | `primer-org.object.json` |
120
+
121
+ **Object structure rules:**
122
+ - **The object file's top-level structure IS the value that `$ref` resolves to.** If the flow has `"repositories": { "$ref": "repositories" }`, the object file should be a bare array `[...]`, not `{ "repositories": [...] }` — otherwise the data double-nests as `repositories.repositories`.
123
+ - Same rule for single-entity objects: if the flow has `"user": { "$ref": "jane-doe" }`, the object file should contain the user fields directly at root (`{ "name": "Jane", ... }`), not wrapped in `{ "user": { "name": "Jane", ... } }`.
124
+ - Keep objects flat where possible — avoid deep nesting
125
+ - Use arrays for lists of items
126
+ - Include all fields that the UI needs — don't make the component compute derived data
127
+ - Use realistic placeholder data (real GitHub avatar URLs, plausible repo names, etc.)
128
+
129
+ ### Step 3: Compose the flow
130
+
131
+ Create a flow file that composes the objects:
132
+
133
+ ```json
134
+ // default.flow.json
135
+ {
136
+ "$global": ["org-navigation"],
137
+ "user": { "$ref": "jane-doe" },
138
+ "org": {
139
+ "name": "my-org",
140
+ "avatar": "https://avatars.githubusercontent.com/u/9919?v=4"
141
+ },
142
+ "repositories": { "$ref": "repositories" }
143
+ }
144
+ ```
145
+
146
+ **flow composition rules:**
147
+ - Use `$global` for navigation — it merges at the root level, making nav data available at the top
148
+ - Use `$ref` for entity objects — keeps them reusable across flows
149
+ - `$ref` and `$global` use **names**, not paths: `"jane-doe"` not `"../objects/jane-doe"`
150
+ - Inline small, flow-specific data directly (org name, settings, filter state)
151
+ - Name the flow after the page/flow: `org-repos.flow.json`, `issue-detail.flow.json`
152
+ - Rule of thumb: a flow can be named after its corresponding page
153
+
154
+ ### Step 4: Wire up the component
155
+
156
+ In the page component, use `useflowData()` with dot-notation paths:
157
+
158
+ ```jsx
159
+ import { useflowData, useflowLoading } from '../storyboard'
160
+
161
+ function ReposPage() {
162
+ const topnav = useflowData('topnav') // from $global navigation
163
+ const sidenav = useflowData('sidenav') // from $global navigation
164
+ const org = useflowData('org')
165
+ const repos = useflowData('repositories')
166
+ const loading = useflowLoading()
167
+
168
+ if (loading) return <Spinner />
169
+
170
+ return (
171
+ <Application title={org.name} topnav={topnav} sidenav={sidenav}>
172
+ {/* Hardcoded UI chrome */}
173
+ <h2>All repositories</h2>
174
+ <Button variant="primary">New repository</Button>
175
+ <TextInput placeholder="Find a repository..." />
176
+
177
+ {/* Data-driven content */}
178
+ {repos.map(repo => (
179
+ <article key={repo.name}>
180
+ <h3><a href={repo.url}>{repo.name}</a></h3>
181
+ <p>{repo.description}</p>
182
+ </article>
183
+ ))}
184
+ </Application>
185
+ )
186
+ }
187
+ ```
188
+
189
+ ## Records & Dynamic Routes
190
+
191
+ Records power **parameterized pages** — the same page template renders different content based on the URL.
192
+
193
+ ### Creating a record
194
+
195
+ A record file is a **collection** — an array of entries, each with a unique `id`:
196
+
197
+ ```json
198
+ // posts.record.json
199
+ [
200
+ {
201
+ "id": "welcome-to-storyboard",
202
+ "title": "Welcome to Storyboard",
203
+ "date": "2026-02-14",
204
+ "author": "Jane Doe",
205
+ "body": "..."
206
+ },
207
+ {
208
+ "id": "another-post",
209
+ "title": "Another Post",
210
+ "date": "2026-02-13",
211
+ "author": "Jane Doe",
212
+ "body": "..."
213
+ }
214
+ ]
215
+ ```
216
+
217
+ ### Creating a dynamic route page
218
+
219
+ The filename convention `[field].jsx` determines which record field the route matches against:
220
+
221
+ - `pages/issues/[id].jsx` → `useRecord('issues')` matches `entry.id`
222
+ - `pages/posts/[permalink].jsx` → `useRecord('posts', 'permalink')` matches `entry.permalink`
223
+
224
+ The second argument to `useRecord` defaults to `'id'`, so `useRecord('issues')` is equivalent to `useRecord('issues', 'id')`.
225
+
226
+ In the component, use `useRecord()`:
227
+
228
+ ```jsx
229
+ import { useRecord } from '../../storyboard'
230
+
231
+ function BlogPost() {
232
+ // 'posts' = record file name, 'id' = route param matched against entry.id
233
+ const post = useRecord('posts', 'id')
234
+ // URL /posts/welcome-to-storyboard → entry with id "welcome-to-storyboard"
235
+
236
+ if (!post) return <p>Post not found</p>
237
+ return <h1>{post.title}</h1>
238
+ }
239
+ ```
240
+
241
+ ### Listing all records
242
+
243
+ Use `useRecords()` for index/listing pages:
244
+
245
+ ```jsx
246
+ import { useRecords } from '../../storyboard'
247
+
248
+ function PostsIndex() {
249
+ const posts = useRecords('posts')
250
+ return posts.map(post => (
251
+ <a key={post.id} href={`/posts/${post.id}`}>{post.title}</a>
252
+ ))
253
+ }
254
+ ```
255
+
256
+ ### Records + flows
257
+
258
+ A page can use both a flow (for page-level data like navigation) and a record (for parameterized content). Pass `recordName` and `recordParam` to `StoryboardProvider` to merge record data under the `record` key:
259
+
260
+ ```jsx
261
+ <StoryboardProvider recordName="posts" recordParam="slug">
262
+ {/* useflowData('record.title') works here */}
263
+ </StoryboardProvider>
264
+ ```
265
+
266
+ ### No `useState` in pages or components
267
+
268
+ All state management must happen through storyboard hooks. Storyboard state lives in the URL hash — not in React component state.
269
+
270
+ **Use these hooks instead:**
271
+
272
+ | Hook | Purpose |
273
+ |------|---------|
274
+ | `useflowData(path?)` | Read flow data by dot-notation path |
275
+ | `useOverride(path)` | Read/write hash-param overrides on flow or object data. Works with or without `<StoryboardProvider>`. |
276
+ | `useObject(name, path?)` | Load an object data file directly by name, without a flow. Supports dot-notation path and hash overrides (`object.{name}.{field}`). |
277
+ | `useRecord(name, param?)` | Load a single record entry matched by URL param (defaults to `'id'`) |
278
+ | `useRecords(name)` | Load all entries from a record collection |
279
+ | `useflowLoading()` | Returns true while flow is loading |
280
+
281
+ **Why:** Storyboard is a prototyping framework where all data flows through the URL hash. This makes every state change shareable via URL, inspectable in the address bar, and framework-portable. Using `useState` breaks this contract — the state becomes invisible, unshareable, and tied to React.
282
+
283
+ ## Common Pitfall: Double-Nesting with `$ref`
284
+
285
+ The most frequent data bug is double-nesting. This happens when an object file wraps its data in a key that matches the flow's `$ref` key:
286
+
287
+ ```
288
+ // ❌ WRONG — causes double-nesting (advisory.advisory)
289
+
290
+ // flow: { "advisory": { "$ref": "advisory" } }
291
+ // advisory.object.json:
292
+ { "advisory": { "title": "Bug", "severity": "High" } }
293
+ // Result: flow.advisory = { "advisory": { "title": "Bug", ... } }
294
+
295
+ // ✅ CORRECT — object file is the raw value
296
+
297
+ // flow: { "advisory": { "$ref": "advisory" } }
298
+ // advisory.object.json:
299
+ { "title": "Bug", "severity": "High" }
300
+ // Result: flow.advisory = { "title": "Bug", "severity": "High" }
301
+ ```
302
+
303
+ **Rule of thumb:** The `$ref` key in the flow IS the namespace. The object file provides the value.
304
+
305
+ ## Hash Param Preservation (CRITICAL)
306
+
307
+ URL hash params are the foundation of the override system. They carry user-set and session-set values across navigations. **Never write code that drops them.**
308
+
309
+ ### How it works
310
+
311
+ `installHashPreserver(router)` in `src/index.jsx` patches both `<a>` click interception and `router.navigate()` so that hash params automatically carry forward on every navigation — including programmatic `navigate('/SomePage')` calls.
312
+
313
+ ### Rules
314
+
315
+ 1. **Never manually strip or omit the hash.** The global preserver handles it. Plain `navigate('/Page')` works — the hash carries forward automatically.
316
+ 2. **Never bypass the router.** Using `window.location.href = '/Page'` or `window.location.assign()` will drop the hash. Always use React Router's `navigate()` or `<Link>`.
317
+ 3. **If a page reads overrides, it must use the hooks.** `useflowData(path)` automatically merges hash overrides. `useOverride(path)` gives read/write access.
318
+ 4. **If a page writes overrides, downstream pages get them for free.** The Signup→Dashboard flow works because Signup writes via `useOverride`, navigation carries the hash, and Dashboard reads via `useflowData` — no manual plumbing needed.
319
+ 5. **To intentionally clear overrides**, use `clearValue` from `useOverride` or `removeParam` from `session.js`. Never clear by navigating without the hash.
320
+
321
+ ## Checklist
322
+
323
+ Before finishing data structuring, verify:
324
+
325
+ - [ ] **No double-nesting:** Object files referenced via `$ref` contain raw values, not wrapped in a key
326
+ - [ ] Every navigation array is in a data object (not hardcoded in the component)
327
+ - [ ] Every list of content items is in a data object
328
+ - [ ] User/org profile data is in a data object
329
+ - [ ] Button labels, placeholder text, and section headings are hardcoded
330
+ - [ ] The flow file uses `$global` for navigation and `$ref` for entities
331
+ - [ ] `$ref` and `$global` use **names** (not relative paths)
332
+ - [ ] Data files use the correct suffix: `.flow.json`, `.object.json`, `.record.json`
333
+ - [ ] The component uses `useflowData()` for all externalized data
334
+ - [ ] Shared data objects use `useObject()` when not part of a flow
335
+ - [ ] Dynamic route pages use `useRecord()` for parameterized content
336
+ - [ ] Data objects use realistic placeholder values
337
+ - [ ] The flow name matches the page name or flow
338
+ - [ ] **Hash params are never dropped** — see "Hash Param Preservation" above
339
+
340
+ ## Final Step: Provide the URL
341
+
342
+ After creating the flow and wiring up the component, **always provide the full dev URL** so the user can immediately preview the page.
343
+
344
+ **Page-flow matching:** If the flow file name matches the page file name exactly (e.g. `Repositories.flow.json` for `pages/Repositories.jsx`), the flow loads automatically — no `?flow=` param needed:
345
+
346
+ ```
347
+ http://localhost:1234/Repositories
348
+ ```
349
+
350
+ If the flow name differs from the page name, add the `?flow=` parameter:
351
+
352
+ ```
353
+ http://localhost:1234/Repositories?flow=heron-silver
354
+ ```
355
+
356
+ For dynamic routes, use the record entry's `id` as the URL slug:
357
+
358
+ ```
359
+ http://localhost:1234/posts/welcome-to-storyboard
360
+ ```
@@ -0,0 +1,16 @@
1
+ # Update Storyboard Packages
2
+
3
+ Updates all `@dfosco/storyboard-*` packages together to the same version and runs the scaffold sync.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npm run update # Update to latest stable
9
+ npm run update:beta # Update to latest beta
10
+ npm run update:alpha # Update to latest alpha
11
+ ```
12
+
13
+ ## What it does
14
+
15
+ 1. Updates all `@dfosco/storyboard-*` packages to the specified tag
16
+ 2. Runs `npx storyboard-scaffold` to sync skills and scripts from the latest version
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ # Update all @dfosco/storyboard-* packages and sync scaffold files.
3
+ # Run from storyboard client root: npm run update
4
+ set -euo pipefail
5
+
6
+ TAG="${1:-latest}"
7
+
8
+ PACKAGES=(
9
+ "@dfosco/storyboard-core"
10
+ "@dfosco/storyboard-react"
11
+ "@dfosco/storyboard-react-primer"
12
+ "@dfosco/storyboard-react-reshaped"
13
+ "@dfosco/tiny-canvas"
14
+ )
15
+
16
+ echo "Updating @dfosco/* packages to @${TAG}..."
17
+ for pkg in "${PACKAGES[@]}"; do
18
+ npm install "${pkg}@${TAG}" --save
19
+ done
20
+
21
+ echo ""
22
+ echo "Running scaffold sync..."
23
+ npx storyboard-scaffold
24
+
25
+ echo ""
26
+ echo "✔ All packages updated to @${TAG} and scaffold synced."
@@ -0,0 +1,5 @@
1
+ # Generation Info
2
+
3
+ - **Source:** `sources/vitest`
4
+ - **Git SHA:** `4a7321e10672f00f0bb698823a381c2cc245b8f7`
5
+ - **Generated:** 2026-01-28
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: vitest
3
+ description: Vitest fast unit testing framework powered by Vite with Jest-compatible API. Use when writing tests, mocking, configuring coverage, or working with test filtering and fixtures.
4
+ metadata:
5
+ author: Anthony Fu
6
+ version: "2026.1.28"
7
+ source: Generated from https://github.com/vitest-dev/vitest, scripts located at https://github.com/antfu/skills
8
+ ---
9
+
10
+ Vitest is a next-generation testing framework powered by Vite. It provides a Jest-compatible API with native ESM, TypeScript, and JSX support out of the box. Vitest shares the same config, transformers, resolvers, and plugins with your Vite app.
11
+
12
+ **Key Features:**
13
+ - Vite-native: Uses Vite's transformation pipeline for fast HMR-like test updates
14
+ - Jest-compatible: Drop-in replacement for most Jest test suites
15
+ - Smart watch mode: Only reruns affected tests based on module graph
16
+ - Native ESM, TypeScript, JSX support without configuration
17
+ - Multi-threaded workers for parallel test execution
18
+ - Built-in coverage via V8 or Istanbul
19
+ - Snapshot testing, mocking, and spy utilities
20
+
21
+ > The skill is based on Vitest 3.x, generated at 2026-01-28.
22
+
23
+ ## Core
24
+
25
+ | Topic | Description | Reference |
26
+ |-------|-------------|-----------|
27
+ | Configuration | Vitest and Vite config integration, defineConfig usage | [core-config](references/core-config.md) |
28
+ | CLI | Command line interface, commands and options | [core-cli](references/core-cli.md) |
29
+ | Test API | test/it function, modifiers like skip, only, concurrent | [core-test-api](references/core-test-api.md) |
30
+ | Describe API | describe/suite for grouping tests and nested suites | [core-describe](references/core-describe.md) |
31
+ | Expect API | Assertions with toBe, toEqual, matchers and asymmetric matchers | [core-expect](references/core-expect.md) |
32
+ | Hooks | beforeEach, afterEach, beforeAll, afterAll, aroundEach | [core-hooks](references/core-hooks.md) |
33
+
34
+ ## Features
35
+
36
+ | Topic | Description | Reference |
37
+ |-------|-------------|-----------|
38
+ | Mocking | Mock functions, modules, timers, dates with vi utilities | [features-mocking](references/features-mocking.md) |
39
+ | Snapshots | Snapshot testing with toMatchSnapshot and inline snapshots | [features-snapshots](references/features-snapshots.md) |
40
+ | Coverage | Code coverage with V8 or Istanbul providers | [features-coverage](references/features-coverage.md) |
41
+ | Test Context | Test fixtures, context.expect, test.extend for custom fixtures | [features-context](references/features-context.md) |
42
+ | Concurrency | Concurrent tests, parallel execution, sharding | [features-concurrency](references/features-concurrency.md) |
43
+ | Filtering | Filter tests by name, file patterns, tags | [features-filtering](references/features-filtering.md) |
44
+
45
+ ## Advanced
46
+
47
+ | Topic | Description | Reference |
48
+ |-------|-------------|-----------|
49
+ | Vi Utilities | vi helper: mock, spyOn, fake timers, hoisted, waitFor | [advanced-vi](references/advanced-vi.md) |
50
+ | Environments | Test environments: node, jsdom, happy-dom, custom | [advanced-environments](references/advanced-environments.md) |
51
+ | Type Testing | Type-level testing with expectTypeOf and assertType | [advanced-type-testing](references/advanced-type-testing.md) |
52
+ | Projects | Multi-project workspaces, different configs per project | [advanced-projects](references/advanced-projects.md) |