@dfosco/storyboard-core 3.2.0 → 3.3.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.
Files changed (61) hide show
  1. package/dist/storyboard-ui.css +1 -0
  2. package/dist/storyboard-ui.js +26304 -0
  3. package/dist/storyboard-ui.js.map +1 -0
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +24 -18
  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 +23 -12
  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-consumer.js +28 -0
  44. package/src/devtools.js +4 -1
  45. package/src/index.js +8 -3
  46. package/src/inspector/highlighter.js +28 -17
  47. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  48. package/src/lib/components/ui/trigger-button/trigger-button.svelte +8 -4
  49. package/src/mountStoryboardCore.js +223 -0
  50. package/src/scaffold.js +100 -0
  51. package/src/stores/themeStore.ts +29 -8
  52. package/src/styles/tailwind.css +16 -0
  53. package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
  54. package/src/ui-entry.js +30 -0
  55. package/src/vite/server-plugin.js +8 -24
  56. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
  57. package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
  58. package/src/workshop/features/createFlow/index.js +0 -1
  59. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
  60. package/src/workshop/features/createPrototype/index.js +0 -1
  61. /package/{core-ui.config.json → toolbar.config.json} +0 -0
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: lifecycle-hooks
3
+ description: beforeEach, afterEach, beforeAll, afterAll, and around hooks
4
+ ---
5
+
6
+ # Lifecycle Hooks
7
+
8
+ ## Basic Hooks
9
+
10
+ ```ts
11
+ import { afterAll, afterEach, beforeAll, beforeEach, test } from 'vitest'
12
+
13
+ beforeAll(async () => {
14
+ // Runs once before all tests in file/suite
15
+ await setupDatabase()
16
+ })
17
+
18
+ afterAll(async () => {
19
+ // Runs once after all tests in file/suite
20
+ await teardownDatabase()
21
+ })
22
+
23
+ beforeEach(async () => {
24
+ // Runs before each test
25
+ await clearTestData()
26
+ })
27
+
28
+ afterEach(async () => {
29
+ // Runs after each test
30
+ await cleanupMocks()
31
+ })
32
+ ```
33
+
34
+ ## Cleanup Return Pattern
35
+
36
+ Return cleanup function from `before*` hooks:
37
+
38
+ ```ts
39
+ beforeAll(async () => {
40
+ const server = await startServer()
41
+
42
+ // Returned function runs as afterAll
43
+ return async () => {
44
+ await server.close()
45
+ }
46
+ })
47
+
48
+ beforeEach(async () => {
49
+ const connection = await connect()
50
+
51
+ // Runs as afterEach
52
+ return () => connection.close()
53
+ })
54
+ ```
55
+
56
+ ## Scoped Hooks
57
+
58
+ Hooks apply to current suite and nested suites:
59
+
60
+ ```ts
61
+ describe('outer', () => {
62
+ beforeEach(() => console.log('outer before'))
63
+
64
+ test('test 1', () => {}) // outer before → test
65
+
66
+ describe('inner', () => {
67
+ beforeEach(() => console.log('inner before'))
68
+
69
+ test('test 2', () => {}) // outer before → inner before → test
70
+ })
71
+ })
72
+ ```
73
+
74
+ ## Hook Timeout
75
+
76
+ ```ts
77
+ beforeAll(async () => {
78
+ await slowSetup()
79
+ }, 30_000) // 30 second timeout
80
+ ```
81
+
82
+ ## Around Hooks
83
+
84
+ Wrap tests with setup/teardown context:
85
+
86
+ ```ts
87
+ import { aroundEach, test } from 'vitest'
88
+
89
+ // Wrap each test in database transaction
90
+ aroundEach(async (runTest) => {
91
+ await db.beginTransaction()
92
+ await runTest() // Must be called!
93
+ await db.rollback()
94
+ })
95
+
96
+ test('insert user', async () => {
97
+ await db.insert({ name: 'Alice' })
98
+ // Automatically rolled back after test
99
+ })
100
+ ```
101
+
102
+ ### aroundAll
103
+
104
+ Wrap entire suite:
105
+
106
+ ```ts
107
+ import { aroundAll, test } from 'vitest'
108
+
109
+ aroundAll(async (runSuite) => {
110
+ console.log('before all tests')
111
+ await runSuite() // Must be called!
112
+ console.log('after all tests')
113
+ })
114
+ ```
115
+
116
+ ### Multiple Around Hooks
117
+
118
+ Nested like onion layers:
119
+
120
+ ```ts
121
+ aroundEach(async (runTest) => {
122
+ console.log('outer before')
123
+ await runTest()
124
+ console.log('outer after')
125
+ })
126
+
127
+ aroundEach(async (runTest) => {
128
+ console.log('inner before')
129
+ await runTest()
130
+ console.log('inner after')
131
+ })
132
+
133
+ // Order: outer before → inner before → test → inner after → outer after
134
+ ```
135
+
136
+ ## Test Hooks
137
+
138
+ Inside test body:
139
+
140
+ ```ts
141
+ import { onTestFailed, onTestFinished, test } from 'vitest'
142
+
143
+ test('with cleanup', () => {
144
+ const db = connect()
145
+
146
+ // Runs after test finishes (pass or fail)
147
+ onTestFinished(() => db.close())
148
+
149
+ // Only runs if test fails
150
+ onTestFailed(({ task }) => {
151
+ console.log('Failed:', task.result?.errors)
152
+ })
153
+
154
+ db.query('SELECT * FROM users')
155
+ })
156
+ ```
157
+
158
+ ### Reusable Cleanup Pattern
159
+
160
+ ```ts
161
+ function useTestDb() {
162
+ const db = connect()
163
+ onTestFinished(() => db.close())
164
+ return db
165
+ }
166
+
167
+ test('query users', () => {
168
+ const db = useTestDb()
169
+ expect(db.query('SELECT * FROM users')).toBeDefined()
170
+ })
171
+
172
+ test('query orders', () => {
173
+ const db = useTestDb() // Fresh connection, auto-closed
174
+ expect(db.query('SELECT * FROM orders')).toBeDefined()
175
+ })
176
+ ```
177
+
178
+ ## Concurrent Test Hooks
179
+
180
+ For concurrent tests, use context's hooks:
181
+
182
+ ```ts
183
+ test.concurrent('concurrent', ({ onTestFinished }) => {
184
+ const resource = allocate()
185
+ onTestFinished(() => resource.release())
186
+ })
187
+ ```
188
+
189
+ ## Extended Test Hooks
190
+
191
+ With `test.extend`, hooks are type-aware:
192
+
193
+ ```ts
194
+ const test = base.extend<{ db: Database }>({
195
+ db: async ({}, use) => {
196
+ const db = await createDb()
197
+ await use(db)
198
+ await db.close()
199
+ },
200
+ })
201
+
202
+ // These hooks know about `db` fixture
203
+ test.beforeEach(({ db }) => {
204
+ db.seed()
205
+ })
206
+
207
+ test.afterEach(({ db }) => {
208
+ db.clear()
209
+ })
210
+ ```
211
+
212
+ ## Hook Execution Order
213
+
214
+ Default order (stack):
215
+ 1. `beforeAll` (in order)
216
+ 2. `beforeEach` (in order)
217
+ 3. Test
218
+ 4. `afterEach` (reverse order)
219
+ 5. `afterAll` (reverse order)
220
+
221
+ Configure with `sequence.hooks`:
222
+
223
+ ```ts
224
+ defineConfig({
225
+ test: {
226
+ sequence: {
227
+ hooks: 'list', // 'stack' (default), 'list', 'parallel'
228
+ },
229
+ },
230
+ })
231
+ ```
232
+
233
+ ## Key Points
234
+
235
+ - Hooks are not called during type checking
236
+ - Return cleanup function from `before*` to avoid `after*` duplication
237
+ - `aroundEach`/`aroundAll` must call `runTest()`/`runSuite()`
238
+ - `onTestFinished` always runs, even if test fails
239
+ - Use context hooks for concurrent tests
240
+
241
+ <!--
242
+ Source references:
243
+ - https://vitest.dev/api/hooks.html
244
+ -->
@@ -0,0 +1,233 @@
1
+ ---
2
+ name: test-api
3
+ description: test/it function for defining tests with modifiers
4
+ ---
5
+
6
+ # Test API
7
+
8
+ ## Basic Test
9
+
10
+ ```ts
11
+ import { expect, test } from 'vitest'
12
+
13
+ test('adds numbers', () => {
14
+ expect(1 + 1).toBe(2)
15
+ })
16
+
17
+ // Alias: it
18
+ import { it } from 'vitest'
19
+
20
+ it('works the same', () => {
21
+ expect(true).toBe(true)
22
+ })
23
+ ```
24
+
25
+ ## Async Tests
26
+
27
+ ```ts
28
+ test('async test', async () => {
29
+ const result = await fetchData()
30
+ expect(result).toBeDefined()
31
+ })
32
+
33
+ // Promises are automatically awaited
34
+ test('returns promise', () => {
35
+ return fetchData().then(result => {
36
+ expect(result).toBeDefined()
37
+ })
38
+ })
39
+ ```
40
+
41
+ ## Test Options
42
+
43
+ ```ts
44
+ // Timeout (default: 5000ms)
45
+ test('slow test', async () => {
46
+ // ...
47
+ }, 10_000)
48
+
49
+ // Or with options object
50
+ test('with options', { timeout: 10_000, retry: 2 }, async () => {
51
+ // ...
52
+ })
53
+ ```
54
+
55
+ ## Test Modifiers
56
+
57
+ ### Skip Tests
58
+
59
+ ```ts
60
+ test.skip('skipped test', () => {
61
+ // Won't run
62
+ })
63
+
64
+ // Conditional skip
65
+ test.skipIf(process.env.CI)('not in CI', () => {})
66
+ test.runIf(process.env.CI)('only in CI', () => {})
67
+
68
+ // Dynamic skip via context
69
+ test('dynamic skip', ({ skip }) => {
70
+ skip(someCondition, 'reason')
71
+ // ...
72
+ })
73
+ ```
74
+
75
+ ### Focus Tests
76
+
77
+ ```ts
78
+ test.only('only this runs', () => {
79
+ // Other tests in file are skipped
80
+ })
81
+ ```
82
+
83
+ ### Todo Tests
84
+
85
+ ```ts
86
+ test.todo('implement later')
87
+
88
+ test.todo('with body', () => {
89
+ // Not run, shows in report
90
+ })
91
+ ```
92
+
93
+ ### Failing Tests
94
+
95
+ ```ts
96
+ test.fails('expected to fail', () => {
97
+ expect(1).toBe(2) // Test passes because assertion fails
98
+ })
99
+ ```
100
+
101
+ ### Concurrent Tests
102
+
103
+ ```ts
104
+ // Run tests in parallel
105
+ test.concurrent('test 1', async ({ expect }) => {
106
+ // Use context.expect for concurrent tests
107
+ expect(await fetch1()).toBe('result')
108
+ })
109
+
110
+ test.concurrent('test 2', async ({ expect }) => {
111
+ expect(await fetch2()).toBe('result')
112
+ })
113
+ ```
114
+
115
+ ### Sequential Tests
116
+
117
+ ```ts
118
+ // Force sequential in concurrent context
119
+ test.sequential('must run alone', async () => {})
120
+ ```
121
+
122
+ ## Parameterized Tests
123
+
124
+ ### test.each
125
+
126
+ ```ts
127
+ test.each([
128
+ [1, 1, 2],
129
+ [1, 2, 3],
130
+ [2, 1, 3],
131
+ ])('add(%i, %i) = %i', (a, b, expected) => {
132
+ expect(a + b).toBe(expected)
133
+ })
134
+
135
+ // With objects
136
+ test.each([
137
+ { a: 1, b: 1, expected: 2 },
138
+ { a: 1, b: 2, expected: 3 },
139
+ ])('add($a, $b) = $expected', ({ a, b, expected }) => {
140
+ expect(a + b).toBe(expected)
141
+ })
142
+
143
+ // Template literal
144
+ test.each`
145
+ a | b | expected
146
+ ${1} | ${1} | ${2}
147
+ ${1} | ${2} | ${3}
148
+ `('add($a, $b) = $expected', ({ a, b, expected }) => {
149
+ expect(a + b).toBe(expected)
150
+ })
151
+ ```
152
+
153
+ ### test.for
154
+
155
+ Preferred over `.each` - doesn't spread arrays:
156
+
157
+ ```ts
158
+ test.for([
159
+ [1, 1, 2],
160
+ [1, 2, 3],
161
+ ])('add(%i, %i) = %i', ([a, b, expected], { expect }) => {
162
+ // Second arg is TestContext
163
+ expect(a + b).toBe(expected)
164
+ })
165
+ ```
166
+
167
+ ## Test Context
168
+
169
+ First argument provides context utilities:
170
+
171
+ ```ts
172
+ test('with context', ({ expect, skip, task }) => {
173
+ console.log(task.name) // Test name
174
+ skip(someCondition) // Skip dynamically
175
+ expect(1).toBe(1) // Context-bound expect
176
+ })
177
+ ```
178
+
179
+ ## Custom Test with Fixtures
180
+
181
+ ```ts
182
+ import { test as base } from 'vitest'
183
+
184
+ const test = base.extend({
185
+ db: async ({}, use) => {
186
+ const db = await createDb()
187
+ await use(db)
188
+ await db.close()
189
+ },
190
+ })
191
+
192
+ test('query', async ({ db }) => {
193
+ const users = await db.query('SELECT * FROM users')
194
+ expect(users).toBeDefined()
195
+ })
196
+ ```
197
+
198
+ ## Retry Configuration
199
+
200
+ ```ts
201
+ test('flaky test', { retry: 3 }, async () => {
202
+ // Retries up to 3 times on failure
203
+ })
204
+
205
+ // Advanced retry options
206
+ test('with delay', {
207
+ retry: {
208
+ count: 3,
209
+ delay: 1000,
210
+ condition: /timeout/i, // Only retry on timeout errors
211
+ },
212
+ }, async () => {})
213
+ ```
214
+
215
+ ## Tags
216
+
217
+ ```ts
218
+ test('database test', { tags: ['db', 'slow'] }, async () => {})
219
+
220
+ // Run with: vitest --tags db
221
+ ```
222
+
223
+ ## Key Points
224
+
225
+ - Tests with no body are marked as `todo`
226
+ - `test.only` throws in CI unless `allowOnly: true`
227
+ - Use context's `expect` for concurrent tests and snapshots
228
+ - Function name is used as test name if passed as first arg
229
+
230
+ <!--
231
+ Source references:
232
+ - https://vitest.dev/api/test.html
233
+ -->
@@ -0,0 +1,250 @@
1
+ ---
2
+ name: concurrency-parallelism
3
+ description: Concurrent tests, parallel execution, and sharding
4
+ ---
5
+
6
+ # Concurrency & Parallelism
7
+
8
+ ## File Parallelism
9
+
10
+ By default, Vitest runs test files in parallel across workers:
11
+
12
+ ```ts
13
+ defineConfig({
14
+ test: {
15
+ // Run files in parallel (default: true)
16
+ fileParallelism: true,
17
+
18
+ // Number of worker threads
19
+ maxWorkers: 4,
20
+ minWorkers: 1,
21
+
22
+ // Pool type: 'threads', 'forks', 'vmThreads'
23
+ pool: 'threads',
24
+ },
25
+ })
26
+ ```
27
+
28
+ ## Concurrent Tests
29
+
30
+ Run tests within a file in parallel:
31
+
32
+ ```ts
33
+ // Individual concurrent tests
34
+ test.concurrent('test 1', async ({ expect }) => {
35
+ expect(await fetch1()).toBe('result')
36
+ })
37
+
38
+ test.concurrent('test 2', async ({ expect }) => {
39
+ expect(await fetch2()).toBe('result')
40
+ })
41
+
42
+ // All tests in suite concurrent
43
+ describe.concurrent('parallel suite', () => {
44
+ test('test 1', async ({ expect }) => {})
45
+ test('test 2', async ({ expect }) => {})
46
+ })
47
+ ```
48
+
49
+ **Important:** Use `{ expect }` from context for concurrent tests.
50
+
51
+ ## Sequential in Concurrent Context
52
+
53
+ Force sequential execution:
54
+
55
+ ```ts
56
+ describe.concurrent('mostly parallel', () => {
57
+ test('parallel 1', async () => {})
58
+ test('parallel 2', async () => {})
59
+
60
+ test.sequential('must run alone 1', async () => {})
61
+ test.sequential('must run alone 2', async () => {})
62
+ })
63
+
64
+ // Or entire suite
65
+ describe.sequential('sequential suite', () => {
66
+ test('first', () => {})
67
+ test('second', () => {})
68
+ })
69
+ ```
70
+
71
+ ## Max Concurrency
72
+
73
+ Limit concurrent tests:
74
+
75
+ ```ts
76
+ defineConfig({
77
+ test: {
78
+ maxConcurrency: 5, // Max concurrent tests per file
79
+ },
80
+ })
81
+ ```
82
+
83
+ ## Isolation
84
+
85
+ Each file runs in isolated environment by default:
86
+
87
+ ```ts
88
+ defineConfig({
89
+ test: {
90
+ // Disable isolation for faster runs (less safe)
91
+ isolate: false,
92
+ },
93
+ })
94
+ ```
95
+
96
+ ## Sharding
97
+
98
+ Split tests across machines:
99
+
100
+ ```bash
101
+ # Machine 1
102
+ vitest run --shard=1/3
103
+
104
+ # Machine 2
105
+ vitest run --shard=2/3
106
+
107
+ # Machine 3
108
+ vitest run --shard=3/3
109
+ ```
110
+
111
+ ### CI Example (GitHub Actions)
112
+
113
+ ```yaml
114
+ jobs:
115
+ test:
116
+ strategy:
117
+ matrix:
118
+ shard: [1, 2, 3]
119
+ steps:
120
+ - run: vitest run --shard=${{ matrix.shard }}/3 --reporter=blob
121
+
122
+ merge:
123
+ needs: test
124
+ steps:
125
+ - run: vitest --merge-reports --reporter=junit
126
+ ```
127
+
128
+ ### Merge Reports
129
+
130
+ ```bash
131
+ # Each shard outputs blob
132
+ vitest run --shard=1/3 --reporter=blob --coverage
133
+ vitest run --shard=2/3 --reporter=blob --coverage
134
+
135
+ # Merge all blobs
136
+ vitest --merge-reports --reporter=json --coverage
137
+ ```
138
+
139
+ ## Test Sequence
140
+
141
+ Control test order:
142
+
143
+ ```ts
144
+ defineConfig({
145
+ test: {
146
+ sequence: {
147
+ // Run tests in random order
148
+ shuffle: true,
149
+
150
+ // Seed for reproducible shuffle
151
+ seed: 12345,
152
+
153
+ // Hook execution order
154
+ hooks: 'stack', // 'stack', 'list', 'parallel'
155
+
156
+ // All tests concurrent by default
157
+ concurrent: true,
158
+ },
159
+ },
160
+ })
161
+ ```
162
+
163
+ ## Shuffle Tests
164
+
165
+ Randomize to catch hidden dependencies:
166
+
167
+ ```ts
168
+ // Via CLI
169
+ vitest --sequence.shuffle
170
+
171
+ // Per suite
172
+ describe.shuffle('random order', () => {
173
+ test('test 1', () => {})
174
+ test('test 2', () => {})
175
+ test('test 3', () => {})
176
+ })
177
+ ```
178
+
179
+ ## Pool Options
180
+
181
+ ### Threads (Default)
182
+
183
+ ```ts
184
+ defineConfig({
185
+ test: {
186
+ pool: 'threads',
187
+ poolOptions: {
188
+ threads: {
189
+ maxThreads: 8,
190
+ minThreads: 2,
191
+ isolate: true,
192
+ },
193
+ },
194
+ },
195
+ })
196
+ ```
197
+
198
+ ### Forks
199
+
200
+ Better isolation, slower:
201
+
202
+ ```ts
203
+ defineConfig({
204
+ test: {
205
+ pool: 'forks',
206
+ poolOptions: {
207
+ forks: {
208
+ maxForks: 4,
209
+ isolate: true,
210
+ },
211
+ },
212
+ },
213
+ })
214
+ ```
215
+
216
+ ### VM Threads
217
+
218
+ Full VM isolation per file:
219
+
220
+ ```ts
221
+ defineConfig({
222
+ test: {
223
+ pool: 'vmThreads',
224
+ },
225
+ })
226
+ ```
227
+
228
+ ## Bail on Failure
229
+
230
+ Stop after first failure:
231
+
232
+ ```bash
233
+ vitest --bail 1 # Stop after 1 failure
234
+ vitest --bail # Stop on first failure (same as --bail 1)
235
+ ```
236
+
237
+ ## Key Points
238
+
239
+ - Files run in parallel by default
240
+ - Use `.concurrent` for parallel tests within file
241
+ - Always use context's `expect` in concurrent tests
242
+ - Sharding splits tests across CI machines
243
+ - Use `--merge-reports` to combine sharded results
244
+ - Shuffle tests to find hidden dependencies
245
+
246
+ <!--
247
+ Source references:
248
+ - https://vitest.dev/guide/features.html#running-tests-concurrently
249
+ - https://vitest.dev/guide/improving-performance.html
250
+ -->