duck-types 0.1.0 → 0.1.1

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.1] — 2026-10-25
10
+
11
+ ### Changed
12
+
13
+ - README: before/after examples no longer use side-by-side tables, which did
14
+ not fit on GitHub or npm.
15
+ - `duck-types generate` explains itself when it finds no `sql()` calls at all:
16
+ which globs it scanned, under which directory, and that `sql` must be
17
+ imported from `duck-types`.
18
+
9
19
  ## [0.1.0] — 2026-10-24
10
20
 
11
21
  First release.
@@ -30,5 +40,6 @@ First release.
30
40
  `MAP`, `UNION` and `ENUM` resolved exactly; every column and nested element
31
41
  is nullable.
32
42
 
33
- [Unreleased]: https://github.com/gavricci/duck-types/compare/v0.1.0...HEAD
43
+ [Unreleased]: https://github.com/gavricci/duck-types/compare/v0.1.1...HEAD
44
+ [0.1.1]: https://github.com/gavricci/duck-types/compare/v0.1.0...v0.1.1
34
45
  [0.1.0]: https://github.com/gavricci/duck-types/releases/tag/v0.1.0
package/README.md CHANGED
@@ -26,74 +26,49 @@ Run `duck-types generate` once (or in `--watch`), and every `sql('...')` in the
26
26
 
27
27
  **Hand-written row types** — the interface, the cast and the SQL drift apart; nothing checks them.
28
28
 
29
- <table><tr><td>
30
-
31
29
  ```ts
30
+ // before
32
31
  interface OrderRow { id: number; total: number }
33
32
 
34
- const reader = await conn.runAndReadAll(
35
- 'select id, total from orders'
36
- );
33
+ const reader = await conn.runAndReadAll('select id, total from orders');
37
34
  const rows = reader.getRowObjectsJS() as OrderRow[];
38
- ```
39
35
 
40
- </td><td>
41
-
42
- ```ts
36
+ // after — the type is produced by DuckDB and regenerated on change
43
37
  const orders = sql('select id, total from orders');
44
38
 
45
39
  const rows = await db.all(orders);
46
- // { id: number | null; total: number | null }[]
47
- // — produced by DuckDB, regenerated on change
40
+ // ^? { id: number | null; total: number | null }[]
48
41
  ```
49
42
 
50
- </td></tr></table>
51
-
52
43
  **Value normalisers** — every project grows its own `instanceof DuckDBDecimalValue` branch.
53
44
 
54
- <table><tr><td>
55
-
56
45
  ```ts
46
+ // before
57
47
  const rows = reader.getRowObjects().map((r) => ({
58
48
  total: (r.total as DuckDBDecimalValue).toDouble(),
59
- placed_at: new Date(Number(
60
- (r.placed_at as DuckDBTimestampValue).micros / 1000n
61
- )),
49
+ placed_at: new Date(Number((r.placed_at as DuckDBTimestampValue).micros / 1000n)),
62
50
  }));
63
- ```
64
-
65
- </td><td>
66
51
 
67
- ```ts
52
+ // after — the @duckdb/node-api JS converter, and the types say what it returns
68
53
  const rows = await db.all(orders);
69
- rows[0].total; // number
70
- rows[0].placed_at; // Date
71
- // the @duckdb/node-api JS converter,
72
- // and the types say what it returns
54
+ rows[0].total; // number
55
+ rows[0].placed_at; // Date
73
56
  ```
74
57
 
75
- </td></tr></table>
76
-
77
58
  **Caught by `tsc`, not in production** — typos in columns and parameters are compile errors.
78
59
 
79
- <table><tr><td>
80
-
81
60
  ```ts
61
+ // before
82
62
  const rows = reader.getRowObjectsJS();
83
- rows[0].totl; // undefined at runtime
84
- prepared.bind({ name: 'ann' }); // throws at runtime
85
- ```
63
+ rows[0].totl; // undefined at runtime
64
+ prepared.bind({ name: 'ann' }); // throws at runtime
86
65
 
87
- </td><td>
88
-
89
- ```ts
66
+ // after
90
67
  const rows = await db.all(ordersFor, { customer: 'ann' });
91
- rows[0].totl; // error: no property 'totl'
68
+ rows[0].totl; // error: no property 'totl'
92
69
  await db.all(ordersFor, { name: 'ann' }); // error: expected { customer }
93
70
  ```
94
71
 
95
- </td></tr></table>
96
-
97
72
  ## In the editor
98
73
 
99
74
  ![Add a column to the SQL on the right; the row type on the left follows](https://raw.githubusercontent.com/gavricci/duck-types/main/docs/demo.gif)
package/dist/cli.js CHANGED
@@ -240,6 +240,7 @@ async function main(argv, options = {}) {
240
240
  } else await writeFile(out, result.declarations);
241
241
  const typed = result.queries.length - result.diagnostics.length;
242
242
  stderr(`${rel(cwd, out)}: ${typed} of ${result.queries.length} queries typed`);
243
+ if (result.queries.length === 0) stderr(`no sql() calls found in ${generateOptions.include.join(" ")} under ${cwd}; sql must be imported from 'duck-types' (run from the project directory, or pass --cwd)`);
243
244
  return result.diagnostics.length > 0 ? 1 : 0;
244
245
  };
