@chrismo/superkit 1.1.0 → 1.2.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=recipes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.test.d.ts","sourceRoot":"","sources":["../src/recipes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,142 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { execFileSync } from 'child_process';
3
+ import { readFileSync, readdirSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { superRecipes } from './lib/recipes.js';
6
+ const recipesDir = join(import.meta.dirname, '..', 'docs', 'recipes');
7
+ // Load all .spq files and strip skdoc blocks — they contain nested
8
+ // brackets in example fields that super can't parse as valid syntax.
9
+ // Keep only the actual fn/op implementations.
10
+ const allSpqFiles = readdirSync(recipesDir)
11
+ .filter(f => f.endsWith('.spq'))
12
+ .sort();
13
+ // Strip skdoc blocks and functions that can't be parsed by super -I
14
+ // (sk_shell_quote has nested quotes in f-strings that break the file parser)
15
+ const UNPARSEABLE_FNS = [
16
+ 'sk_shell_quote', // nested quotes in f-string
17
+ 'sk_merge_records', // string literal with braces
18
+ 'sk_chr', // uses `let` keyword (broken in current super)
19
+ 'sk_alpha', // depends on sk_chr
20
+ 'sk_seq', // depends on sk_chr (via sk_pad_left, but also broken)
21
+ 'sk_add_ids', // uses `that` (old syntax for `this`)
22
+ ];
23
+ function stripSkdocBlocks(content) {
24
+ const lines = content.split('\n');
25
+ const result = [];
26
+ let inSkdoc = false;
27
+ let inUnparseable = false;
28
+ let skipNextClosingParen = false;
29
+ let parenDepth = 0;
30
+ for (const line of lines) {
31
+ // Strip skdoc metadata blocks
32
+ if (!inSkdoc && !inUnparseable && /^(?:fn|op)\s+skdoc_/.test(line)) {
33
+ inSkdoc = true;
34
+ continue;
35
+ }
36
+ if (inSkdoc) {
37
+ if (line.includes('<skdoc>)')) {
38
+ inSkdoc = false;
39
+ skipNextClosingParen = true;
40
+ }
41
+ continue;
42
+ }
43
+ if (skipNextClosingParen) {
44
+ if (line.trim() === '') {
45
+ result.push(line);
46
+ continue;
47
+ }
48
+ if (line.trim() === ')') {
49
+ skipNextClosingParen = false;
50
+ continue;
51
+ }
52
+ skipNextClosingParen = false;
53
+ }
54
+ // Strip functions that cause parse errors when loaded via -I
55
+ if (!inUnparseable) {
56
+ const fnMatch = line.match(/^(?:fn|op)\s+(\w+)/);
57
+ if (fnMatch && UNPARSEABLE_FNS.includes(fnMatch[1])) {
58
+ inUnparseable = true;
59
+ parenDepth = 0;
60
+ for (const ch of line) {
61
+ if (ch === '(')
62
+ parenDepth++;
63
+ if (ch === ')')
64
+ parenDepth--;
65
+ }
66
+ if (parenDepth <= 0)
67
+ inUnparseable = false;
68
+ continue;
69
+ }
70
+ }
71
+ if (inUnparseable) {
72
+ for (const ch of line) {
73
+ if (ch === '(')
74
+ parenDepth++;
75
+ if (ch === ')')
76
+ parenDepth--;
77
+ }
78
+ if (parenDepth <= 0)
79
+ inUnparseable = false;
80
+ continue;
81
+ }
82
+ result.push(line);
83
+ }
84
+ return result.join('\n');
85
+ }
86
+ import { writeFileSync } from 'fs';
87
+ import { tmpdir } from 'os';
88
+ const allDefinitions = allSpqFiles
89
+ .map(f => stripSkdocBlocks(readFileSync(join(recipesDir, f), 'utf-8')))
90
+ .join('\n');
91
+ function normalizeOutput(s) {
92
+ return s
93
+ .replace(/^'(.*)'$/, '"$1"')
94
+ .trim();
95
+ }
96
+ // Write stripped defs to a file that super can load with -I
97
+ const defsFile = join(tmpdir(), 'superkit-test-defs.spq');
98
+ writeFileSync(defsFile, allDefinitions);
99
+ function runSuper(query) {
100
+ const result = execFileSync('super', ['-I', defsFile, '-s', '-c', query], {
101
+ encoding: 'utf-8',
102
+ timeout: 10_000,
103
+ });
104
+ return result.trim();
105
+ }
106
+ // Functions stripped from defs that can't be tested
107
+ const SKIPPED_FNS = new Set(UNPARSEABLE_FNS);
108
+ const { recipes } = superRecipes();
109
+ // Skip examples that are prose descriptions, not executable assertions
110
+ function isExecutableExample(example) {
111
+ const nonExecutable = [
112
+ 'quoted and wrapped', 'single-quoted', 'tabs replaced',
113
+ 'newlines replaced', 'safely embedded', 'properly escaped',
114
+ 'single record', 'arbitrary user', 'formatted',
115
+ ];
116
+ return !nonExecutable.some(s => example.o.toLowerCase().includes(s));
117
+ }
118
+ describe('recipe skdoc examples', () => {
119
+ for (const recipe of recipes) {
120
+ if (SKIPPED_FNS.has(recipe.name))
121
+ continue;
122
+ // Skip shell-pattern recipes (type: "shell") — not SuperDB functions
123
+ if (recipe.type === 'shell')
124
+ continue;
125
+ for (const example of recipe.examples) {
126
+ if (!isExecutableExample(example))
127
+ continue;
128
+ it(`${recipe.name}: ${example.i}`, () => {
129
+ // Some examples already include "values", don't double-prefix
130
+ const query = example.i.startsWith('values ')
131
+ ? example.i
132
+ : `values ${example.i}`;
133
+ const actual = runSuper(query);
134
+ const expected = normalizeOutput(example.o);
135
+ // Strip type decorators (e.g., "29::uint8" → "29") for comparison
136
+ const actualClean = actual.replace(/::\w+$/, '');
137
+ expect(actualClean).toBe(expected);
138
+ });
139
+ }
140
+ }
141
+ });
142
+ //# sourceMappingURL=recipes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.test.js","sourceRoot":"","sources":["../src/recipes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAEtE,mEAAmE;AACnE,qEAAqE;AACrE,8CAA8C;AAC9C,MAAM,WAAW,GAAG,WAAW,CAAC,UAAU,CAAC;KACxC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KAC/B,IAAI,EAAE,CAAC;AAEV,oEAAoE;AACpE,6EAA6E;AAC7E,MAAM,eAAe,GAAG;IACtB,gBAAgB,EAAG,4BAA4B;IAC/C,kBAAkB,EAAE,6BAA6B;IACjD,QAAQ,EAAW,+CAA+C;IAClE,UAAU,EAAS,oBAAoB;IACvC,QAAQ,EAAW,uDAAuD;IAC1E,YAAY,EAAO,sCAAsC;CAC1D,CAAC;AAEF,SAAS,gBAAgB,CAAC,OAAe;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,oBAAoB,GAAG,KAAK,CAAC;IACjC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,8BAA8B;QAC9B,IAAI,CAAC,OAAO,IAAI,CAAC,aAAa,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,OAAO,GAAG,KAAK,CAAC;gBAChB,oBAAoB,GAAG,IAAI,CAAC;YAC9B,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,oBAAoB,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClB,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACxB,oBAAoB,GAAG,KAAK,CAAC;gBAC7B,SAAS;YACX,CAAC;YACD,oBAAoB,GAAG,KAAK,CAAC;QAC/B,CAAC;QAED,6DAA6D;QAC7D,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YACjD,IAAI,OAAO,IAAI,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpD,aAAa,GAAG,IAAI,CAAC;gBACrB,UAAU,GAAG,CAAC,CAAC;gBACf,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;oBACtB,IAAI,EAAE,KAAK,GAAG;wBAAE,UAAU,EAAE,CAAC;oBAC7B,IAAI,EAAE,KAAK,GAAG;wBAAE,UAAU,EAAE,CAAC;gBAC/B,CAAC;gBACD,IAAI,UAAU,IAAI,CAAC;oBAAE,aAAa,GAAG,KAAK,CAAC;gBAC3C,SAAS;YACX,CAAC;QACH,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;gBACtB,IAAI,EAAE,KAAK,GAAG;oBAAE,UAAU,EAAE,CAAC;gBAC7B,IAAI,EAAE,KAAK,GAAG;oBAAE,UAAU,EAAE,CAAC;YAC/B,CAAC;YACD,IAAI,UAAU,IAAI,CAAC;gBAAE,aAAa,GAAG,KAAK,CAAC;YAC3C,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,MAAM,cAAc,GAAG,WAAW;KAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;KACtE,IAAI,CAAC,IAAI,CAAC,CAAC;AAEd,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC;SACL,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC;SAC3B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,4DAA4D;AAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC;AAC1D,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AAExC,SAAS,QAAQ,CAAC,KAAa;IAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE;QACxE,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,MAAM;KAChB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,oDAAoD;AACpD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;AAE7C,MAAM,EAAE,OAAO,EAAE,GAAG,YAAY,EAAE,CAAC;AAEnC,uEAAuE;AACvE,SAAS,mBAAmB,CAAC,OAAiC;IAC5D,MAAM,aAAa,GAAG;QACpB,oBAAoB,EAAE,eAAe,EAAE,eAAe;QACtD,mBAAmB,EAAE,iBAAiB,EAAE,kBAAkB;QAC1D,eAAe,EAAE,gBAAgB,EAAE,WAAW;KAC/C,CAAC;IACF,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3C,qEAAqE;QACrE,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;YAAE,SAAS;QAEtC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;gBAAE,SAAS;YAE5C,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE;gBACtC,8DAA8D;gBAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC;oBAC3C,CAAC,CAAC,OAAO,CAAC,CAAC;oBACX,CAAC,CAAC,UAAU,OAAO,CAAC,CAAC,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC5C,kEAAkE;gBAClE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBACjD,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -9,7 +9,7 @@ op skdoc_seq: (
9
9
  examples:[{i:"sk_seq(3)",o:"0, 1, 2"}] }, <skdoc>)
