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,314 @@
1
+ # Advanced SQLite Queries -- Deep Dive
2
+
3
+ ## 1. Common Table Expressions (CTEs)
4
+
5
+ CTEs define named temporary result sets within a single query. They improve readability and allow recursive queries.
6
+
7
+ ### Non-Recursive CTEs
8
+
9
+ ```sql
10
+ WITH active_users AS (
11
+ SELECT id, email, display_name
12
+ FROM users
13
+ WHERE last_login > date('now', '-30 days')
14
+ ),
15
+ user_stats AS (
16
+ SELECT user_id, COUNT(*) AS post_count
17
+ FROM posts
18
+ GROUP BY user_id
19
+ )
20
+ SELECT au.email, au.display_name, COALESCE(us.post_count, 0) AS post_count
21
+ FROM active_users au
22
+ LEFT JOIN user_stats us ON au.id = us.user_id
23
+ ORDER BY post_count DESC;
24
+ ```
25
+
26
+ ### Recursive CTEs
27
+
28
+ Recursive CTEs self-reference to traverse hierarchical data:
29
+
30
+ ```sql
31
+ -- Organizational hierarchy
32
+ WITH RECURSIVE org_tree AS (
33
+ -- Base case: root nodes (no manager)
34
+ SELECT id, name, manager_id, 0 AS depth, name AS path
35
+ FROM employees
36
+ WHERE manager_id IS NULL
37
+
38
+ UNION ALL
39
+
40
+ -- Recursive case: children of already-found nodes
41
+ SELECT e.id, e.name, e.manager_id, ot.depth + 1,
42
+ ot.path || ' > ' || e.name
43
+ FROM employees e
44
+ JOIN org_tree ot ON e.manager_id = ot.id
45
+ )
46
+ SELECT * FROM org_tree ORDER BY path;
47
+ ```
48
+
49
+ ```sql
50
+ -- Bill of materials (tree of components)
51
+ WITH RECURSIVE bom AS (
52
+ SELECT component_id, parent_id, quantity, 1 AS level
53
+ FROM components
54
+ WHERE parent_id = :root_id
55
+
56
+ UNION ALL
57
+
58
+ SELECT c.component_id, c.parent_id, c.quantity * bom.quantity, bom.level + 1
59
+ FROM components c
60
+ JOIN bom ON c.parent_id = bom.component_id
61
+ )
62
+ SELECT * FROM bom;
63
+ ```
64
+
65
+ ### Limit Recursion Depth
66
+
67
+ SQLite defaults to a maximum recursion depth of 1000. Override with `LIMIT` on the CTE or adjust `SQLITE_MAX_VARIABLE_NUMBER`:
68
+
69
+ ```sql
70
+ WITH RECURSIVE seq(n) AS (
71
+ SELECT 1
72
+ UNION ALL
73
+ SELECT n + 1 FROM seq WHERE n < 100
74
+ )
75
+ SELECT n FROM seq;
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 2. Window Functions
81
+
82
+ Window functions compute values across a set of rows related to the current row, without collapsing the result set.
83
+
84
+ ### ROW_NUMBER, RANK, DENSE_RANK
85
+
86
+ ```sql
87
+ -- Rank users by post count within each category
88
+ SELECT
89
+ u.name,
90
+ c.category_name,
91
+ COUNT(p.id) AS post_count,
92
+ ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY COUNT(p.id) DESC) AS row_num,
93
+ RANK() OVER (PARTITION BY c.id ORDER BY COUNT(p.id) DESC) AS rank,
94
+ DENSE_RANK() OVER (PARTITION BY c.id ORDER BY COUNT(p.id) DESC) AS dense_rank
95
+ FROM users u
96
+ JOIN posts p ON u.id = p.user_id
97
+ JOIN categories c ON p.category_id = c.id
98
+ GROUP BY u.id, c.id;
99
+ ```
100
+
101
+ | Function | Ties | Gaps |
102
+ |----------|------|------|
103
+ | `ROW_NUMBER` | Breaks ties arbitrarily | No gaps |
104
+ | `RANK` | Same rank for ties | Gaps after ties |
105
+ | `DENSE_RANK` | Same rank for ties | No gaps |
106
+
107
+ ### LAG and LEAD
108
+
109
+ Access previous or next row values without a self-join:
110
+
111
+ ```sql
112
+ -- Compare each sale to the previous day
113
+ SELECT
114
+ date,
115
+ revenue,
116
+ LAG(revenue, 1) OVER (ORDER BY date) AS prev_day_revenue,
117
+ revenue - LAG(revenue, 1) OVER (ORDER BY date) AS daily_change,
118
+ LEAD(revenue, 1) OVER (ORDER BY date) AS next_day_revenue
119
+ FROM daily_sales;
120
+ ```
121
+
122
+ ### Running Totals and Moving Averages
123
+
124
+ ```sql
125
+ -- Running total of revenue
126
+ SELECT
127
+ date,
128
+ revenue,
129
+ SUM(revenue) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) AS running_total
130
+ FROM daily_sales;
131
+
132
+ -- 7-day moving average
133
+ SELECT
134
+ date,
135
+ revenue,
136
+ AVG(revenue) OVER (
137
+ ORDER BY date
138
+ ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
139
+ ) AS moving_avg_7d
140
+ FROM daily_sales;
141
+ ```
142
+
143
+ ### NTILE and Percentiles
144
+
145
+ ```sql
146
+ -- Divide users into quartiles by score
147
+ SELECT
148
+ name,
149
+ score,
150
+ NTILE(4) OVER (ORDER BY score DESC) AS quartile
151
+ FROM users;
152
+
153
+ -- First and last value in partition
154
+ SELECT
155
+ department,
156
+ name,
157
+ salary,
158
+ FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary DESC) AS highest_paid,
159
+ LAST_VALUE(name) OVER (
160
+ PARTITION BY department ORDER BY salary DESC
161
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
162
+ ) AS lowest_paid
163
+ FROM employees;
164
+ ```
165
+
166
+ ---
167
+
168
+ ## 3. Upsert (INSERT ... ON CONFLICT)
169
+
170
+ Insert a row or update it if a unique constraint would be violated:
171
+
172
+ ```sql
173
+ -- Update on conflict with specific columns
174
+ INSERT INTO settings (key, value, updated_at)
175
+ VALUES ('theme', 'dark', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
176
+ ON CONFLICT (key) DO UPDATE SET
177
+ value = excluded.value,
178
+ updated_at = excluded.updated_at;
179
+
180
+ -- Ignore duplicates silently
181
+ INSERT INTO tags (name) VALUES ('sqlite')
182
+ ON CONFLICT (name) DO NOTHING;
183
+
184
+ -- Conditional upsert
185
+ INSERT INTO counters (key, count) VALUES ('visits', 1)
186
+ ON CONFLICT (key) DO UPDATE SET count = count + 1
187
+ WHERE count < 1000000;
188
+ ```
189
+
190
+ `excluded` refers to the row that would have been inserted. Use it to reference the new values in the `DO UPDATE` clause.
191
+
192
+ ---
193
+
194
+ ## 4. RETURNING
195
+
196
+ `RETURNING` retrieves the affected rows from `INSERT`, `UPDATE`, or `DELETE` without a separate query:
197
+
198
+ ```sql
199
+ -- Get the inserted row with auto-generated id
200
+ INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice')
201
+ RETURNING id, email, name, created_at;
202
+
203
+ -- Get old values during update
204
+ UPDATE products SET price = price * 1.1
205
+ WHERE category = 'electronics'
206
+ RETURNING id, name, price AS new_price;
207
+
208
+ -- Confirm what was deleted
209
+ DELETE FROM sessions WHERE expires_at < datetime('now')
210
+ RETURNING id, user_id;
211
+ ```
212
+
213
+ `RETURNING` is particularly useful with `INSERT` to avoid a round-trip `SELECT` for the auto-generated `rowid`.
214
+
215
+ ---
216
+
217
+ ## 5. EXPLAIN QUERY PLAN
218
+
219
+ Analyze how SQLite executes a query to identify missing indexes or inefficient scans:
220
+
221
+ ```sql
222
+ EXPLAIN QUERY PLAN
223
+ SELECT u.name, COUNT(p.id)
224
+ FROM users u
225
+ JOIN posts p ON u.id = p.user_id
226
+ WHERE u.created_at > '2024-01-01'
227
+ GROUP BY u.id;
228
+ ```
229
+
230
+ Output interpretation:
231
+
232
+ | Term | Meaning |
233
+ |------|---------|
234
+ | `SCAN` | Full table scan -- no index used |
235
+ | `SEARCH` | Index lookup -- efficient |
236
+ | `USING INDEX` | Specifies which index |
237
+ | `USING COVERING INDEX` | Index contains all needed columns, no table access |
238
+ | `TEMP B-TREE` | Temporary sort/group structure |
239
+
240
+ ### Optimization Workflow
241
+
242
+ 1. Run `EXPLAIN QUERY PLAN` on slow queries.
243
+ 2. Look for `SCAN` on large tables -- this indicates a missing index.
244
+ 3. Add an index on the filtered/joined column.
245
+ 4. Re-run `EXPLAIN QUERY PLAN` to verify `SEARCH USING INDEX`.
246
+ 5. Run `ANALYZE` after adding indexes to update the query planner's statistics.
247
+
248
+ ```sql
249
+ -- Before: SCAN users
250
+ EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'alice@example.com';
251
+
252
+ -- Add index
253
+ CREATE INDEX idx_users_email ON users(email);
254
+
255
+ -- After: SEARCH users USING INDEX idx_users_email
256
+ EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'alice@example.com';
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 6. Covering Indexes
262
+
263
+ A covering index includes all columns referenced by a query, eliminating the need to read the main table:
264
+
265
+ ```sql
266
+ -- This query needs id, email, and display_name
267
+ SELECT id, email, display_name FROM users WHERE email LIKE 'a%';
268
+
269
+ -- Covering index for this query
270
+ CREATE INDEX idx_users_email_covering ON users(email, id, display_name);
271
+ ```
272
+
273
+ After adding the covering index, `EXPLAIN QUERY PLAN` shows `USING COVERING INDEX` instead of `USING INDEX` followed by a table lookup.
274
+
275
+ ### When to Use Covering Indexes
276
+
277
+ - High-frequency read queries with a known column set.
278
+ - Queries where the table is wide (many columns) but only a few are needed.
279
+ - The tradeoff is increased write overhead and storage for the index.
280
+
281
+ ---
282
+
283
+ ## 7. Partial and Expression Indexes
284
+
285
+ ### Partial Indexes
286
+
287
+ Index only rows matching a condition, reducing index size and write overhead:
288
+
289
+ ```sql
290
+ -- Index only active users (smaller, faster)
291
+ CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';
292
+
293
+ -- Index only non-null values
294
+ CREATE INDEX idx_users_display_name ON users(display_name)
295
+ WHERE display_name IS NOT NULL;
296
+ ```
297
+
298
+ The query planner uses a partial index only when the `WHERE` clause of the query matches the index condition.
299
+
300
+ ### Expression Indexes
301
+
302
+ Index computed values:
303
+
304
+ ```sql
305
+ -- Case-insensitive email lookup
306
+ CREATE INDEX idx_users_email_lower ON users(lower(email));
307
+ -- SELECT * FROM users WHERE lower(email) = 'alice@example.com';
308
+
309
+ -- Date extraction from ISO timestamp
310
+ CREATE INDEX idx_posts_date ON posts(date(created_at));
311
+ -- SELECT * FROM posts WHERE date(created_at) = '2024-06-15';
312
+ ```
313
+
314
+ Expression indexes work with any deterministic SQL expression. The query must use the exact same expression for the planner to select the index.
@@ -0,0 +1,323 @@
1
+ # JavaScript SQLite Patterns -- Deep Dive
2
+
3
+ ## 1. better-sqlite3 User-Defined Functions
4
+
5
+ ### Scalar Functions
6
+
7
+ ```javascript
8
+ const Database = require("better-sqlite3");
9
+ const db = new Database("app.db");
10
+
11
+ db.function("reverse", (str) => str.split("").reverse().join(""));
12
+ // SELECT reverse(name) FROM users;
13
+
14
+ db.function("json_arr_len", (json) => {
15
+ if (json === null) return 0;
16
+ return JSON.parse(json).length;
17
+ });
18
+ // SELECT json_arr_len(tags) FROM posts;
19
+ ```
20
+
21
+ ### Aggregate Functions
22
+
23
+ ```javascript
24
+ db.aggregate("median", {
25
+ start: () => [],
26
+ step: (arr, value) => {
27
+ if (value !== null) arr.push(value);
28
+ return arr;
29
+ },
30
+ result: (arr) => {
31
+ if (arr.length === 0) return null;
32
+ arr.sort((a, b) => a - b);
33
+ const mid = Math.floor(arr.length / 2);
34
+ return arr.length % 2 === 0
35
+ ? (arr[mid - 1] + arr[mid]) / 2
36
+ : arr[mid];
37
+ },
38
+ });
39
+ // SELECT median(price) FROM products;
40
+ ```
41
+
42
+ ### Table-Valued Functions
43
+
44
+ ```javascript
45
+ db.table("generate_series", {
46
+ columns: ["value"],
47
+ parameters: ["start", "stop", "step"],
48
+ *rows(start, stop, step = 1) {
49
+ for (let i = start; i <= stop; i += step) {
50
+ yield { value: i };
51
+ }
52
+ },
53
+ });
54
+ // SELECT value FROM generate_series(1, 10, 2);
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 2. WAL Checkpoints
60
+
61
+ WAL files grow until checkpointed. better-sqlite3 enables automatic checkpointing by default, but manual control is available:
62
+
63
+ ```javascript
64
+ // Check WAL size
65
+ const walInfo = db.pragma("wal_checkpoint(PASSIVE)");
66
+ // Returns: { busy: 0, checkpointed: N, log: N }
67
+
68
+ // Force a full checkpoint (waits for readers)
69
+ db.pragma("wal_checkpoint(TRUNCATE)");
70
+ ```
71
+
72
+ ### Checkpoint Strategies
73
+
74
+ | Mode | Behavior | Use Case |
75
+ |------|----------|----------|
76
+ | `PASSIVE` | Checkpoint pages not locked by readers | Background maintenance |
77
+ | `FULL` | Wait for readers, then checkpoint all pages | Before backup |
78
+ | `TRUNCATE` | Like FULL, then truncate WAL to zero | Reduce disk usage |
79
+ | `RESTART` | Like FULL, then reset WAL to beginning | Reclaim WAL file space |
80
+
81
+ Schedule periodic `PASSIVE` checkpoints in long-running applications. Use `TRUNCATE` before backup or deployment.
82
+
83
+ ---
84
+
85
+ ## 3. Prepared Statement Patterns
86
+
87
+ ### Caching Prepared Statements
88
+
89
+ better-sqlite3 `prepare()` compiles the SQL once. Store references for reuse:
90
+
91
+ ```javascript
92
+ class UserRepository {
93
+ constructor(db) {
94
+ this.db = db;
95
+ this.stmts = {
96
+ getById: db.prepare("SELECT * FROM users WHERE id = ?"),
97
+ getByEmail: db.prepare("SELECT * FROM users WHERE email = ?"),
98
+ insert: db.prepare(
99
+ "INSERT INTO users (email, name) VALUES (@email, @name) RETURNING *"
100
+ ),
101
+ update: db.prepare(
102
+ "UPDATE users SET name = @name WHERE id = @id RETURNING *"
103
+ ),
104
+ delete: db.prepare("DELETE FROM users WHERE id = ?"),
105
+ };
106
+ }
107
+
108
+ getById(id) {
109
+ return this.stmts.getById.get(id);
110
+ }
111
+
112
+ getByEmail(email) {
113
+ return this.stmts.getByEmail.get(email);
114
+ }
115
+
116
+ create(data) {
117
+ return this.stmts.insert.get(data);
118
+ }
119
+
120
+ update(id, data) {
121
+ return this.stmts.update.get({ ...data, id });
122
+ }
123
+
124
+ delete(id) {
125
+ return this.stmts.delete.run(id);
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Iterate vs All
131
+
132
+ Use `.iterate()` for large result sets to avoid loading everything into memory:
133
+
134
+ ```javascript
135
+ const stmt = db.prepare("SELECT * FROM logs WHERE date > ?");
136
+
137
+ // All at once (small result sets)
138
+ const rows = stmt.all("2024-01-01");
139
+
140
+ // Iterator (large result sets)
141
+ for (const row of stmt.iterate("2024-01-01")) {
142
+ processRow(row);
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 4. Transaction Patterns
149
+
150
+ ### Basic Transactions
151
+
152
+ ```javascript
153
+ const transferFunds = db.transaction((fromId, toId, amount) => {
154
+ const from = db.prepare("SELECT balance FROM accounts WHERE id = ?").get(fromId);
155
+ if (from.balance < amount) {
156
+ throw new Error("Insufficient funds");
157
+ }
158
+ db.prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?").run(amount, fromId);
159
+ db.prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?").run(amount, toId);
160
+ });
161
+
162
+ transferFunds(1, 2, 100);
163
+ ```
164
+
165
+ The `db.transaction()` wrapper automatically commits on success and rolls back on error. Nested `db.transaction()` calls use savepoints.
166
+
167
+ ### Deferred vs Immediate
168
+
169
+ ```javascript
170
+ const immediateTransaction = db.transaction(() => {
171
+ // Acquires write lock immediately
172
+ db.prepare("INSERT INTO logs (msg) VALUES (?)").run("action");
173
+ });
174
+
175
+ // Force IMMEDIATE mode for write transactions
176
+ immediateTransaction.immediate();
177
+ ```
178
+
179
+ Use `.immediate()` when the transaction will write, to avoid SQLITE_BUSY errors from lock promotion.
180
+
181
+ ---
182
+
183
+ ## 5. Cloudflare D1 Patterns
184
+
185
+ ### Batch Operations
186
+
187
+ D1 batches execute all statements in a single round-trip as an implicit transaction:
188
+
189
+ ```javascript
190
+ export default {
191
+ async fetch(request, env) {
192
+ const results = await env.DB.batch([
193
+ env.DB.prepare("INSERT INTO users (email) VALUES (?)").bind("alice@example.com"),
194
+ env.DB.prepare("INSERT INTO profiles (user_id, bio) VALUES (last_insert_rowid(), ?)").bind("Hello"),
195
+ env.DB.prepare("SELECT * FROM users WHERE email = ?").bind("alice@example.com"),
196
+ ]);
197
+
198
+ const user = results[2].results[0];
199
+ return Response.json(user);
200
+ },
201
+ };
202
+ ```
203
+
204
+ ### D1 Query Helpers
205
+
206
+ ```javascript
207
+ // .first() -- single row or null
208
+ const user = await env.DB.prepare("SELECT * FROM users WHERE id = ?")
209
+ .bind(id)
210
+ .first();
211
+
212
+ // .all() -- all rows with metadata
213
+ const { results, meta } = await env.DB.prepare("SELECT * FROM users").all();
214
+ // meta: { duration, rows_read, rows_written, changes }
215
+
216
+ // .raw() -- array of arrays (no column names)
217
+ const rows = await env.DB.prepare("SELECT id, email FROM users").raw();
218
+ // [[1, "alice@example.com"], [2, "bob@example.com"]]
219
+
220
+ // .run() -- for INSERT/UPDATE/DELETE
221
+ const { meta } = await env.DB.prepare("DELETE FROM sessions WHERE expired < ?")
222
+ .bind(Date.now())
223
+ .run();
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 6. D1 Migrations
229
+
230
+ ### File-Based Migrations
231
+
232
+ D1 uses numbered SQL files in a `migrations/` directory:
233
+
234
+ ```
235
+ migrations/
236
+ ├── 0001_create_users.sql
237
+ ├── 0002_add_posts.sql
238
+ └── 0003_add_fts.sql
239
+ ```
240
+
241
+ ```sql
242
+ -- 0001_create_users.sql
243
+ CREATE TABLE users (
244
+ id INTEGER PRIMARY KEY,
245
+ email TEXT NOT NULL UNIQUE,
246
+ name TEXT,
247
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
248
+ );
249
+ ```
250
+
251
+ Apply migrations with Wrangler:
252
+
253
+ ```bash
254
+ npx wrangler d1 migrations apply my-database # production
255
+ npx wrangler d1 migrations apply my-database --local # local dev
256
+ ```
257
+
258
+ ### Migration Best Practices
259
+
260
+ - Each migration file should be idempotent where possible (use `IF NOT EXISTS`).
261
+ - Never modify an already-applied migration -- create a new one.
262
+ - Test migrations locally before applying to production.
263
+ - D1 tracks applied migrations automatically; manual version tables are unnecessary.
264
+
265
+ ---
266
+
267
+ ## 7. Testing with Miniflare
268
+
269
+ Miniflare provides a local D1 simulator for testing Workers without deploying:
270
+
271
+ ```javascript
272
+ import { Miniflare } from "miniflare";
273
+
274
+ const mf = new Miniflare({
275
+ modules: true,
276
+ script: `export default { async fetch(request, env) { return new Response("ok"); } }`,
277
+ d1Databases: ["DB"],
278
+ });
279
+
280
+ const db = await mf.getD1Database("DB");
281
+
282
+ // Run migrations
283
+ await db.exec(`
284
+ CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT NOT NULL);
285
+ `);
286
+
287
+ // Test queries
288
+ await db.prepare("INSERT INTO users (email) VALUES (?)").bind("test@example.com").run();
289
+ const user = await db.prepare("SELECT * FROM users WHERE email = ?")
290
+ .bind("test@example.com")
291
+ .first();
292
+ console.assert(user.email === "test@example.com");
293
+
294
+ await mf.dispose();
295
+ ```
296
+
297
+ ### Integration Test Pattern
298
+
299
+ ```javascript
300
+ import { describe, it, beforeEach, afterAll } from "vitest";
301
+ import { Miniflare } from "miniflare";
302
+
303
+ describe("User API", () => {
304
+ let mf;
305
+ let db;
306
+
307
+ beforeEach(async () => {
308
+ mf = new Miniflare({ /* config */ });
309
+ db = await mf.getD1Database("DB");
310
+ await db.exec(SCHEMA_SQL);
311
+ });
312
+
313
+ afterAll(async () => {
314
+ await mf?.dispose();
315
+ });
316
+
317
+ it("creates a user", async () => {
318
+ await db.prepare("INSERT INTO users (email) VALUES (?)").bind("a@b.com").run();
319
+ const user = await db.prepare("SELECT * FROM users").first();
320
+ expect(user.email).toBe("a@b.com");
321
+ });
322
+ });
323
+ ```