245
246
  const code = await runOnce();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/generate/emit.ts","../src/generate/scan.ts","../src/generate/index.ts","../src/cli/main.ts","../src/cli.ts"],"sourcesContent":["import type { QueryDescription } from '../describe.js';\nimport { nullable, propertyKey, stringLiteral, tsTypeFor } from '../types/emit.js';\n\nexport interface GeneratedEntry {\n text: string;\n description: QueryDescription;\n}\n\n/** Renders the `duck-types.d.ts` module augmentation for the given queries. */\nexport function emitDeclarations(entries: readonly GeneratedEntry[]): string {\n const sorted = [...entries].sort((a, b) => (a.text < b.text ? -1 : a.text > b.text ? 1 : 0));\n const lines: string[] = [\n '// Generated by duck-types. Do not edit.',\n \"import type { JS } from '@duckdb/node-api';\",\n '',\n \"declare module 'duck-types' {\",\n ' interface Queries {',\n ];\n for (const { text, description } of sorted) {\n lines.push(` ${stringLiteral(text)}: {`);\n lines.push(` row: ${rowType(description)};`);\n lines.push(` params: ${paramsType(description)};`);\n lines.push(' };');\n }\n lines.push(' }', '}', '');\n return lines.join('\\n');\n}\n\nexport function rowType(description: QueryDescription): string {\n if (description.columns.length === 0) {\n return '{}';\n }\n const entries = description.columns.map(\n (c) =>\n `${propertyKey(c.key)}: ${c.type === null ? 'unknown' : nullable(tsTypeFor(c.type, 'output'))}`,\n );\n return `{ ${entries.join('; ')} }`;\n}\n\nexport function paramsType(description: QueryDescription): string {\n const { params, positional } = description;\n if (params.length === 0) {\n return '[]';\n }\n const render = (p: QueryDescription['params'][number]) =>\n p.type === null ? 'unknown' : nullable(tsTypeFor(p.type, 'input'));\n if (positional) {\n return `[${params.map(render).join(', ')}]`;\n }\n return `{ ${params.map((p) => `${propertyKey(p.name)}: ${render(p)}`).join('; ')} }`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { parse } from '@babel/parser';\nimport type { Node, SourceLocation } from '@babel/types';\n\nexport interface FoundQuery {\n text: string;\n file: string;\n line: number;\n column: number;\n}\n\n/**\n * Finds every `sql('...')` call in a TypeScript source file whose `sql` is\n * imported from `duck-types`, and returns the literal text passed to it.\n *\n * Only string literals and `${}`-free template literals count: anything else\n * cannot be typed by name, so the generator ignores it the way the type\n * lookup would.\n */\nexport async function scanFile(file: string): Promise<FoundQuery[]> {\n return scanSource(await readFile(file, 'utf8'), file);\n}\n\nexport function scanSource(code: string, file: string): FoundQuery[] {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', ...(file.endsWith('.tsx') ? (['jsx'] as const) : [])],\n errorRecovery: true,\n });\n const names = sqlBindings(ast.program.body);\n if (names.size === 0) {\n return [];\n }\n const found: FoundQuery[] = [];\n walk(ast.program, (node) => {\n if (\n node.type !== 'CallExpression' ||\n node.callee.type !== 'Identifier' ||\n !names.has(node.callee.name) ||\n node.arguments.length !== 1\n ) {\n return;\n }\n const arg = node.arguments[0];\n if (!arg) {\n return;\n }\n let text: string | undefined;\n if (arg.type === 'StringLiteral') {\n text = arg.value;\n } else if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0) {\n text = arg.quasis[0]?.value.cooked ?? undefined;\n }\n if (text !== undefined) {\n found.push({ text, file, ...position(arg.loc) });\n }\n });\n return found;\n}\n\n/** Local names bound to duck-types's `sql`: `import { sql }` and `import { sql as q }`. */\nfunction sqlBindings(statements: readonly Node[]): Set<string> {\n const names = new Set<string>();\n for (const statement of statements) {\n if (statement.type !== 'ImportDeclaration' || statement.source.value !== 'duck-types') {\n continue;\n }\n for (const specifier of statement.specifiers) {\n if (specifier.type !== 'ImportSpecifier') {\n continue;\n }\n const imported =\n specifier.imported.type === 'Identifier'\n ? specifier.imported.name\n : specifier.imported.value;\n if (imported === 'sql') {\n names.add(specifier.local.name);\n }\n }\n }\n return names;\n}\n\nfunction position(loc: SourceLocation | null | undefined): { line: number; column: number } {\n return { line: loc?.start.line ?? 0, column: (loc?.start.column ?? -1) + 1 };\n}\n\nfunction walk(node: Node, visit: (node: Node) => void): void {\n visit(node);\n for (const key of Object.keys(node)) {\n if (key === 'loc' || key === 'leadingComments' || key === 'trailingComments') {\n continue;\n }\n const child = (node as unknown as Record<string, unknown>)[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (isNode(item)) {\n walk(item, visit);\n }\n }\n } else if (isNode(child)) {\n walk(child, visit);\n }\n }\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && typeof (value as Node).type === 'string';\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { DuckDBInstance } from '@duckdb/node-api';\nimport { glob } from 'tinyglobby';\nimport { describeQuery } from '../describe.js';\nimport { emitDeclarations, type GeneratedEntry } from './emit.js';\nimport { type FoundQuery, scanFile } from './scan.js';\n\nexport interface GenerateOptions {\n /** Globs of source files to scan. */\n include: string[];\n /** Database file to describe queries against; in-memory when omitted. */\n database?: string | undefined;\n /** SQL file to run first, e.g. the schema DDL. */\n init?: string | undefined;\n /** Directory globs, `database` and `init` are resolved against; also where relative file paths in SQL point. */\n cwd?: string | undefined;\n}\n\nexport interface Diagnostic {\n query: FoundQuery;\n message: string;\n}\n\nexport interface GenerateResult {\n declarations: string;\n queries: FoundQuery[];\n diagnostics: Diagnostic[];\n}\n\nexport async function generate(options: GenerateOptions): Promise<GenerateResult> {\n const cwd = options.cwd ?? process.cwd();\n const files = await glob(options.include, {\n cwd,\n absolute: true,\n ignore: ['**/node_modules/**'],\n });\n const queries = (await Promise.all(files.sort().map(scanFile))).flat();\n\n const instance = await DuckDBInstance.create(\n options.database ? resolve(cwd, options.database) : ':memory:',\n { access_mode: options.database ? 'READ_ONLY' : 'READ_WRITE' },\n );\n const connection = await instance.connect();\n try {\n // Relative paths in SQL (`read_parquet('data/x.parquet')`) resolve against\n // the process cwd; point DuckDB at the project directory instead.\n await connection.run(`set file_search_path = ${quoted(cwd)}`);\n if (options.init) {\n await connection.run(await readFile(resolve(cwd, options.init), 'utf8'));\n }\n const entries = new Map<string, GeneratedEntry>();\n const diagnostics: Diagnostic[] = [];\n for (const query of queries) {\n if (entries.has(query.text)) {\n continue;\n }\n try {\n const description = await describeQuery(connection, query.text);\n entries.set(query.text, { text: query.text, description });\n for (const p of description.params) {\n if (p.type === null) {\n diagnostics.push({\n query,\n message: `cannot infer the type of parameter $${p.name}; add a cast, e.g. $${p.name}::integer`,\n });\n }\n }\n } catch (error) {\n diagnostics.push({ query, message: describeError(error) });\n }\n }\n return { declarations: emitDeclarations([...entries.values()]), queries, diagnostics };\n } finally {\n connection.closeSync();\n instance.closeSync();\n }\n}\n\nfunction describeError(error: unknown): string {\n const message = (error as Error).message.split('\\n')[0] ?? String(error);\n if (/No files found that match/.test(message)) {\n return `${message} (relative paths resolve against the project directory; pass --cwd to change it)`;\n }\n return message;\n}\n\nfunction quoted(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n","import { watch } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { dirname, relative, resolve, sep } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport { type GenerateOptions, generate } from '../generate/index.js';\n\nexport const USAGE = `Usage: duck-types generate [globs...] [options]\n\nScans TypeScript sources for sql('...') calls, describes each query against\nDuckDB, and writes their row and parameter types as a module augmentation.\n\nOptions:\n -d, --database <file> DuckDB file to describe queries against (opened read-only)\n -i, --init <file> SQL to run first, e.g. schema DDL (in-memory by default)\n -o, --out <file> Output file (default: duck-types.d.ts)\n -c, --cwd <dir> Project directory (default: current directory)\n --check Exit 1 if the output file is out of date; write nothing\n -w, --watch Regenerate when source files change\n -h, --help\n\nDefaults come from duck-types.config.json in the project directory when present:\n { \"include\": [\"src/**/*.ts\"], \"database\": \"...\", \"init\": \"...\", \"out\": \"...\" }\nDefault globs: src/**/*.ts src/**/*.tsx`;\n\nexport interface Config {\n include?: string[];\n database?: string;\n init?: string;\n out?: string;\n}\n\nexport interface MainOptions {\n cwd?: string;\n stdout?: (line: string) => void;\n stderr?: (line: string) => void;\n /** Stops `--watch`. */\n signal?: AbortSignal;\n}\n\nexport async function main(argv: string[], options: MainOptions = {}): Promise<number> {\n const stdout = options.stdout ?? ((line) => console.log(line));\n const stderr = options.stderr ?? ((line) => console.error(line));\n\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n database: { type: 'string', short: 'd' },\n init: { type: 'string', short: 'i' },\n out: { type: 'string', short: 'o' },\n cwd: { type: 'string', short: 'c' },\n check: { type: 'boolean', default: false },\n watch: { type: 'boolean', short: 'w', default: false },\n help: { type: 'boolean', short: 'h', default: false },\n },\n });\n const [command, ...globs] = positionals;\n if (values.help || command !== 'generate') {\n stdout(USAGE);\n return values.help ? 0 : 1;\n }\n\n const cwd = resolve(options.cwd ?? process.cwd(), values.cwd ?? '.');\n const config = await readConfig(cwd);\n const generateOptions: GenerateOptions = {\n include: globs.length > 0 ? globs : (config.include ?? ['src/**/*.ts', 'src/**/*.tsx']),\n database: values.database ?? config.database,\n init: values.init ?? config.init,\n cwd,\n };\n const out = resolve(cwd, values.out ?? config.out ?? 'duck-types.d.ts');\n\n const runOnce = async (): Promise<number> => {\n const result = await generate(generateOptions);\n for (const { query, message } of result.diagnostics) {\n stderr(`${rel(cwd, query.file)}:${query.line}:${query.column}: ${message}`);\n }\n if (values.check) {\n const current = await readFile(out, 'utf8').catch(() => null);\n if (current !== result.declarations) {\n stderr(`${rel(cwd, out)} is out of date; run duck-types generate`);\n return 1;\n }\n } else {\n await writeFile(out, result.declarations);\n }\n const typed = result.queries.length - result.diagnostics.length;\n stderr(`${rel(cwd, out)}: ${typed} of ${result.queries.length} queries typed`);\n return result.diagnostics.length > 0 ? 1 : 0;\n };\n\n const code = await runOnce();\n if (!values.watch) {\n return code;\n }\n await watchAndRerun(cwd, generateOptions.include, out, runOnce, options.signal);\n return 0;\n}\n\n/** A path relative to `cwd`, with `/` separators on every platform so messages are stable. */\nfunction rel(cwd: string, path: string): string {\n return relative(cwd, path).split(sep).join('/');\n}\n\nasync function readConfig(cwd: string): Promise<Config> {\n const text = await readFile(resolve(cwd, 'duck-types.config.json'), 'utf8').catch(() => null);\n return text === null ? {} : (JSON.parse(text) as Config);\n}\n\n/** Watches the directories the globs start in and reruns on any change, debounced. */\nfunction watchAndRerun(\n cwd: string,\n include: string[],\n out: string,\n rerun: () => Promise<number>,\n signal?: AbortSignal,\n): Promise<void> {\n const roots = new Set(include.map((g) => resolve(cwd, globRoot(g))));\n return new Promise((done) => {\n let timer: NodeJS.Timeout | undefined;\n let running = Promise.resolve();\n const watchers = [...roots].map((root) =>\n watch(root, { recursive: true }, (_event, file) => {\n if (file && resolve(root, file) === out) {\n return;\n }\n clearTimeout(timer);\n timer = setTimeout(() => {\n running = running.then(() => rerun().then(() => undefined));\n }, 100);\n }),\n );\n const stop = () => {\n clearTimeout(timer);\n for (const w of watchers) {\n w.close();\n }\n running.then(done);\n };\n if (signal?.aborted) {\n stop();\n } else {\n signal?.addEventListener('abort', stop, { once: true });\n }\n });\n}\n\n/** The literal directory prefix of a glob: `src/**\\/*.ts` → `src`, `*.ts` → `.`. */\nexport function globRoot(pattern: string): string {\n const parts = pattern.split('/');\n const literal: string[] = [];\n for (const part of parts) {\n if (/[*?[{]/.test(part)) {\n break;\n }\n literal.push(part);\n }\n if (literal.length === parts.length) {\n return dirname(pattern);\n }\n return literal.length > 0 ? literal.join('/') : '.';\n}\n","#!/usr/bin/env node\nimport { main } from './cli/main.js';\n\nconst controller = new AbortController();\nprocess.once('SIGINT', () => controller.abort());\nprocess.exitCode = await main(process.argv.slice(2), { signal: controller.signal });\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/generate/emit.ts","../src/generate/scan.ts","../src/generate/index.ts","../src/cli/main.ts","../src/cli.ts"],"sourcesContent":["import type { QueryDescription } from '../describe.js';\nimport { nullable, propertyKey, stringLiteral, tsTypeFor } from '../types/emit.js';\n\nexport interface GeneratedEntry {\n text: string;\n description: QueryDescription;\n}\n\n/** Renders the `duck-types.d.ts` module augmentation for the given queries. */\nexport function emitDeclarations(entries: readonly GeneratedEntry[]): string {\n const sorted = [...entries].sort((a, b) => (a.text < b.text ? -1 : a.text > b.text ? 1 : 0));\n const lines: string[] = [\n '// Generated by duck-types. Do not edit.',\n \"import type { JS } from '@duckdb/node-api';\",\n '',\n \"declare module 'duck-types' {\",\n ' interface Queries {',\n ];\n for (const { text, description } of sorted) {\n lines.push(` ${stringLiteral(text)}: {`);\n lines.push(` row: ${rowType(description)};`);\n lines.push(` params: ${paramsType(description)};`);\n lines.push(' };');\n }\n lines.push(' }', '}', '');\n return lines.join('\\n');\n}\n\nexport function rowType(description: QueryDescription): string {\n if (description.columns.length === 0) {\n return '{}';\n }\n const entries = description.columns.map(\n (c) =>\n `${propertyKey(c.key)}: ${c.type === null ? 'unknown' : nullable(tsTypeFor(c.type, 'output'))}`,\n );\n return `{ ${entries.join('; ')} }`;\n}\n\nexport function paramsType(description: QueryDescription): string {\n const { params, positional } = description;\n if (params.length === 0) {\n return '[]';\n }\n const render = (p: QueryDescription['params'][number]) =>\n p.type === null ? 'unknown' : nullable(tsTypeFor(p.type, 'input'));\n if (positional) {\n return `[${params.map(render).join(', ')}]`;\n }\n return `{ ${params.map((p) => `${propertyKey(p.name)}: ${render(p)}`).join('; ')} }`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { parse } from '@babel/parser';\nimport type { Node, SourceLocation } from '@babel/types';\n\nexport interface FoundQuery {\n text: string;\n file: string;\n line: number;\n column: number;\n}\n\n/**\n * Finds every `sql('...')` call in a TypeScript source file whose `sql` is\n * imported from `duck-types`, and returns the literal text passed to it.\n *\n * Only string literals and `${}`-free template literals count: anything else\n * cannot be typed by name, so the generator ignores it the way the type\n * lookup would.\n */\nexport async function scanFile(file: string): Promise<FoundQuery[]> {\n return scanSource(await readFile(file, 'utf8'), file);\n}\n\nexport function scanSource(code: string, file: string): FoundQuery[] {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', ...(file.endsWith('.tsx') ? (['jsx'] as const) : [])],\n errorRecovery: true,\n });\n const names = sqlBindings(ast.program.body);\n if (names.size === 0) {\n return [];\n }\n const found: FoundQuery[] = [];\n walk(ast.program, (node) => {\n if (\n node.type !== 'CallExpression' ||\n node.callee.type !== 'Identifier' ||\n !names.has(node.callee.name) ||\n node.arguments.length !== 1\n ) {\n return;\n }\n const arg = node.arguments[0];\n if (!arg) {\n return;\n }\n let text: string | undefined;\n if (arg.type === 'StringLiteral') {\n text = arg.value;\n } else if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0) {\n text = arg.quasis[0]?.value.cooked ?? undefined;\n }\n if (text !== undefined) {\n found.push({ text, file, ...position(arg.loc) });\n }\n });\n return found;\n}\n\n/** Local names bound to duck-types's `sql`: `import { sql }` and `import { sql as q }`. */\nfunction sqlBindings(statements: readonly Node[]): Set<string> {\n const names = new Set<string>();\n for (const statement of statements) {\n if (statement.type !== 'ImportDeclaration' || statement.source.value !== 'duck-types') {\n continue;\n }\n for (const specifier of statement.specifiers) {\n if (specifier.type !== 'ImportSpecifier') {\n continue;\n }\n const imported =\n specifier.imported.type === 'Identifier'\n ? specifier.imported.name\n : specifier.imported.value;\n if (imported === 'sql') {\n names.add(specifier.local.name);\n }\n }\n }\n return names;\n}\n\nfunction position(loc: SourceLocation | null | undefined): { line: number; column: number } {\n return { line: loc?.start.line ?? 0, column: (loc?.start.column ?? -1) + 1 };\n}\n\nfunction walk(node: Node, visit: (node: Node) => void): void {\n visit(node);\n for (const key of Object.keys(node)) {\n if (key === 'loc' || key === 'leadingComments' || key === 'trailingComments') {\n continue;\n }\n const child = (node as unknown as Record<string, unknown>)[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (isNode(item)) {\n walk(item, visit);\n }\n }\n } else if (isNode(child)) {\n walk(child, visit);\n }\n }\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && typeof (value as Node).type === 'string';\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { DuckDBInstance } from '@duckdb/node-api';\nimport { glob } from 'tinyglobby';\nimport { describeQuery } from '../describe.js';\nimport { emitDeclarations, type GeneratedEntry } from './emit.js';\nimport { type FoundQuery, scanFile } from './scan.js';\n\nexport interface GenerateOptions {\n /** Globs of source files to scan. */\n include: string[];\n /** Database file to describe queries against; in-memory when omitted. */\n database?: string | undefined;\n /** SQL file to run first, e.g. the schema DDL. */\n init?: string | undefined;\n /** Directory globs, `database` and `init` are resolved against; also where relative file paths in SQL point. */\n cwd?: string | undefined;\n}\n\nexport interface Diagnostic {\n query: FoundQuery;\n message: string;\n}\n\nexport interface GenerateResult {\n declarations: string;\n queries: FoundQuery[];\n diagnostics: Diagnostic[];\n}\n\nexport async function generate(options: GenerateOptions): Promise<GenerateResult> {\n const cwd = options.cwd ?? process.cwd();\n const files = await glob(options.include, {\n cwd,\n absolute: true,\n ignore: ['**/node_modules/**'],\n });\n const queries = (await Promise.all(files.sort().map(scanFile))).flat();\n\n const instance = await DuckDBInstance.create(\n options.database ? resolve(cwd, options.database) : ':memory:',\n { access_mode: options.database ? 'READ_ONLY' : 'READ_WRITE' },\n );\n const connection = await instance.connect();\n try {\n // Relative paths in SQL (`read_parquet('data/x.parquet')`) resolve against\n // the process cwd; point DuckDB at the project directory instead.\n await connection.run(`set file_search_path = ${quoted(cwd)}`);\n if (options.init) {\n await connection.run(await readFile(resolve(cwd, options.init), 'utf8'));\n }\n const entries = new Map<string, GeneratedEntry>();\n const diagnostics: Diagnostic[] = [];\n for (const query of queries) {\n if (entries.has(query.text)) {\n continue;\n }\n try {\n const description = await describeQuery(connection, query.text);\n entries.set(query.text, { text: query.text, description });\n for (const p of description.params) {\n if (p.type === null) {\n diagnostics.push({\n query,\n message: `cannot infer the type of parameter $${p.name}; add a cast, e.g. $${p.name}::integer`,\n });\n }\n }\n } catch (error) {\n diagnostics.push({ query, message: describeError(error) });\n }\n }\n return { declarations: emitDeclarations([...entries.values()]), queries, diagnostics };\n } finally {\n connection.closeSync();\n instance.closeSync();\n }\n}\n\nfunction describeError(error: unknown): string {\n const message = (error as Error).message.split('\\n')[0] ?? String(error);\n if (/No files found that match/.test(message)) {\n return `${message} (relative paths resolve against the project directory; pass --cwd to change it)`;\n }\n return message;\n}\n\nfunction quoted(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n","import { watch } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { dirname, relative, resolve, sep } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport { type GenerateOptions, generate } from '../generate/index.js';\n\nexport const USAGE = `Usage: duck-types generate [globs...] [options]\n\nScans TypeScript sources for sql('...') calls, describes each query against\nDuckDB, and writes their row and parameter types as a module augmentation.\n\nOptions:\n -d, --database <file> DuckDB file to describe queries against (opened read-only)\n -i, --init <file> SQL to run first, e.g. schema DDL (in-memory by default)\n -o, --out <file> Output file (default: duck-types.d.ts)\n -c, --cwd <dir> Project directory (default: current directory)\n --check Exit 1 if the output file is out of date; write nothing\n -w, --watch Regenerate when source files change\n -h, --help\n\nDefaults come from duck-types.config.json in the project directory when present:\n { \"include\": [\"src/**/*.ts\"], \"database\": \"...\", \"init\": \"...\", \"out\": \"...\" }\nDefault globs: src/**/*.ts src/**/*.tsx`;\n\nexport interface Config {\n include?: string[];\n database?: string;\n init?: string;\n out?: string;\n}\n\nexport interface MainOptions {\n cwd?: string;\n stdout?: (line: string) => void;\n stderr?: (line: string) => void;\n /** Stops `--watch`. */\n signal?: AbortSignal;\n}\n\nexport async function main(argv: string[], options: MainOptions = {}): Promise<number> {\n const stdout = options.stdout ?? ((line) => console.log(line));\n const stderr = options.stderr ?? ((line) => console.error(line));\n\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n database: { type: 'string', short: 'd' },\n init: { type: 'string', short: 'i' },\n out: { type: 'string', short: 'o' },\n cwd: { type: 'string', short: 'c' },\n check: { type: 'boolean', default: false },\n watch: { type: 'boolean', short: 'w', default: false },\n help: { type: 'boolean', short: 'h', default: false },\n },\n });\n const [command, ...globs] = positionals;\n if (values.help || command !== 'generate') {\n stdout(USAGE);\n return values.help ? 0 : 1;\n }\n\n const cwd = resolve(options.cwd ?? process.cwd(), values.cwd ?? '.');\n const config = await readConfig(cwd);\n const generateOptions: GenerateOptions = {\n include: globs.length > 0 ? globs : (config.include ?? ['src/**/*.ts', 'src/**/*.tsx']),\n database: values.database ?? config.database,\n init: values.init ?? config.init,\n cwd,\n };\n const out = resolve(cwd, values.out ?? config.out ?? 'duck-types.d.ts');\n\n const runOnce = async (): Promise<number> => {\n const result = await generate(generateOptions);\n for (const { query, message } of result.diagnostics) {\n stderr(`${rel(cwd, query.file)}:${query.line}:${query.column}: ${message}`);\n }\n if (values.check) {\n const current = await readFile(out, 'utf8').catch(() => null);\n if (current !== result.declarations) {\n stderr(`${rel(cwd, out)} is out of date; run duck-types generate`);\n return 1;\n }\n } else {\n await writeFile(out, result.declarations);\n }\n const typed = result.queries.length - result.diagnostics.length;\n stderr(`${rel(cwd, out)}: ${typed} of ${result.queries.length} queries typed`);\n if (result.queries.length === 0) {\n stderr(\n `no sql() calls found in ${generateOptions.include.join(' ')} under ${cwd}; ` +\n \"sql must be imported from 'duck-types' (run from the project directory, or pass --cwd)\",\n );\n }\n return result.diagnostics.length > 0 ? 1 : 0;\n };\n\n const code = await runOnce();\n if (!values.watch) {\n return code;\n }\n await watchAndRerun(cwd, generateOptions.include, out, runOnce, options.signal);\n return 0;\n}\n\n/** A path relative to `cwd`, with `/` separators on every platform so messages are stable. */\nfunction rel(cwd: string, path: string): string {\n return relative(cwd, path).split(sep).join('/');\n}\n\nasync function readConfig(cwd: string): Promise<Config> {\n const text = await readFile(resolve(cwd, 'duck-types.config.json'), 'utf8').catch(() => null);\n return text === null ? {} : (JSON.parse(text) as Config);\n}\n\n/** Watches the directories the globs start in and reruns on any change, debounced. */\nfunction watchAndRerun(\n cwd: string,\n include: string[],\n out: string,\n rerun: () => Promise<number>,\n signal?: AbortSignal,\n): Promise<void> {\n const roots = new Set(include.map((g) => resolve(cwd, globRoot(g))));\n return new Promise((done) => {\n let timer: NodeJS.Timeout | undefined;\n let running = Promise.resolve();\n const watchers = [...roots].map((root) =>\n watch(root, { recursive: true }, (_event, file) => {\n if (file && resolve(root, file) === out) {\n return;\n }\n clearTimeout(timer);\n timer = setTimeout(() => {\n running = running.then(() => rerun().then(() => undefined));\n }, 100);\n }),\n );\n const stop = () => {\n clearTimeout(timer);\n for (const w of watchers) {\n w.close();\n }\n running.then(done);\n };\n if (signal?.aborted) {\n stop();\n } else {\n signal?.addEventListener('abort', stop, { once: true });\n }\n });\n}\n\n/** The literal directory prefix of a glob: `src/**\\/*.ts` → `src`, `*.ts` → `.`. */\nexport function globRoot(pattern: string): string {\n const parts = pattern.split('/');\n const literal: string[] = [];\n for (const part of parts) {\n if (/[*?[{]/.test(part)) {\n break;\n }\n literal.push(part);\n }\n if (literal.length === parts.length) {\n return dirname(pattern);\n }\n return literal.length > 0 ? literal.join('/') : '.';\n}\n","#!/usr/bin/env node\nimport { main } from './cli/main.js';\n\nconst controller = new AbortController();\nprocess.once('SIGINT', () => controller.abort());\nprocess.exitCode = await main(process.argv.slice(2), { signal: controller.signal });\n"],"mappings":";;;;;;;;;;;AASA,SAAgB,iBAAiB,SAA4C;CAC3E,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,CAAE;CAC3F,MAAM,QAAkB;EACtB;EACA;EACA;EACA;EACA;CACF;CACA,KAAK,MAAM,EAAE,MAAM,iBAAiB,QAAQ;EAC1C,MAAM,KAAK,OAAO,cAAc,IAAI,EAAE,IAAI;EAC1C,MAAM,KAAK,cAAc,QAAQ,WAAW,EAAE,EAAE;EAChD,MAAM,KAAK,iBAAiB,WAAW,WAAW,EAAE,EAAE;EACtD,MAAM,KAAK,QAAQ;CACrB;CACA,MAAM,KAAK,OAAO,KAAK,EAAE;CACzB,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAgB,QAAQ,aAAuC;CAC7D,IAAI,YAAY,QAAQ,WAAW,GACjC,OAAO;CAMT,OAAO,KAJS,YAAY,QAAQ,KACjC,MACC,GAAG,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,OAAO,YAAY,SAAS,UAAU,EAAE,MAAM,QAAQ,CAAC,GAE9E,CAAC,CAAC,KAAK,IAAI,EAAE;AACjC;AAEA,SAAgB,WAAW,aAAuC;CAChE,MAAM,EAAE,QAAQ,eAAe;CAC/B,IAAI,OAAO,WAAW,GACpB,OAAO;CAET,MAAM,UAAU,MACd,EAAE,SAAS,OAAO,YAAY,SAAS,UAAU,EAAE,MAAM,OAAO,CAAC;CACnE,IAAI,YACF,OAAO,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE;CAE3C,OAAO,KAAK,OAAO,KAAK,MAAM,GAAG,YAAY,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;AACnF;;;;;;;;;;AC/BA,eAAsB,SAAS,MAAqC;CAClE,OAAO,WAAW,MAAM,SAAS,MAAM,MAAM,GAAG,IAAI;AACtD;AAEA,SAAgB,WAAW,MAAc,MAA4B;CACnE,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY;EACZ,SAAS,CAAC,cAAc,GAAI,KAAK,SAAS,MAAM,IAAK,CAAC,KAAK,IAAc,CAAC,CAAE;EAC5E,eAAe;CACjB,CAAC;CACD,MAAM,QAAQ,YAAY,IAAI,QAAQ,IAAI;CAC1C,IAAI,MAAM,SAAS,GACjB,OAAO,CAAC;CAEV,MAAM,QAAsB,CAAC;CAC7B,KAAK,IAAI,UAAU,SAAS;EAC1B,IACE,KAAK,SAAS,oBACd,KAAK,OAAO,SAAS,gBACrB,CAAC,MAAM,IAAI,KAAK,OAAO,IAAI,KAC3B,KAAK,UAAU,WAAW,GAE1B;EAEF,MAAM,MAAM,KAAK,UAAU;EAC3B,IAAI,CAAC,KACH;EAEF,IAAI;EACJ,IAAI,IAAI,SAAS,iBACf,OAAO,IAAI;OACN,IAAI,IAAI,SAAS,qBAAqB,IAAI,YAAY,WAAW,GACtE,OAAO,IAAI,OAAO,EAAE,EAAE,MAAM,UAAU,KAAA;EAExC,IAAI,SAAS,KAAA,GACX,MAAM,KAAK;GAAE;GAAM;GAAM,GAAG,SAAS,IAAI,GAAG;EAAE,CAAC;CAEnD,CAAC;CACD,OAAO;AACT;;AAGA,SAAS,YAAY,YAA0C;CAC7D,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,UAAU,SAAS,uBAAuB,UAAU,OAAO,UAAU,cACvE;EAEF,KAAK,MAAM,aAAa,UAAU,YAAY;GAC5C,IAAI,UAAU,SAAS,mBACrB;GAMF,KAHE,UAAU,SAAS,SAAS,eACxB,UAAU,SAAS,OACnB,UAAU,SAAS,WACR,OACf,MAAM,IAAI,UAAU,MAAM,IAAI;EAElC;CACF;CACA,OAAO;AACT;AAEA,SAAS,SAAS,KAA0E;CAC1F,OAAO;EAAE,MAAM,KAAK,MAAM,QAAQ;EAAG,SAAS,KAAK,MAAM,UAAU,MAAM;CAAE;AAC7E;AAEA,SAAS,KAAK,MAAY,OAAmC;CAC3D,MAAM,IAAI;CACV,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;EACnC,IAAI,QAAQ,SAAS,QAAQ,qBAAqB,QAAQ,oBACxD;EAEF,MAAM,QAAS,KAA4C;EAC3D,IAAI,MAAM,QAAQ,KAAK,GAChB;QAAA,MAAM,QAAQ,OACjB,IAAI,OAAO,IAAI,GACb,KAAK,MAAM,KAAK;EAAA,OAGf,IAAI,OAAO,KAAK,GACrB,KAAK,OAAO,KAAK;CAErB;AACF;AAEA,SAAS,OAAO,OAA+B;CAC7C,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAQ,MAAe,SAAS;AACxF;;AC9EA,eAAsB,SAAS,SAAmD;CAChF,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,QAAQ,MAAM,KAAK,QAAQ,SAAS;EACxC;EACA,UAAU;EACV,QAAQ,CAAC,oBAAoB;CAC/B,CAAC;CACD,MAAM,WAAW,MAAM,QAAQ,IAAI,MAAM,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,EAAA,CAAG,KAAK;CAErE,MAAM,WAAW,MAAM,eAAe,OACpC,QAAQ,WAAW,QAAQ,KAAK,QAAQ,QAAQ,IAAI,YACpD,EAAE,aAAa,QAAQ,WAAW,cAAc,aAAa,CAC/D;CACA,MAAM,aAAa,MAAM,SAAS,QAAQ;CAC1C,IAAI;EAGF,MAAM,WAAW,IAAI,0BAA0B,OAAO,GAAG,GAAG;EAC5D,IAAI,QAAQ,MACV,MAAM,WAAW,IAAI,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,GAAG,MAAM,CAAC;EAEzE,MAAM,0BAAU,IAAI,IAA4B;EAChD,MAAM,cAA4B,CAAC;EACnC,KAAK,MAAM,SAAS,SAAS;GAC3B,IAAI,QAAQ,IAAI,MAAM,IAAI,GACxB;GAEF,IAAI;IACF,MAAM,cAAc,MAAM,cAAc,YAAY,MAAM,IAAI;IAC9D,QAAQ,IAAI,MAAM,MAAM;KAAE,MAAM,MAAM;KAAM;IAAY,CAAC;IACzD,KAAK,MAAM,KAAK,YAAY,QAC1B,IAAI,EAAE,SAAS,MACb,YAAY,KAAK;KACf;KACA,SAAS,uCAAuC,EAAE,KAAK,sBAAsB,EAAE,KAAK;IACtF,CAAC;GAGP,SAAS,OAAO;IACd,YAAY,KAAK;KAAE;KAAO,SAAS,cAAc,KAAK;IAAE,CAAC;GAC3D;EACF;EACA,OAAO;GAAE,cAAc,iBAAiB,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;GAAG;GAAS;EAAY;CACvF,UAAU;EACR,WAAW,UAAU;EACrB,SAAS,UAAU;CACrB;AACF;AAEA,SAAS,cAAc,OAAwB;CAC7C,MAAM,UAAW,MAAgB,QAAQ,MAAM,IAAI,CAAC,CAAC,MAAM,OAAO,KAAK;CACvE,IAAI,4BAA4B,KAAK,OAAO,GAC1C,OAAO,GAAG,QAAQ;CAEpB,OAAO;AACT;AAEA,SAAS,OAAO,OAAuB;CACrC,OAAO,IAAI,MAAM,WAAW,KAAK,IAAI,EAAE;AACzC;;ACnFA,MAAa,QAAQ;;;;;;;;;;;;;;;;;AAiCrB,eAAsB,KAAK,MAAgB,UAAuB,CAAC,GAAoB;CACrF,MAAM,SAAS,QAAQ,YAAY,SAAS,QAAQ,IAAI,IAAI;CAC5D,MAAM,SAAS,QAAQ,YAAY,SAAS,QAAQ,MAAM,IAAI;CAE9D,MAAM,EAAE,QAAQ,gBAAgB,UAAU;EACxC,MAAM;EACN,kBAAkB;EAClB,SAAS;GACP,UAAU;IAAE,MAAM;IAAU,OAAO;GAAI;GACvC,MAAM;IAAE,MAAM;IAAU,OAAO;GAAI;GACnC,KAAK;IAAE,MAAM;IAAU,OAAO;GAAI;GAClC,KAAK;IAAE,MAAM;IAAU,OAAO;GAAI;GAClC,OAAO;IAAE,MAAM;IAAW,SAAS;GAAM;GACzC,OAAO;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAM;GACrD,MAAM;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAM;EACtD;CACF,CAAC;CACD,MAAM,CAAC,SAAS,GAAG,SAAS;CAC5B,IAAI,OAAO,QAAQ,YAAY,YAAY;EACzC,OAAO,KAAK;EACZ,OAAO,OAAO,OAAO,IAAI;CAC3B;CAEA,MAAM,MAAM,QAAQ,QAAQ,OAAO,QAAQ,IAAI,GAAG,OAAO,OAAO,GAAG;CACnE,MAAM,SAAS,MAAM,WAAW,GAAG;CACnC,MAAM,kBAAmC;EACvC,SAAS,MAAM,SAAS,IAAI,QAAS,OAAO,WAAW,CAAC,eAAe,cAAc;EACrF,UAAU,OAAO,YAAY,OAAO;EACpC,MAAM,OAAO,QAAQ,OAAO;EAC5B;CACF;CACA,MAAM,MAAM,QAAQ,KAAK,OAAO,OAAO,OAAO,OAAO,iBAAiB;CAEtE,MAAM,UAAU,YAA6B;EAC3C,MAAM,SAAS,MAAM,SAAS,eAAe;EAC7C,KAAK,MAAM,EAAE,OAAO,aAAa,OAAO,aACtC,OAAO,GAAG,IAAI,KAAK,MAAM,IAAI,EAAE,GAAG,MAAM,KAAK,GAAG,MAAM,OAAO,IAAI,SAAS;EAE5E,IAAI,OAAO,OAEL;OAAA,MADkB,SAAS,KAAK,MAAM,CAAC,CAAC,YAAY,IAAI,MAC5C,OAAO,cAAc;IACnC,OAAO,GAAG,IAAI,KAAK,GAAG,EAAE,yCAAyC;IACjE,OAAO;GACT;SAEA,MAAM,UAAU,KAAK,OAAO,YAAY;EAE1C,MAAM,QAAQ,OAAO,QAAQ,SAAS,OAAO,YAAY;EACzD,OAAO,GAAG,IAAI,KAAK,GAAG,EAAE,IAAI,MAAM,MAAM,OAAO,QAAQ,OAAO,eAAe;EAC7E,IAAI,OAAO,QAAQ,WAAW,GAC5B,OACE,2BAA2B,gBAAgB,QAAQ,KAAK,GAAG,EAAE,SAAS,IAAI,yFAE5E;EAEF,OAAO,OAAO,YAAY,SAAS,IAAI,IAAI;CAC7C;CAEA,MAAM,OAAO,MAAM,QAAQ;CAC3B,IAAI,CAAC,OAAO,OACV,OAAO;CAET,MAAM,cAAc,KAAK,gBAAgB,SAAS,KAAK,SAAS,QAAQ,MAAM;CAC9E,OAAO;AACT;;AAGA,SAAS,IAAI,KAAa,MAAsB;CAC9C,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;AAChD;AAEA,eAAe,WAAW,KAA8B;CACtD,MAAM,OAAO,MAAM,SAAS,QAAQ,KAAK,wBAAwB,GAAG,MAAM,CAAC,CAAC,YAAY,IAAI;CAC5F,OAAO,SAAS,OAAO,CAAC,IAAK,KAAK,MAAM,IAAI;AAC9C;;AAGA,SAAS,cACP,KACA,SACA,KACA,OACA,QACe;CACf,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAM,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;CACnE,OAAO,IAAI,SAAS,SAAS;EAC3B,IAAI;EACJ,IAAI,UAAU,QAAQ,QAAQ;EAC9B,MAAM,WAAW,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,SAC/B,MAAM,MAAM,EAAE,WAAW,KAAK,IAAI,QAAQ,SAAS;GACjD,IAAI,QAAQ,QAAQ,MAAM,IAAI,MAAM,KAClC;GAEF,aAAa,KAAK;GAClB,QAAQ,iBAAiB;IACvB,UAAU,QAAQ,WAAW,MAAM,CAAC,CAAC,WAAW,KAAA,CAAS,CAAC;GAC5D,GAAG,GAAG;EACR,CAAC,CACH;EACA,MAAM,aAAa;GACjB,aAAa,KAAK;GAClB,KAAK,MAAM,KAAK,UACd,EAAE,MAAM;GAEV,QAAQ,KAAK,IAAI;EACnB;EACA,IAAI,QAAQ,SACV,KAAK;OAEL,QAAQ,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;CAE1D,CAAC;AACH;;AAGA,SAAgB,SAAS,SAAyB;CAChD,MAAM,QAAQ,QAAQ,MAAM,GAAG;CAC/B,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,SAAS,KAAK,IAAI,GACpB;EAEF,QAAQ,KAAK,IAAI;CACnB;CACA,IAAI,QAAQ,WAAW,MAAM,QAC3B,OAAO,QAAQ,OAAO;CAExB,OAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,GAAG,IAAI;AAClD;;ACpKA,MAAM,aAAa,IAAI,gBAAgB;AACvC,QAAQ,KAAK,gBAAgB,WAAW,MAAM,CAAC;AAC/C,QAAQ,WAAW,MAAM,KAAK,QAAQ,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,WAAW,OAAO,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "duck-types",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Type-safe DuckDB client for TypeScript — row types inferred from your SQL, plain JS values out of the box.",
5
5
  "license": "MIT",
6
- "author": "Oleksandr Havrylov",
6
+ "author": "Alex Gavrilov",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/gavricci/duck-types.git"
@@ -39,6 +39,17 @@
39
39
  "engines": {
40
40
  "node": ">=20"
41
41
  },
42
+ "packageManager": "pnpm@10.33.0",
43
+ "scripts": {
44
+ "build": "tsdown",
45
+ "dev": "tsdown --watch",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "typecheck": "tsc --noEmit",
49
+ "lint": "biome check .",
50
+ "lint:fix": "biome check --write .",
51
+ "prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
52
+ },
42
53
  "peerDependencies": {
43
54
  "@duckdb/node-api": ">=2.0.0-r.1"
44
55
  },
@@ -61,14 +72,5 @@
61
72
  },
62
73
  "publishConfig": {
63
74
  "access": "public"
64
- },
65
- "scripts": {
66
- "build": "tsdown",
67
- "dev": "tsdown --watch",
68
- "test": "vitest run",
69
- "test:watch": "vitest",
70
- "typecheck": "tsc --noEmit",
71
- "lint": "biome check .",
72
- "lint:fix": "biome check --write ."
73
75
  }
74
- }
76
+ }