10
10
  )
11
11
 
12
- op sk_seq(n): (
12
+ op sk_seq n: (
13
13
  split(sk_pad_left('', '0', n), '')
14
14
  | unnest this
15
15
  | count
@@ -21,7 +21,7 @@ fn skdoc_csv_row(): (
21
21
  type:"func",
22
22
  desc:"Builds a CSV row from an array of values. Each element is cast to string and escaped with sk_csv_field, then joined with commas.",
23
23
  args:[{name:"arr",desc:"Array of values to format as a CSV row"}],
24
- examples:[{i:"sk_csv_row(arr) where arr has commas",o:"fields with commas get quoted"}] }, <skdoc>)
24
+ examples:[{i:"sk_csv_row(['a', 'b,c', 'd'])",o:"'a,\"b,c\",d'"}] }, <skdoc>)
25
25
  )
26
26
 
27
27
  fn sk_csv_row(arr): (
@@ -24,8 +24,8 @@ Returns a slice of the string passed in, even if indexes are out of range.
24
24
  | `end` | Ending index, exclusive. |
25
25
 
26
26
  ```supersql
27
- sk_slice('howdy')
28
- -- => 'Howdy'
27
+ sk_slice('howdy', 0, 3)
28
+ -- => 'how'
29
29
  ```
30
30
 
31
31
  **Implementation:**
@@ -241,9 +241,9 @@ op sk_decode_seg s: (
241
241
  )
242
242
 
243
243
  op sk_urldecode url: (
244
- split(url, "%")
244
+ split(url, "%%")
245
245
  | unnest this
246
- | decode_seg this
246
+ | sk_decode_seg this
247
247
  | collect(this)
248
248
  | join(this, "")
249
249
  )
@@ -9,7 +9,7 @@ op skdoc_slice: (
9
9
  args:[{name:"s",desc:"The string to slice."}
10
10
  {name:"start",desc:"Starting index, zero-based, inclusive."}
11
11
  {name:"end",desc:"Ending index, exclusive."}],
12
- examples:[{i:"sk_slice('howdy')",o:"'Howdy'"}] }, <skdoc>)
12
+ examples:[{i:"sk_slice('howdy', 0, 3)",o:"'how'"}] }, <skdoc>)
13
13
  )
14
14
 
15
15
  -- This isn't necessary with zq, but during early releases of super, the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrismo/superkit",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "SuperDB toolkit — docs, recipes, grok patterns, and CLI tools for the super binary",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",