codeforge-dev 1.4.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 (131) hide show
  1. package/.devcontainer/.env +22 -0
  2. package/.devcontainer/CHANGELOG.md +197 -0
  3. package/.devcontainer/CLAUDE.md +117 -0
  4. package/.devcontainer/README.md +222 -0
  5. package/.devcontainer/config/main-system-prompt.md +502 -0
  6. package/.devcontainer/config/settings.json +47 -0
  7. package/.devcontainer/devcontainer.json +94 -0
  8. package/.devcontainer/features/README.md +113 -0
  9. package/.devcontainer/features/agent-browser/README.md +65 -0
  10. package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
  11. package/.devcontainer/features/agent-browser/install.sh +79 -0
  12. package/.devcontainer/features/ast-grep/README.md +24 -0
  13. package/.devcontainer/features/ast-grep/devcontainer-feature.json +24 -0
  14. package/.devcontainer/features/ast-grep/install.sh +51 -0
  15. package/.devcontainer/features/ccstatusline/README.md +296 -0
  16. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +19 -0
  17. package/.devcontainer/features/ccstatusline/install.sh +290 -0
  18. package/.devcontainer/features/ccusage/README.md +205 -0
  19. package/.devcontainer/features/ccusage/devcontainer-feature.json +38 -0
  20. package/.devcontainer/features/ccusage/install.sh +132 -0
  21. package/.devcontainer/features/claude-code/README.md +498 -0
  22. package/.devcontainer/features/claude-code/config/settings.json +36 -0
  23. package/.devcontainer/features/claude-code/config/system-prompt.md +118 -0
  24. package/.devcontainer/features/claude-code/config/world-building-sp.md +1432 -0
  25. package/.devcontainer/features/claude-code/devcontainer-feature.json +42 -0
  26. package/.devcontainer/features/claude-code/install.sh +466 -0
  27. package/.devcontainer/features/claude-monitor/README.md +74 -0
  28. package/.devcontainer/features/claude-monitor/devcontainer-feature.json +38 -0
  29. package/.devcontainer/features/claude-monitor/install.sh +99 -0
  30. package/.devcontainer/features/lsp-servers/README.md +85 -0
  31. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +40 -0
  32. package/.devcontainer/features/lsp-servers/install.sh +116 -0
  33. package/.devcontainer/features/mcp-qdrant/CHANGES.md +399 -0
  34. package/.devcontainer/features/mcp-qdrant/README.md +474 -0
  35. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +57 -0
  36. package/.devcontainer/features/mcp-qdrant/install.sh +295 -0
  37. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +129 -0
  38. package/.devcontainer/features/mcp-reasoner/README.md +177 -0
  39. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +20 -0
  40. package/.devcontainer/features/mcp-reasoner/install.sh +177 -0
  41. package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +67 -0
  42. package/.devcontainer/features/notify-hook/README.md +86 -0
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
  44. package/.devcontainer/features/notify-hook/install.sh +38 -0
  45. package/.devcontainer/features/splitrail/README.md +140 -0
  46. package/.devcontainer/features/splitrail/devcontainer-feature.json +34 -0
  47. package/.devcontainer/features/splitrail/install.sh +129 -0
  48. package/.devcontainer/features/tree-sitter/README.md +138 -0
  49. package/.devcontainer/features/tree-sitter/devcontainer-feature.json +52 -0
  50. package/.devcontainer/features/tree-sitter/install.sh +173 -0
  51. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +106 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
  56. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
  57. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/.claude-plugin/plugin.json +8 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/SKILL.md +387 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/cli-flags-and-output.md +312 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/sdk-and-mcp.md +569 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/SKILL.md +309 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/compose-services.md +438 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/dockerfile-patterns.md +340 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/SKILL.md +412 -0
  66. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/container-lifecycle.md +388 -0
  67. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/resources-and-security.md +444 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/SKILL.md +344 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/middleware-and-lifespan.md +254 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/pydantic-models.md +245 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/routing-and-dependencies.md +255 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/sse-and-streaming.md +318 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/SKILL.md +345 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/agents-and-tools.md +271 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/models-and-streaming.md +422 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/SKILL.md +220 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/cross-vendor-principles.md +139 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/patterns-and-antipatterns.md +376 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/skill-authoring-patterns.md +356 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/SKILL.md +329 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/advanced-queries.md +314 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/javascript-patterns.md +323 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/python-patterns.md +354 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/schema-and-pragmas.md +326 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/SKILL.md +356 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/ai-sdk-svelte.md +128 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/component-patterns.md +332 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/layercake.md +203 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/migration-guide.md +350 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/runes-and-reactivity.md +328 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/spa-and-routing.md +262 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/svelte-dnd-action.md +181 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/SKILL.md +414 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/fastapi-testing.md +411 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/svelte-testing.md +538 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +7 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
  101. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +17 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
  106. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
  107. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272create-pr.md +337 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272new.md +166 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272review-commit.md +290 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272work.md +257 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +6 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +14 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +989 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +33 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +71 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +68 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +120 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +133 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +253 -0
  123. package/.devcontainer/scripts/setup-aliases.sh +80 -0
  124. package/.devcontainer/scripts/setup-config.sh +28 -0
  125. package/.devcontainer/scripts/setup-irie-claude.sh +32 -0
  126. package/.devcontainer/scripts/setup-plugins.sh +80 -0
  127. package/.devcontainer/scripts/setup.sh +58 -0
  128. package/LICENSE.txt +674 -0
  129. package/README.md +267 -0
  130. package/package.json +44 -0
  131. package/setup.js +83 -0
