sensemaking 0.9.2 → 0.9.3

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.
@@ -1,5 +1,6 @@
1
1
  import { search } from '../commands.js';
2
2
  import { printRows } from '../output.js';
3
+ import { usageError } from './exit.js';
3
4
  import { CONFIG, FORMAT, formatOf, parse, parseK, runSql, SEARCH_FLAGS, withDb } from './shared.js';
4
5
  // Fallback when the first positional is not a command: a named query from config, either a
5
6
  // SQL string (run as-is) or a saved search (run through the `search` command's own machinery).
@@ -17,11 +18,7 @@ export default async function named(ctx, queryName) {
17
18
  const configPath = values.config;
18
19
  const cfg = ctx.resolveConfig(configPath);
19
20
  const entry = cfg.queries[queryName];
20
- if (entry === undefined) {
21
- console.error(`unknown query: "${queryName}"`);
22
- console.error(`valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);
23
- process.exit(2);
24
- }
21
+ if (entry === undefined) usageError(`unknown query: "${queryName}"`, `valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);
25
22
  if (typeof entry === 'string') {
26
23
  runSql(cfg, entry, params, format, `query "${queryName}"`);
27
24
  return;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/named.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { printRows } from '../output.ts';\nimport { CONFIG, FORMAT, formatOf, parse, parseK, runSql, SEARCH_FLAGS, withDb } from './shared.ts';\nimport type { Ctx } from './types.ts';\n\n// Fallback when the first positional is not a command: a named query from config, either a\n// SQL string (run as-is) or a saved search (run through the `search` command's own machinery).\n// Flags cover both shapes -- SEARCH_FLAGS override a saved search's fields the same way they\n// override a preset's.\nexport default async function named(ctx: Ctx, queryName: string): Promise<void> {\n const usage = `usage: ${ctx.name} ${queryName} [params...] [--format table|json] [--config path] [--where \"<sql>\"] [--k n] [--preset name] [--include glob ...] [--lexical]`;\n const { values, positionals: params } = parse(ctx.argv, usage, { ...SEARCH_FLAGS, ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const configPath = values.config as string | undefined;\n\n const cfg = ctx.resolveConfig(configPath);\n const entry = cfg.queries[queryName];\n if (entry === undefined) {\n console.error(`unknown query: \"${queryName}\"`);\n console.error(`valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);\n process.exit(2);\n }\n\n if (typeof entry === 'string') {\n runSql(cfg, entry, params, format, `query \"${queryName}\"`);\n return;\n }\n if ('sql' in entry) {\n runSql(cfg, entry.sql, params, format, `query \"${queryName}\"`);\n return;\n }\n\n // A saved search's text is fixed in config; there is nowhere for a positional to bind.\n if (params.length > 0) {\n ctx.usageError(`\"${queryName}\" is a saved search and takes no positional parameters; edit its \"search\" in sense.config.json, or use \"${ctx.name} search\" directly`);\n }\n const k = parseK(values.k as string | undefined, ctx.usageError) ?? entry.k;\n const where = (values.where as string | undefined) ?? entry.where;\n const preset = (values.preset as string | undefined) ?? entry.preset;\n const include = (values.include as string[] | undefined) ?? entry.include;\n // --lexical upgrades a saved search's semantic behavior the same way --where/--k\n // override; absent means the saved value (the flag has no default false override).\n const semantic = values.lexical ? false : entry.semantic;\n await withDb(ctx, configPath, async (db, resolvedCfg) => printRows(await search(db, resolvedCfg, entry.search, { k, where, preset, include, semantic }), format));\n}\n"],"names":["search","printRows","CONFIG","FORMAT","formatOf","parse","parseK","runSql","SEARCH_FLAGS","withDb","named","ctx","queryName","values","usage","name","positionals","params","argv","format","configPath","config","cfg","resolveConfig","entry","queries","undefined","console","error","Object","keys","sort","join","process","exit","sql","length","usageError","k","where","preset","include","semantic","lexical","db","resolvedCfg"],"mappings":"AAAA,SAASA,MAAM,QAAQ,iBAAiB;AACxC,SAASC,SAAS,QAAQ,eAAe;AACzC,SAASC,MAAM,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,YAAY,EAAEC,MAAM,QAAQ,cAAc;AAGpG,2FAA2F;AAC3F,+FAA+F;AAC/F,6FAA6F;AAC7F,uBAAuB;AACvB,eAAe,eAAeC,MAAMC,GAAQ,EAAEC,SAAiB;QA2BnDN,SACKO,eACCA,gBACCA;IA7BjB,MAAMC,QAAQ,CAAC,OAAO,EAAEH,IAAII,IAAI,CAAC,CAAC,EAAEH,UAAU,6HAA6H,CAAC;IAC5K,MAAM,EAAEC,MAAM,EAAEG,aAAaC,MAAM,EAAE,GAAGZ,MAAMM,IAAIO,IAAI,EAAEJ,OAAO;QAAE,GAAGN,YAAY;QAAE,GAAGL,MAAM;QAAE,GAAGD,MAAM;IAAC;IACvG,MAAMiB,SAASf,SAASS;IACxB,MAAMO,aAAaP,OAAOQ,MAAM;IAEhC,MAAMC,MAAMX,IAAIY,aAAa,CAACH;IAC9B,MAAMI,QAAQF,IAAIG,OAAO,CAACb,UAAU;IACpC,IAAIY,UAAUE,WAAW;QACvBC,QAAQC,KAAK,CAAC,CAAC,gBAAgB,EAAEhB,UAAU,CAAC,CAAC;QAC7Ce,QAAQC,KAAK,CAAC,CAAC,eAAe,EAAEC,OAAOC,IAAI,CAACR,IAAIG,OAAO,EAAEM,IAAI,GAAGC,IAAI,CAAC,OAAO;QAC5EC,QAAQC,IAAI,CAAC;IACf;IAEA,IAAI,OAAOV,UAAU,UAAU;QAC7BjB,OAAOe,KAAKE,OAAOP,QAAQE,QAAQ,CAAC,OAAO,EAAEP,UAAU,CAAC,CAAC;QACzD;IACF;IACA,IAAI,SAASY,OAAO;QAClBjB,OAAOe,KAAKE,MAAMW,GAAG,EAAElB,QAAQE,QAAQ,CAAC,OAAO,EAAEP,UAAU,CAAC,CAAC;QAC7D;IACF;IAEA,uFAAuF;IACvF,IAAIK,OAAOmB,MAAM,GAAG,GAAG;QACrBzB,IAAI0B,UAAU,CAAC,CAAC,CAAC,EAAEzB,UAAU,wGAAwG,EAAED,IAAII,IAAI,CAAC,iBAAiB,CAAC;IACpK;IACA,MAAMuB,KAAIhC,UAAAA,OAAOO,OAAOyB,CAAC,EAAwB3B,IAAI0B,UAAU,eAArD/B,qBAAAA,UAA0DkB,MAAMc,CAAC;IAC3E,MAAMC,SAAS1B,gBAAAA,OAAO0B,KAAK,cAAZ1B,2BAAAA,gBAAuCW,MAAMe,KAAK;IACjE,MAAMC,UAAU3B,iBAAAA,OAAO2B,MAAM,cAAb3B,4BAAAA,iBAAwCW,MAAMgB,MAAM;IACpE,MAAMC,WAAW5B,kBAAAA,OAAO4B,OAAO,cAAd5B,6BAAAA,kBAA2CW,MAAMiB,OAAO;IACzE,iFAAiF;IACjF,mFAAmF;IACnF,MAAMC,WAAW7B,OAAO8B,OAAO,GAAG,QAAQnB,MAAMkB,QAAQ;IACxD,MAAMjC,OAAOE,KAAKS,YAAY,OAAOwB,IAAIC,cAAgB5C,UAAU,MAAMD,OAAO4C,IAAIC,aAAarB,MAAMxB,MAAM,EAAE;YAAEsC;YAAGC;YAAOC;YAAQC;YAASC;QAAS,IAAIvB;AAC3J"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/named.ts"],"sourcesContent":["import { search } from '../commands.ts';\nimport { printRows } from '../output.ts';\nimport { usageError } from './exit.ts';\nimport { CONFIG, FORMAT, formatOf, parse, parseK, runSql, SEARCH_FLAGS, withDb } from './shared.ts';\nimport type { Ctx } from './types.ts';\n\n// Fallback when the first positional is not a command: a named query from config, either a\n// SQL string (run as-is) or a saved search (run through the `search` command's own machinery).\n// Flags cover both shapes -- SEARCH_FLAGS override a saved search's fields the same way they\n// override a preset's.\nexport default async function named(ctx: Ctx, queryName: string): Promise<void> {\n const usage = `usage: ${ctx.name} ${queryName} [params...] [--format table|json] [--config path] [--where \"<sql>\"] [--k n] [--preset name] [--include glob ...] [--lexical]`;\n const { values, positionals: params } = parse(ctx.argv, usage, { ...SEARCH_FLAGS, ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const configPath = values.config as string | undefined;\n\n const cfg = ctx.resolveConfig(configPath);\n const entry = cfg.queries[queryName];\n if (entry === undefined) usageError(`unknown query: \"${queryName}\"`, `valid queries: ${Object.keys(cfg.queries).sort().join(', ')}`);\n\n if (typeof entry === 'string') {\n runSql(cfg, entry, params, format, `query \"${queryName}\"`);\n return;\n }\n if ('sql' in entry) {\n runSql(cfg, entry.sql, params, format, `query \"${queryName}\"`);\n return;\n }\n\n // A saved search's text is fixed in config; there is nowhere for a positional to bind.\n if (params.length > 0) {\n ctx.usageError(`\"${queryName}\" is a saved search and takes no positional parameters; edit its \"search\" in sense.config.json, or use \"${ctx.name} search\" directly`);\n }\n const k = parseK(values.k as string | undefined, ctx.usageError) ?? entry.k;\n const where = (values.where as string | undefined) ?? entry.where;\n const preset = (values.preset as string | undefined) ?? entry.preset;\n const include = (values.include as string[] | undefined) ?? entry.include;\n // --lexical upgrades a saved search's semantic behavior the same way --where/--k\n // override; absent means the saved value (the flag has no default false override).\n const semantic = values.lexical ? false : entry.semantic;\n await withDb(ctx, configPath, async (db, resolvedCfg) => printRows(await search(db, resolvedCfg, entry.search, { k, where, preset, include, semantic }), format));\n}\n"],"names":["search","printRows","usageError","CONFIG","FORMAT","formatOf","parse","parseK","runSql","SEARCH_FLAGS","withDb","named","ctx","queryName","values","usage","name","positionals","params","argv","format","configPath","config","cfg","resolveConfig","entry","queries","undefined","Object","keys","sort","join","sql","length","k","where","preset","include","semantic","lexical","db","resolvedCfg"],"mappings":"AAAA,SAASA,MAAM,QAAQ,iBAAiB;AACxC,SAASC,SAAS,QAAQ,eAAe;AACzC,SAASC,UAAU,QAAQ,YAAY;AACvC,SAASC,MAAM,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,YAAY,EAAEC,MAAM,QAAQ,cAAc;AAGpG,2FAA2F;AAC3F,+FAA+F;AAC/F,6FAA6F;AAC7F,uBAAuB;AACvB,eAAe,eAAeC,MAAMC,GAAQ,EAAEC,SAAiB;QAuBnDN,SACKO,eACCA,gBACCA;IAzBjB,MAAMC,QAAQ,CAAC,OAAO,EAAEH,IAAII,IAAI,CAAC,CAAC,EAAEH,UAAU,6HAA6H,CAAC;IAC5K,MAAM,EAAEC,MAAM,EAAEG,aAAaC,MAAM,EAAE,GAAGZ,MAAMM,IAAIO,IAAI,EAAEJ,OAAO;QAAE,GAAGN,YAAY;QAAE,GAAGL,MAAM;QAAE,GAAGD,MAAM;IAAC;IACvG,MAAMiB,SAASf,SAASS;IACxB,MAAMO,aAAaP,OAAOQ,MAAM;IAEhC,MAAMC,MAAMX,IAAIY,aAAa,CAACH;IAC9B,MAAMI,QAAQF,IAAIG,OAAO,CAACb,UAAU;IACpC,IAAIY,UAAUE,WAAWzB,WAAW,CAAC,gBAAgB,EAAEW,UAAU,CAAC,CAAC,EAAE,CAAC,eAAe,EAAEe,OAAOC,IAAI,CAACN,IAAIG,OAAO,EAAEI,IAAI,GAAGC,IAAI,CAAC,OAAO;IAEnI,IAAI,OAAON,UAAU,UAAU;QAC7BjB,OAAOe,KAAKE,OAAOP,QAAQE,QAAQ,CAAC,OAAO,EAAEP,UAAU,CAAC,CAAC;QACzD;IACF;IACA,IAAI,SAASY,OAAO;QAClBjB,OAAOe,KAAKE,MAAMO,GAAG,EAAEd,QAAQE,QAAQ,CAAC,OAAO,EAAEP,UAAU,CAAC,CAAC;QAC7D;IACF;IAEA,uFAAuF;IACvF,IAAIK,OAAOe,MAAM,GAAG,GAAG;QACrBrB,IAAIV,UAAU,CAAC,CAAC,CAAC,EAAEW,UAAU,wGAAwG,EAAED,IAAII,IAAI,CAAC,iBAAiB,CAAC;IACpK;IACA,MAAMkB,KAAI3B,UAAAA,OAAOO,OAAOoB,CAAC,EAAwBtB,IAAIV,UAAU,eAArDK,qBAAAA,UAA0DkB,MAAMS,CAAC;IAC3E,MAAMC,SAASrB,gBAAAA,OAAOqB,KAAK,cAAZrB,2BAAAA,gBAAuCW,MAAMU,KAAK;IACjE,MAAMC,UAAUtB,iBAAAA,OAAOsB,MAAM,cAAbtB,4BAAAA,iBAAwCW,MAAMW,MAAM;IACpE,MAAMC,WAAWvB,kBAAAA,OAAOuB,OAAO,cAAdvB,6BAAAA,kBAA2CW,MAAMY,OAAO;IACzE,iFAAiF;IACjF,mFAAmF;IACnF,MAAMC,WAAWxB,OAAOyB,OAAO,GAAG,QAAQd,MAAMa,QAAQ;IACxD,MAAM5B,OAAOE,KAAKS,YAAY,OAAOmB,IAAIC,cAAgBxC,UAAU,MAAMD,OAAOwC,IAAIC,aAAahB,MAAMzB,MAAM,EAAE;YAAEkC;YAAGC;YAAOC;YAAQC;YAASC;QAAS,IAAIlB;AAC3J"}
@@ -1,18 +1,23 @@
1
- import type { ParseArgsOptionsConfig } from 'node:util';
2
1
  import type { ResolvedConfig } from '../config.js';
3
2
  import type { OpenResult } from '../db.js';
4
3
  import type { Ctx } from './types.js';
5
- export declare const FORMAT: ParseArgsOptionsConfig;
6
- export declare const CONFIG: ParseArgsOptionsConfig;
7
- export declare const SEARCH_FLAGS: ParseArgsOptionsConfig;
8
- type Values = Record<string, string | boolean | string[] | undefined>;
4
+ export interface Flag {
5
+ type: 'string' | 'boolean';
6
+ default?: string | boolean;
7
+ multiple?: boolean;
8
+ short?: string;
9
+ }
10
+ export type Flags = Record<string, Flag>;
11
+ export declare const FORMAT: Flags;
12
+ export declare const CONFIG: Flags;
13
+ export declare const SEARCH_FLAGS: Flags;
14
+ export type Values = Record<string, string | boolean | string[] | undefined>;
9
15
  export declare function formatOf(values: Values): 'table' | 'json';
10
- export declare function parse(argv: string[], usage: string, options: ParseArgsOptionsConfig): {
16
+ export declare function parse(argv: string[], usage: string, flags?: Flags): {
11
17
  values: Values;
12
18
  positionals: string[];
13
19
  };
14
- export declare function parseK(k: string | undefined, usageError: (message: string) => never): number | undefined;
20
+ export declare function parseK(k: string | undefined, usageErrorFn: (message: string) => never): number | undefined;
15
21
  export declare function printWarnings(warnings: string[]): void;
16
22
  export declare function withDb(ctx: Ctx, configPath: string | undefined, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void>;
17
23
  export declare function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void;
18
- export {};
@@ -1,8 +1,8 @@
1
- import { parseArgs } from 'node:util';
1
+ import getopts from 'getopts-compat';
2
2
  import { open } from '../db.js';
3
3
  import { printRows } from '../output.js';
4
4
  import { searchError } from '../search-error.js';
5
- // Spreadable option fragments -- one flag name keeps one meaning across every command's table.
5
+ import { ExitError, usageError } from './exit.js';
6
6
  export const FORMAT = {
7
7
  format: {
8
8
  type: 'string',
@@ -36,45 +36,70 @@ export const SEARCH_FLAGS = {
36
36
  export function formatOf(values) {
37
37
  return values.format === 'json' ? 'json' : 'table';
38
38
  }
39
- // Per-command parseArgs: strict (a foreign flag exits 2), and every table gets --help for free.
40
- export function parse(argv, usage, options) {
41
- let values;
42
- let positionals;
43
- try {
44
- ({ values, positionals } = parseArgs({
45
- args: argv,
46
- options: {
47
- ...options,
48
- help: {
49
- type: 'boolean',
50
- default: false,
51
- short: 'h'
52
- }
53
- },
54
- strict: true,
55
- allowPositionals: true
56
- }));
57
- } catch (err) {
58
- console.error(err.message);
59
- console.error(usage);
60
- process.exit(2);
39
+ // Per-command parse: strict (a foreign flag exits 2), and every table gets --help for free.
40
+ export function parse(argv, usage, flags = {}) {
41
+ const table = {
42
+ ...flags,
43
+ help: {
44
+ type: 'boolean',
45
+ default: false,
46
+ short: 'h'
47
+ }
48
+ };
49
+ const string = [];
50
+ const boolean = [];
51
+ const alias = {};
52
+ const defaults = {};
53
+ for (const [name, flag] of Object.entries(table)){
54
+ (flag.type === 'boolean' ? boolean : string).push(name);
55
+ if (flag.default !== undefined) defaults[name] = flag.default;
56
+ if (flag.short !== undefined) alias[name] = flag.short;
61
57
  }
62
- if (values.help) {
58
+ // getopts is lenient about undeclared flags -- they land in the result and are silently
59
+ // ignored, which is the bug 0.9.2 fixed. Record the first and fail below; returning false
60
+ // also keeps it out of the result. `--k -1` arrives here as unknown option "1", because
61
+ // getopts reads a leading dash as a new option rather than as --k's value; the message is
62
+ // blunter than the old one but it is still a usage error rather than a silent default.
63
+ let unknown;
64
+ const parsed = getopts(argv, {
65
+ string,
66
+ boolean,
67
+ alias,
68
+ default: defaults,
69
+ unknown: (name)=>{
70
+ if (unknown === undefined) unknown = name;
71
+ return false;
72
+ }
73
+ });
74
+ if (unknown !== undefined) usageError(`unknown option: ${unknown}`, usage);
75
+ if (parsed.help) {
63
76
  console.log(usage);
64
- process.exit(0);
77
+ throw new ExitError(0);
78
+ }
79
+ // Two getopts shapes the callers must not see: an unset string flag reads as "" rather
80
+ // than undefined, and a flag passed once reads as a string rather than a one-element
81
+ // array. Both matter -- `?? saved.field` means "flag absent", and --include is a list.
82
+ const values = {};
83
+ for (const [name, flag] of Object.entries(table)){
84
+ const raw = parsed[name];
85
+ if (flag.type === 'boolean') values[name] = raw;
86
+ else if (flag.multiple) values[name] = raw === '' ? undefined : Array.isArray(raw) ? raw : [
87
+ raw
88
+ ];
89
+ else values[name] = raw === '' ? undefined : raw;
65
90
  }
66
91
  return {
67
92
  values,
68
- positionals
93
+ positionals: parsed._
69
94
  };
70
95
  }
71
96
  // --k must be a positive integer: SQLite reads a bound LIMIT of -1 as "unlimited" and 0 as
72
97
  // "nothing", and parseInt would silently truncate "5.9" -- all three are caller mistakes
73
98
  // worth a usage error, matching the config-level SavedSearch validation.
74
- export function parseK(k, usageError) {
99
+ export function parseK(k, usageErrorFn) {
75
100
  if (k === undefined) return undefined;
76
101
  const parsed = Number(k);
77
- if (!Number.isInteger(parsed) || parsed <= 0) usageError(`--k expects a positive integer, got "${k}"`);
102
+ if (!Number.isInteger(parsed) || parsed <= 0) usageErrorFn(`--k expects a positive integer, got "${k}"`);
78
103
  return parsed;
79
104
  }
80
105
  // Shared open-query-close envelope for commands that touch the tree.
@@ -95,10 +120,7 @@ export async function withDb(ctx, configPath, fn) {
95
120
  export function runSql(cfg, sql, params, format, label) {
96
121
  var _sql_match;
97
122
  const placeholderCount = ((_sql_match = sql.match(/\?/g)) !== null && _sql_match !== void 0 ? _sql_match : []).length;
98
- if (params.length !== placeholderCount) {
99
- console.error(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);
100
- process.exit(2);
101
- }
123
+ if (params.length !== placeholderCount) usageError(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);
102
124
  const { db, warnings } = open(cfg);
103
125
  printWarnings(warnings);
104
126
  let rows;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/shared.ts"],"sourcesContent":["import type { ParseArgsOptionsConfig } from 'node:util';\nimport { parseArgs } from 'node:util';\nimport type { ResolvedConfig } from '../config.ts';\nimport type { OpenResult } from '../db.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { searchError } from '../search-error.ts';\nimport type { Ctx } from './types.ts';\n\n// Spreadable option fragments -- one flag name keeps one meaning across every command's table.\nexport const FORMAT: ParseArgsOptionsConfig = { format: { type: 'string', default: 'table' } };\nexport const CONFIG: ParseArgsOptionsConfig = { config: { type: 'string' } };\nexport const SEARCH_FLAGS: ParseArgsOptionsConfig = {\n where: { type: 'string' },\n k: { type: 'string' },\n preset: { type: 'string' },\n include: { type: 'string', multiple: true },\n lexical: { type: 'boolean', default: false },\n};\n\ntype Values = Record<string, string | boolean | string[] | undefined>;\n\nexport function formatOf(values: Values): 'table' | 'json' {\n return values.format === 'json' ? 'json' : 'table';\n}\n\n// Per-command parseArgs: strict (a foreign flag exits 2), and every table gets --help for free.\nexport function parse(argv: string[], usage: string, options: ParseArgsOptionsConfig): { values: Values; positionals: string[] } {\n let values: Values;\n let positionals: string[];\n try {\n ({ values, positionals } = parseArgs({\n args: argv,\n options: { ...options, help: { type: 'boolean', default: false, short: 'h' } },\n strict: true,\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage);\n process.exit(2);\n }\n if (values.help) {\n console.log(usage);\n process.exit(0);\n }\n return { values, positionals };\n}\n\n// --k must be a positive integer: SQLite reads a bound LIMIT of -1 as \"unlimited\" and 0 as\n// \"nothing\", and parseInt would silently truncate \"5.9\" -- all three are caller mistakes\n// worth a usage error, matching the config-level SavedSearch validation.\nexport function parseK(k: string | undefined, usageError: (message: string) => never): number | undefined {\n if (k === undefined) return undefined;\n const parsed = Number(k);\n if (!Number.isInteger(parsed) || parsed <= 0) usageError(`--k expects a positive integer, got \"${k}\"`);\n return parsed;\n}\n\n// Shared open-query-close envelope for commands that touch the tree.\n\nexport function printWarnings(warnings: string[]): void {\n for (const w of warnings) console.warn(w);\n}\n\nexport async function withDb(ctx: Ctx, configPath: string | undefined, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void> {\n const cfg = ctx.resolveConfig(configPath);\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n try {\n await fn(db, cfg);\n } finally {\n db.close();\n }\n}\n\n// An unbound `?` silently binds NULL, so mismatched param counts fail loudly instead.\nexport function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void {\n const placeholderCount = (sql.match(/\\?/g) ?? []).length;\n if (params.length !== placeholderCount) {\n console.error(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);\n process.exit(2);\n }\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n let rows: Row[];\n try {\n rows = db.prepare(sql).all(...params) as Row[];\n } catch (err) {\n db.close();\n // A saved query or ad-hoc SQL can carry `content MATCH ?` too, so the same FTS5\n // punctuation trap applies -- the bound parameters are the search terms there. SQL\n // without MATCH gets its error verbatim; search advice on a plain typo would mislead.\n if (/\\bMATCH\\b/i.test(sql)) throw searchError(err as Error, params.join(' '));\n throw err;\n }\n printRows(rows, format);\n db.close();\n}\n"],"names":["parseArgs","open","printRows","searchError","FORMAT","format","type","default","CONFIG","config","SEARCH_FLAGS","where","k","preset","include","multiple","lexical","formatOf","values","parse","argv","usage","options","positionals","args","help","short","strict","allowPositionals","err","console","error","message","process","exit","log","parseK","usageError","undefined","parsed","Number","isInteger","printWarnings","warnings","w","warn","withDb","ctx","configPath","fn","cfg","resolveConfig","db","close","runSql","sql","params","label","placeholderCount","match","length","rows","prepare","all","test","join"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/shared.ts"],"sourcesContent":["import getopts from 'getopts-compat';\nimport type { ResolvedConfig } from '../config.ts';\nimport type { OpenResult } from '../db.ts';\nimport { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { searchError } from '../search-error.ts';\nimport { ExitError, usageError } from './exit.ts';\nimport type { Ctx } from './types.ts';\n\n// Flag descriptors, spreadable so one flag name keeps one meaning across every command's\n// table. parse() converts them to the getopts call the rest of the CLIs here make directly.\nexport interface Flag {\n type: 'string' | 'boolean';\n default?: string | boolean;\n multiple?: boolean;\n short?: string;\n}\nexport type Flags = Record<string, Flag>;\n\nexport const FORMAT: Flags = { format: { type: 'string', default: 'table' } };\nexport const CONFIG: Flags = { config: { type: 'string' } };\nexport const SEARCH_FLAGS: Flags = {\n where: { type: 'string' },\n k: { type: 'string' },\n preset: { type: 'string' },\n include: { type: 'string', multiple: true },\n lexical: { type: 'boolean', default: false },\n};\n\nexport type Values = Record<string, string | boolean | string[] | undefined>;\n\nexport function formatOf(values: Values): 'table' | 'json' {\n return values.format === 'json' ? 'json' : 'table';\n}\n\n// Per-command parse: strict (a foreign flag exits 2), and every table gets --help for free.\nexport function parse(argv: string[], usage: string, flags: Flags = {}): { values: Values; positionals: string[] } {\n const table: Flags = { ...flags, help: { type: 'boolean', default: false, short: 'h' } };\n const string: string[] = [];\n const boolean: string[] = [];\n const alias: Record<string, string> = {};\n const defaults: Record<string, string | boolean> = {};\n for (const [name, flag] of Object.entries(table)) {\n (flag.type === 'boolean' ? boolean : string).push(name);\n if (flag.default !== undefined) defaults[name] = flag.default;\n if (flag.short !== undefined) alias[name] = flag.short;\n }\n\n // getopts is lenient about undeclared flags -- they land in the result and are silently\n // ignored, which is the bug 0.9.2 fixed. Record the first and fail below; returning false\n // also keeps it out of the result. `--k -1` arrives here as unknown option \"1\", because\n // getopts reads a leading dash as a new option rather than as --k's value; the message is\n // blunter than the old one but it is still a usage error rather than a silent default.\n let unknown: string | undefined;\n const parsed = getopts(argv, {\n string,\n boolean,\n alias,\n default: defaults,\n unknown: (name: string): boolean => {\n if (unknown === undefined) unknown = name;\n return false;\n },\n });\n if (unknown !== undefined) usageError(`unknown option: ${unknown}`, usage);\n if (parsed.help) {\n console.log(usage);\n throw new ExitError(0);\n }\n\n // Two getopts shapes the callers must not see: an unset string flag reads as \"\" rather\n // than undefined, and a flag passed once reads as a string rather than a one-element\n // array. Both matter -- `?? saved.field` means \"flag absent\", and --include is a list.\n const values: Values = {};\n for (const [name, flag] of Object.entries(table)) {\n const raw = parsed[name];\n if (flag.type === 'boolean') values[name] = raw as boolean;\n else if (flag.multiple) values[name] = raw === '' ? undefined : Array.isArray(raw) ? (raw as string[]) : [raw as string];\n else values[name] = raw === '' ? undefined : (raw as string);\n }\n return { values, positionals: parsed._ };\n}\n\n// --k must be a positive integer: SQLite reads a bound LIMIT of -1 as \"unlimited\" and 0 as\n// \"nothing\", and parseInt would silently truncate \"5.9\" -- all three are caller mistakes\n// worth a usage error, matching the config-level SavedSearch validation.\nexport function parseK(k: string | undefined, usageErrorFn: (message: string) => never): number | undefined {\n if (k === undefined) return undefined;\n const parsed = Number(k);\n if (!Number.isInteger(parsed) || parsed <= 0) usageErrorFn(`--k expects a positive integer, got \"${k}\"`);\n return parsed;\n}\n\n// Shared open-query-close envelope for commands that touch the tree.\n\nexport function printWarnings(warnings: string[]): void {\n for (const w of warnings) console.warn(w);\n}\n\nexport async function withDb(ctx: Ctx, configPath: string | undefined, fn: (db: OpenResult['db'], cfg: ResolvedConfig) => void | Promise<void>): Promise<void> {\n const cfg = ctx.resolveConfig(configPath);\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n try {\n await fn(db, cfg);\n } finally {\n db.close();\n }\n}\n\n// An unbound `?` silently binds NULL, so mismatched param counts fail loudly instead.\nexport function runSql(cfg: ResolvedConfig, sql: string, params: string[], format: 'table' | 'json', label: string): void {\n const placeholderCount = (sql.match(/\\?/g) ?? []).length;\n if (params.length !== placeholderCount) usageError(`${label} expects ${placeholderCount} parameter(s), got ${params.length}`);\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n let rows: Row[];\n try {\n rows = db.prepare(sql).all(...params) as Row[];\n } catch (err) {\n db.close();\n // A saved query or ad-hoc SQL can carry `content MATCH ?` too, so the same FTS5\n // punctuation trap applies -- the bound parameters are the search terms there. SQL\n // without MATCH gets its error verbatim; search advice on a plain typo would mislead.\n if (/\\bMATCH\\b/i.test(sql)) throw searchError(err as Error, params.join(' '));\n throw err;\n }\n printRows(rows, format);\n db.close();\n}\n"],"names":["getopts","open","printRows","searchError","ExitError","usageError","FORMAT","format","type","default","CONFIG","config","SEARCH_FLAGS","where","k","preset","include","multiple","lexical","formatOf","values","parse","argv","usage","flags","table","help","short","string","boolean","alias","defaults","name","flag","Object","entries","push","undefined","unknown","parsed","console","log","raw","Array","isArray","positionals","_","parseK","usageErrorFn","Number","isInteger","printWarnings","warnings","w","warn","withDb","ctx","configPath","fn","cfg","resolveConfig","db","close","runSql","sql","params","label","placeholderCount","match","length","rows","prepare","all","err","test","join"],"mappings":"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"}
package/dist/esm/cli.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { createRequire } from 'node:module';
2
- import { parseArgs } from 'node:util';
2
+ import exit from 'exit-compat';
3
+ import getopts from 'getopts-compat';
4
+ import { ExitError, usageError } from './cli/exit.js';
3
5
  import { COMMANDS, USAGE } from './cli/index.js';
4
6
  import { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.js';
5
7
  // Parsing and dispatch only. Commands live in src/cli/, one file each, lazy-loaded --
6
8
  // nothing tree- or dependency-heavy may be imported at the top of this file. Flags parse
7
9
  // per command (each command's own parse() call); this file only handles the bare top-level
8
- // flags (--version/--help/--list/--config) and dispatch.
10
+ // flags (--version/--help/--list/--config) and dispatch, and owns the single exit.
9
11
  // Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.
10
12
  function packageVersion() {
11
13
  const load = createRequire(import.meta.url);
@@ -40,87 +42,79 @@ function resolveConfigFor(name, configPath) {
40
42
  }
41
43
  return cfg;
42
44
  }
43
- // Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.
44
- export default async function cli(argv, name) {
45
- const first = argv[0];
46
- // No command word, or the first token looks like a flag: the only flags this level knows.
47
- if (!first || first.startsWith('-')) {
48
- let values;
49
- try {
50
- ({ values } = parseArgs({
51
- args: argv,
52
- options: {
53
- version: {
54
- type: 'boolean',
55
- default: false,
56
- short: 'v'
57
- },
58
- help: {
59
- type: 'boolean',
60
- default: false,
61
- short: 'h'
62
- },
63
- list: {
64
- type: 'boolean',
65
- default: false
66
- },
67
- config: {
68
- type: 'string'
69
- }
70
- },
71
- allowPositionals: true
72
- }));
73
- } catch (err) {
74
- console.error(err.message);
75
- console.error(usage(name));
76
- process.exit(2);
45
+ // The top level knows four flags and nothing else; everything past the command word is the
46
+ // command's to parse. Reached only when there is no command word, or the first token is a flag.
47
+ function topLevel(argv, name) {
48
+ let unknown;
49
+ const opts = getopts(argv, {
50
+ string: [
51
+ 'config'
52
+ ],
53
+ boolean: [
54
+ 'version',
55
+ 'help',
56
+ 'list'
57
+ ],
58
+ alias: {
59
+ version: 'v',
60
+ help: 'h'
61
+ },
62
+ default: {
63
+ version: false,
64
+ help: false,
65
+ list: false
66
+ },
67
+ unknown: (flag)=>{
68
+ if (unknown === undefined) unknown = flag;
69
+ return false;
77
70
  }
78
- try {
79
- if (values.version) {
80
- console.log(`v${packageVersion()}`);
81
- return;
82
- }
83
- if (values.help) {
84
- console.log(usage(name));
85
- return;
86
- }
87
- if (values.list) {
88
- const queries = resolveConfigFor(name, values.config).queries;
89
- for (const queryName of Object.keys(queries).sort()){
90
- const entry = queries[queryName];
91
- const isSavedSearch = typeof entry === 'object' && entry !== null && 'search' in entry;
92
- console.log(isSavedSearch ? `${queryName} (search)` : queryName);
93
- }
94
- return;
95
- }
96
- } catch (err) {
97
- console.error(err.message);
98
- process.exit(1);
99
- }
100
- console.error(usage(name));
101
- process.exit(2);
71
+ });
72
+ if (unknown !== undefined) usageError(`unknown option: ${unknown}`, usage(name));
73
+ if (opts.version) {
74
+ console.log(packageVersion());
75
+ return;
102
76
  }
103
- // find -> search (breaking rename): one release of a pointer instead of "unknown query".
104
- if (first === 'find') {
105
- console.error(`${name}: find is now search`);
106
- console.error(usage(name));
107
- process.exit(2);
77
+ if (opts.help) {
78
+ console.log(usage(name));
79
+ return;
108
80
  }
109
- const ctx = {
110
- name,
111
- argv: argv.slice(1),
112
- resolveConfig: (configPath)=>resolveConfigFor(name, configPath),
113
- usageError (message) {
114
- console.error(message);
115
- process.exit(2);
81
+ if (opts.list) {
82
+ const queries = resolveConfigFor(name, opts.config === '' ? undefined : opts.config).queries;
83
+ for (const queryName of Object.keys(queries).sort()){
84
+ const entry = queries[queryName];
85
+ const isSavedSearch = typeof entry === 'object' && entry !== null && 'search' in entry;
86
+ console.log(isSavedSearch ? `${queryName} (search)` : queryName);
116
87
  }
117
- };
88
+ return;
89
+ }
90
+ console.error(usage(name));
91
+ throw new ExitError(2);
92
+ }
93
+ // ExitError carries a chosen code; anything else is an error -> exit 1 with its message.
94
+ export default async function cli(argv, name) {
118
95
  try {
96
+ const first = argv[0];
97
+ if (!first || first.startsWith('-')) {
98
+ topLevel(argv, name);
99
+ return;
100
+ }
101
+ // find -> search (breaking rename): one release of a pointer instead of "unknown query".
102
+ if (first === 'find') usageError(`${name}: find is now search`, usage(name));
103
+ const ctx = {
104
+ name,
105
+ argv: argv.slice(1),
106
+ resolveConfig: (configPath)=>resolveConfigFor(name, configPath),
107
+ usageError: (message)=>usageError(message)
108
+ };
119
109
  const load = COMMANDS[first];
120
110
  if (load) await (await load()).default(ctx);
121
111
  else await (await import('./cli/named.js')).default(ctx, first);
122
112
  } catch (err) {
113
+ if (err instanceof ExitError) {
114
+ exit(err.code);
115
+ return;
116
+ }
123
117
  console.error(err.message);
124
- process.exit(1);
118
+ exit(1);
125
119
  }
126
120
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS, USAGE } from './cli/index.ts';\nimport type { Ctx } from './cli/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/cli/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file. Flags parse\n// per command (each command's own parse() call); this file only handles the bare top-level\n// flags (--version/--help/--list/--config) and dispatch.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n const lines = Object.values(USAGE).map((u) => ` ${name} ${u}`);\n return [`usage: ${name} <name> [params...] [--format table|json] [--config path]`, ...lines, ` ${name} --list`, ` ${name} --version`].join('\\n');\n}\n\nfunction resolveConfigFor(name: string, configPath: string | undefined) {\n const cfg = loadConfig(configPath);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n if (cfg.unknownKeys !== undefined) {\n console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);\n }\n return cfg;\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n const first = argv[0];\n\n // No command word, or the first token looks like a flag: the only flags this level knows.\n if (!first || first.startsWith('-')) {\n let values: Record<string, string | boolean | undefined>;\n try {\n ({ values } = parseArgs({\n args: argv,\n options: {\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n list: { type: 'boolean', default: false },\n config: { type: 'string' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n try {\n if (values.version) {\n console.log(`v${packageVersion()}`);\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n if (values.list) {\n const queries = resolveConfigFor(name, values.config as string | undefined).queries;\n for (const queryName of Object.keys(queries).sort()) {\n const entry = queries[queryName];\n const isSavedSearch = typeof entry === 'object' && entry !== null && 'search' in entry;\n console.log(isSavedSearch ? `${queryName} (search)` : queryName);\n }\n return;\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n\n console.error(usage(name));\n process.exit(2);\n }\n\n // find -> search (breaking rename): one release of a pointer instead of \"unknown query\".\n if (first === 'find') {\n console.error(`${name}: find is now search`);\n console.error(usage(name));\n process.exit(2);\n }\n\n const ctx: Ctx = {\n name,\n argv: argv.slice(1),\n resolveConfig: (configPath) => resolveConfigFor(name, configPath),\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else await (await import('./cli/named.ts')).default(ctx, first);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","USAGE","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","lines","Object","values","map","u","join","resolveConfigFor","configPath","cfg","migratedFrom","undefined","console","warn","unknownKeys","cli","argv","first","startsWith","args","options","type","default","short","help","list","config","allowPositionals","err","error","message","process","exit","log","queries","queryName","keys","sort","entry","isSavedSearch","ctx","slice","resolveConfig","usageError"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport exit from 'exit-compat';\nimport getopts from 'getopts-compat';\nimport { ExitError, usageError } from './cli/exit.ts';\nimport { COMMANDS, USAGE } from './cli/index.ts';\nimport type { Ctx } from './cli/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/cli/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file. Flags parse\n// per command (each command's own parse() call); this file only handles the bare top-level\n// flags (--version/--help/--list/--config) and dispatch, and owns the single exit.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n const lines = Object.values(USAGE).map((u) => ` ${name} ${u}`);\n return [`usage: ${name} <name> [params...] [--format table|json] [--config path]`, ...lines, ` ${name} --list`, ` ${name} --version`].join('\\n');\n}\n\nfunction resolveConfigFor(name: string, configPath: string | undefined) {\n const cfg = loadConfig(configPath);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n if (cfg.unknownKeys !== undefined) {\n console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);\n }\n return cfg;\n}\n\n// The top level knows four flags and nothing else; everything past the command word is the\n// command's to parse. Reached only when there is no command word, or the first token is a flag.\nfunction topLevel(argv: string[], name: string): void {\n let unknown: string | undefined;\n const opts = getopts(argv, {\n string: ['config'],\n boolean: ['version', 'help', 'list'],\n alias: { version: 'v', help: 'h' },\n default: { version: false, help: false, list: false },\n unknown: (flag: string): boolean => {\n if (unknown === undefined) unknown = flag;\n return false;\n },\n });\n if (unknown !== undefined) usageError(`unknown option: ${unknown}`, usage(name));\n\n if (opts.version) {\n console.log(packageVersion());\n return;\n }\n if (opts.help) {\n console.log(usage(name));\n return;\n }\n if (opts.list) {\n const queries = resolveConfigFor(name, opts.config === '' ? undefined : (opts.config as string)).queries;\n for (const queryName of Object.keys(queries).sort()) {\n const entry = queries[queryName];\n const isSavedSearch = typeof entry === 'object' && entry !== null && 'search' in entry;\n console.log(isSavedSearch ? `${queryName} (search)` : queryName);\n }\n return;\n }\n console.error(usage(name));\n throw new ExitError(2);\n}\n\n// ExitError carries a chosen code; anything else is an error -> exit 1 with its message.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n try {\n const first = argv[0];\n if (!first || first.startsWith('-')) {\n topLevel(argv, name);\n return;\n }\n\n // find -> search (breaking rename): one release of a pointer instead of \"unknown query\".\n if (first === 'find') usageError(`${name}: find is now search`, usage(name));\n\n const ctx: Ctx = {\n name,\n argv: argv.slice(1),\n resolveConfig: (configPath) => resolveConfigFor(name, configPath),\n usageError: (message) => usageError(message),\n };\n\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else await (await import('./cli/named.ts')).default(ctx, first);\n } catch (err) {\n if (err instanceof ExitError) {\n exit(err.code);\n return;\n }\n console.error((err as Error).message);\n exit(1);\n }\n}\n"],"names":["createRequire","exit","getopts","ExitError","usageError","COMMANDS","USAGE","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","lines","Object","values","map","u","join","resolveConfigFor","configPath","cfg","migratedFrom","undefined","console","warn","unknownKeys","topLevel","argv","unknown","opts","string","boolean","alias","help","default","list","flag","log","queries","config","queryName","keys","sort","entry","isSavedSearch","error","cli","first","startsWith","ctx","slice","resolveConfig","message","err","code"],"mappings":"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"}
package/dist/esm/db.js CHANGED
@@ -1,3 +1,8 @@
1
+ // The package's Node floor (>=22.16) is set here and nowhere else: node:sqlite arrived in
2
+ // 22.5, but FTS5 -- which search.ts's whole lexical half is built on -- and
3
+ // StatementSync.columns() both landed in 22.16. 22.15 fails with "no such module: fts5".
4
+ // Nothing outside this file, features/, and commands.ts needs anything past Node 12, so
5
+ // raise the floor only for a sqlite capability, and lower it for nothing.
1
6
  import { mkdirSync, rmSync } from 'node:fs';
2
7
  import { join } from 'node:path';
3
8
  import { DatabaseSync } from 'node:sqlite';