Skip to content

Commit 1f35f55

Browse files
chore: regenerate llms-full.txt
1 parent c17c6f3 commit 1f35f55

1 file changed

Lines changed: 106 additions & 10 deletions

File tree

llms-full.txt

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ src/
4747
schema.ts
4848
types.ts
4949
validator.ts
50+
writer.ts
5051
tests/
5152
fixtures/
5253
duplicate-vars.sql
@@ -166,16 +167,17 @@ jobs:
166167
import { loadTemplate } from './loader';
167168
import { validateAndConvert, escapeValue } from './validator';
168169
import { render } from './renderer';
170+
import { writeRendered } from './writer';
169171
import type { SchemaDefinition } from './schema';
170172
import type {
171-
QueryResult, GenericQueryFn, SchemaQueryFn,
173+
QueryResult, QueryOptions, GenericQueryFn, SchemaQueryFn,
172174
} from './types';
173175

174176
export { SQL_INJECTION_PATTERNS } from './validator';
175177
export { schema } from './schema';
176178
export type { TypeDescriptor, SchemaDefinition, InferParams } from './schema';
177179
export type {
178-
QueryResult, GenericQueryFn, SchemaQueryFn,
180+
QueryResult, QueryOptions, GenericQueryFn, SchemaQueryFn,
179181
} from './types';
180182

181183
export function defineQuery<S extends SchemaDefinition>(
@@ -188,7 +190,7 @@ export function defineQuery<T extends Record<string, string | number | boolean>>
188190
export function defineQuery(
189191
filePath: string,
190192
schemaDef?: SchemaDefinition,
191-
): (params: Record<string, unknown>) => QueryResult {
193+
): (params: Record<string, unknown>, options?: QueryOptions) => QueryResult {
192194
const { template, tokens } = loadTemplate(filePath);
193195

194196
if (schemaDef) {
@@ -218,7 +220,7 @@ export function defineQuery(
218220
}
219221
}
220222

221-
return (params) => {
223+
return (params, options) => {
222224
const paramKeys = Object.keys(params);
223225

224226
const missing = tokens.filter((tok) => !paramKeys.includes(tok));
@@ -252,7 +254,13 @@ export function defineQuery(
252254
}
253255
}
254256

255-
return { sql: render(template, values) };
257+
const sql = render(template, values);
258+
259+
if (options?.exportTo) {
260+
writeRendered(options.exportTo, sql);
261+
}
262+
263+
return { sql };
256264
};
257265
}
258266
````
@@ -383,9 +391,16 @@ export interface QueryResult {
383391
sql: string;
384392
}
385393

386-
export type GenericQueryFn<T> = (params: T) => QueryResult;
394+
export interface QueryOptions {
395+
exportTo?: string;
396+
}
387397

388-
export type SchemaQueryFn<S extends SchemaDefinition> = (params: InferParams<S>) => QueryResult;
398+
export type GenericQueryFn<T> = (params: T, options?: QueryOptions) => QueryResult;
399+
400+
export type SchemaQueryFn<S extends SchemaDefinition> = (
401+
params: InferParams<S>,
402+
options?: QueryOptions,
403+
) => QueryResult;
389404
````
390405

391406
## File: src/validator.ts
@@ -453,6 +468,18 @@ export function validateAndConvert(key: string, value: unknown): string {
453468
}
454469
````
455470

471+
## File: src/writer.ts
472+
````typescript
473+
import fs from 'node:fs';
474+
import path from 'node:path';
475+
476+
export function writeRendered(filePath: string, sql: string): void {
477+
const resolved = path.resolve(filePath);
478+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
479+
fs.writeFileSync(resolved, sql, 'utf-8');
480+
}
481+
````
482+
456483
## File: tests/fixtures/duplicate-vars.sql
457484
````sql
458485
SELECT
@@ -498,8 +525,10 @@ WHERE
498525

499526
## File: tests/define-query.test.ts
500527
````typescript
528+
import fs from 'node:fs';
529+
import os from 'node:os';
501530
import path from 'node:path';
502-
import { describe, it, expect } from 'vitest';
531+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
503532
import { defineQuery, schema } from '../src/index';
504533

505534
const fixture = (name: string) => path.join(__dirname, 'fixtures', name);
@@ -740,6 +769,54 @@ describe('defineQuery — schema mode', () => {
740769
});
741770
});
742771