@@ -0,0 +1,350 @@
1
+ # Svelte 4 → Svelte 5 Migration Guide
2
+
3
+ Ten migration patterns with complete before/after code. Each pattern includes rationale and edge cases to handle during conversion.
4
+
5
+ ---
6
+
7
+ ## 1. Reactive State Declaration
8
+
9
+ ### Svelte 4
10
+ ```svelte
11
+ <script>
12
+ let count = 0;
13
+ let user = { name: 'Alice', age: 30 };
14
+ </script>
15
+ <button on:click={() => count++}>{count}</button>
16
+ ```
17
+
18
+ ### Svelte 5
19
+ ```svelte
20
+ <script>
21
+ let count = $state(0);
22
+ let user = $state({ name: 'Alice', age: 30 });
23
+ </script>
24
+ <button onclick={() => count++}>{count}</button>
25
+ ```
26
+
27
+ ### Rationale
28
+ Svelte 4 relied on compile-time analysis of top-level `let` declarations to infer reactivity. This was implicit and fragile — reassigning inside callbacks, passing to functions, or destructuring could silently break reactivity. `$state` makes the reactive intent explicit.
29
+
30
+ ### Edge Cases
31
+ - Variables that are assigned once and never change do not need `$state`. Plain `const` or `let` works for non-reactive values.
32
+ - Objects and arrays become deeply reactive proxies. If a variable holds large, frequently replaced data (API responses), consider `$state.raw()` instead.
33
+ - Module-level variables in `.js` files are not reactive. Rename the file to `.svelte.js` to enable runes.
34
+
35
+ ---
36
+
37
+ ## 2. Reactive Derivations
38
+
39
+ ### Svelte 4
40
+ ```svelte
41
+ <script>
42
+ let items = [];
43
+ $: remaining = items.filter(i => !i.done).length;
44
+ $: summary = `${remaining} of ${items.length} left`;
45
+ </script>
46
+ ```
47
+
48
+ ### Svelte 5
49
+ ```svelte
50
+ <script>
51
+ let items = $state([]);
52
+ let remaining = $derived(items.filter(i => !i.done).length);
53
+ let summary = $derived(`${remaining} of ${items.length} left`);
54
+ </script>
55
+ ```
56
+
57
+ ### Rationale
58
+ `$:` reactive declarations had confusing semantics — they could be statements or expressions, ran in topological order that was hard to predict, and failed silently when dependencies were indirect. `$derived` is always an expression with synchronous consistency.
59
+
60
+ ### Edge Cases
61
+ - Multi-statement computations need `$derived.by(() => { ... })`.
62
+ - `$derived` cannot have side effects. Move side effects to `$effect`.
63
+ - Circular derivations are compile-time errors in Svelte 5 (previously they caused infinite loops silently).
64
+
65
+ ---
66
+
67
+ ## 3. Reactive Side Effects
68
+
69
+ ### Svelte 4
70
+ ```svelte
71
+ <script>
72
+ let query = '';
73
+ $: {
74
+ console.log('Query changed:', query);
75
+ document.title = `Search: ${query}`;
76
+ }
77
+ </script>
78
+ ```
79
+
80
+ ### Svelte 5
81
+ ```svelte
82
+ <script>
83
+ let query = $state('');
84
+
85
+ $effect(() => {
86
+ console.log('Query changed:', query);
87
+ document.title = `Search: ${query}`;
88
+ });
89
+ </script>
90
+ ```
91
+
92
+ ### Rationale
93
+ `$:` blocks for side effects looked identical to derivations, making intent unclear. `$effect` clearly signals "this code runs for side effects, not to produce a value." It also provides a cleanup function mechanism that `$:` lacked.
94
+
95
+ ### Edge Cases
96
+ - `$effect` runs asynchronously after DOM updates. Code that previously ran synchronously in `$:` may behave differently.
97
+ - `$effect` tracks dependencies automatically — variables read during execution are tracked. Variables read only inside callbacks (e.g., `setTimeout`) are not tracked.
98
+ - Return a cleanup function for teardown: `$effect(() => { ... return () => cleanup(); });`
99
+
100
+ ---
101
+
102
+ ## 4. Component Props
103
+
104
+ ### Svelte 4
105
+ ```svelte
106
+ <script>
107
+ export let title;
108
+ export let variant = 'primary';
109
+ export let items = [];
110
+ </script>
111
+ ```
112
+
113
+ ### Svelte 5
114
+ ```svelte
115
+ <script>
116
+ let { title, variant = 'primary', items = [] } = $props();
117
+ </script>
118
+ ```
119
+
120
+ ### Rationale
121
+ `export let` overloaded the `export` keyword — props were not actual exports. `$props()` uses standard JavaScript destructuring, making defaults and rest patterns natural.
122
+
123
+ ### Edge Cases
124
+ - `$$props` and `$$restProps` are replaced by rest syntax: `let { known, ...rest } = $props();`
125
+ - TypeScript: define an interface and apply it to the destructuring — `let { title }: Props = $props();`
126
+ - Props are read-only by default. Attempting to reassign a prop causes a warning. Use `$bindable()` for two-way binding.
127
+
128
+ ---
129
+
130
+ ## 5. DOM Event Handlers
131
+
132
+ ### Svelte 4
133
+ ```svelte
134
+ <button on:click={handleClick}>Click</button>
135
+ <button on:click|preventDefault={handleSubmit}>Submit</button>
136
+ <input on:input={(e) => query = e.target.value} />
137
+ ```
138
+
139
+ ### Svelte 5
140
+ ```svelte
141
+ <button onclick={handleClick}>Click</button>
142
+ <button onclick={(e) => { e.preventDefault(); handleSubmit(e); }}>Submit</button>
143
+ <input oninput={(e) => query = e.currentTarget.value} />
144
+ ```
145
+
146
+ ### Rationale
147
+ Svelte 5 uses standard HTML event attributes, aligning with the DOM API and removing the need for a custom directive syntax. This improves interoperability with tools and editors that understand standard HTML.
148
+
149
+ ### Edge Cases
150
+ - Event modifiers (`|preventDefault`, `|stopPropagation`) are removed. Call the methods directly in the handler.
151
+ - `e.target` is replaced by `e.currentTarget` for TypeScript correctness (the element the handler is attached to, not the event origin).
152
+ - Multiple handlers on the same event: use a single handler that calls multiple functions, or compose with a helper.
153
+
154
+ ---
155
+
156
+ ## 6. Callback Props (Replacing createEventDispatcher)
157
+
158
+ ### Svelte 4
159
+ ```svelte
160
+ <!-- Child.svelte -->
161
+ <script>
162
+ import { createEventDispatcher } from 'svelte';
163
+ const dispatch = createEventDispatcher();
164
+ </script>
165
+ <button on:click={() => dispatch('select', { id: 1 })}>Select</button>
166
+
167
+ <!-- Parent.svelte -->
168
+ <Child on:select={(e) => handleSelect(e.detail)} />
169
+ ```
170
+
171
+ ### Svelte 5
172
+ ```svelte
173
+ <!-- Child.svelte -->
174
+ <script>
175
+ let { onselect } = $props();
176
+ </script>
177
+ <button onclick={() => onselect?.({ id: 1 })}>Select</button>
178
+
179
+ <!-- Parent.svelte -->
180
+ <Child onselect={(data) => handleSelect(data)} />
181
+ ```
182
+
183
+ ### Rationale
184
+ `createEventDispatcher` used a custom event system with `.detail` unwrapping. Callback props are plain functions — typed, composable, and requiring no special API knowledge.
185
+
186
+ ### Edge Cases
187
+ - Convention: prefix with `on` (e.g., `onselect`, `onchange`, `ondelete`). This matches DOM event naming.
188
+ - Optional callbacks: use optional chaining (`onselect?.()`) to safely skip when the parent does not provide a handler.
189
+ - Data is passed directly as arguments, not wrapped in `event.detail`.
190
+
191
+ ---
192
+
193
+ ## 7. Snippets (Replacing Slots)
194
+
195
+ ### Svelte 4
196
+ ```svelte
197
+ <!-- Card.svelte -->
198
+ <div class="card">
199
+ <slot name="header" />
200
+ <slot />
201
+ <slot name="footer" />
202
+ </div>
203
+
204
+ <!-- Parent -->
205
+ <Card>
206
+ <h2 slot="header">Title</h2>
207
+ <p>Content</p>
208
+ <small slot="footer">Footer</small>
209
+ </Card>
210
+ ```
211
+
212
+ ### Svelte 5
213
+ ```svelte
214
+ <!-- Card.svelte -->
215
+ <script>
216
+ let { header, children, footer } = $props();
217
+ </script>
218
+ <div class="card">
219
+ {@render header?.()}
220
+ {@render children?.()}
221
+ {@render footer?.()}
222
+ </div>
223
+
224
+ <!-- Parent -->
225
+ <Card>
226
+ {#snippet header()}
227
+ <h2>Title</h2>
228
+ {/snippet}
229
+
230
+ <p>Content</p>
231
+
232
+ {#snippet footer()}
233
+ <small>Footer</small>
234
+ {/snippet}
235
+ </Card>
236
+ ```
237
+
238
+ ### Rationale
239
+ Slots had limited type safety and could not receive parameters without the awkward `let:` directive. Snippets are typed, parameterized, and composable — they are first-class values that can be stored, passed, and conditionally rendered.
240
+
241
+ ### Edge Cases
242
+ - Default content: use the nullish check pattern — `{@render header?.() ?? fallbackSnippet()}` or `{#if header}{@render header()}{:else}Default{/if}`.
243
+ - `<slot let:item>` (slot props) → parameterized snippets: `{#snippet row(item)}...{/snippet}`.
244
+ - Snippet type in TypeScript: `import type { Snippet } from 'svelte';`
245
+
246
+ ---
247
+
248
+ ## 8. Bindable Props
249
+
250
+ ### Svelte 4
251
+ ```svelte
252
+ <!-- Input.svelte -->
253
+ <script>
254
+ export let value = '';
255
+ </script>
256
+ <input bind:value />
257
+
258
+ <!-- Parent -->
259
+ <Input bind:value={name} />
260
+ ```
261
+
262
+ ### Svelte 5
263
+ ```svelte
264
+ <!-- Input.svelte -->
265
+ <script>
266
+ let { value = $bindable('') } = $props();
267
+ </script>
268
+ <input bind:value />
269
+
270
+ <!-- Parent -->
271
+ <Input bind:value={name} />
272
+ ```
273
+
274
+ ### Rationale
275
+ In Svelte 4, all `export let` props were implicitly bindable, creating an unclear API surface. `$bindable()` makes two-way binding an explicit opt-in — consumers know which props support `bind:`.
276
+
277
+ ### Edge Cases
278
+ - The default value goes inside `$bindable()`: `$bindable('')`, not `$bindable() ?? ''`.
279
+ - Parent still uses `bind:value={variable}` syntax — the binding syntax is unchanged on the consumer side.
280
+ - Only mark props as bindable when bidirectional data flow is the intended component API.
281
+
282
+ ---
283
+
284
+ ## 9. Dynamic Components
285
+
286
+ ### Svelte 4
287
+ ```svelte
288
+ <script>
289
+ import Home from './Home.svelte';
290
+ import About from './About.svelte';
291
+ let current = Home;
292
+ </script>
293
+ <svelte:component this={current} />
294
+ ```
295
+
296
+ ### Svelte 5
297
+ ```svelte
298
+ <script>
299
+ import Home from './Home.svelte';
300
+ import About from './About.svelte';
301
+ let Current = $state(Home);
302
+ </script>
303
+ <Current />
304
+ ```
305
+
306
+ ### Rationale
307
+ `<svelte:component>` was a workaround for a compiler limitation. Svelte 5 treats components as values — any variable holding a component constructor can be rendered directly as a tag.
308
+
309
+ ### Edge Cases
310
+ - The variable name must start with an uppercase letter to distinguish from HTML elements.
311
+ - `<svelte:component>` still works for backwards compatibility but is deprecated.
312
+ - Props can be passed normally: `<Current {title} {data} />`.
313
+
314
+ ---
315
+
316
+ ## 10. Imperative Component API (mount / unmount)
317
+
318
+ ### Svelte 4
319
+ ```js
320
+ import App from './App.svelte';
321
+
322
+ const app = new App({
323
+ target: document.getElementById('app'),
324
+ props: { name: 'World' }
325
+ });
326
+
327
+ app.$destroy();
328
+ ```
329
+
330
+ ### Svelte 5
331
+ ```js
332
+ import { mount, unmount } from 'svelte';
333
+ import App from './App.svelte';
334
+
335
+ const app = mount(App, {
336
+ target: document.getElementById('app'),
337
+ props: { name: 'World' }
338
+ });
339
+
340
+ unmount(app);
341
+ ```
342
+
343
+ ### Rationale
344
+ The `new Component()` pattern implied class instantiation, but Svelte 5 components are not classes. `mount` and `unmount` are explicit functions that clearly communicate lifecycle intent.
345
+
346
+ ### Edge Cases
347
+ - `mount` returns an opaque reference, not a component instance with `$set` or `$on` methods. Those methods are removed.
348
+ - For hydration (SSR), use `hydrate` instead of `mount`.
349
+ - `mount` accepts an `events` option for attaching listeners, replacing the `$on` method.
350
+ - In SPA mode, the entry point in `src/main.js` does not typically need manual `mount` — SvelteKit handles it. Manual mounting is for embedding Svelte components in non-SvelteKit contexts.
@@ -0,0 +1,328 @@
1
+ # Runes and Reactivity — Deep Dive
2
+
3
+ ## 1. Reactivity Model
4
+
5
+ Svelte 5 reactivity is signal-based. Each `$state` call creates a signal — a getter/setter pair tracked by the compiler. When a signal is read inside `$derived` or `$effect`, the compiler registers a dependency. When the signal changes, dependents re-execute.
6
+
7
+ Objects and arrays wrapped in `$state` are converted to deeply reactive proxies. Property access on the proxy registers fine-grained dependencies — reading `obj.name` tracks only `name`, not the entire object.
8
+
9
+ ### Proxy Tracking Semantics
10
+
11
+ ```js
12
+ let user = $state({ name: 'Alice', age: 30 });
13
+
14
+ // Reading user.name inside $derived tracks only the name property.
15
+ let greeting = $derived(`Hello, ${user.name}`);
16
+
17
+ // Mutating user.age does NOT cause greeting to recalculate.
18
+ user.age = 31;
19
+
20
+ // Mutating user.name DOES cause greeting to recalculate.
21
+ user.name = 'Bob';
22
+ ```
23
+
24
+ Array mutations (`push`, `splice`, `pop`, etc.) are intercepted by the proxy and trigger reactive updates for any dependent that reads the array's length or iterates over it.
25
+
26
+ ### When Proxies Break
27
+
28
+ Proxies do not survive structured clone, `JSON.parse(JSON.stringify())`, or transfer to web workers. Use `$state.snapshot()` before any of these operations:
29
+
30
+ ```js
31
+ let data = $state({ items: [1, 2, 3] });
32
+ const plain = $state.snapshot(data);
33
+ postMessage(plain); // safe — plain object, no proxy
34
+ ```
35
+
36
+ ## 2. $state Deep Dive
37
+
38
+ ### Basic Usage
39
+
40
+ ```js
41
+ let count = $state(0); // primitive
42
+ let user = $state({ name: 'Alice' }); // deep proxy
43
+ let tags = $state(['svelte', 'runes']); // deep proxy array
44
+ ```
45
+
46
+ ### $state.raw — Opt Out of Deep Tracking
47
+
48
+ Use `$state.raw()` for large data structures that are always replaced, never mutated. Only reassignment triggers reactivity.
49
+
50
+ ```js
51
+ let rows = $state.raw(initialData);
52
+
53
+ // Does NOT trigger updates (mutation on raw state):
54
+ rows.push(newRow);
55
+
56
+ // Triggers updates (reassignment):
57
+ rows = [...rows, newRow];
58
+ ```
59
+
60
+ Appropriate for: API response caches, immutable data from external sources, performance-critical large lists.
61
+
62
+ ### $state.snapshot — Plain Copy
63
+
64
+ Returns a non-reactive, structurally cloned copy of the reactive value.
65
+
66
+ ```js
67
+ let form = $state({ name: '', email: '' });
68
+
69
+ function submit() {
70
+ const payload = $state.snapshot(form);
71
+ fetch('/api/submit', { method: 'POST', body: JSON.stringify(payload) });
72
+ }
73
+ ```
74
+
75
+ ### Class Fields
76
+
77
+ `$state` works in class fields, enabling reactive class-based stores:
78
+
79
+ ```js
80
+ // lib/stores/counter.svelte.js
81
+ export class Counter {
82
+ count = $state(0);
83
+ doubled = $derived(this.count * 2);
84
+
85
+ increment() {
86
+ this.count++;
87
+ }
88
+
89
+ reset() {
90
+ this.count = 0;
91
+ }
92
+ }
93
+ ```
94
+
95
+ ```svelte
96
+ <script>
97
+ import { Counter } from '$lib/stores/counter.svelte.js';
98
+ const counter = new Counter();
99
+ </script>
100
+ <button onclick={() => counter.increment()}>{counter.count} (doubled: {counter.doubled})</button>
101
+ ```
102
+
103
+ Class-based stores are ideal when a piece of state has associated behaviors (methods) and derived values. The class encapsulates the reactive graph.
104
+
105
+ ## 3. $derived Deep Dive
106
+
107
+ ### Sync Guarantee
108
+
109
+ `$derived` values are always synchronous and consistent. After updating a `$state` value, any `$derived` that depends on it reflects the new value immediately — there is no "stale read" window.
110
+
111
+ ```js
112
+ let count = $state(0);
113
+ let doubled = $derived(count * 2);
114
+
115
+ count = 5;
116
+ console.log(doubled); // 10 — immediately consistent
117
+ ```
118
+
119
+ ### Cascading Derivations
120
+
121
+ Derived values can depend on other derived values. The compiler resolves the dependency graph at compile time:
122
+
123
+ ```js
124
+ let price = $state(100);
125
+ let taxRate = $state(0.08);
126
+ let tax = $derived(price * taxRate);
127
+ let total = $derived(price + tax);
128
+ ```
129
+
130
+ ### $derived.by — Multi-Statement Derivation
131
+
132
+ When derivation requires intermediate steps, use `$derived.by()` with a function body:
133
+
134
+ ```js
135
+ let items = $state([
136
+ { name: 'A', price: 10, qty: 2 },
137
+ { name: 'B', price: 5, qty: 3 }
138
+ ]);
139
+
140
+ let orderSummary = $derived.by(() => {
141
+ const subtotal = items.reduce((sum, i) => sum + i.price * i.qty, 0);
142
+ const tax = subtotal * 0.08;
143
+ return { subtotal, tax, total: subtotal + tax };
144
+ });
145
+ ```
146
+
147
+ Avoid `$effect` for computations that produce values. `$derived.by()` handles complex computations while maintaining the synchronous consistency guarantee.
148
+
149
+ ## 4. $effect Deep Dive
150
+
151
+ ### Execution Timing
152
+
153
+ Effects run **after** the DOM has been updated. This means the DOM reflects the latest state when the effect body executes.
154
+
155
+ ```js
156
+ let count = $state(0);
157
+
158
+ $effect(() => {
159
+ // DOM is already updated when this runs
160
+ document.title = `Count: ${count}`;
161
+ });
162
+ ```
163
+
164
+ ### Cleanup Functions
165
+
166
+ Return a function from `$effect` for teardown. Cleanup runs before the next effect execution and when the component is destroyed:
167
+
168
+ ```js
169
+ $effect(() => {
170
+ const interval = setInterval(() => tick(), 1000);
171
+ return () => clearInterval(interval);
172
+ });
173
+ ```
174
+
175
+ ### Async Pattern
176
+
177
+ Effects cannot be `async`. Start async work inside the effect and use an AbortController for cleanup:
178
+
179
+ ```js
180
+ $effect(() => {
181
+ const controller = new AbortController();
182
+
183
+ async function fetchData() {
184
+ try {
185
+ const res = await fetch(`/api/data?q=${query}`, { signal: controller.signal });
186
+ if (!controller.signal.aborted) {
187
+ results = await res.json();
188
+ }
189
+ } catch (e) {
190
+ if (e.name !== 'AbortError') throw e;
191
+ }
192
+ }
193
+
194
+ fetchData();
195
+ return () => controller.abort();
196
+ });
197
+ ```
198
+
199
+ ### $effect.pre — Before DOM Updates
200
+
201
+ `$effect.pre()` runs before the DOM is updated. Use for measuring DOM state that will change:
202
+
203
+ ```js
204
+ $effect.pre(() => {
205
+ // Read scroll position before DOM update
206
+ previousScrollHeight = container.scrollHeight;
207
+ });
208
+ ```
209
+
210
+ This is rarely needed. Default to `$effect()` unless DOM measurement before an update is required.
211
+
212
+ ### $effect.root — Detached Effect Scope
213
+
214
+ `$effect.root()` creates an effect scope independent of any component. It returns a cleanup function that must be called manually:
215
+
216
+ ```js
217
+ const cleanup = $effect.root(() => {
218
+ $effect(() => {
219
+ console.log('This runs outside any component');
220
+ });
221
+ });
222
+
223
+ // Later, when done:
224
+ cleanup();
225
+ ```
226
+
227
+ Use for effects in tests, in `.svelte.js` module-level logic, or when manually managing effect lifecycles.
228
+
229
+ ## 5. Tracking Pitfalls
230
+
231
+ ### Destructuring Loses Reactivity
232
+
233
+ Destructuring a `$state` object captures the current values as plain variables. Those variables are not tracked:
234
+
235
+ ```js
236
+ let user = $state({ name: 'Alice', age: 30 });
237
+
238
+ // BAD: name and age are plain strings/numbers, not reactive
239
+ const { name, age } = user;
240
+
241
+ // GOOD: access properties on the proxy
242
+ $effect(() => {
243
+ console.log(user.name); // tracked
244
+ });
245
+ ```
246
+
247
+ ### Closure Reads
248
+
249
+ A value read inside a closure is tracked only if the closure runs during effect or derived execution:
250
+
251
+ ```js
252
+ let count = $state(0);
253
+
254
+ // Tracked: the function body runs during $derived evaluation
255
+ let doubled = $derived(count * 2);
256
+
257
+ // NOT tracked: setTimeout callback runs outside the reactive context
258
+ $effect(() => {
259
+ setTimeout(() => {
260
+ console.log(count); // reads the value, but this read is NOT tracked
261
+ }, 1000);
262
+ });
263
+ ```
264
+
265
+ ### Passing State to Non-Svelte Code
266
+
267
+ External libraries, Web APIs, and non-`.svelte.js` modules do not understand Svelte proxies. Always pass `$state.snapshot()` or plain values:
268
+
269
+ ```js
270
+ let formData = $state({ name: '', email: '' });
271
+
272
+ // External validation library
273
+ import { validate } from 'some-lib';
274
+ const errors = validate($state.snapshot(formData));
275
+ ```
276
+
277
+ ## 6. EventSource and SSE Streams
278
+
279
+ ### Construction and Cleanup in $effect
280
+
281
+ EventSource connections follow the same `$effect` cleanup pattern as fetch and WebSocket. Construct the EventSource in the effect body and close it in the cleanup function:
282
+
283
+ ```js
284
+ let messages = $state('');
285
+ let status = $state('connecting');
286
+
287
+ $effect(() => {
288
+ const es = new EventSource(`/api/stream?topic=${topic}`);
289
+
290
+ es.onopen = () => { status = 'connected'; };
291
+
292
+ es.onmessage = (event) => {
293
+ messages += event.data;
294
+ };
295
+
296
+ es.onerror = () => {
297
+ status = es.readyState === EventSource.CLOSED ? 'closed' : 'reconnecting';
298
+ };
299
+
300
+ return () => es.close();
301
+ });
302
+ ```
303
+
304
+ When `topic` changes, the effect re-runs: the previous EventSource is closed via cleanup, and a new one opens with the updated URL. The `$state` string accumulates tokens across messages, and Svelte updates the DOM after each assignment.
305
+
306
+ ### Named Events
307
+
308
+ Servers can send named events (`event: delta`). Use `addEventListener` for these — `onmessage` only fires for unnamed events:
309
+
310
+ ```js
311
+ $effect(() => {
312
+ const es = new EventSource('/api/completions');
313
+
314
+ es.addEventListener('delta', (e) => {
315
+ tokens += e.data;
316
+ });
317
+
318
+ es.addEventListener('done', () => {
319
+ es.close();
320
+ });
321
+
322
+ return () => es.close();
323
+ });
324
+ ```
325
+
326
+ ### When to Use Raw EventSource
327
+
328
+ Use `@ai-sdk/svelte` (`useChat`, `useCompletion`) for AI SDK endpoints — the SDK manages the EventSource lifecycle, parsing, and error recovery internally. Use raw EventSource for custom SSE endpoints not backed by the AI SDK, or when fine-grained control over reconnection and event handling is required.