@chrismo/superkit 1.0.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 (74) hide show
  1. package/LICENSE.txt +29 -0
  2. package/README.md +26 -0
  3. package/dist/cli/pager.d.ts +6 -0
  4. package/dist/cli/pager.d.ts.map +1 -0
  5. package/dist/cli/pager.js +21 -0
  6. package/dist/cli/pager.js.map +1 -0
  7. package/dist/cli/skdoc.d.ts +3 -0
  8. package/dist/cli/skdoc.d.ts.map +1 -0
  9. package/dist/cli/skdoc.js +42 -0
  10. package/dist/cli/skdoc.js.map +1 -0
  11. package/dist/cli/skgrok.d.ts +3 -0
  12. package/dist/cli/skgrok.d.ts.map +1 -0
  13. package/dist/cli/skgrok.js +21 -0
  14. package/dist/cli/skgrok.js.map +1 -0
  15. package/dist/cli/skops.d.ts +3 -0
  16. package/dist/cli/skops.d.ts.map +1 -0
  17. package/dist/cli/skops.js +32 -0
  18. package/dist/cli/skops.js.map +1 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/docs.d.ts +11 -0
  24. package/dist/lib/docs.d.ts.map +1 -0
  25. package/dist/lib/docs.js +29 -0
  26. package/dist/lib/docs.js.map +1 -0
  27. package/dist/lib/expert-sections.d.ts +32 -0
  28. package/dist/lib/expert-sections.d.ts.map +1 -0
  29. package/dist/lib/expert-sections.js +130 -0
  30. package/dist/lib/expert-sections.js.map +1 -0
  31. package/dist/lib/grok.d.ts +15 -0
  32. package/dist/lib/grok.d.ts.map +1 -0
  33. package/dist/lib/grok.js +57 -0
  34. package/dist/lib/grok.js.map +1 -0
  35. package/dist/lib/help.d.ts +20 -0
  36. package/dist/lib/help.d.ts.map +1 -0
  37. package/dist/lib/help.js +163 -0
  38. package/dist/lib/help.js.map +1 -0
  39. package/dist/lib/recipes.d.ts +29 -0
  40. package/dist/lib/recipes.d.ts.map +1 -0
  41. package/dist/lib/recipes.js +133 -0
  42. package/dist/lib/recipes.js.map +1 -0
  43. package/dist/superkit.tar.gz +0 -0
  44. package/docs/grok-patterns.sup +89 -0
  45. package/docs/recipes/array.md +66 -0
  46. package/docs/recipes/array.spq +31 -0
  47. package/docs/recipes/character.md +110 -0
  48. package/docs/recipes/character.spq +57 -0
  49. package/docs/recipes/escape.md +159 -0
  50. package/docs/recipes/escape.spq +102 -0
  51. package/docs/recipes/format.md +51 -0
  52. package/docs/recipes/format.spq +24 -0
  53. package/docs/recipes/index.md +23 -0
  54. package/docs/recipes/integer.md +101 -0
  55. package/docs/recipes/integer.spq +53 -0
  56. package/docs/recipes/records.md +84 -0
  57. package/docs/recipes/records.spq +61 -0
  58. package/docs/recipes/string.md +177 -0
  59. package/docs/recipes/string.spq +105 -0
  60. package/docs/superdb-expert.md +929 -0
  61. package/docs/tutorials/bash_to_sup.md +123 -0
  62. package/docs/tutorials/chess-tiebreaks.md +233 -0
  63. package/docs/tutorials/debug.md +439 -0
  64. package/docs/tutorials/fork_for_window.md +296 -0
  65. package/docs/tutorials/grok.md +166 -0
  66. package/docs/tutorials/index.md +10 -0
  67. package/docs/tutorials/joins.md +79 -0
  68. package/docs/tutorials/moar_subqueries.md +35 -0
  69. package/docs/tutorials/subqueries.md +236 -0
  70. package/docs/tutorials/sup_to_bash.md +164 -0
  71. package/docs/tutorials/super_db_update.md +34 -0
  72. package/docs/tutorials/unnest.md +113 -0
  73. package/docs/zq-to-super-upgrades.md +549 -0
  74. package/package.json +46 -0