772+
describe('exportTo option', () => {
773+
let tmpDir: string;
774+
775+
beforeEach(() => {
776+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sql-render-'));
777+
});
778+
779+
afterEach(() => {
780+
fs.rmSync(tmpDir, { recursive: true, force: true });
781+
});
782+
783+
it('writes rendered sql to the given file', () => {
784+
const query = defineQuery(fixture('simple.sql'), {
785+
table: schema.identifier,
786+
id: schema.number,
787+
});
788+
const outPath = path.join(tmpDir, 'rendered.sql');
789+
const { sql } = query({ table: 'users', id: 33 }, { exportTo: outPath });
790+
expect(fs.readFileSync(outPath, 'utf-8')).toBe(sql);
791+
});
792+
793+
it('creates parent directories recursively', () => {
794+
const query = defineQuery(fixture('simple.sql'), {
795+
table: schema.identifier,
796+
id: schema.number,
797+
});
798+
const outPath = path.join(tmpDir, 'nested', 'deep', 'out.sql');
799+
query({ table: 'users', id: 1 }, { exportTo: outPath });
800+
expect(fs.existsSync(outPath)).toBe(true);
801+
});
802+
803+
it('does not write a file when exportTo is omitted', () => {
804+
const query = defineQuery(fixture('simple.sql'), {
805+
table: schema.identifier,
806+
id: schema.number,
807+
});
808+
query({ table: 'users', id: 1 });
809+
expect(fs.readdirSync(tmpDir)).toHaveLength(0);
810+
});
811+
812+
it('works in generic mode', () => {
813+
const query = defineQuery<{ table: string; id: number }>(fixture('simple.sql'));
814+
const outPath = path.join(tmpDir, 'generic.sql');
815+
const { sql } = query({ table: 'users', id: 7 }, { exportTo: outPath });
816+
expect(fs.readFileSync(outPath, 'utf-8')).toBe(sql);
817+
});
818+
});
819+
743820
describe('custom schema types', () => {
744821
it('accepts a custom type descriptor', () => {
745822
const prodTable = {
@@ -1337,12 +1414,20 @@ ORDER BY {{orderBy}}
13371414
LIMIT {{limit}}
13381415
```
13391416

1417+
### Exporting rendered SQL
1418+
1419+
Pass `{ exportTo: string }` as the second argument to write the rendered SQL to disk. Missing parent directories are created recursively.
1420+
1421+
```typescript
1422+
const { sql } = getEvents(params, { exportTo: './debug/getEvents.sql' });
1423+
```
1424+
13401425
### Exports
13411426

13421427
- `defineQuery` - main function (overloaded for generic and schema modes)
13431428
- `schema` - built-in schema type descriptors
13441429
- `SQL_INJECTION_PATTERNS` - array of { name, regex } patterns used for injection detection
1345-
- Types: `TypeDescriptor`, `SchemaDefinition`, `InferParams`, `QueryResult`, `GenericQueryFn`, `SchemaQueryFn`
1430+
- Types: `TypeDescriptor`, `SchemaDefinition`, `InferParams`, `QueryResult`, `QueryOptions`, `GenericQueryFn`, `SchemaQueryFn`
13461431

13471432
## Key details
13481433

@@ -1605,6 +1690,17 @@ const query = defineQuery('./query.sql', {
16051690

16061691
A type descriptor is any object with a `validate(val: unknown) => boolean` method.
16071692

1693+
## Exporting Rendered SQL
1694+
1695+
Pass an `exportTo` path to write the rendered SQL to disk for debugging or audit purposes. Missing parent directories are created automatically.
1696+
1697+
```typescript
1698+
const { sql } = getEvents(
1699+
{ tableName: 'prod_events', status: 'active', startDate: '2022-02-22', orderBy: 'created_at', limit: 99 },
1700+
{ exportTo: './debug/getEvents.sql' },
1701+
);
1702+
```
1703+
16081704
## SQL Injection Protection
16091705

16101706
`schema.string` and the generic `string` type check values against built-in patterns:
@@ -1699,7 +1795,7 @@ Two machine-readable summaries are maintained for AI consumption, following the
16991795
## v0.2
17001796

17011797
- [ ] **Inline template support** - Allow passing SQL strings directly without requiring a `.sql` file, reducing boilerplate for short one-off queries
1702-
- [ ] **Export rendered SQL to file** - Add an option to write the rendered SQL to a file for debugging and audit purposes
1798+
- [x] **Export rendered SQL to file** - Add an option to write the rendered SQL to a file for debugging and audit purposes
17031799
- [ ] **TypeDescriptor `description` field** - Add an optional `description` property to `TypeDescriptor` so schema validation errors can display the expected format (e.g. `Expected: ISO date YYYY-MM-DD`)
17041800

17051801
## Maybe

0 commit comments

Comments
 (0)