@@ -0,0 +1,439 @@
1
+ ---
2
+ title: "debug"
3
+ name: debug
4
+ description: "Guide to the debug operator for tapping pipelines and inspecting intermediate values."
5
+ layout: default
6
+ nav_order: 3
7
+ parent: Tutorials
8
+ superdb_version: "0.3.0"
9
+ last_updated: "2026-03-28"
10
+ ---
11
+
12
+ # debug
13
+
14
+ The `debug` operator lets you tap into a pipeline to inspect intermediate values
15
+ without affecting the main output. It has this signature:
16
+
17
+ ```
18
+ debug [ <expr> ] [ filter ( <pred> ) ]
19
+ ```
20
+
21
+ Debug output goes to **stderr** in SUP format, while the main pipeline flows
22
+ through to stdout unchanged. This makes it a non-invasive way to see what's
23
+ happening inside a query.
24
+
25
+ ## Basic usage
26
+
27
+ With no arguments, `debug` sends every value to stderr as-is. The main pipeline
28
+ is unaffected. Use `| where false` to suppress normal output so you can see
29
+ just the debug side:
30
+
31
+ ```mdtest-command
32
+ super -s -c "
33
+ values 1, 2, 3
34
+ | debug
35
+ | where false
36
+ " 2>&1
37
+ ```
38
+ ```mdtest-output
39
+ 1
40
+ 2
41
+ 3
42
+ ```
43
+
44
+ All three values flowed through debug to stderr. Meanwhile the main output
45
+ (stdout) is empty because `where false` filtered everything. Without `2>&1`,
46
+ you'd see the debug output on your terminal's stderr while stdout stays empty.
47
+
48
+ Now look at just the main pipeline — suppressing stderr with `2>/dev/null`:
49
+
50
+ ```mdtest-command
51
+ super -s -c "
52
+ values 1, 2, 3
53
+ | debug
54
+ | where this > 1
55
+ " 2>/dev/null
56
+ ```
57
+ ```mdtest-output
58
+ 2
59
+ 3
60
+ ```
61
+
62
+ The `debug` operator didn't change what passes through. Only values greater
63
+ than 1 made it past the `where` filter.
64
+
65
+ ## Debug with an expression
66
+
67
+ You can transform what gets emitted to debug output by providing an expression.
68
+ This is useful for adding labels or extracting specific fields. The expression
69
+ only affects what goes to stderr — the pipeline still sees the original values.
70
+
71
+ ```mdtest-command
72
+ super -s -c "
73
+ values 10, 20, 30
74
+ | debug this * 2
75
+ | where false
76
+ " 2>&1
77
+ ```
78
+ ```mdtest-output
79
+ 20
80
+ 40
81
+ 60
82
+ ```
83
+
84
+ The debug output shows doubled values. The main pipeline (if we hadn't filtered
85
+ it) would still see 10, 20, 30.
86
+
87
+ You can wrap values in a record to add context:
88
+
89
+ ```mdtest-command
90
+ super -s -c "
91
+ values {name:\"alice\",age:30}, {name:\"bob\",age:17}
92
+ | debug {check:name}
93
+ | where false
94
+ " 2>&1
95
+ ```
96
+ ```mdtest-output
97
+ {check:"alice"}
98
+ {check:"bob"}
99
+ ```
100
+
101
+ ## Debug with filter
102
+
103
+ The `filter` clause controls **which values trigger debug output**. Only values
104
+ matching the predicate are emitted to stderr. This is syntax specific to the
105
+ `debug` operator — not the standalone `where` operator.
106
+
107
+ ```mdtest-command
108
+ super -s -c "
109
+ values 1, 2, 3, 4, 5
110
+ | debug filter (this > 3)
111
+ | where false
112
+ " 2>&1
113
+ ```
114
+ ```mdtest-output
115
+ 4
116
+ 5
117
+ ```
118
+
119
+ Only 4 and 5 matched the filter, so only they appeared in debug output. All
120
+ five values still pass through the main pipeline regardless.
121
+
122
+ You can combine an expression with a filter:
123
+
124
+ ```mdtest-command
125
+ super -s -c "
126
+ values {x:1,y:2}, {x:3,y:4}
127
+ | debug y filter (x=1)
128
+ | where false
129
+ " 2>&1
130
+ ```
131
+ ```mdtest-output
132
+ 2
133
+ ```
134
+
135
+ This emits `y` to debug output, but only for records where `x=1`.
136
+
137
+ ## Practical example: grading with debug alerts
138
+
139
+ Here's a more realistic use case. Say you're processing exam scores — you want
140
+ to add a `pass` field to every record, write the results to a file, and get
141
+ alerts on stderr for anyone who failed badly.
142
+
143
+ A single command does all three. Redirect stdout to a file and the debug
144
+ alerts appear on your terminal via stderr:
145
+
146
+ ```mdtest-command
147
+ super -s -c "
148
+ values
149
+ {name:\"alice\",score:85},
150
+ {name:\"bob\",score:42},
151
+ {name:\"carol\",score:91},
152
+ {name:\"dave\",score:67}
153
+ | debug f'FAIL: {name} ({score})' filter (score < 70)
154
+ | put pass:=score >= 70
155
+ | sort name
156
+ " > /tmp/scores.sup
157
+ ```
158
+ ```mdtest-output
159
+ "FAIL: bob (42)"
160
+ "FAIL: dave (67)"
161
+ ```
162
+
163
+ The failures showed up on your terminal while the results went to the file.
164
+ Every student has the new `pass` field:
165
+
166
+ ```mdtest-command
167
+ cat /tmp/scores.sup
168
+ ```
169
+ ```mdtest-output
170
+ {name:"alice",score:85,pass:true}
171
+ {name:"bob",score:42,pass:false}
172
+ {name:"carol",score:91,pass:true}
173
+ {name:"dave",score:67,pass:false}
174
+ ```
175
+
176
+ The `debug` operator didn't change the pipeline — every record flows through
177
+ with `pass` added. It just tapped into the stream to flag the failures on
178
+ stderr.
179
+
180
+ ## Advanced: debug with a subquery
181
+
182
+ Since `debug` operates per-value, it can't aggregate across the whole stream by
183
+ itself. But you can use `collect` to gather all records, then use a `(...)`
184
+ lateral subquery inside debug to compute a summary.
185
+
186
+ Building on the previous example, let's add a count of total failures to the
187
+ debug output. The trick is: first debug the per-record failures, then collect
188
+ into an array, debug the count via a subquery, and unnest back out:
189
+
190
+ ```mdtest-command
191
+ super -s -c "
192
+ values
193
+ {name:\"alice\",score:85},
194
+ {name:\"bob\",score:42},
195
+ {name:\"carol\",score:91},
196
+ {name:\"dave\",score:67}
197
+ | put pass:=score >= 70
198
+ | debug f'FAIL: {name} ({score})' filter (pass=false)
199
+ | collect(this)
200
+ | debug (unnest this
201
+ | where pass=false
202
+ | count()
203
+ | values f'{this} student(s) failed')
204
+ | unnest this
205
+ | sort name
206
+ " > /tmp/scores.sup
207
+ ```
208
+ ```mdtest-output
209
+ "FAIL: bob (42)"
210
+ "FAIL: dave (67)"
211
+ "2 student(s) failed"
212
+ ```
213
+
214
+ The first `debug` fires per-record, flagging each failure. Then after `collect`
215
+ gathers everything into a single array, the second `debug` runs a `(...)`
216
+ subquery that unnests the array, filters to failures, counts them, and formats
217
+ a summary.
218
+
219
+ The file still has the same clean output:
220
+
221
+ ```mdtest-command
222
+ cat /tmp/scores.sup
223
+ ```
224
+ ```mdtest-output
225
+ {name:"alice",score:85,pass:true}
226
+ {name:"bob",score:42,pass:false}
227
+ {name:"carol",score:91,pass:true}
228
+ {name:"dave",score:67,pass:false}
229
+ ```
230
+
231
+ ## Alternative: fork instead of collect/unnest
232
+
233
+ The `collect`/`unnest` sandwich works, but it buffers the entire dataset into
234
+ memory. The `fork` operator offers an alternative — each branch processes the
235
+ data independently:
236
+
237
+ ```mdtest-command
238
+ super -s -c "
239
+ values
240
+ {name:\"alice\",score:85},
241
+ {name:\"bob\",score:42},
242
+ {name:\"carol\",score:91},
243
+ {name:\"dave\",score:67}
244
+ | put pass:=score >= 70
245
+ | fork
246
+ (debug f'FAIL: {name} ({score})' filter (pass=false)
247
+ | where pass=false
248
+ | count()
249
+ | debug f'{this} student(s) failed'
250
+ | where false)
251
+ (sort name)
252
+ " > /tmp/scores.sup
253
+ ```
254
+ ```mdtest-output
255
+ "FAIL: bob (42)"
256
+ "FAIL: dave (67)"
257
+ "2 student(s) failed"
258
+ ```
259
+
260
+ ```mdtest-command
261
+ cat /tmp/scores.sup
262
+ ```
263
+ ```mdtest-output
264
+ {name:"alice",score:85,pass:true}
265
+ {name:"bob",score:42,pass:false}
266
+ {name:"carol",score:91,pass:true}
267
+ {name:"dave",score:67,pass:false}
268
+ ```
269
+
270
+ The first fork branch handles all the debug output: per-record failure alerts,
271
+ then filters to just failures, counts them, and emits a summary via a second
272
+ `debug`. The `where false` at the end drops everything from that branch's stdout.
273
+ The second branch is just the clean main output.
274
+
275
+ Note that the `filter` clause on `debug` only controls what goes to stderr — it
276
+ doesn't filter the branch itself. That's why `where pass=false` is needed again
277
+ before `count()`.
278
+
279
+ ### Streaming behavior
280
+
281
+ The key advantage of `fork` is that the main branch can emit results
282
+ immediately while the debug branch accumulates. With `collect`/`unnest`,
283
+ nothing can emit until the entire dataset is buffered.
284
+
285
+ You can see this with a large input — pipe a million numbers in, fork into a
286
+ debug count and a main branch that filters to milestones:
287
+
288
+ ```mdtest-command
289
+ seq 1 1000000 | super -s -c "
290
+ fork
291
+ (count() | debug f'{this} total records' | where false)
292
+ (where this % 100000 = 0 | head 5)
293
+ " -
294
+ ```
295
+ ```mdtest-output
296
+ 100000
297
+ 200000
298
+ 300000
299
+ 400000
300
+ 500000
301
+ "1000001 total records"
302
+ ```
303
+
304
+ The milestones appear **before** the debug count — they streamed through as the
305
+ data flowed. Compare with `collect`/`unnest`, where the count appears **first**
306
+ because everything must be buffered before any output:
307
+
308
+ ```mdtest-command
309
+ seq 1 1000000 | super -s -c "
310
+ collect(this)
311
+ | debug (unnest this | count() | values f'{this} total records')
312
+ | unnest this
313
+ | where this % 100000 = 0
314
+ | head 5
315
+ " -
316
+ ```
317
+ ```mdtest-output
318
+ "1000001 total records"
319
+ 100000
320
+ 200000
321
+ 300000
322
+ 400000
323
+ 500000
324
+ ```
325
+
326
+ `fork` also uses O(1) memory for the counting branch (just an integer
327
+ accumulator), while `collect` stores every record in an array (O(n)). The
328
+ `collect`/`unnest` approach is more compact and keeps the aggregation in a
329
+ single self-contained subquery, but `fork` scales better.
330
+
331
+ ## Mixing SQL and pipeline debug
332
+
333
+ SQL and pipeline syntax can be mixed freely. Use SQL `SELECT` for the
334
+ relational logic, then pipe into `debug` for instrumentation. This also
335
+ shows `FROM (values ...)` — inline data in a SQL FROM clause:
336
+
337
+ ```mdtest-command
338
+ super -s -c "
339
+ SELECT *, score >= 70 as pass
340
+ FROM (values
341
+ {name:\"alice\",score:85},
342
+ {name:\"bob\",score:42},
343
+ {name:\"carol\",score:91},
344
+ {name:\"dave\",score:67})
345
+ | fork
346
+ (debug f'FAIL: {name} ({score})' filter (pass=false)
347
+ | where pass=false
348
+ | count()
349
+ | debug f'{this} student(s) failed'
350
+ | where false)
351
+ (sort name)
352
+ " > /tmp/scores.sup
353
+ ```
354
+ ```mdtest-output
355
+ "FAIL: bob (42)"
356
+ "FAIL: dave (67)"
357
+ "2 student(s) failed"
358
+ ```
359
+
360
+ ```mdtest-command
361
+ cat /tmp/scores.sup
362
+ ```
363
+ ```mdtest-output
364
+ {name:"alice",score:85,pass:true}
365
+ {name:"bob",score:42,pass:false}
366
+ {name:"carol",score:91,pass:true}
367
+ {name:"dave",score:67,pass:false}
368
+ ```
369
+
370
+ The SQL handles the data shaping (`SELECT *, score >= 70 as pass`), then the
371
+ pipeline takes over for the debug side-channel. This is a natural split — use
372
+ each syntax for what it's best at.
373
+
374
+ ## Using fn and op with debug
375
+
376
+ User-defined functions work in debug's `filter` clause, which is a clean way to
377
+ name your predicates:
378
+
379
+ ```mdtest-command
380
+ super -s -c "
381
+ fn is_fail(s): s < 70
382
+ values {name:\"alice\",score:85},{name:\"bob\",score:42}
383
+ | debug f'FAIL: {name} ({score})' filter (is_fail(score))
384
+ | where false
385
+ " 2>&1
386
+ ```
387
+ ```mdtest-output
388
+ "FAIL: bob (42)"
389
+ ```
390
+
391
+ Debug also works inside `op` bodies, so you can package up a debug-instrumented
392
+ pipeline for reuse:
393
+
394
+ ```mdtest-command
395
+ super -s -c "
396
+ op grade_report: (
397
+ fork
398
+ (debug f'FAIL: {name} ({score})' filter (score < 70)
399
+ | where false)
400
+ (put pass:=score >= 70 | sort name)
401
+ )
402
+ values {name:\"alice\",score:85},{name:\"bob\",score:42},{name:\"carol\",score:91},{name:\"dave\",score:67}
403
+ | grade_report
404
+ " > /tmp/scores.sup
405
+ ```
406
+ ```mdtest-output
407
+ "FAIL: bob (42)"
408
+ "FAIL: dave (67)"
409
+ ```
410
+
411
+ ```mdtest-command
412
+ cat /tmp/scores.sup
413
+ ```
414
+ ```mdtest-output
415
+ {name:"alice",score:85,pass:true}
416
+ {name:"bob",score:42,pass:false}
417
+ {name:"carol",score:91,pass:true}
418
+ {name:"dave",score:67,pass:false}
419
+ ```
420
+
421
+ ## Notes
422
+
423
+ - Debug output is always in SUP format, even when the main output uses `-j`,
424
+ `-f csv`, etc.
425
+ - The `filter` clause is part of the `debug` operator's syntax, not a separate
426
+ pipeline stage.
427
+ - `debug` passes all input values through to its output unchanged, whether or
428
+ not they match the filter.
429
+ - When using the superdb-mcp server, debug output is returned in a `debug`
430
+ field in the query result.
431
+
432
+ ## as of versions
433
+
434
+ ```mdtest-command
435
+ super --version
436
+ ```
437
+ ```mdtest-output
438
+ Version: v0.3.0
439
+ ```