@sqg/sqg 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sqg.mjs CHANGED
@@ -2,11 +2,15 @@
2
2
  import { exit } from "node:process";
3
3
  import { Command } from "commander";
4
4
  import consola, { LogLevels } from "consola";
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import { homedir } from "node:os";
5
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { randomUUID } from "node:crypto";
7
+ import { homedir, tmpdir } from "node:os";
7
8
  import { basename, dirname, extname, join, resolve } from "node:path";
8
- import Handlebars from "handlebars";
9
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
12
  import YAML from "yaml";
13
+ import Handlebars from "handlebars";
10
14
  import { z } from "zod";
11
15
  import { DuckDBEnumType, DuckDBInstance, DuckDBListType, DuckDBMapType, DuckDBStructType } from "@duckdb/node-api";
12
16
  import { LRParser } from "@lezer/lr";
@@ -22,8 +26,10 @@ import estree from "prettier/plugins/estree";
22
26
 
23
27
  //#region src/constants.ts
24
28
  /**
25
- * SQG Constants - Centralized definitions for supported engines and generators
26
- * This file enables self-documenting CLI help and validation.
29
+ * SQG Constants - Centralized definitions for supported generators
30
+ *
31
+ * Generator format: <language>/<engine>/<driver>
32
+ * Short format: <language>/<engine> (uses default driver)
27
33
  */
28
34
  /** Supported database engines */
29
35
  const DB_ENGINES = [
@@ -31,35 +37,136 @@ const DB_ENGINES = [
31
37
  "duckdb",
32
38
  "postgres"
33
39
  ];
34
- /** Supported code generators with their descriptions */
35
- const SUPPORTED_GENERATORS = {
36
- "typescript/better-sqlite3": {
40
+ /** All supported generators with their full specification */
41
+ const GENERATORS = {
42
+ "typescript/sqlite/better-sqlite3": {
43
+ language: "typescript",
44
+ engine: "sqlite",
45
+ driver: "better-sqlite3",
37
46
  description: "TypeScript with better-sqlite3 driver",
38
- compatibleEngines: ["sqlite"],
39
- extension: ".ts"
47
+ extension: ".ts",
48
+ template: "better-sqlite3.hbs"
40
49
  },
41
- "typescript/duckdb": {
50
+ "typescript/sqlite/node": {
51
+ language: "typescript",
52
+ engine: "sqlite",
53
+ driver: "node",
54
+ description: "TypeScript with Node.js built-in SQLite",
55
+ extension: ".ts",
56
+ template: "node-sqlite.hbs"
57
+ },
58
+ "typescript/duckdb/node-api": {
59
+ language: "typescript",
60
+ engine: "duckdb",
61
+ driver: "node-api",
42
62
  description: "TypeScript with @duckdb/node-api driver",
43
- compatibleEngines: ["duckdb"],
44
- extension: ".ts"
63
+ extension: ".ts",
64
+ template: "typescript-duckdb.hbs"
65
+ },
66
+ "java/sqlite/jdbc": {
67
+ language: "java",
68
+ engine: "sqlite",
69
+ driver: "jdbc",
70
+ description: "Java with JDBC for SQLite",
71
+ extension: ".java",
72
+ template: "java-jdbc.hbs"
45
73
  },
46
- "java/jdbc": {
47
- description: "Java with JDBC (SQLite, DuckDB, PostgreSQL)",
48
- compatibleEngines: [
49
- "sqlite",
50
- "duckdb",
51
- "postgres"
52
- ],
53
- extension: ".java"
74
+ "java/duckdb/jdbc": {
75
+ language: "java",
76
+ engine: "duckdb",
77
+ driver: "jdbc",
78
+ description: "Java with JDBC for DuckDB",
79
+ extension: ".java",
80
+ template: "java-jdbc.hbs"
54
81
  },
55
- "java/duckdb-arrow": {
82
+ "java/duckdb/arrow": {
83
+ language: "java",
84
+ engine: "duckdb",
85
+ driver: "arrow",
56
86
  description: "Java with DuckDB Arrow API",
57
- compatibleEngines: ["duckdb"],
58
- extension: ".java"
87
+ extension: ".java",
88
+ template: "java-duckdb-arrow.hbs"
89
+ },
90
+ "java/postgres/jdbc": {
91
+ language: "java",
92
+ engine: "postgres",
93
+ driver: "jdbc",
94
+ description: "Java with JDBC for PostgreSQL",
95
+ extension: ".java",
96
+ template: "java-jdbc.hbs"
59
97
  }
60
98
  };
61
- /** List of all generator names for validation */
62
- const GENERATOR_NAMES = Object.keys(SUPPORTED_GENERATORS);
99
+ /** Default drivers for language/engine combinations */
100
+ const DEFAULT_DRIVERS = {
101
+ "typescript/sqlite": "better-sqlite3",
102
+ "typescript/duckdb": "node-api",
103
+ "java/sqlite": "jdbc",
104
+ "java/duckdb": "jdbc",
105
+ "java/postgres": "jdbc"
106
+ };
107
+ /** List of all full generator names */
108
+ const GENERATOR_NAMES = Object.keys(GENERATORS);
109
+ /** List of short generator names (language/engine) */
110
+ const SHORT_GENERATOR_NAMES = Object.keys(DEFAULT_DRIVERS);
111
+ /**
112
+ * Resolve a generator string to its full form.
113
+ * Accepts both short (language/engine) and full (language/engine/driver) formats.
114
+ */
115
+ function resolveGenerator(generator) {
116
+ if (generator in GENERATORS) return generator;
117
+ if (generator in DEFAULT_DRIVERS) return `${generator}/${DEFAULT_DRIVERS[generator]}`;
118
+ return generator;
119
+ }
120
+ /**
121
+ * Parse a generator string and return its info.
122
+ * Throws if the generator is invalid.
123
+ */
124
+ function parseGenerator(generator) {
125
+ const info = GENERATORS[resolveGenerator(generator)];
126
+ if (!info) throw new Error(`Invalid generator: ${generator}`);
127
+ return info;
128
+ }
129
+ /**
130
+ * Check if a generator string is valid (either short or full form).
131
+ */
132
+ function isValidGenerator(generator) {
133
+ return resolveGenerator(generator) in GENERATORS;
134
+ }
135
+ /**
136
+ * Get the database engine for a generator.
137
+ */
138
+ function getGeneratorEngine(generator) {
139
+ return parseGenerator(generator).engine;
140
+ }
141
+ /**
142
+ * Find similar generator names for typo suggestions.
143
+ */
144
+ function findSimilarGenerators(input) {
145
+ const normalized = input.toLowerCase();
146
+ return [...GENERATOR_NAMES, ...SHORT_GENERATOR_NAMES].filter((name) => {
147
+ const nameLower = name.toLowerCase();
148
+ if (nameLower.includes(normalized) || normalized.includes(nameLower)) return true;
149
+ return [
150
+ normalized.replace(/\//g, "-"),
151
+ normalized.replace(/-/g, "/"),
152
+ normalized.replace(/_/g, "/"),
153
+ normalized.replace(/_/g, "-")
154
+ ].some((v) => nameLower.includes(v) || v.includes(nameLower));
155
+ });
156
+ }
157
+ /**
158
+ * Format generators for CLI help output.
159
+ */
160
+ function formatGeneratorsHelp() {
161
+ const lines = [];
162
+ for (const shortName of SHORT_GENERATOR_NAMES) {
163
+ const fullName = `${shortName}/${DEFAULT_DRIVERS[shortName]}`;
164
+ const info = GENERATORS[fullName];
165
+ lines.push(` ${shortName.padEnd(24)} ${info.description} (default)`);
166
+ for (const [generatorName, generatorInfo] of Object.entries(GENERATORS)) if (generatorName.startsWith(`${shortName}/`) && generatorName !== fullName) lines.push(` ${generatorName.padEnd(24)} ${generatorInfo.description}`);
167
+ }
168
+ return lines.join("\n");
169
+ }
63
170
  /** SQL annotation syntax reference */
64
171
  const SQL_SYNTAX_REFERENCE = `
65
172
  SQL Annotation Syntax:
@@ -88,34 +195,6 @@ Example:
88
195
 
89
196
  -- TABLE users :appender
90
197
  `.trim();
91
- /**
92
- * Find similar generator names for typo suggestions
93
- */
94
- function findSimilarGenerators(input) {
95
- const normalized = input.toLowerCase();
96
- return GENERATOR_NAMES.filter((name) => {
97
- const nameLower = name.toLowerCase();
98
- if (nameLower.includes(normalized) || normalized.includes(nameLower)) return true;
99
- return [
100
- normalized.replace("/", "-"),
101
- normalized.replace("-", "/"),
102
- normalized.replace("_", "/"),
103
- normalized.replace("_", "-")
104
- ].some((v) => nameLower.includes(v) || v.includes(nameLower));
105
- });
106
- }
107
- /**
108
- * Format generators for CLI help output
109
- */
110
- function formatGeneratorsHelp() {
111
- return Object.entries(SUPPORTED_GENERATORS).map(([name, info]) => ` ${name.padEnd(28)} ${info.description} (${info.compatibleEngines.join(", ")})`).join("\n");
112
- }
113
- /**
114
- * Format engines for CLI help output
115
- */
116
- function formatEnginesHelp() {
117
- return DB_ENGINES.map((e) => ` ${e}`).join("\n");
118
- }
119
198
 
120
199
  //#endregion
121
200
  //#region src/errors.ts
@@ -174,41 +253,20 @@ var ConfigError = class extends SqgError {
174
253
  * Error for invalid generator names
175
254
  */
176
255
  var InvalidGeneratorError = class extends SqgError {
177
- constructor(generatorName, validGenerators, suggestion) {
256
+ constructor(generatorName, validGenerators$1, suggestion) {
178
257
  const similarMsg = suggestion ? ` Did you mean '${suggestion}'?` : "";
179
- super(`Invalid generator '${generatorName}'.${similarMsg} Valid generators: ${validGenerators.join(", ")}`, "INVALID_GENERATOR", suggestion ? `Use '${suggestion}' instead` : `Choose from: ${validGenerators.join(", ")}`, { generator: generatorName });
258
+ super(`Invalid generator '${generatorName}'.${similarMsg} Valid generators: ${validGenerators$1.join(", ")}`, "INVALID_GENERATOR", suggestion ? `Use '${suggestion}' instead` : `Choose from: ${validGenerators$1.join(", ")}`, { generator: generatorName });
180
259
  this.name = "InvalidGeneratorError";
181
260
  }
182
261
  };
183
262
  /**
184
- * Error for invalid engine names
185
- */
186
- var InvalidEngineError = class extends SqgError {
187
- constructor(engineName, validEngines) {
188
- super(`Invalid engine '${engineName}'. Valid engines: ${validEngines.join(", ")}`, "INVALID_ENGINE", `Choose from: ${validEngines.join(", ")}`, { engine: engineName });
189
- this.name = "InvalidEngineError";
190
- }
191
- };
192
- /**
193
- * Error for generator/engine compatibility
194
- */
195
- var GeneratorEngineMismatchError = class extends SqgError {
196
- constructor(generator, engine, compatibleEngines) {
197
- super(`Generator '${generator}' is not compatible with engine '${engine}'`, "GENERATOR_ENGINE_MISMATCH", `Generator '${generator}' works with: ${compatibleEngines.join(", ")}`, {
198
- generator,
199
- engine
200
- });
201
- this.name = "GeneratorEngineMismatchError";
202
- }
203
- };
204
- /**
205
263
  * Error for database initialization/connection issues
206
264
  */
207
265
  var DatabaseError = class extends SqgError {
208
266
  constructor(message, engine, suggestion, context) {
209
267
  super(message, "DATABASE_ERROR", suggestion, {
210
- engine,
211
- ...context
268
+ ...context,
269
+ engine
212
270
  });
213
271
  this.name = "DatabaseError";
214
272
  }
@@ -281,201 +339,468 @@ function formatErrorForOutput(err) {
281
339
  }
282
340
 
283
341
  //#endregion
284
- //#region src/parser/sql-parser.ts
285
- const parser = LRParser.deserialize({
286
- version: 14,
287
- states: "&SOVQPOOO_QPO'#CwOdQPO'#CzOiQPO'#CvO!SQQO'#C^OOQO'#Cn'#CnQVQPOOO!aQSO,59cO!lQPO,59fOOQO'#Cp'#CpO!tQQO,59bOOQO'#C}'#C}O#iQQO'#CiOOQO,58x,58xOOQO-E6l-E6lOOQO'#Co'#CoO!aQSO1G.}O!dQSO1G.}OOQO'#Cb'#CbOOQO1G.}1G.}O#yQPO1G/QOOQO-E6n-E6nO$RQPO'#CdOOQO'#Cq'#CqO$WQQO1G.|OOQO'#Cm'#CmOOQO'#Cr'#CrO$xQQO,59TOOQO-E6m-E6mO!dQSO7+$iOOQO7+$i7+$iO%YQPO,59OOOQO-E6o-E6oOOQO-E6p-E6pOOQO<<HT<<HTO%_QQO1G.jOOQO'#Ce'#CeOiQPO7+$UO%jQQO<<Gp",
288
- stateData: "&l~OiOS~ORPOVQO~OSVO~OSWO~OlXO~OYZOZZO[ZO^ZO_ZO`ZO~OR]PV]Pg]P~PnOT_OlXOmbO~OT_Olna~OlXOofORjaVjaYjaZja[ja^ja_ja`jagja~OliOR]XV]Xg]X~PnOT_Olni~OSoO~OofORjiVjiYjiZji[ji^ji_ji`jigji~OliOR]aV]ag]a~PnOpsO~OYtOZtO[tO~OlXORWyVWyYWyZWy[Wy^Wy_Wy`WygWyoWy~OR`o^iZTmYV_[~",
289
- goto: "#prPPsPPPwP!R!VPPP!YPPP!]!a!g!q#T#ZPPP#a#ePP#ePP#iTTOUQcVSn`aRrmTgYhRusR]STj[kQUOR^UQ`VQdWTl`dQYRQaVWeYamvQm`RvuQhYRphQk[RqkTSOUTROUQ[STj[k",
290
- nodeNames: "⚠ File QueryBlock BlockCommentStartSpecial Name Modifiers Config LineCommentStartSpecial SetVarLine Value StringLiteral StringLiteralSingle SQLText SQLBlock BlockComment LineComment VarRef BR",
291
- maxTerm: 33,
292
- skippedNodes: [0],
293
- repeatNodeCount: 5,
294
- tokenData: "$3b~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#%p!Q![$)l![!]$+U!]!_#Y!_!`$.U!`!b#Y!b!c$/U!c!}$)l!}#R#Y#R#S$)l#S#T#Y#T#o$)l#o;'S#Y;'S;=`'q<%lO#YU#_][QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YS$ZTOz$Wz{$j{;'S$W;'S;=`%X<%lO$WS$mVOz$Wz{$j{!P$W!P!Q%S!Q;'S$W;'S;=`%X<%lO$WS%XOmSS%[P;=`<%l$WU%d_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!P#Y!P!Q&c!Q;'S#Y;'S;=`'q<%lO#YU&jVmS[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'UV[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'nP;=`<%l'PU'tP;=`<%l#Y~'|Xi~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTl~Oz$Wz{$j{;'S$W;'S;=`%X<%lO$WU)Sb[QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U*_ZOY*[YZ$WZr*[rs+Qsz*[z{+f{#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U+VTYQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU+i]OY*[YZ$WZr*[rs+Qsz*[z{+f{!P*[!P!Q,b!Q#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U,gWmSOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-SWOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-qOYQQ-tTOY-PYZ-PZ;'S-P;'S;=`.T<%lO-PQ.WP;=`<%l-PU.^VOY*[YZ*[Zz*[z{+f{;'S*[;'S;=`.s<%lO*[U.vP;=`<%l*[U/Q]YQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU0Od[QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{!P(}!P!Q1^!Q#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U1e_mS[QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ2i_[QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ3oVYQ[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ4Z[[QOX2dXY-PYZ-PZ]2d]^-P^p2dpq-Pqt2dtu-Pu;'S2d;'S;=`5P<%lO2dQ5SP;=`<%l2dU5[^[QOX(}XY*[YZ*[Z](}]^*[^p(}pq*[qt(}tu*[uz(}z{/y{;'S(};'S;=`6W<%lO(}U6ZP;=`<%l(}U6cV[QOz$Wz{$j{#o$W#o#p6x#p;'S$W;'S;=`%X<%lO$WU6{]Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o;'S$W;'S;=`%X<%lO$WU7w_Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o#q$W#q#r8v#r;'S$W;'S;=`%X<%lO$WU8{T`QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU9ab[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[U:lZOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU;dTZQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU;v]OY:iYZ$WZw:iwx;_xz:iz{;s{!P:i!P!Q<o!Q#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU<tWmSOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q=aWOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>OOZQQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sWZQOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q?`P;=`<%l=^U?fZOY:iYZ:iZw:iwx@Xxz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU@^ZZQOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]ZQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YUB[d[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{!P9[!P!QCj!Q#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UCq_mS[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQDu_[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQE{VZQ[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQFg`[QOXDpXY=^YZ=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxGix#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQGp_ZQ[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQHrP;=`<%lDpUHzb[QOX9[XY:iYZ:iZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxJSxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UJZbZQ[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UKfP;=`<%l9[VKn_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{}#Y}!OLm!O;'S#Y;'S;=`'q<%lO#YVLtf_Q[QOXNYXY!&vYZ$WZ]NY]^! ]^pNYpq!&vqtNYtu! ]uzNYz{!#k{!gNY!g!h!8V!h!oNY!o!p!<w!p!sNY!s!t!DP!t!vNY!v!w!Hu!w;'SNY;'S;=`!&p<%lONYUNa^_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{;'SNY;'S;=`!&p<%lONYU! bV_QOY! ]YZ$WZz! ]z{! w{;'S! ];'S;=`!#e<%lO! ]U! |X_QOY! ]YZ$WZz! ]z{! w{!P! ]!P!Q!!i!Q;'S! ];'S;=`!#e<%lO! ]U!!pSmS_QOY!!|Z;'S!!|;'S;=`!#_<%lO!!|Q!#RS_QOY!!|Z;'S!!|;'S;=`!#_<%lO!!|Q!#bP;=`<%l!!|U!#hP;=`<%l! ]U!#r`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!PNY!P!Q!$t!Q;'SNY;'S;=`!&p<%lONYU!$}ZmS_Q[QOX!%pXY!!|Z]!%p]^!!|^p!%ppq!!|qt!%ptu!!|u;'S!%p;'S;=`!&j<%lO!%pQ!%wZ_Q[QOX!%pXY!!|Z]!%p]^!!|^p!%ppq!!|qt!%ptu!!|u;'S!%p;'S;=`!&j<%lO!%pQ!&mP;=`<%l!%pU!&sP;=`<%lNYV!&{b_QOX! ]XY!&vYZ$WZp! ]pq!&vqz! ]z{! w{!g! ]!g!h!(T!h!o! ]!o!p!*u!p!s! ]!s!t!.}!t!v! ]!v!w!1s!w;'S! ];'S;=`!#e<%lO! ]V!(YX_QOY! ]YZ$WZz! ]z{! w{!z! ]!z!{!(u!{;'S! ];'S;=`!#e<%lO! ]V!(zX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!)g!h;'S! ];'S;=`!#e<%lO! ]V!)lX_QOY! ]YZ$WZz! ]z{! w{!e! ]!e!f!*X!f;'S! ];'S;=`!#e<%lO! ]V!*`VVR_QOY! ]YZ$WZz! ]z{! w{;'S! ];'S;=`!#e<%lO! ]V!*zX_QOY! ]YZ$WZz! ]z{! w{!k! ]!k!l!+g!l;'S! ];'S;=`!#e<%lO! ]V!+lX_QOY! ]YZ$WZz! ]z{! w{!i! ]!i!j!,X!j;'S! ];'S;=`!#e<%lO! ]V!,^X_QOY! ]YZ$WZz! ]z{! w{!t! ]!t!u!,y!u;'S! ];'S;=`!#e<%lO! ]V!-OX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!-k!d;'S! ];'S;=`!#e<%lO! ]V!-pX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!.]!w;'S! ];'S;=`!#e<%lO! ]V!.bX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!*X!h;'S! ];'S;=`!#e<%lO! ]V!/SX_QOY! ]YZ$WZz! ]z{! w{!w! ]!w!x!/o!x;'S! ];'S;=`!#e<%lO! ]V!/tX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!0a!h;'S! ];'S;=`!#e<%lO! ]V!0fX_QOY! ]YZ$WZz! ]z{! w{!t! ]!t!u!1R!u;'S! ];'S;=`!#e<%lO! ]V!1WX_QOY! ]YZ$WZz! ]z{! w{!{! ]!{!|!*X!|;'S! ];'S;=`!#e<%lO! ]V!1xZ_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!2k!d!g! ]!g!h!3}!h;'S! ];'S;=`!#e<%lO! ]V!2pX_QOY! ]YZ$WZz! ]z{! w{!d! ]!d!e!3]!e;'S! ];'S;=`!#e<%lO! ]V!3bX_QOY! ]YZ$WZz! ]z{! w{!n! ]!n!o!.]!o;'S! ];'S;=`!#e<%lO! ]V!4SX_QOY! ]YZ$WZz! ]z{! w{!u! ]!u!v!4o!v;'S! ];'S;=`!#e<%lO! ]V!4tX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!5a!w;'S! ];'S;=`!#e<%lO! ]V!5fX_QOY! ]YZ$WZz! ]z{! w{!f! ]!f!g!6R!g;'S! ];'S;=`!#e<%lO! ]V!6WX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!6s!d;'S! ];'S;=`!#e<%lO! ]V!6xX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!7e!w;'S! ];'S;=`!#e<%lO! ]V!7jX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!*X!d;'S! ];'S;=`!#e<%lO! ]V!8^`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!zNY!z!{!9`!{;'SNY;'S;=`!&p<%lONYV!9g`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!:i!h;'SNY;'S;=`!&p<%lONYV!:p`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!eNY!e!f!;r!f;'SNY;'S;=`!&p<%lONYV!;{^VR_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{;'SNY;'S;=`!&p<%lONYV!=O`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!kNY!k!l!>Q!l;'SNY;'S;=`!&p<%lONYV!>X`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!iNY!i!j!?Z!j;'SNY;'S;=`!&p<%lONYV!?b`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!tNY!t!u!@d!u;'SNY;'S;=`!&p<%lONYV!@k`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!Am!d;'SNY;'S;=`!&p<%lONYV!At`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w!Bv!w;'SNY;'S;=`!&p<%lONYV!B}`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!;r!h;'SNY;'S;=`!&p<%lONYV!DW`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!wNY!w!x!EY!x;'SNY;'S;=`!&p<%lONYV!Ea`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!Fc!h;'SNY;'S;=`!&p<%lONYV!Fj`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!tNY!t!u!Gl!u;'SNY;'S;=`!&p<%lONYV!Gs`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!{NY!{!|!;r!|;'SNY;'S;=`!&p<%lONYV!H|b_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!JU!d!gNY!g!h!Lh!h;'SNY;'S;=`!&p<%lONYV!J]`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!dNY!d!e!K_!e;'SNY;'S;=`!&p<%lONYV!Kf`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!nNY!n!o!Bv!o;'SNY;'S;=`!&p<%lONYV!Lo`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!uNY!u!v!Mq!v;'SNY;'S;=`!&p<%lONYV!Mx`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w!Nz!w;'SNY;'S;=`!&p<%lONYV# R`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!fNY!f!g#!T!g;'SNY;'S;=`!&p<%lONYV#![`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d##^!d;'SNY;'S;=`!&p<%lONYV##e`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w#$g!w;'SNY;'S;=`!&p<%lONYV#$n`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!;r!d;'SNY;'S;=`!&p<%lONYV#%u][QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#&n{;'S#Y;'S;=`'q<%lO#YV#&sh[QOX#(_XY#,`YZ#,`Z]#(_]^#)]^p#(_pq#,`qt#(_tu#)]uz#(_z{#*f{!P#(_!P!Q#9m!Q!g#(_!g!h#>j!h!o#(_!o!p#Bv!p!s#(_!s!t#I`!t!v#(_!v!w#Mp!w;'S#(_;'S;=`#,Y<%lO#(_U#(d][QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{;'S#(_;'S;=`#,Y<%lO#(_U#)`TOz#)]z{#)o{;'S#)];'S;=`#*`<%lO#)]U#)rVOz#)]z{#)o{!P#)]!P!Q#*X!Q;'S#)];'S;=`#*`<%lO#)]U#*`O^QmSU#*cP;=`<%l#)]U#*k_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!P#(_!P!Q#+j!Q;'S#(_;'S;=`#,Y<%lO#(_U#+sV^QmS[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PU#,]P;=`<%l#(_V#,cbOX#)]XY#,`YZ#,`Zp#)]pq#,`qz#)]z{#)o{!g#)]!g!h#-k!h!o#)]!o!p#/k!p!s#)]!s!t#2q!t!v#)]!v!w#4u!w;'S#)];'S;=`#*`<%lO#)]V#-nVOz#)]z{#)o{!z#)]!z!{#.T!{;'S#)];'S;=`#*`<%lO#)]V#.WVOz#)]z{#)o{!g#)]!g!h#.m!h;'S#)];'S;=`#*`<%lO#)]V#.pVOz#)]z{#)o{!e#)]!e!f#/V!f;'S#)];'S;=`#*`<%lO#)]V#/[TRROz#)]z{#)o{;'S#)];'S;=`#*`<%lO#)]V#/nVOz#)]z{#)o{!k#)]!k!l#0T!l;'S#)];'S;=`#*`<%lO#)]V#0WVOz#)]z{#)o{!i#)]!i!j#0m!j;'S#)];'S;=`#*`<%lO#)]V#0pVOz#)]z{#)o{!t#)]!t!u#1V!u;'S#)];'S;=`#*`<%lO#)]V#1YVOz#)]z{#)o{!c#)]!c!d#1o!d;'S#)];'S;=`#*`<%lO#)]V#1rVOz#)]z{#)o{!v#)]!v!w#2X!w;'S#)];'S;=`#*`<%lO#)]V#2[VOz#)]z{#)o{!g#)]!g!h#/V!h;'S#)];'S;=`#*`<%lO#)]V#2tVOz#)]z{#)o{!w#)]!w!x#3Z!x;'S#)];'S;=`#*`<%lO#)]V#3^VOz#)]z{#)o{!g#)]!g!h#3s!h;'S#)];'S;=`#*`<%lO#)]V#3vVOz#)]z{#)o{!t#)]!t!u#4]!u;'S#)];'S;=`#*`<%lO#)]V#4`VOz#)]z{#)o{!{#)]!{!|#/V!|;'S#)];'S;=`#*`<%lO#)]V#4xXOz#)]z{#)o{!c#)]!c!d#5e!d!g#)]!g!h#6g!h;'S#)];'S;=`#*`<%lO#)]V#5hVOz#)]z{#)o{!d#)]!d!e#5}!e;'S#)];'S;=`#*`<%lO#)]V#6QVOz#)]z{#)o{!n#)]!n!o#2X!o;'S#)];'S;=`#*`<%lO#)]V#6jVOz#)]z{#)o{!u#)]!u!v#7P!v;'S#)];'S;=`#*`<%lO#)]V#7SVOz#)]z{#)o{!v#)]!v!w#7i!w;'S#)];'S;=`#*`<%lO#)]V#7lVOz#)]z{#)o{!f#)]!f!g#8R!g;'S#)];'S;=`#*`<%lO#)]V#8UVOz#)]z{#)o{!c#)]!c!d#8k!d;'S#)];'S;=`#*`<%lO#)]V#8nVOz#)]z{#)o{!v#)]!v!w#9T!w;'S#)];'S;=`#*`<%lO#)]V#9WVOz#)]z{#)o{!c#)]!c!d#/V!d;'S#)];'S;=`#*`<%lO#)]U#9t]mS[QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{;'S#:m;'S;=`#>d<%lO#:mQ#:r][QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{;'S#:m;'S;=`#>d<%lO#:mQ#;nTOz#;kz{#;}{;'S#;k;'S;=`#<l<%lO#;kQ#<QVOz#;kz{#;}{!P#;k!P!Q#<g!Q;'S#;k;'S;=`#<l<%lO#;kQ#<lO^QQ#<oP;=`<%l#;kQ#<w_[QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{!P#:m!P!Q#=v!Q;'S#:m;'S;=`#>d<%lO#:mQ#=}V^Q[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ#>gP;=`<%l#:mV#>o_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!z#(_!z!{#?n!{;'S#(_;'S;=`#,Y<%lO#(_V#?s_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#@r!h;'S#(_;'S;=`#,Y<%lO#(_V#@w_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!e#(_!e!f#Av!f;'S#(_;'S;=`#,Y<%lO#(_V#A}]RR[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{;'S#(_;'S;=`#,Y<%lO#(_V#B{_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!k#(_!k!l#Cz!l;'S#(_;'S;=`#,Y<%lO#(_V#DP_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!i#(_!i!j#EO!j;'S#(_;'S;=`#,Y<%lO#(_V#ET_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!t#(_!t!u#FS!u;'S#(_;'S;=`#,Y<%lO#(_V#FX_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#GW!d;'S#(_;'S;=`#,Y<%lO#(_V#G]_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w#H[!w;'S#(_;'S;=`#,Y<%lO#(_V#Ha_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#Av!h;'S#(_;'S;=`#,Y<%lO#(_V#Ie_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!w#(_!w!x#Jd!x;'S#(_;'S;=`#,Y<%lO#(_V#Ji_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#Kh!h;'S#(_;'S;=`#,Y<%lO#(_V#Km_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!t#(_!t!u#Ll!u;'S#(_;'S;=`#,Y<%lO#(_V#Lq_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!{#(_!{!|#Av!|;'S#(_;'S;=`#,Y<%lO#(_V#Mua[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#Nz!d!g#(_!g!h$#S!h;'S#(_;'S;=`#,Y<%lO#(_V$ P_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!d#(_!d!e$!O!e;'S#(_;'S;=`#,Y<%lO#(_V$!T_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!n#(_!n!o#H[!o;'S#(_;'S;=`#,Y<%lO#(_V$#X_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!u#(_!u!v$$W!v;'S#(_;'S;=`#,Y<%lO#(_V$$]_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w$%[!w;'S#(_;'S;=`#,Y<%lO#(_V$%a_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!f#(_!f!g$&`!g;'S#(_;'S;=`#,Y<%lO#(_V$&e_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d$'d!d;'S#(_;'S;=`#,Y<%lO#(_V$'i_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w$(h!w;'S#(_;'S;=`#,Y<%lO#(_V$(m_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#Av!d;'S#(_;'S;=`#,Y<%lO#(_V$)seSP[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$)l![!c#Y!c!}$)l!}#R#Y#R#S$)l#S#T#Y#T#o$)l#o;'S#Y;'S;=`'q<%lO#Y~$+Ze[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$,l![!c#Y!c!}$,l!}#R#Y#R#S$,l#S#T#Y#T#o$,l#o;'S#Y;'S;=`'q<%lO#Y~$,seT~[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$,l![!c#Y!c!}$,l!}#R#Y#R#S$,l#S#T#Y#T#o$,l#o;'S#Y;'S;=`'q<%lO#YV$.]]pP[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$/Z_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$0Y#h;'S#Y;'S;=`'q<%lO#YU$0__[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$1^#Y;'S#Y;'S;=`'q<%lO#YU$1c_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$2b#i;'S#Y;'S;=`'q<%lO#YU$2i]oQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
295
- tokenizers: [
296
- 0,
297
- 1,
298
- 2
299
- ],
300
- topRules: { "File": [0, 1] },
301
- tokenPrec: 245
302
- });
303
-
304
- //#endregion
305
- //#region src/sql-query.ts
306
- var ListType = class {
307
- constructor(baseType) {
308
- this.baseType = baseType;
309
- }
310
- toString() {
311
- return `${this.baseType.toString()}[]`;
312
- }
313
- };
314
- var StructType = class {
315
- constructor(fields) {
316
- this.fields = fields;
317
- }
318
- toString() {
319
- return `STRUCT(${this.fields.map((f) => `"${f.name}" ${f.type.toString()}`).join(", ")})`;
320
- }
321
- };
322
- var MapType = class {
323
- constructor(keyType, valueType) {
324
- this.keyType = keyType;
325
- this.valueType = valueType;
326
- }
327
- toString() {
328
- return `MAP(${this.keyType.toString()}, ${this.valueType.toString()})`;
329
- }
330
- };
331
- var EnumType = class {
332
- constructor(values) {
333
- this.values = values;
334
- }
335
- toString() {
336
- return `ENUM(${this.values.map((v) => `'${v}'`).join(", ")})`;
337
- }
338
- };
339
- var SQLQuery = class {
340
- columns;
341
- allColumns;
342
- constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
343
- this.filename = filename;
344
- this.id = id;
345
- this.rawQuery = rawQuery;
346
- this.queryAnonymous = queryAnonymous;
347
- this.queryNamed = queryNamed;
348
- this.queryPositional = queryPositional;
349
- this.type = type;
350
- this.isOne = isOne;
351
- this.isPluck = isPluck;
352
- this.variables = variables;
353
- this.config = config;
354
- this.columns = [];
355
- }
356
- get isQuery() {
357
- return this.type === "QUERY";
358
- }
359
- get isExec() {
360
- return this.type === "EXEC";
361
- }
362
- get isMigrate() {
363
- return this.type === "MIGRATE";
364
- }
365
- get isTestdata() {
366
- return this.type === "TESTDATA";
367
- }
368
- get skipGenerateFunction() {
369
- return this.isTestdata || this.isMigrate || this.id.startsWith("_");
370
- }
371
- validateVariables() {
372
- const missingVars = [];
373
- const varRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
374
- let match;
375
- while (true) {
376
- match = varRegex.exec(this.rawQuery);
377
- if (match === null) break;
378
- const varName = match[1];
379
- if (!this.variables.has(varName)) missingVars.push(varName);
380
- }
381
- return missingVars;
382
- }
383
- };
342
+ //#region src/init.ts
384
343
  /**
385
- * Represents a TABLE annotation for generating appenders.
386
- * TABLE annotations specify a table name for which to generate bulk insert appenders.
344
+ * SQG Project Initialization - Creates new SQG projects with example files
387
345
  */
388
- var TableInfo = class {
389
- /** Columns introspected from the database table schema */
390
- columns = [];
391
- constructor(filename, id, tableName, includeColumns, hasAppender) {
392
- this.filename = filename;
393
- this.id = id;
394
- this.tableName = tableName;
395
- this.includeColumns = includeColumns;
396
- this.hasAppender = hasAppender;
397
- }
398
- get skipGenerateFunction() {
399
- return !this.hasAppender;
400
- }
401
- };
402
- function parseSQLQueries(filePath, extraVariables) {
403
- const content = readFileSync(filePath, "utf-8");
404
- consola.info(`Parsing SQL file: ${filePath}`);
405
- consola.debug(`File start: ${content.slice(0, 200)}`);
406
- const queries = [];
407
- const tables = [];
408
- const cursor = parser.parse(content).cursor();
409
- function getLineNumber(position) {
410
- return content.slice(0, position).split("\n").length;
411
- }
412
- function getStr(nodeName, optional = false) {
413
- const node = cursor.node.getChild(nodeName);
414
- if (!node) {
415
- if (optional) return;
416
- const lineNumber = getLineNumber(cursor.node.from);
417
- throw new Error(`Node '${nodeName}' not found at line ${lineNumber}`);
418
- }
419
- return nodeStr(node);
420
- }
421
- function nodeStr(node) {
422
- return content.slice(node.from, node.to);
423
- }
424
- const queryNames = /* @__PURE__ */ new Set();
425
- do
426
- if (cursor.name === "QueryBlock") {
427
- const queryType = (getStr("LineCommentStartSpecial", true) ?? getStr("BlockCommentStartSpecial")).replace("--", "").replace("/*", "").trim();
428
- const name = getStr("Name").trim();
429
- const modifiers = cursor.node.getChildren("Modifiers").map((node) => nodeStr(node));
430
- const isOne = modifiers.includes(":one");
431
- const isPluck = modifiers.includes(":pluck");
432
- let configStr = getStr("Config", true);
433
- if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
434
- let config = null;
435
- if (configStr) config = Config.fromYaml(name, filePath, configStr);
436
- const setVars = cursor.node.getChildren("SetVarLine");
437
- const variables = /* @__PURE__ */ new Map();
438
- for (const setVar of setVars) {
439
- const varName = nodeStr(setVar.getChild("Name"));
440
- const value = nodeStr(setVar.getChild("Value"));
441
- variables.set(varName, value.trim());
442
- }
443
- function getVariable(varName) {
444
- if (variables.has(varName)) return variables.get(varName);
445
- for (const extraVariable of extraVariables) if (extraVariable.name === varName) {
446
- variables.set(varName, extraVariable.value);
447
- return extraVariable.value;
448
- }
449
- const definedVars = Array.from(variables.keys());
450
- const suggestion = definedVars.length > 0 ? `Add '@set ${varName} = <value>' before the query. Defined variables: ${definedVars.join(", ")}` : `Add '@set ${varName} = <value>' before the query`;
451
- throw SqgError.inQuery(`Variable '\${${varName}}' is referenced but not defined`, "MISSING_VARIABLE", name, filePath, { suggestion });
452
- }
453
- const sqlNode = cursor.node.getChild("SQLBlock");
454
- if (!sqlNode) throw SqgError.inQuery("SQL block not found", "SQL_PARSE_ERROR", name, filePath, { suggestion: "Ensure the query has valid SQL content after the annotation comment" });
455
- const sqlContentStr = nodeStr(sqlNode).trim();
456
- const sqlCursor = sqlNode.cursor();
457
- let from = -1;
458
- let to = -1;
459
- class SQLQueryBuilder {
460
- sqlParts = [];
461
- appendSql(sql$1) {
462
- this.sqlParts.push(sql$1);
463
- }
464
- appendVariable(varName, value) {
465
- this.sqlParts.push({
466
- name: varName,
467
- value
468
- });
469
- }
470
- trim() {
471
- const lastPart = this.sqlParts.length > 0 ? this.sqlParts[this.sqlParts.length - 1] : null;
472
- if (lastPart && typeof lastPart === "string") this.sqlParts[this.sqlParts.length - 1] = lastPart.trimEnd();
473
- }
474
- parameters() {
475
- return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
476
- }
477
- toSqlWithAnonymousPlaceholders() {
478
- let sql$1 = "";
346
+ /**
347
+ * Get the default generator for a language preference
348
+ */
349
+ function getDefaultGenerator() {
350
+ return "typescript/sqlite";
351
+ }
352
+ /**
353
+ * Generate example SQL content based on engine
354
+ */
355
+ function getExampleSql(engine) {
356
+ return {
357
+ sqlite: `-- MIGRATE 1
358
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
359
+ CREATE TABLE users (
360
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
361
+ name TEXT NOT NULL,
362
+ email TEXT UNIQUE NOT NULL,
363
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
364
+ );
365
+
366
+ -- MIGRATE 2
367
+ CREATE TABLE posts (
368
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
369
+ user_id INTEGER NOT NULL REFERENCES users(id),
370
+ title TEXT NOT NULL,
371
+ content TEXT,
372
+ published INTEGER DEFAULT 0,
373
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
374
+ );
375
+
376
+ -- TESTDATA seed_data
377
+ INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
378
+ INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
379
+ INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', 1);
380
+ `,
381
+ duckdb: `-- MIGRATE 1
382
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
383
+ CREATE TABLE users (
384
+ id INTEGER PRIMARY KEY,
385
+ name VARCHAR NOT NULL,
386
+ email VARCHAR UNIQUE NOT NULL,
387
+ metadata STRUCT(role VARCHAR, active BOOLEAN),
388
+ tags VARCHAR[],
389
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
390
+ );
391
+
392
+ -- MIGRATE 2
393
+ CREATE TABLE posts (
394
+ id INTEGER PRIMARY KEY,
395
+ user_id INTEGER NOT NULL REFERENCES users(id),
396
+ title VARCHAR NOT NULL,
397
+ content VARCHAR,
398
+ published BOOLEAN DEFAULT FALSE,
399
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
400
+ );
401
+
402
+ -- TESTDATA seed_data
403
+ INSERT INTO users (id, name, email, metadata, tags)
404
+ VALUES (1, 'Alice', 'alice@example.com', {'role': 'admin', 'active': true}, ['developer', 'lead']);
405
+ INSERT INTO users (id, name, email, metadata, tags)
406
+ VALUES (2, 'Bob', 'bob@example.com', {'role': 'user', 'active': true}, ['developer']);
407
+ INSERT INTO posts (id, user_id, title, content, published)
408
+ VALUES (1, 1, 'Hello World', 'My first post!', TRUE);
409
+ `,
410
+ postgres: `-- MIGRATE 1
411
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
412
+ CREATE TABLE users (
413
+ id SERIAL PRIMARY KEY,
414
+ name TEXT NOT NULL,
415
+ email TEXT UNIQUE NOT NULL,
416
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
417
+ );
418
+
419
+ -- MIGRATE 2
420
+ CREATE TABLE posts (
421
+ id SERIAL PRIMARY KEY,
422
+ user_id INTEGER NOT NULL REFERENCES users(id),
423
+ title TEXT NOT NULL,
424
+ content TEXT,
425
+ published BOOLEAN DEFAULT FALSE,
426
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
427
+ );
428
+
429
+ -- TESTDATA seed_data
430
+ INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
431
+ INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
432
+ INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', TRUE);
433
+ `
434
+ }[engine] + `
435
+ -- QUERY list_users
436
+ SELECT id, name, email, created_at
437
+ FROM users
438
+ ORDER BY created_at DESC;
439
+
440
+ -- QUERY get_user_by_id :one
441
+ @set id = 1
442
+ SELECT id, name, email, created_at
443
+ FROM users
444
+ WHERE id = \${id};
445
+
446
+ -- QUERY get_user_by_email :one
447
+ @set email = 'alice@example.com'
448
+ SELECT id, name, email, created_at
449
+ FROM users
450
+ WHERE email = \${email};
451
+
452
+ -- QUERY count_users :one :pluck
453
+ SELECT COUNT(*) FROM users;
454
+
455
+ -- QUERY list_posts_by_user
456
+ @set user_id = 1
457
+ SELECT p.id, p.title, p.content, p.published, p.created_at
458
+ FROM posts p
459
+ WHERE p.user_id = \${user_id}
460
+ ORDER BY p.created_at DESC;
461
+
462
+ -- QUERY list_published_posts
463
+ SELECT
464
+ p.id,
465
+ p.title,
466
+ p.content,
467
+ p.created_at,
468
+ u.name as author_name,
469
+ u.email as author_email
470
+ FROM posts p
471
+ JOIN users u ON p.user_id = u.id
472
+ WHERE p.published = 1
473
+ ORDER BY p.created_at DESC;
474
+ ` + {
475
+ sqlite: `
476
+ -- EXEC create_user
477
+ @set name = 'New User'
478
+ @set email = 'new@example.com'
479
+ INSERT INTO users (name, email)
480
+ VALUES (\${name}, \${email});
481
+
482
+ -- EXEC create_post
483
+ @set user_id = 1
484
+ @set title = 'New Post'
485
+ @set content = 'Post content here'
486
+ INSERT INTO posts (user_id, title, content)
487
+ VALUES (\${user_id}, \${title}, \${content});
488
+
489
+ -- EXEC publish_post
490
+ @set id = 1
491
+ UPDATE posts SET published = 1 WHERE id = \${id};
492
+
493
+ -- EXEC delete_post
494
+ @set id = 1
495
+ DELETE FROM posts WHERE id = \${id};
496
+ `,
497
+ duckdb: `
498
+ -- EXEC create_user
499
+ @set id = 100
500
+ @set name = 'New User'
501
+ @set email = 'new@example.com'
502
+ INSERT INTO users (id, name, email)
503
+ VALUES (\${id}, \${name}, \${email});
504
+
505
+ -- EXEC create_post
506
+ @set id = 100
507
+ @set user_id = 1
508
+ @set title = 'New Post'
509
+ @set content = 'Post content here'
510
+ INSERT INTO posts (id, user_id, title, content)
511
+ VALUES (\${id}, \${user_id}, \${title}, \${content});
512
+
513
+ -- EXEC publish_post
514
+ @set id = 1
515
+ UPDATE posts SET published = TRUE WHERE id = \${id};
516
+
517
+ -- EXEC delete_post
518
+ @set id = 1
519
+ DELETE FROM posts WHERE id = \${id};
520
+ `,
521
+ postgres: `
522
+ -- EXEC create_user
523
+ @set name = 'New User'
524
+ @set email = 'new@example.com'
525
+ INSERT INTO users (name, email)
526
+ VALUES (\${name}, \${email});
527
+
528
+ -- EXEC create_post
529
+ @set user_id = 1
530
+ @set title = 'New Post'
531
+ @set content = 'Post content here'
532
+ INSERT INTO posts (user_id, title, content)
533
+ VALUES (\${user_id}, \${title}, \${content});
534
+
535
+ -- EXEC publish_post
536
+ @set id = 1
537
+ UPDATE posts SET published = TRUE WHERE id = \${id};
538
+
539
+ -- EXEC delete_post
540
+ @set id = 1
541
+ DELETE FROM posts WHERE id = \${id};
542
+ `
543
+ }[engine];
544
+ }
545
+ /**
546
+ * Generate sqg.yaml configuration
547
+ */
548
+ function getConfigYaml(generator, output) {
549
+ const isJava = parseGenerator(generator).language === "java";
550
+ return `# SQG Configuration
551
+ # Generated by: sqg init
552
+ # Documentation: https://sqg.dev
553
+
554
+ version: 1
555
+ name: my-project
556
+
557
+ sql:
558
+ - files:
559
+ - queries.sql
560
+ gen:
561
+ - generator: ${generator}
562
+ output: ${output.endsWith("/") ? output : `${output}/`}${isJava ? `
563
+ config:
564
+ package: generated` : ""}
565
+ `;
566
+ }
567
+ /**
568
+ * Initialize a new SQG project
569
+ */
570
+ async function initProject(options) {
571
+ const generator = options.generator || getDefaultGenerator();
572
+ const output = options.output || "./generated";
573
+ if (!isValidGenerator(generator)) {
574
+ const similar = findSimilarGenerators(generator);
575
+ throw new InvalidGeneratorError(generator, [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
576
+ }
577
+ const engine = parseGenerator(generator).engine;
578
+ const configPath = "sqg.yaml";
579
+ const sqlPath = "queries.sql";
580
+ if (!options.force) {
581
+ if (existsSync(configPath)) throw new SqgError(`File already exists: ${configPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
582
+ if (existsSync(sqlPath)) throw new SqgError(`File already exists: ${sqlPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
583
+ }
584
+ if (!existsSync(output)) {
585
+ mkdirSync(output, { recursive: true });
586
+ consola.success(`Created output directory: ${output}`);
587
+ }
588
+ writeFileSync(configPath, getConfigYaml(generator, output));
589
+ consola.success(`Created ${configPath}`);
590
+ writeFileSync(sqlPath, getExampleSql(engine));
591
+ consola.success(`Created ${sqlPath}`);
592
+ consola.box(`
593
+ SQG project initialized!
594
+
595
+ Generator: ${generator}
596
+ Engine: ${engine}
597
+ Output: ${output}
598
+
599
+ Next steps:
600
+ 1. Edit queries.sql to add your SQL queries
601
+ 2. Run: sqg sqg.yaml
602
+ 3. Import the generated code from ${output}
603
+
604
+ Documentation: https://sqg.dev
605
+ `);
606
+ }
607
+
608
+ //#endregion
609
+ //#region src/parser/sql-parser.ts
610
+ const parser = LRParser.deserialize({
611
+ version: 14,
612
+ states: "&SOVQPOOO_QPO'#CwOdQPO'#CzOiQPO'#CvO!SQQO'#C^OOQO'#Cn'#CnQVQPOOO!aQSO,59cO!lQPO,59fOOQO'#Cp'#CpO!tQQO,59bOOQO'#C}'#C}O#iQQO'#CiOOQO,58x,58xOOQO-E6l-E6lOOQO'#Co'#CoO!aQSO1G.}O!dQSO1G.}OOQO'#Cb'#CbOOQO1G.}1G.}O#yQPO1G/QOOQO-E6n-E6nO$RQPO'#CdOOQO'#Cq'#CqO$WQQO1G.|OOQO'#Cm'#CmOOQO'#Cr'#CrO$xQQO,59TOOQO-E6m-E6mO!dQSO7+$iOOQO7+$i7+$iO%YQPO,59OOOQO-E6o-E6oOOQO-E6p-E6pOOQO<<HT<<HTO%_QQO1G.jOOQO'#Ce'#CeOiQPO7+$UO%jQQO<<Gp",
613
+ stateData: "&l~OiOS~ORPOVQO~OSVO~OSWO~OlXO~OYZOZZO[ZO^ZO_ZO`ZO~OR]PV]Pg]P~PnOT_OlXOmbO~OT_Olna~OlXOofORjaVjaYjaZja[ja^ja_ja`jagja~OliOR]XV]Xg]X~PnOT_Olni~OSoO~OofORjiVjiYjiZji[ji^ji_ji`jigji~OliOR]aV]ag]a~PnOpsO~OYtOZtO[tO~OlXORWyVWyYWyZWy[Wy^Wy_Wy`WygWyoWy~OR`o^iZTmYV_[~",
614
+ goto: "#prPPsPPPwP!R!VPPP!YPPP!]!a!g!q#T#ZPPP#a#ePP#ePP#iTTOUQcVSn`aRrmTgYhRusR]STj[kQUOR^UQ`VQdWTl`dQYRQaVWeYamvQm`RvuQhYRphQk[RqkTSOUTROUQ[STj[k",
615
+ nodeNames: "⚠ File QueryBlock BlockCommentStartSpecial Name Modifiers Config LineCommentStartSpecial SetVarLine Value StringLiteral StringLiteralSingle SQLText SQLBlock BlockComment LineComment VarRef BR",
616
+ maxTerm: 33,
617
+ skippedNodes: [0],
618
+ repeatNodeCount: 5,
619
+ tokenData: "$3b~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#%p!Q![$)l![!]$+U!]!_#Y!_!`$.U!`!b#Y!b!c$/U!c!}$)l!}#R#Y#R#S$)l#S#T#Y#T#o$)l#o;'S#Y;'S;=`'q<%lO#YU#_][QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YS$ZTOz$Wz{$j{;'S$W;'S;=`%X<%lO$WS$mVOz$Wz{$j{!P$W!P!Q%S!Q;'S$W;'S;=`%X<%lO$WS%XOmSS%[P;=`<%l$WU%d_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!P#Y!P!Q&c!Q;'S#Y;'S;=`'q<%lO#YU&jVmS[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'UV[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'nP;=`<%l'PU'tP;=`<%l#Y~'|Xi~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTl~Oz$Wz{$j{;'S$W;'S;=`%X<%lO$WU)Sb[QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U*_ZOY*[YZ$WZr*[rs+Qsz*[z{+f{#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U+VTYQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU+i]OY*[YZ$WZr*[rs+Qsz*[z{+f{!P*[!P!Q,b!Q#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U,gWmSOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-SWOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-qOYQQ-tTOY-PYZ-PZ;'S-P;'S;=`.T<%lO-PQ.WP;=`<%l-PU.^VOY*[YZ*[Zz*[z{+f{;'S*[;'S;=`.s<%lO*[U.vP;=`<%l*[U/Q]YQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU0Od[QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{!P(}!P!Q1^!Q#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U1e_mS[QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ2i_[QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ3oVYQ[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ4Z[[QOX2dXY-PYZ-PZ]2d]^-P^p2dpq-Pqt2dtu-Pu;'S2d;'S;=`5P<%lO2dQ5SP;=`<%l2dU5[^[QOX(}XY*[YZ*[Z](}]^*[^p(}pq*[qt(}tu*[uz(}z{/y{;'S(};'S;=`6W<%lO(}U6ZP;=`<%l(}U6cV[QOz$Wz{$j{#o$W#o#p6x#p;'S$W;'S;=`%X<%lO$WU6{]Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o;'S$W;'S;=`%X<%lO$WU7w_Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o#q$W#q#r8v#r;'S$W;'S;=`%X<%lO$WU8{T`QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU9ab[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[U:lZOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU;dTZQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU;v]OY:iYZ$WZw:iwx;_xz:iz{;s{!P:i!P!Q<o!Q#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU<tWmSOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q=aWOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>OOZQQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sWZQOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q?`P;=`<%l=^U?fZOY:iYZ:iZw:iwx@Xxz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU@^ZZQOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]ZQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YUB[d[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{!P9[!P!QCj!Q#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UCq_mS[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQDu_[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQE{VZQ[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQFg`[QOXDpXY=^YZ=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxGix#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQGp_ZQ[QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQHrP;=`<%lDpUHzb[QOX9[XY:iYZ:iZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxJSxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UJZbZQ[QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UKfP;=`<%l9[VKn_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{}#Y}!OLm!O;'S#Y;'S;=`'q<%lO#YVLtf_Q[QOXNYXY!&vYZ$WZ]NY]^! ]^pNYpq!&vqtNYtu! ]uzNYz{!#k{!gNY!g!h!8V!h!oNY!o!p!<w!p!sNY!s!t!DP!t!vNY!v!w!Hu!w;'SNY;'S;=`!&p<%lONYUNa^_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{;'SNY;'S;=`!&p<%lONYU! bV_QOY! ]YZ$WZz! ]z{! w{;'S! ];'S;=`!#e<%lO! ]U! |X_QOY! ]YZ$WZz! ]z{! w{!P! ]!P!Q!!i!Q;'S! ];'S;=`!#e<%lO! ]U!!pSmS_QOY!!|Z;'S!!|;'S;=`!#_<%lO!!|Q!#RS_QOY!!|Z;'S!!|;'S;=`!#_<%lO!!|Q!#bP;=`<%l!!|U!#hP;=`<%l! ]U!#r`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!PNY!P!Q!$t!Q;'SNY;'S;=`!&p<%lONYU!$}ZmS_Q[QOX!%pXY!!|Z]!%p]^!!|^p!%ppq!!|qt!%ptu!!|u;'S!%p;'S;=`!&j<%lO!%pQ!%wZ_Q[QOX!%pXY!!|Z]!%p]^!!|^p!%ppq!!|qt!%ptu!!|u;'S!%p;'S;=`!&j<%lO!%pQ!&mP;=`<%l!%pU!&sP;=`<%lNYV!&{b_QOX! ]XY!&vYZ$WZp! ]pq!&vqz! ]z{! w{!g! ]!g!h!(T!h!o! ]!o!p!*u!p!s! ]!s!t!.}!t!v! ]!v!w!1s!w;'S! ];'S;=`!#e<%lO! ]V!(YX_QOY! ]YZ$WZz! ]z{! w{!z! ]!z!{!(u!{;'S! ];'S;=`!#e<%lO! ]V!(zX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!)g!h;'S! ];'S;=`!#e<%lO! ]V!)lX_QOY! ]YZ$WZz! ]z{! w{!e! ]!e!f!*X!f;'S! ];'S;=`!#e<%lO! ]V!*`VVR_QOY! ]YZ$WZz! ]z{! w{;'S! ];'S;=`!#e<%lO! ]V!*zX_QOY! ]YZ$WZz! ]z{! w{!k! ]!k!l!+g!l;'S! ];'S;=`!#e<%lO! ]V!+lX_QOY! ]YZ$WZz! ]z{! w{!i! ]!i!j!,X!j;'S! ];'S;=`!#e<%lO! ]V!,^X_QOY! ]YZ$WZz! ]z{! w{!t! ]!t!u!,y!u;'S! ];'S;=`!#e<%lO! ]V!-OX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!-k!d;'S! ];'S;=`!#e<%lO! ]V!-pX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!.]!w;'S! ];'S;=`!#e<%lO! ]V!.bX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!*X!h;'S! ];'S;=`!#e<%lO! ]V!/SX_QOY! ]YZ$WZz! ]z{! w{!w! ]!w!x!/o!x;'S! ];'S;=`!#e<%lO! ]V!/tX_QOY! ]YZ$WZz! ]z{! w{!g! ]!g!h!0a!h;'S! ];'S;=`!#e<%lO! ]V!0fX_QOY! ]YZ$WZz! ]z{! w{!t! ]!t!u!1R!u;'S! ];'S;=`!#e<%lO! ]V!1WX_QOY! ]YZ$WZz! ]z{! w{!{! ]!{!|!*X!|;'S! ];'S;=`!#e<%lO! ]V!1xZ_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!2k!d!g! ]!g!h!3}!h;'S! ];'S;=`!#e<%lO! ]V!2pX_QOY! ]YZ$WZz! ]z{! w{!d! ]!d!e!3]!e;'S! ];'S;=`!#e<%lO! ]V!3bX_QOY! ]YZ$WZz! ]z{! w{!n! ]!n!o!.]!o;'S! ];'S;=`!#e<%lO! ]V!4SX_QOY! ]YZ$WZz! ]z{! w{!u! ]!u!v!4o!v;'S! ];'S;=`!#e<%lO! ]V!4tX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!5a!w;'S! ];'S;=`!#e<%lO! ]V!5fX_QOY! ]YZ$WZz! ]z{! w{!f! ]!f!g!6R!g;'S! ];'S;=`!#e<%lO! ]V!6WX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!6s!d;'S! ];'S;=`!#e<%lO! ]V!6xX_QOY! ]YZ$WZz! ]z{! w{!v! ]!v!w!7e!w;'S! ];'S;=`!#e<%lO! ]V!7jX_QOY! ]YZ$WZz! ]z{! w{!c! ]!c!d!*X!d;'S! ];'S;=`!#e<%lO! ]V!8^`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!zNY!z!{!9`!{;'SNY;'S;=`!&p<%lONYV!9g`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!:i!h;'SNY;'S;=`!&p<%lONYV!:p`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!eNY!e!f!;r!f;'SNY;'S;=`!&p<%lONYV!;{^VR_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{;'SNY;'S;=`!&p<%lONYV!=O`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!kNY!k!l!>Q!l;'SNY;'S;=`!&p<%lONYV!>X`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!iNY!i!j!?Z!j;'SNY;'S;=`!&p<%lONYV!?b`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!tNY!t!u!@d!u;'SNY;'S;=`!&p<%lONYV!@k`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!Am!d;'SNY;'S;=`!&p<%lONYV!At`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w!Bv!w;'SNY;'S;=`!&p<%lONYV!B}`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!;r!h;'SNY;'S;=`!&p<%lONYV!DW`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!wNY!w!x!EY!x;'SNY;'S;=`!&p<%lONYV!Ea`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!gNY!g!h!Fc!h;'SNY;'S;=`!&p<%lONYV!Fj`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!tNY!t!u!Gl!u;'SNY;'S;=`!&p<%lONYV!Gs`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!{NY!{!|!;r!|;'SNY;'S;=`!&p<%lONYV!H|b_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!JU!d!gNY!g!h!Lh!h;'SNY;'S;=`!&p<%lONYV!J]`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!dNY!d!e!K_!e;'SNY;'S;=`!&p<%lONYV!Kf`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!nNY!n!o!Bv!o;'SNY;'S;=`!&p<%lONYV!Lo`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!uNY!u!v!Mq!v;'SNY;'S;=`!&p<%lONYV!Mx`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w!Nz!w;'SNY;'S;=`!&p<%lONYV# R`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!fNY!f!g#!T!g;'SNY;'S;=`!&p<%lONYV#![`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d##^!d;'SNY;'S;=`!&p<%lONYV##e`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!vNY!v!w#$g!w;'SNY;'S;=`!&p<%lONYV#$n`_Q[QOXNYXY! ]YZ$WZ]NY]^! ]^pNYpq! ]qtNYtu! ]uzNYz{!#k{!cNY!c!d!;r!d;'SNY;'S;=`!&p<%lONYV#%u][QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#&n{;'S#Y;'S;=`'q<%lO#YV#&sh[QOX#(_XY#,`YZ#,`Z]#(_]^#)]^p#(_pq#,`qt#(_tu#)]uz#(_z{#*f{!P#(_!P!Q#9m!Q!g#(_!g!h#>j!h!o#(_!o!p#Bv!p!s#(_!s!t#I`!t!v#(_!v!w#Mp!w;'S#(_;'S;=`#,Y<%lO#(_U#(d][QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{;'S#(_;'S;=`#,Y<%lO#(_U#)`TOz#)]z{#)o{;'S#)];'S;=`#*`<%lO#)]U#)rVOz#)]z{#)o{!P#)]!P!Q#*X!Q;'S#)];'S;=`#*`<%lO#)]U#*`O^QmSU#*cP;=`<%l#)]U#*k_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!P#(_!P!Q#+j!Q;'S#(_;'S;=`#,Y<%lO#(_U#+sV^QmS[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PU#,]P;=`<%l#(_V#,cbOX#)]XY#,`YZ#,`Zp#)]pq#,`qz#)]z{#)o{!g#)]!g!h#-k!h!o#)]!o!p#/k!p!s#)]!s!t#2q!t!v#)]!v!w#4u!w;'S#)];'S;=`#*`<%lO#)]V#-nVOz#)]z{#)o{!z#)]!z!{#.T!{;'S#)];'S;=`#*`<%lO#)]V#.WVOz#)]z{#)o{!g#)]!g!h#.m!h;'S#)];'S;=`#*`<%lO#)]V#.pVOz#)]z{#)o{!e#)]!e!f#/V!f;'S#)];'S;=`#*`<%lO#)]V#/[TRROz#)]z{#)o{;'S#)];'S;=`#*`<%lO#)]V#/nVOz#)]z{#)o{!k#)]!k!l#0T!l;'S#)];'S;=`#*`<%lO#)]V#0WVOz#)]z{#)o{!i#)]!i!j#0m!j;'S#)];'S;=`#*`<%lO#)]V#0pVOz#)]z{#)o{!t#)]!t!u#1V!u;'S#)];'S;=`#*`<%lO#)]V#1YVOz#)]z{#)o{!c#)]!c!d#1o!d;'S#)];'S;=`#*`<%lO#)]V#1rVOz#)]z{#)o{!v#)]!v!w#2X!w;'S#)];'S;=`#*`<%lO#)]V#2[VOz#)]z{#)o{!g#)]!g!h#/V!h;'S#)];'S;=`#*`<%lO#)]V#2tVOz#)]z{#)o{!w#)]!w!x#3Z!x;'S#)];'S;=`#*`<%lO#)]V#3^VOz#)]z{#)o{!g#)]!g!h#3s!h;'S#)];'S;=`#*`<%lO#)]V#3vVOz#)]z{#)o{!t#)]!t!u#4]!u;'S#)];'S;=`#*`<%lO#)]V#4`VOz#)]z{#)o{!{#)]!{!|#/V!|;'S#)];'S;=`#*`<%lO#)]V#4xXOz#)]z{#)o{!c#)]!c!d#5e!d!g#)]!g!h#6g!h;'S#)];'S;=`#*`<%lO#)]V#5hVOz#)]z{#)o{!d#)]!d!e#5}!e;'S#)];'S;=`#*`<%lO#)]V#6QVOz#)]z{#)o{!n#)]!n!o#2X!o;'S#)];'S;=`#*`<%lO#)]V#6jVOz#)]z{#)o{!u#)]!u!v#7P!v;'S#)];'S;=`#*`<%lO#)]V#7SVOz#)]z{#)o{!v#)]!v!w#7i!w;'S#)];'S;=`#*`<%lO#)]V#7lVOz#)]z{#)o{!f#)]!f!g#8R!g;'S#)];'S;=`#*`<%lO#)]V#8UVOz#)]z{#)o{!c#)]!c!d#8k!d;'S#)];'S;=`#*`<%lO#)]V#8nVOz#)]z{#)o{!v#)]!v!w#9T!w;'S#)];'S;=`#*`<%lO#)]V#9WVOz#)]z{#)o{!c#)]!c!d#/V!d;'S#)];'S;=`#*`<%lO#)]U#9t]mS[QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{;'S#:m;'S;=`#>d<%lO#:mQ#:r][QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{;'S#:m;'S;=`#>d<%lO#:mQ#;nTOz#;kz{#;}{;'S#;k;'S;=`#<l<%lO#;kQ#<QVOz#;kz{#;}{!P#;k!P!Q#<g!Q;'S#;k;'S;=`#<l<%lO#;kQ#<lO^QQ#<oP;=`<%l#;kQ#<w_[QOX#:mXZ#;kZ]#:m]^#;k^p#:mpq#;kqt#:mtu#;kuz#:mz{#<r{!P#:m!P!Q#=v!Q;'S#:m;'S;=`#>d<%lO#:mQ#=}V^Q[QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ#>gP;=`<%l#:mV#>o_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!z#(_!z!{#?n!{;'S#(_;'S;=`#,Y<%lO#(_V#?s_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#@r!h;'S#(_;'S;=`#,Y<%lO#(_V#@w_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!e#(_!e!f#Av!f;'S#(_;'S;=`#,Y<%lO#(_V#A}]RR[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{;'S#(_;'S;=`#,Y<%lO#(_V#B{_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!k#(_!k!l#Cz!l;'S#(_;'S;=`#,Y<%lO#(_V#DP_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!i#(_!i!j#EO!j;'S#(_;'S;=`#,Y<%lO#(_V#ET_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!t#(_!t!u#FS!u;'S#(_;'S;=`#,Y<%lO#(_V#FX_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#GW!d;'S#(_;'S;=`#,Y<%lO#(_V#G]_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w#H[!w;'S#(_;'S;=`#,Y<%lO#(_V#Ha_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#Av!h;'S#(_;'S;=`#,Y<%lO#(_V#Ie_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!w#(_!w!x#Jd!x;'S#(_;'S;=`#,Y<%lO#(_V#Ji_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!g#(_!g!h#Kh!h;'S#(_;'S;=`#,Y<%lO#(_V#Km_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!t#(_!t!u#Ll!u;'S#(_;'S;=`#,Y<%lO#(_V#Lq_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!{#(_!{!|#Av!|;'S#(_;'S;=`#,Y<%lO#(_V#Mua[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#Nz!d!g#(_!g!h$#S!h;'S#(_;'S;=`#,Y<%lO#(_V$ P_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!d#(_!d!e$!O!e;'S#(_;'S;=`#,Y<%lO#(_V$!T_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!n#(_!n!o#H[!o;'S#(_;'S;=`#,Y<%lO#(_V$#X_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!u#(_!u!v$$W!v;'S#(_;'S;=`#,Y<%lO#(_V$$]_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w$%[!w;'S#(_;'S;=`#,Y<%lO#(_V$%a_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!f#(_!f!g$&`!g;'S#(_;'S;=`#,Y<%lO#(_V$&e_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d$'d!d;'S#(_;'S;=`#,Y<%lO#(_V$'i_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!v#(_!v!w$(h!w;'S#(_;'S;=`#,Y<%lO#(_V$(m_[QOX#(_XZ#)]Z]#(_]^#)]^p#(_pq#)]qt#(_tu#)]uz#(_z{#*f{!c#(_!c!d#Av!d;'S#(_;'S;=`#,Y<%lO#(_V$)seSP[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$)l![!c#Y!c!}$)l!}#R#Y#R#S$)l#S#T#Y#T#o$)l#o;'S#Y;'S;=`'q<%lO#Y~$+Ze[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$,l![!c#Y!c!}$,l!}#R#Y#R#S$,l#S#T#Y#T#o$,l#o;'S#Y;'S;=`'q<%lO#Y~$,seT~[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$,l![!c#Y!c!}$,l!}#R#Y#R#S$,l#S#T#Y#T#o$,l#o;'S#Y;'S;=`'q<%lO#YV$.]]pP[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$/Z_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$0Y#h;'S#Y;'S;=`'q<%lO#YU$0__[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$1^#Y;'S#Y;'S;=`'q<%lO#YU$1c_[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$2b#i;'S#Y;'S;=`'q<%lO#YU$2i]oQ[QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
620
+ tokenizers: [
621
+ 0,
622
+ 1,
623
+ 2
624
+ ],
625
+ topRules: { "File": [0, 1] },
626
+ tokenPrec: 245
627
+ });
628
+
629
+ //#endregion
630
+ //#region src/sql-query.ts
631
+ var ListType = class {
632
+ constructor(baseType) {
633
+ this.baseType = baseType;
634
+ }
635
+ toString() {
636
+ return `${this.baseType.toString()}[]`;
637
+ }
638
+ };
639
+ var StructType = class {
640
+ constructor(fields) {
641
+ this.fields = fields;
642
+ }
643
+ toString() {
644
+ return `STRUCT(${this.fields.map((f) => `"${f.name}" ${f.type.toString()}`).join(", ")})`;
645
+ }
646
+ };
647
+ var MapType = class {
648
+ constructor(keyType, valueType) {
649
+ this.keyType = keyType;
650
+ this.valueType = valueType;
651
+ }
652
+ toString() {
653
+ return `MAP(${this.keyType.toString()}, ${this.valueType.toString()})`;
654
+ }
655
+ };
656
+ var EnumType = class {
657
+ constructor(values) {
658
+ this.values = values;
659
+ }
660
+ toString() {
661
+ return `ENUM(${this.values.map((v) => `'${v}'`).join(", ")})`;
662
+ }
663
+ };
664
+ var SQLQuery = class {
665
+ columns;
666
+ allColumns;
667
+ constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
668
+ this.filename = filename;
669
+ this.id = id;
670
+ this.rawQuery = rawQuery;
671
+ this.queryAnonymous = queryAnonymous;
672
+ this.queryNamed = queryNamed;
673
+ this.queryPositional = queryPositional;
674
+ this.type = type;
675
+ this.isOne = isOne;
676
+ this.isPluck = isPluck;
677
+ this.variables = variables;
678
+ this.config = config;
679
+ this.columns = [];
680
+ }
681
+ get isQuery() {
682
+ return this.type === "QUERY";
683
+ }
684
+ get isExec() {
685
+ return this.type === "EXEC";
686
+ }
687
+ get isMigrate() {
688
+ return this.type === "MIGRATE";
689
+ }
690
+ get isTestdata() {
691
+ return this.type === "TESTDATA";
692
+ }
693
+ get skipGenerateFunction() {
694
+ return this.isTestdata || this.isMigrate || this.id.startsWith("_");
695
+ }
696
+ validateVariables() {
697
+ const missingVars = [];
698
+ const varRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
699
+ let match;
700
+ while (true) {
701
+ match = varRegex.exec(this.rawQuery);
702
+ if (match === null) break;
703
+ const varName = match[1];
704
+ if (!this.variables.has(varName)) missingVars.push(varName);
705
+ }
706
+ return missingVars;
707
+ }
708
+ };
709
+ /**
710
+ * Represents a TABLE annotation for generating appenders.
711
+ * TABLE annotations specify a table name for which to generate bulk insert appenders.
712
+ */
713
+ var TableInfo = class {
714
+ /** Columns introspected from the database table schema */
715
+ columns = [];
716
+ constructor(filename, id, tableName, includeColumns, hasAppender) {
717
+ this.filename = filename;
718
+ this.id = id;
719
+ this.tableName = tableName;
720
+ this.includeColumns = includeColumns;
721
+ this.hasAppender = hasAppender;
722
+ }
723
+ get skipGenerateFunction() {
724
+ return !this.hasAppender;
725
+ }
726
+ };
727
+ function parseSQLQueries(filePath, extraVariables) {
728
+ const content = readFileSync(filePath, "utf-8");
729
+ consola.info(`Parsing SQL file: ${filePath}`);
730
+ consola.debug(`File start: ${content.slice(0, 200)}`);
731
+ const queries = [];
732
+ const tables = [];
733
+ const cursor = parser.parse(content).cursor();
734
+ function getLineNumber(position) {
735
+ return content.slice(0, position).split("\n").length;
736
+ }
737
+ function getStr(nodeName, optional = false) {
738
+ const node = cursor.node.getChild(nodeName);
739
+ if (!node) {
740
+ if (optional) return;
741
+ const lineNumber = getLineNumber(cursor.node.from);
742
+ throw new Error(`Node '${nodeName}' not found at line ${lineNumber}`);
743
+ }
744
+ return nodeStr(node);
745
+ }
746
+ function nodeStr(node) {
747
+ return content.slice(node.from, node.to);
748
+ }
749
+ const queryNames = /* @__PURE__ */ new Set();
750
+ do
751
+ if (cursor.name === "QueryBlock") {
752
+ const queryType = (getStr("LineCommentStartSpecial", true) ?? getStr("BlockCommentStartSpecial")).replace("--", "").replace("/*", "").trim();
753
+ const name = getStr("Name").trim();
754
+ const modifiers = cursor.node.getChildren("Modifiers").map((node) => nodeStr(node));
755
+ const isOne = modifiers.includes(":one");
756
+ const isPluck = modifiers.includes(":pluck");
757
+ let configStr = getStr("Config", true);
758
+ if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
759
+ let config = null;
760
+ if (configStr) config = Config.fromYaml(name, filePath, configStr);
761
+ const setVars = cursor.node.getChildren("SetVarLine");
762
+ const variables = /* @__PURE__ */ new Map();
763
+ for (const setVar of setVars) {
764
+ const varName = nodeStr(setVar.getChild("Name"));
765
+ const value = nodeStr(setVar.getChild("Value"));
766
+ variables.set(varName, value.trim());
767
+ }
768
+ function getVariable(varName) {
769
+ if (variables.has(varName)) return variables.get(varName);
770
+ for (const extraVariable of extraVariables) if (extraVariable.name === varName) {
771
+ variables.set(varName, extraVariable.value);
772
+ return extraVariable.value;
773
+ }
774
+ const definedVars = Array.from(variables.keys());
775
+ const suggestion = definedVars.length > 0 ? `Add '@set ${varName} = <value>' before the query. Defined variables: ${definedVars.join(", ")}` : `Add '@set ${varName} = <value>' before the query`;
776
+ throw SqgError.inQuery(`Variable '\${${varName}}' is referenced but not defined`, "MISSING_VARIABLE", name, filePath, { suggestion });
777
+ }
778
+ const sqlNode = cursor.node.getChild("SQLBlock");
779
+ if (!sqlNode) throw SqgError.inQuery("SQL block not found", "SQL_PARSE_ERROR", name, filePath, { suggestion: "Ensure the query has valid SQL content after the annotation comment" });
780
+ const sqlContentStr = nodeStr(sqlNode).trim();
781
+ const sqlCursor = sqlNode.cursor();
782
+ let from = -1;
783
+ let to = -1;
784
+ class SQLQueryBuilder {
785
+ sqlParts = [];
786
+ appendSql(sql$1) {
787
+ this.sqlParts.push(sql$1);
788
+ }
789
+ appendVariable(varName, value) {
790
+ this.sqlParts.push({
791
+ name: varName,
792
+ value
793
+ });
794
+ }
795
+ trim() {
796
+ const lastPart = this.sqlParts.length > 0 ? this.sqlParts[this.sqlParts.length - 1] : null;
797
+ if (lastPart && typeof lastPart === "string") this.sqlParts[this.sqlParts.length - 1] = lastPart.trimEnd();
798
+ }
799
+ parameters() {
800
+ return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
801
+ }
802
+ toSqlWithAnonymousPlaceholders() {
803
+ let sql$1 = "";
479
804
  const sqlParts = [];
480
805
  for (const part of this.sqlParts) if (typeof part === "string") {
481
806
  sql$1 += part;
@@ -568,7 +893,7 @@ function parseSQLQueries(filePath, extraVariables) {
568
893
  if (match) includeColumns.push(...match[1].split(",").map((c) => c.trim()));
569
894
  }
570
895
  const table = new TableInfo(filePath, name, tableName, includeColumns, hasAppender);
571
- if (queryNames.has(name)) throw SqgError.inFile(`Duplicate name '${name}'`, "DUPLICATE_QUERY", filePath, { suggestion: `Rename one of the tables/queries to have a unique name` });
896
+ if (queryNames.has(name)) throw SqgError.inFile(`Duplicate name '${name}'`, "DUPLICATE_QUERY", filePath, { suggestion: "Rename one of the tables/queries to have a unique name" });
572
897
  queryNames.add(name);
573
898
  tables.push(table);
574
899
  consola.debug(`Added table: ${name} -> ${tableName} (appender: ${hasAppender})`);
@@ -584,7 +909,7 @@ function parseSQLQueries(filePath, extraVariables) {
584
909
  config
585
910
  });
586
911
  const query = new SQLQuery(filePath, name, sqlContentStr, sql.toSqlWithAnonymousPlaceholders(), sql.toSqlWithNamedPlaceholders(), sql.toSqlWithPositionalPlaceholders(), queryType, isOne, isPluck, variables, config);
587
- if (queryNames.has(name)) throw SqgError.inFile(`Duplicate query name '${name}'`, "DUPLICATE_QUERY", filePath, { suggestion: `Rename one of the queries to have a unique name` });
912
+ if (queryNames.has(name)) throw SqgError.inFile(`Duplicate query name '${name}'`, "DUPLICATE_QUERY", filePath, { suggestion: "Rename one of the queries to have a unique name" });
588
913
  queryNames.add(name);
589
914
  queries.push(query);
590
915
  consola.debug(`Added query: ${name} (${queryType})`);
@@ -986,7 +1311,7 @@ var TypeMapper = class {
986
1311
  }
987
1312
  if (column.type instanceof StructType) return path + this.formatStructTypeName(column.name);
988
1313
  if (column.type instanceof MapType) return path + this.formatMapTypeName(column.name);
989
- if (!column.type) throw new TypeMappingError(`Missing type information`, column.name);
1314
+ if (!column.type) throw new TypeMappingError("Missing type information", column.name);
990
1315
  return this.mapPrimitiveType(column.type.toString(), column.nullable);
991
1316
  }
992
1317
  /**
@@ -1457,7 +1782,7 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
1457
1782
  const name = `${gen.name}-jdbc`;
1458
1783
  writeGeneratedFile(projectDir, {
1459
1784
  name,
1460
- generator: "java/jdbc",
1785
+ generator: "java/duckdb/jdbc",
1461
1786
  output: gen.output,
1462
1787
  config: gen.config
1463
1788
  }, this.javaGenerator, name, q, tables, "duckdb");
@@ -1754,16 +2079,26 @@ var TsDuckDBGenerator = class extends TsGenerator {
1754
2079
 
1755
2080
  //#endregion
1756
2081
  //#region src/generators/index.ts
2082
+ /**
2083
+ * Get a generator instance for the given generator.
2084
+ * Accepts both short (language/engine) and full (language/engine/driver) formats.
2085
+ */
1757
2086
  function getGenerator(generator) {
1758
- switch (generator) {
1759
- case "java/jdbc": return new JavaGenerator("templates/java-jdbc.hbs");
1760
- case "java/duckdb-arrow": return new JavaDuckDBArrowGenerator("templates/java-duckdb-arrow.hbs");
1761
- case "typescript/better-sqlite3": return new TsGenerator("templates/better-sqlite3.hbs");
1762
- case "typescript/duckdb": return new TsDuckDBGenerator("templates/typescript-duckdb.hbs");
1763
- default: {
1764
- const similar = findSimilarGenerators(generator);
1765
- throw new InvalidGeneratorError(generator, [...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
2087
+ const fullGenerator = resolveGenerator(generator);
2088
+ try {
2089
+ const info = parseGenerator(generator);
2090
+ const key = `${info.language}/${info.driver}`;
2091
+ switch (key) {
2092
+ case "typescript/better-sqlite3":
2093
+ case "typescript/node": return new TsGenerator(`templates/${info.template}`);
2094
+ case "typescript/node-api": return new TsDuckDBGenerator(`templates/${info.template}`);
2095
+ case "java/jdbc": return new JavaGenerator(`templates/${info.template}`);
2096
+ case "java/arrow": return new JavaDuckDBArrowGenerator(`templates/${info.template}`);
2097
+ default: throw new Error(`No generator class for ${key}`);
1766
2098
  }
2099
+ } catch {
2100
+ const similar = findSimilarGenerators(generator);
2101
+ throw new InvalidGeneratorError(fullGenerator, [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
1767
2102
  }
1768
2103
  }
1769
2104
 
@@ -1932,6 +2267,8 @@ function generateSourceFile(name, queries, tables, templatePath, generator, engi
1932
2267
  allowProtoMethodsByDefault: true
1933
2268
  });
1934
2269
  }
2270
+ /** All valid generator strings for schema validation */
2271
+ const validGenerators = [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES];
1935
2272
  /**
1936
2273
  * Project configuration schema with descriptions for validation messages
1937
2274
  */
@@ -1939,10 +2276,9 @@ const ProjectSchema = z.object({
1939
2276
  version: z.number().describe("Configuration version (currently 1)"),
1940
2277
  name: z.string().min(1, "Project name is required").describe("Project name used for generated class names"),
1941
2278
  sql: z.array(z.object({
1942
- engine: z.enum(DB_ENGINES).describe(`Database engine: ${DB_ENGINES.join(", ")}`),
1943
2279
  files: z.array(z.string().min(1)).min(1, "At least one SQL file is required").describe("SQL files to process"),
1944
2280
  gen: z.array(z.object({
1945
- generator: z.enum(GENERATOR_NAMES).describe(`Code generator: ${GENERATOR_NAMES.join(", ")}`),
2281
+ generator: z.string().refine((val) => isValidGenerator(val), { message: `Invalid generator. Valid generators: ${validGenerators.join(", ")}` }).describe(`Code generation generator: ${SHORT_GENERATOR_NAMES.join(", ")}`),
1946
2282
  name: z.string().optional().describe("Override the generated class/module name"),
1947
2283
  template: z.string().optional().describe("Custom Handlebars template path"),
1948
2284
  output: z.string().min(1, "Output path is required").describe("Output file or directory path"),
@@ -1960,15 +2296,36 @@ var ExtraVariable = class {
1960
2296
  this.value = value;
1961
2297
  }
1962
2298
  };
1963
- function createExtraVariables(sources) {
2299
+ function createExtraVariables(sources, suppressLogging = false) {
1964
2300
  return sources.map((source) => {
1965
2301
  const path = source.path;
1966
2302
  const resolvedPath = path.replace("$HOME", homedir());
1967
2303
  const varName = `sources_${(source.name ?? basename(path, extname(resolvedPath))).replace(/\s+/g, "_")}`;
1968
- consola.info("Extra variable:", varName, resolvedPath);
2304
+ if (!suppressLogging) consola.info("Extra variable:", varName, resolvedPath);
1969
2305
  return new ExtraVariable(varName, `'${resolvedPath}'`);
1970
2306
  });
1971
2307
  }
2308
+ function buildProjectFromCliOptions(options) {
2309
+ if (!isValidGenerator(options.generator)) {
2310
+ const similar = findSimilarGenerators(options.generator);
2311
+ const allGenerators = [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES];
2312
+ throw new InvalidGeneratorError(options.generator, allGenerators, similar.length > 0 ? similar[0] : void 0);
2313
+ }
2314
+ const generatorInfo = parseGenerator(options.generator);
2315
+ const genConfig = {
2316
+ generator: options.generator,
2317
+ output: options.output || "."
2318
+ };
2319
+ if (generatorInfo.language === "java") genConfig.config = { package: "generated" };
2320
+ return {
2321
+ version: 1,
2322
+ name: options.name || "generated",
2323
+ sql: [{
2324
+ files: options.files,
2325
+ gen: [genConfig]
2326
+ }]
2327
+ };
2328
+ }
1972
2329
  /**
1973
2330
  * Parse and validate project configuration with helpful error messages
1974
2331
  */
@@ -1990,12 +2347,12 @@ function parseProjectConfig(filePath) {
1990
2347
  const obj = parsed;
1991
2348
  if (obj.sql && Array.isArray(obj.sql)) for (let i = 0; i < obj.sql.length; i++) {
1992
2349
  const sqlConfig = obj.sql[i];
1993
- if (sqlConfig.engine && !DB_ENGINES.includes(sqlConfig.engine)) throw new InvalidEngineError(String(sqlConfig.engine), [...DB_ENGINES]);
1994
2350
  if (sqlConfig.gen && Array.isArray(sqlConfig.gen)) for (let j = 0; j < sqlConfig.gen.length; j++) {
1995
2351
  const genConfig = sqlConfig.gen[j];
1996
- if (genConfig.generator && !GENERATOR_NAMES.includes(genConfig.generator)) {
2352
+ if (genConfig.generator && !isValidGenerator(String(genConfig.generator))) {
1997
2353
  const similar = findSimilarGenerators(String(genConfig.generator));
1998
- throw new InvalidGeneratorError(String(genConfig.generator), [...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
2354
+ const allGenerators = [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES];
2355
+ throw new InvalidGeneratorError(String(genConfig.generator), allGenerators, similar.length > 0 ? similar[0] : void 0);
1999
2356
  }
2000
2357
  }
2001
2358
  }
@@ -2039,11 +2396,16 @@ function validateQueries(queries) {
2039
2396
  };
2040
2397
  }
2041
2398
  }
2042
- async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine) {
2399
+ async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine, writeToStdout = false) {
2043
2400
  await generator.beforeGenerate(projectDir, gen, queries, tables);
2044
2401
  const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
2045
2402
  const name = gen.name ?? basename(file, extname(file));
2046
2403
  const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.config);
2404
+ if (writeToStdout) {
2405
+ process.stdout.write(sourceFile);
2406
+ if (!sourceFile.endsWith("\n")) process.stdout.write("\n");
2407
+ return null;
2408
+ }
2047
2409
  const outputPath = getOutputPath(projectDir, name, gen, generator);
2048
2410
  writeFileSync(outputPath, sourceFile);
2049
2411
  consola.success(`Generated ${outputPath}`);
@@ -2051,33 +2413,11 @@ async function writeGeneratedFile(projectDir, gen, generator, file, queries, tab
2051
2413
  return outputPath;
2052
2414
  }
2053
2415
  /**
2054
- * Validate project configuration without executing queries
2416
+ * Validate project configuration from a Project object without executing queries
2055
2417
  * Use this for pre-flight checks before generation
2056
2418
  */
2057
- async function validateProject(projectPath) {
2419
+ async function validateProjectFromConfig(project, projectDir) {
2058
2420
  const errors = [];
2059
- const projectDir = resolve(dirname(projectPath));
2060
- let project;
2061
- try {
2062
- project = parseProjectConfig(projectPath);
2063
- } catch (e) {
2064
- if (e instanceof SqgError) return {
2065
- valid: false,
2066
- errors: [{
2067
- code: e.code,
2068
- message: e.message,
2069
- suggestion: e.suggestion,
2070
- context: e.context
2071
- }]
2072
- };
2073
- return {
2074
- valid: false,
2075
- errors: [{
2076
- code: "UNKNOWN_ERROR",
2077
- message: String(e)
2078
- }]
2079
- };
2080
- }
2081
2421
  const sqlFiles = [];
2082
2422
  const generators = [];
2083
2423
  for (const sql of project.sql) for (const sqlFile of sql.files) {
@@ -2091,15 +2431,15 @@ async function validateProject(projectPath) {
2091
2431
  });
2092
2432
  for (const gen of sql.gen) {
2093
2433
  generators.push(gen.generator);
2094
- if (!SUPPORTED_GENERATORS[gen.generator].compatibleEngines.includes(sql.engine)) errors.push({
2095
- code: "GENERATOR_ENGINE_MISMATCH",
2096
- message: `Generator '${gen.generator}' is not compatible with engine '${sql.engine}'`,
2097
- suggestion: `For '${sql.engine}', use one of: ${Object.entries(SUPPORTED_GENERATORS).filter(([_, info]) => info.compatibleEngines.includes(sql.engine)).map(([name]) => name).join(", ")}`,
2098
- context: {
2099
- generator: gen.generator,
2100
- engine: sql.engine
2101
- }
2102
- });
2434
+ if (!isValidGenerator(gen.generator)) {
2435
+ const similar = findSimilarGenerators(gen.generator);
2436
+ errors.push({
2437
+ code: "INVALID_GENERATOR",
2438
+ message: `Invalid generator '${gen.generator}'`,
2439
+ suggestion: similar.length > 0 ? `Did you mean '${similar[0]}'?` : `Valid generators: ${SHORT_GENERATOR_NAMES.join(", ")}`,
2440
+ context: { generator: gen.generator }
2441
+ });
2442
+ }
2103
2443
  }
2104
2444
  }
2105
2445
  return {
@@ -2114,387 +2454,506 @@ async function validateProject(projectPath) {
2114
2454
  };
2115
2455
  }
2116
2456
  /**
2117
- * Process a project configuration and generate code
2457
+ * Validate project configuration from a YAML file without executing queries
2458
+ * Use this for pre-flight checks before generation
2118
2459
  */
2119
- async function processProject(projectPath) {
2460
+ async function validateProject(projectPath) {
2120
2461
  const projectDir = resolve(dirname(projectPath));
2121
- const project = parseProjectConfig(projectPath);
2122
- const extraVariables = createExtraVariables(project.sources ?? []);
2123
- if (extraVariables.length > 0) consola.info("Extra variables:", extraVariables);
2124
- const files = [];
2125
- for (const sql of project.sql) {
2126
- for (const gen of sql.gen) {
2127
- const generatorInfo = SUPPORTED_GENERATORS[gen.generator];
2128
- if (!generatorInfo.compatibleEngines.includes(sql.engine)) throw new GeneratorEngineMismatchError(gen.generator, sql.engine, generatorInfo.compatibleEngines);
2129
- }
2130
- for (const sqlFile of sql.files) {
2131
- const fullPath = join(projectDir, sqlFile);
2132
- if (!existsSync(fullPath)) throw new FileNotFoundError(fullPath, projectDir);
2133
- let queries;
2134
- let tables;
2135
- try {
2136
- const parseResult = parseSQLQueries(fullPath, extraVariables);
2137
- queries = parseResult.queries;
2138
- tables = parseResult.tables;
2139
- } catch (e) {
2140
- if (e instanceof SqgError) throw e;
2141
- throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
2142
- }
2143
- try {
2144
- const dbEngine = getDatabaseEngine(sql.engine);
2145
- await dbEngine.initializeDatabase(queries);
2146
- await dbEngine.executeQueries(queries);
2147
- if (tables.length > 0) await dbEngine.introspectTables(tables);
2148
- validateQueries(queries);
2149
- await dbEngine.close();
2150
- } catch (e) {
2151
- if (e instanceof SqgError) throw e;
2152
- throw new SqgError(`Database error processing ${sqlFile}: ${e.message}`, "DATABASE_ERROR", `Check that the SQL is valid for engine '${sql.engine}'`, {
2153
- file: sqlFile,
2154
- engine: sql.engine
2155
- });
2156
- }
2462
+ let project;
2463
+ try {
2464
+ project = parseProjectConfig(projectPath);
2465
+ } catch (e) {
2466
+ if (e instanceof SqgError) return {
2467
+ valid: false,
2468
+ errors: [{
2469
+ code: e.code,
2470
+ message: e.message,
2471
+ suggestion: e.suggestion,
2472
+ context: e.context
2473
+ }]
2474
+ };
2475
+ return {
2476
+ valid: false,
2477
+ errors: [{
2478
+ code: "UNKNOWN_ERROR",
2479
+ message: String(e)
2480
+ }]
2481
+ };
2482
+ }
2483
+ return await validateProjectFromConfig(project, projectDir);
2484
+ }
2485
+ /**
2486
+ * Process a project configuration and generate code from a Project object
2487
+ */
2488
+ async function processProjectFromConfig(project, projectDir, writeToStdout = false) {
2489
+ const originalLevel = consola.level;
2490
+ if (writeToStdout) consola.level = LogLevels.silent;
2491
+ try {
2492
+ const extraVariables = createExtraVariables(project.sources ?? [], writeToStdout);
2493
+ const files = [];
2494
+ for (const sql of project.sql) {
2495
+ const gensByEngine = /* @__PURE__ */ new Map();
2157
2496
  for (const gen of sql.gen) {
2158
- const outputPath = await writeGeneratedFile(projectDir, gen, getGenerator(gen.generator), sqlFile, queries, tables, sql.engine);
2159
- files.push(outputPath);
2497
+ const engine = getGeneratorEngine(gen.generator);
2498
+ if (!gensByEngine.has(engine)) gensByEngine.set(engine, []);
2499
+ gensByEngine.get(engine).push(gen);
2500
+ }
2501
+ for (const sqlFile of sql.files) {
2502
+ const fullPath = join(projectDir, sqlFile);
2503
+ if (!existsSync(fullPath)) throw new FileNotFoundError(fullPath, projectDir);
2504
+ let queries;
2505
+ let tables;
2506
+ try {
2507
+ const parseResult = parseSQLQueries(fullPath, extraVariables);
2508
+ queries = parseResult.queries;
2509
+ tables = parseResult.tables;
2510
+ } catch (e) {
2511
+ if (e instanceof SqgError) throw e;
2512
+ throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
2513
+ }
2514
+ for (const [engine, gens] of gensByEngine) {
2515
+ try {
2516
+ const dbEngine = getDatabaseEngine(engine);
2517
+ await dbEngine.initializeDatabase(queries);
2518
+ await dbEngine.executeQueries(queries);
2519
+ if (tables.length > 0) await dbEngine.introspectTables(tables);
2520
+ validateQueries(queries);
2521
+ await dbEngine.close();
2522
+ } catch (e) {
2523
+ if (e instanceof SqgError) throw e;
2524
+ throw new SqgError(`Database error processing ${sqlFile}: ${e.message}`, "DATABASE_ERROR", `Check that the SQL is valid for engine '${engine}'`, {
2525
+ file: sqlFile,
2526
+ engine
2527
+ });
2528
+ }
2529
+ for (const gen of gens) {
2530
+ const outputPath = await writeGeneratedFile(projectDir, gen, getGenerator(gen.generator), sqlFile, queries, tables, engine, writeToStdout);
2531
+ if (outputPath !== null) files.push(outputPath);
2532
+ }
2533
+ }
2160
2534
  }
2161
2535
  }
2536
+ return files;
2537
+ } finally {
2538
+ if (writeToStdout) consola.level = originalLevel;
2162
2539
  }
2163
- return files;
2164
2540
  }
2165
-
2166
- //#endregion
2167
- //#region src/init.ts
2168
- /**
2169
- * SQG Project Initialization - Creates new SQG projects with example files
2170
- */
2171
2541
  /**
2172
- * Get the default generator for an engine
2542
+ * Process a project configuration and generate code from a YAML file
2173
2543
  */
2174
- function getDefaultGenerator(engine) {
2175
- return {
2176
- sqlite: "typescript/better-sqlite3",
2177
- duckdb: "typescript/duckdb",
2178
- postgres: "java/jdbc"
2179
- }[engine];
2544
+ async function processProject(projectPath) {
2545
+ const projectDir = resolve(dirname(projectPath));
2546
+ return await processProjectFromConfig(parseProjectConfig(projectPath), projectDir, false);
2180
2547
  }
2181
- /**
2182
- * Generate example SQL content based on engine
2183
- */
2184
- function getExampleSql(engine) {
2185
- return {
2186
- sqlite: `-- MIGRATE 1
2187
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2188
- CREATE TABLE users (
2189
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2190
- name TEXT NOT NULL,
2191
- email TEXT UNIQUE NOT NULL,
2192
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
2193
- );
2194
2548
 
2195
- -- MIGRATE 2
2196
- CREATE TABLE posts (
2197
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2198
- user_id INTEGER NOT NULL REFERENCES users(id),
2199
- title TEXT NOT NULL,
2200
- content TEXT,
2201
- published INTEGER DEFAULT 0,
2202
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
2203
- );
2549
+ //#endregion
2550
+ //#region src/mcp-server.ts
2551
+ const server = new Server({
2552
+ name: "sqg-mcp",
2553
+ version: process.env.npm_package_version ?? "0.6.0"
2554
+ }, { capabilities: {
2555
+ tools: {},
2556
+ resources: {}
2557
+ } });
2558
+ function formatGeneratorListWithDescriptions() {
2559
+ const lines = [];
2560
+ const seen = /* @__PURE__ */ new Set();
2561
+ for (const shortName of SHORT_GENERATOR_NAMES) {
2562
+ const fullName = Object.keys(GENERATORS).find((g) => g.startsWith(`${shortName}/`));
2563
+ if (fullName) {
2564
+ const info = GENERATORS[fullName];
2565
+ lines.push(`- ${shortName} - ${info.description}`);
2566
+ seen.add(fullName);
2567
+ }
2568
+ }
2569
+ for (const [fullName, info] of Object.entries(GENERATORS)) if (!seen.has(fullName)) lines.push(`- ${fullName} - ${info.description}`);
2570
+ return lines.join("\n");
2571
+ }
2572
+ function formatGeneratorListSimple() {
2573
+ const generators = [...SHORT_GENERATOR_NAMES];
2574
+ const seen = new Set(SHORT_GENERATOR_NAMES.map((s) => Object.keys(GENERATORS).find((g) => g.startsWith(`${s}/`))));
2575
+ for (const fullName of Object.keys(GENERATORS)) if (!seen.has(fullName)) generators.push(fullName);
2576
+ return generators.join(", ");
2577
+ }
2578
+ async function generateCode(sql, generator) {
2579
+ const engine = getGeneratorEngine(generator);
2580
+ const tempDir = join(tmpdir(), `sqg-mcp-${randomUUID()}`);
2581
+ const sqlFile = join(tempDir, "queries.sql");
2582
+ const configFile = join(tempDir, "sqg.yaml");
2583
+ try {
2584
+ mkdirSync(tempDir, { recursive: true });
2585
+ writeFileSync(sqlFile, sql, "utf-8");
2586
+ const genConfig = {
2587
+ generator,
2588
+ output: "./generated/"
2589
+ };
2590
+ if (generator.startsWith("java/")) genConfig.config = { package: "sqg.generated" };
2591
+ const projectYaml = {
2592
+ version: 1,
2593
+ name: "generated",
2594
+ sql: [{
2595
+ engine,
2596
+ files: ["queries.sql"],
2597
+ gen: [genConfig]
2598
+ }]
2599
+ };
2600
+ writeFileSync(configFile, YAML.stringify(projectYaml), "utf-8");
2601
+ const files = await processProject(configFile);
2602
+ if (files.length === 0) return {
2603
+ code: "",
2604
+ error: "No files were generated"
2605
+ };
2606
+ return { code: readFileSync(files[0], "utf-8") };
2607
+ } catch (error) {
2608
+ return {
2609
+ code: "",
2610
+ error: error instanceof Error ? error.message : String(error)
2611
+ };
2612
+ } finally {
2613
+ try {
2614
+ rmSync(tempDir, {
2615
+ recursive: true,
2616
+ force: true
2617
+ });
2618
+ } catch (error) {}
2619
+ }
2620
+ }
2621
+ const GENERATE_CODE_DESCRIPTION = `Generate type-safe database access code from annotated SQL queries.
2622
+
2623
+ CRITICAL REQUIREMENTS:
2624
+ 1. MIGRATE statements MUST come BEFORE any QUERY/EXEC that references those tables
2625
+ 2. Each query block needs a unique name
2626
+ 3. Parameters require @set declarations with sample values
2204
2627
 
2205
- -- TESTDATA seed_data
2206
- INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
2207
- INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
2208
- INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', 1);
2209
- `,
2210
- duckdb: `-- MIGRATE 1
2211
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2212
- CREATE TABLE users (
2213
- id INTEGER PRIMARY KEY,
2214
- name VARCHAR NOT NULL,
2215
- email VARCHAR UNIQUE NOT NULL,
2216
- metadata STRUCT(role VARCHAR, active BOOLEAN),
2217
- tags VARCHAR[],
2218
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2219
- );
2628
+ ${SQL_SYNTAX_REFERENCE}
2220
2629
 
2221
- -- MIGRATE 2
2222
- CREATE TABLE posts (
2223
- id INTEGER PRIMARY KEY,
2224
- user_id INTEGER NOT NULL REFERENCES users(id),
2225
- title VARCHAR NOT NULL,
2226
- content VARCHAR,
2227
- published BOOLEAN DEFAULT FALSE,
2228
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2229
- );
2630
+ VALID GENERATORS (use short form):
2631
+ ${formatGeneratorListWithDescriptions()}
2230
2632
 
2231
- -- TESTDATA seed_data
2232
- INSERT INTO users (id, name, email, metadata, tags)
2233
- VALUES (1, 'Alice', 'alice@example.com', {'role': 'admin', 'active': true}, ['developer', 'lead']);
2234
- INSERT INTO users (id, name, email, metadata, tags)
2235
- VALUES (2, 'Bob', 'bob@example.com', {'role': 'user', 'active': true}, ['developer']);
2236
- INSERT INTO posts (id, user_id, title, content, published)
2237
- VALUES (1, 1, 'Hello World', 'My first post!', TRUE);
2238
- `,
2239
- postgres: `-- MIGRATE 1
2240
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2241
- CREATE TABLE users (
2242
- id SERIAL PRIMARY KEY,
2243
- name TEXT NOT NULL,
2244
- email TEXT UNIQUE NOT NULL,
2245
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2246
- );
2633
+ COMMON MISTAKES TO AVOID:
2634
+ - Missing MIGRATE before QUERY (causes "no such table" error)
2635
+ - Missing @set for parameters (causes "undefined variable" error)
2636
+ - Duplicate query names (causes "duplicate query" error)
2637
+ - Using :pluck with multiple columns (only works with 1 column)`;
2638
+ const VALIDATE_SQL_DESCRIPTION = `Validate SQL queries with SQG annotations without generating code. Use this to check for errors before generating.
2247
2639
 
2248
- -- MIGRATE 2
2249
- CREATE TABLE posts (
2250
- id SERIAL PRIMARY KEY,
2251
- user_id INTEGER NOT NULL REFERENCES users(id),
2252
- title TEXT NOT NULL,
2253
- content TEXT,
2254
- published BOOLEAN DEFAULT FALSE,
2255
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2256
- );
2640
+ Returns JSON with validation results including:
2641
+ - valid: boolean indicating success
2642
+ - project: project metadata if valid
2643
+ - sqlFiles: list of SQL files processed
2644
+ - generators: list of generators used
2645
+ - errors: array of error messages if invalid
2257
2646
 
2258
- -- TESTDATA seed_data
2259
- INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
2260
- INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
2261
- INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', TRUE);
2262
- `
2263
- }[engine] + `
2264
- -- QUERY list_users
2265
- SELECT id, name, email, created_at
2266
- FROM users
2267
- ORDER BY created_at DESC;
2647
+ See generate_code tool description for complete syntax reference.`;
2648
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
2649
+ return { tools: [{
2650
+ name: "generate_code",
2651
+ description: GENERATE_CODE_DESCRIPTION,
2652
+ inputSchema: {
2653
+ type: "object",
2654
+ properties: {
2655
+ sql: {
2656
+ type: "string",
2657
+ description: "Complete SQL file content with SQG annotations. IMPORTANT: Include MIGRATE statements first to create tables before QUERY statements that use them."
2658
+ },
2659
+ generator: {
2660
+ type: "string",
2661
+ description: `Code generator to use. Valid options: ${formatGeneratorListSimple()}`
2662
+ }
2663
+ },
2664
+ required: ["sql", "generator"]
2665
+ }
2666
+ }, {
2667
+ name: "validate_sql",
2668
+ description: VALIDATE_SQL_DESCRIPTION,
2669
+ inputSchema: {
2670
+ type: "object",
2671
+ properties: {
2672
+ sql: {
2673
+ type: "string",
2674
+ description: "Complete SQL file content with SQG annotations to validate. Include MIGRATE statements before QUERY statements."
2675
+ },
2676
+ generator: {
2677
+ type: "string",
2678
+ description: `Code generator for validation context. Valid options: ${formatGeneratorListSimple()}`
2679
+ }
2680
+ },
2681
+ required: ["sql", "generator"]
2682
+ }
2683
+ }] };
2684
+ });
2685
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
2686
+ return { resources: [{
2687
+ uri: "sqg://documentation",
2688
+ name: "SQG Documentation",
2689
+ description: "Complete documentation for SQG (SQL Query Generator) including syntax, generators, and usage examples",
2690
+ mimeType: "text/markdown"
2691
+ }] };
2692
+ });
2693
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2694
+ const { uri } = request.params;
2695
+ if (uri === "sqg://documentation") {
2696
+ const generatorListMarkdown = Object.entries(GENERATORS).map(([name, info]) => {
2697
+ const shortName = `${info.language}/${info.engine}`;
2698
+ return `- \`${Object.keys(GENERATORS).find((g) => g.startsWith(`${shortName}/`)) === name ? shortName : name}\` - ${info.description}`;
2699
+ }).filter((line, index, arr) => arr.indexOf(line) === index).join("\n");
2700
+ return { contents: [{
2701
+ uri,
2702
+ mimeType: "text/markdown",
2703
+ text: `# SQG - SQL Query Generator
2268
2704
 
2269
- -- QUERY get_user_by_id :one
2270
- @set id = 1
2271
- SELECT id, name, email, created_at
2272
- FROM users
2273
- WHERE id = \${id};
2705
+ SQG is a type-safe SQL code generator that reads SQL queries from \`.sql\` files with special annotations and generates type-safe database access code in multiple target languages (TypeScript and Java).
2274
2706
 
2275
- -- QUERY get_user_by_email :one
2276
- @set email = 'alice@example.com'
2277
- SELECT id, name, email, created_at
2278
- FROM users
2279
- WHERE email = \${email};
2707
+ ## Overview
2280
2708
 
2281
- -- QUERY count_users :one :pluck
2282
- SELECT COUNT(*) FROM users;
2709
+ SQG introspects SQL queries at build time against real database engines to determine column types and generates strongly-typed wrapper functions.
2283
2710
 
2284
- -- QUERY list_posts_by_user
2285
- @set user_id = 1
2286
- SELECT p.id, p.title, p.content, p.published, p.created_at
2287
- FROM posts p
2288
- WHERE p.user_id = \${user_id}
2289
- ORDER BY p.created_at DESC;
2711
+ **Website:** https://sqg.dev
2712
+ **Repository:** https://github.com/sqg-dev/sqg
2290
2713
 
2291
- -- QUERY list_published_posts
2292
- SELECT
2293
- p.id,
2294
- p.title,
2295
- p.content,
2296
- p.created_at,
2297
- u.name as author_name,
2298
- u.email as author_email
2299
- FROM posts p
2300
- JOIN users u ON p.user_id = u.id
2301
- WHERE p.published = 1
2302
- ORDER BY p.created_at DESC;
2303
- ` + {
2304
- sqlite: `
2305
- -- EXEC create_user
2306
- @set name = 'New User'
2307
- @set email = 'new@example.com'
2308
- INSERT INTO users (name, email)
2309
- VALUES (\${name}, \${email});
2714
+ ## Key Features
2310
2715
 
2311
- -- EXEC create_post
2312
- @set user_id = 1
2313
- @set title = 'New Post'
2314
- @set content = 'Post content here'
2315
- INSERT INTO posts (user_id, title, content)
2316
- VALUES (\${user_id}, \${title}, \${content});
2716
+ - **Type-safe by design** - Generates fully-typed code with accurate column types inferred from your database
2717
+ - **Multiple database engines** - Supports ${DB_ENGINES.join(", ")}
2718
+ - **Multiple language targets** - Generate TypeScript or Java code from the same SQL files
2719
+ - **Arrow API support** - Can generate Apache Arrow API bindings for DuckDB (Java)
2720
+ - **DBeaver compatible** - Works seamlessly with DBeaver for database development and testing
2721
+ - **Complex type support** - DuckDB: Handles structs, lists, and maps
2722
+ - **Migration management** - Built-in support for schema migrations and test data
2317
2723
 
2318
- -- EXEC publish_post
2319
- @set id = 1
2320
- UPDATE posts SET published = 1 WHERE id = \${id};
2724
+ ## SQL Annotations
2321
2725
 
2322
- -- EXEC delete_post
2323
- @set id = 1
2324
- DELETE FROM posts WHERE id = \${id};
2325
- `,
2326
- duckdb: `
2327
- -- EXEC create_user
2328
- @set id = 100
2329
- @set name = 'New User'
2330
- @set email = 'new@example.com'
2331
- INSERT INTO users (id, name, email)
2332
- VALUES (\${id}, \${name}, \${email});
2726
+ ${SQL_SYNTAX_REFERENCE}
2333
2727
 
2334
- -- EXEC create_post
2335
- @set id = 100
2336
- @set user_id = 1
2337
- @set title = 'New Post'
2338
- @set content = 'Post content here'
2339
- INSERT INTO posts (id, user_id, title, content)
2340
- VALUES (\${id}, \${user_id}, \${title}, \${content});
2728
+ ## Supported Generators
2341
2729
 
2342
- -- EXEC publish_post
2343
- @set id = 1
2344
- UPDATE posts SET published = TRUE WHERE id = \${id};
2730
+ Valid generator strings:
2345
2731
 
2346
- -- EXEC delete_post
2347
- @set id = 1
2348
- DELETE FROM posts WHERE id = \${id};
2349
- `,
2350
- postgres: `
2351
- -- EXEC create_user
2352
- @set name = 'New User'
2353
- @set email = 'new@example.com'
2354
- INSERT INTO users (name, email)
2355
- VALUES (\${name}, \${email});
2732
+ ${generatorListMarkdown}
2356
2733
 
2357
- -- EXEC create_post
2358
- @set user_id = 1
2359
- @set title = 'New Post'
2360
- @set content = 'Post content here'
2361
- INSERT INTO posts (user_id, title, content)
2362
- VALUES (\${user_id}, \${title}, \${content});
2734
+ ## MCP Tools
2363
2735
 
2364
- -- EXEC publish_post
2365
- @set id = 1
2366
- UPDATE posts SET published = TRUE WHERE id = \${id};
2736
+ ### generate_code
2367
2737
 
2368
- -- EXEC delete_post
2369
- @set id = 1
2370
- DELETE FROM posts WHERE id = \${id};
2371
- `
2372
- }[engine];
2738
+ Generate type-safe database access code from SQL queries with SQG annotations.
2739
+
2740
+ **Parameters:**
2741
+ - \`sql\` (string, required): SQL queries with SQG annotations
2742
+ - \`generator\` (string, required): Code generation generator (see supported generators above)
2743
+
2744
+ **Example:**
2745
+ \`\`\`json
2746
+ {
2747
+ "sql": "-- MIGRATE 1\\nCREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);\\n\\n-- QUERY getUsers\\nSELECT * FROM users;",
2748
+ "generator": "typescript/sqlite"
2373
2749
  }
2374
- /**
2375
- * Generate sqg.yaml configuration
2376
- */
2377
- function getConfigYaml(engine, generator, output) {
2378
- SUPPORTED_GENERATORS[generator];
2379
- const config = {
2380
- version: 1,
2381
- name: "my-project",
2382
- sql: [{
2383
- engine,
2384
- files: ["queries.sql"],
2385
- gen: [{
2386
- generator,
2387
- output: output.endsWith("/") ? output : `${output}/`
2388
- }]
2389
- }]
2390
- };
2391
- if (generator.startsWith("java/")) config.sql[0].gen[0].config = { package: "generated" };
2392
- return `# SQG Configuration
2393
- # Generated by: sqg init
2394
- # Documentation: https://sqg.dev
2750
+ \`\`\`
2395
2751
 
2396
- version: 1
2397
- name: my-project
2752
+ ### validate_sql
2398
2753
 
2399
- sql:
2400
- - engine: ${engine}
2401
- files:
2402
- - queries.sql
2403
- gen:
2404
- - generator: ${generator}
2405
- output: ${output.endsWith("/") ? output : `${output}/`}${generator.startsWith("java/") ? `
2406
- config:
2407
- package: generated` : ""}
2408
- `;
2754
+ Validate SQL queries with SQG annotations without generating code.
2755
+
2756
+ **Parameters:**
2757
+ - \`sql\` (string, required): SQL queries with SQG annotations to validate
2758
+ - \`generator\` (string, required): Code generation generator to use for validation
2759
+
2760
+ **Example:**
2761
+ \`\`\`json
2762
+ {
2763
+ "sql": "-- QUERY getUsers\\nSELECT * FROM users;",
2764
+ "generator": "typescript/sqlite"
2409
2765
  }
2410
- /**
2411
- * Initialize a new SQG project
2412
- */
2413
- async function initProject(options) {
2414
- const engine = options.engine || "sqlite";
2415
- const output = options.output || "./generated";
2416
- if (!DB_ENGINES.includes(engine)) throw new InvalidEngineError(engine, [...DB_ENGINES]);
2417
- let generator;
2418
- if (options.generator) {
2419
- if (!(options.generator in SUPPORTED_GENERATORS)) {
2420
- const similar = findSimilarGenerators(options.generator);
2421
- throw new InvalidGeneratorError(options.generator, Object.keys(SUPPORTED_GENERATORS), similar.length > 0 ? similar[0] : void 0);
2422
- }
2423
- generator = options.generator;
2424
- if (!SUPPORTED_GENERATORS[generator].compatibleEngines.includes(engine)) throw new SqgError(`Generator '${generator}' is not compatible with engine '${engine}'`, "GENERATOR_ENGINE_MISMATCH", `For '${engine}', use one of: ${Object.entries(SUPPORTED_GENERATORS).filter(([_, info]) => info.compatibleEngines.includes(engine)).map(([name]) => name).join(", ")}`);
2425
- } else generator = getDefaultGenerator(engine);
2426
- const configPath = "sqg.yaml";
2427
- const sqlPath = "queries.sql";
2428
- if (!options.force) {
2429
- if (existsSync(configPath)) throw new SqgError(`File already exists: ${configPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
2430
- if (existsSync(sqlPath)) throw new SqgError(`File already exists: ${sqlPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
2431
- }
2432
- if (!existsSync(output)) {
2433
- mkdirSync(output, { recursive: true });
2434
- consola.success(`Created output directory: ${output}`);
2435
- }
2436
- writeFileSync(configPath, getConfigYaml(engine, generator, output));
2437
- consola.success(`Created ${configPath}`);
2438
- writeFileSync(sqlPath, getExampleSql(engine));
2439
- consola.success(`Created ${sqlPath}`);
2440
- consola.box(`
2441
- SQG project initialized!
2766
+ \`\`\`
2442
2767
 
2443
- Engine: ${engine}
2444
- Generator: ${generator}
2445
- Output: ${output}
2768
+ ## Generator Format
2446
2769
 
2447
- Next steps:
2448
- 1. Edit queries.sql to add your SQL queries
2449
- 2. Run: sqg sqg.yaml
2450
- 3. Import the generated code from ${output}
2770
+ Generators follow the pattern \`<language>/<engine>[/<driver>]\`:
2451
2771
 
2452
- Documentation: https://sqg.dev
2453
- `);
2772
+ - **Short form**: \`typescript/sqlite\`, \`java/duckdb\` (uses default driver)
2773
+ - **Full form**: \`typescript/sqlite/better-sqlite3\`, \`java/duckdb/arrow\` (specifies driver)
2774
+
2775
+ The MCP server accepts both short and full forms, but short forms are recommended.
2776
+
2777
+ ## More Information
2778
+
2779
+ - Full documentation: https://sqg.dev
2780
+ - GitHub: https://github.com/sqg-dev/sqg
2781
+ - SQL Syntax Reference: Run \`sqg syntax\` command
2782
+ `
2783
+ }] };
2784
+ }
2785
+ return {
2786
+ contents: [],
2787
+ isError: true
2788
+ };
2789
+ });
2790
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2791
+ const { name, arguments: args } = request.params;
2792
+ if (name === "generate_code") {
2793
+ const { sql, generator } = args;
2794
+ const result = await generateCode(sql, generator);
2795
+ if (result.error) return {
2796
+ content: [{
2797
+ type: "text",
2798
+ text: `Error generating code: ${result.error}`
2799
+ }],
2800
+ isError: true
2801
+ };
2802
+ return { content: [{
2803
+ type: "text",
2804
+ text: result.code
2805
+ }] };
2806
+ }
2807
+ if (name === "validate_sql") {
2808
+ const { sql, generator } = args;
2809
+ const engine = getGeneratorEngine(generator);
2810
+ const tempDir = join(tmpdir(), `sqg-mcp-validate-${randomUUID()}`);
2811
+ const sqlFile = join(tempDir, "queries.sql");
2812
+ const configFile = join(tempDir, "sqg.yaml");
2813
+ try {
2814
+ mkdirSync(tempDir, { recursive: true });
2815
+ writeFileSync(sqlFile, sql, "utf-8");
2816
+ const projectYaml = {
2817
+ version: 1,
2818
+ name: "validation",
2819
+ sql: [{
2820
+ engine,
2821
+ files: ["queries.sql"],
2822
+ gen: [{
2823
+ generator,
2824
+ output: "./generated/"
2825
+ }]
2826
+ }]
2827
+ };
2828
+ writeFileSync(configFile, YAML.stringify(projectYaml), "utf-8");
2829
+ const validation = await validateProject(configFile);
2830
+ if (validation.valid) return { content: [{
2831
+ type: "text",
2832
+ text: JSON.stringify({
2833
+ valid: true,
2834
+ project: validation.project,
2835
+ sqlFiles: validation.sqlFiles,
2836
+ generators: validation.generators
2837
+ }, null, 2)
2838
+ }] };
2839
+ return {
2840
+ content: [{
2841
+ type: "text",
2842
+ text: JSON.stringify({
2843
+ valid: false,
2844
+ errors: validation.errors
2845
+ }, null, 2)
2846
+ }],
2847
+ isError: true
2848
+ };
2849
+ } catch (error) {
2850
+ return {
2851
+ content: [{
2852
+ type: "text",
2853
+ text: `Error validating SQL: ${error instanceof Error ? error.message : String(error)}`
2854
+ }],
2855
+ isError: true
2856
+ };
2857
+ } finally {
2858
+ try {
2859
+ rmSync(tempDir, {
2860
+ recursive: true,
2861
+ force: true
2862
+ });
2863
+ } catch (error) {}
2864
+ }
2865
+ }
2866
+ return {
2867
+ content: [{
2868
+ type: "text",
2869
+ text: `Unknown tool: ${name}`
2870
+ }],
2871
+ isError: true
2872
+ };
2873
+ });
2874
+ async function startMcpServer() {
2875
+ const transport = new StdioServerTransport();
2876
+ await server.connect(transport);
2877
+ console.error("SQG MCP server running on stdio");
2454
2878
  }
2455
2879
 
2456
2880
  //#endregion
2457
2881
  //#region src/sqg.ts
2458
- const version = process.env.npm_package_version ?? "0.4.0";
2882
+ const version = process.env.npm_package_version ?? "0.6.0";
2459
2883
  const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
2460
2884
  consola.level = LogLevels.info;
2461
2885
  const program = new Command().name("sqg").description(`${description}
2462
2886
 
2463
2887
  Generate type-safe database access code from annotated SQL files.
2464
2888
 
2465
- Supported Engines:
2466
- ${formatEnginesHelp()}
2467
-
2468
2889
  Supported Generators:
2469
- ${formatGeneratorsHelp()}`).version(version, "-v, --version", "output the version number").option("--verbose", "Enable debug logging (shows SQL execution details)").option("--format <format>", "Output format: text (default) or json", "text").option("--validate", "Validate configuration without generating code").showHelpAfterError().showSuggestionAfterError();
2470
- program.argument("<project>", "Path to the project YAML config (sqg.yaml)").hook("preAction", (thisCommand) => {
2890
+ ${formatGeneratorsHelp()}`).version(version, "-v, --version", "output the version number").option("--verbose", "Enable debug logging (shows SQL execution details)").option("--format <format>", "Output format: text (default) or json", "text").option("--validate", "Validate configuration without generating code").option("--generator <generator>", `Code generation generator (${SHORT_GENERATOR_NAMES.join(", ")})`).option("--file <file>", "SQL file path (can be repeated)", (val, prev = []) => {
2891
+ prev.push(val);
2892
+ return prev;
2893
+ }).option("--output <path>", "Output file or directory path (optional, if omitted writes to stdout)").option("--name <name>", "Project name (optional, defaults to 'generated')").showHelpAfterError().showSuggestionAfterError();
2894
+ program.argument("[project]", "Path to the project YAML config (sqg.yaml) or omit to use CLI options").hook("preAction", (thisCommand) => {
2471
2895
  const opts = thisCommand.opts();
2472
2896
  if (opts.verbose) consola.level = LogLevels.debug;
2473
2897
  if (opts.format === "json") consola.level = LogLevels.silent;
2474
2898
  }).action(async (projectPath, options) => {
2475
2899
  try {
2476
- if (options.validate) {
2477
- const result = await validateProject(projectPath);
2478
- if (options.format === "json") console.log(JSON.stringify(result, null, 2));
2479
- else if (result.valid) {
2480
- consola.success("Configuration is valid");
2481
- consola.info(`Project: ${result.project?.name}`);
2482
- consola.info(`SQL files: ${result.sqlFiles?.join(", ")}`);
2483
- consola.info(`Generators: ${result.generators?.join(", ")}`);
2484
- } else {
2485
- consola.error("Validation failed");
2486
- for (const error of result.errors || []) {
2487
- consola.error(` ${error.message}`);
2488
- if (error.suggestion) consola.info(` Suggestion: ${error.suggestion}`);
2900
+ if (!projectPath) {
2901
+ if (!options.generator) throw new SqgError("Missing required option: --generator", "CONFIG_VALIDATION_ERROR", `Specify a code generation generator: ${SHORT_GENERATOR_NAMES.join(", ")}`);
2902
+ if (!options.file || options.file.length === 0) throw new SqgError("Missing required option: --file", "CONFIG_VALIDATION_ERROR", "Specify at least one SQL file with --file <path>");
2903
+ const project = buildProjectFromCliOptions({
2904
+ generator: options.generator,
2905
+ files: options.file,
2906
+ output: options.output,
2907
+ name: options.name
2908
+ });
2909
+ const projectDir = process.cwd();
2910
+ const writeToStdout = !options.output;
2911
+ if (options.validate) {
2912
+ const result = await validateProjectFromConfig(project, projectDir);
2913
+ if (options.format === "json") console.log(JSON.stringify(result, null, 2));
2914
+ else if (result.valid) {
2915
+ consola.success("Configuration is valid");
2916
+ consola.info(`Project: ${result.project?.name}`);
2917
+ consola.info(`SQL files: ${result.sqlFiles?.join(", ")}`);
2918
+ consola.info(`Generators: ${result.generators?.join(", ")}`);
2919
+ } else {
2920
+ consola.error("Validation failed");
2921
+ for (const error of result.errors || []) {
2922
+ consola.error(` ${error.message}`);
2923
+ if (error.suggestion) consola.info(` Suggestion: ${error.suggestion}`);
2924
+ }
2925
+ }
2926
+ exit(result.valid ? 0 : 1);
2927
+ }
2928
+ const files = await processProjectFromConfig(project, projectDir, writeToStdout);
2929
+ if (options.format === "json" && !writeToStdout) console.log(JSON.stringify({
2930
+ status: "success",
2931
+ generatedFiles: files
2932
+ }));
2933
+ } else {
2934
+ if (options.validate) {
2935
+ const result = await validateProject(projectPath);
2936
+ if (options.format === "json") console.log(JSON.stringify(result, null, 2));
2937
+ else if (result.valid) {
2938
+ consola.success("Configuration is valid");
2939
+ consola.info(`Project: ${result.project?.name}`);
2940
+ consola.info(`SQL files: ${result.sqlFiles?.join(", ")}`);
2941
+ consola.info(`Generators: ${result.generators?.join(", ")}`);
2942
+ } else {
2943
+ consola.error("Validation failed");
2944
+ for (const error of result.errors || []) {
2945
+ consola.error(` ${error.message}`);
2946
+ if (error.suggestion) consola.info(` Suggestion: ${error.suggestion}`);
2947
+ }
2489
2948
  }
2949
+ exit(result.valid ? 0 : 1);
2490
2950
  }
2491
- exit(result.valid ? 0 : 1);
2951
+ const files = await processProject(projectPath);
2952
+ if (options.format === "json") console.log(JSON.stringify({
2953
+ status: "success",
2954
+ generatedFiles: files
2955
+ }));
2492
2956
  }
2493
- const files = await processProject(projectPath);
2494
- if (options.format === "json") console.log(JSON.stringify({
2495
- status: "success",
2496
- generatedFiles: files
2497
- }));
2498
2957
  } catch (err) {
2499
2958
  if (options.format === "json") console.log(JSON.stringify(formatErrorForOutput(err)));
2500
2959
  else if (err instanceof SqgError) {
@@ -2505,7 +2964,7 @@ program.argument("<project>", "Path to the project YAML config (sqg.yaml)").hook
2505
2964
  exit(1);
2506
2965
  }
2507
2966
  });
2508
- program.command("init").description("Initialize a new SQG project with example configuration").option("-e, --engine <engine>", `Database engine (${DB_ENGINES.join(", ")})`, "sqlite").option("-g, --generator <generator>", `Code generator (${GENERATOR_NAMES.join(", ")})`).option("-o, --output <dir>", "Output directory for generated files", "./generated").option("-f, --force", "Overwrite existing files").action(async (options) => {
2967
+ program.command("init").description("Initialize a new SQG project with example configuration").option("-t, --generator <generator>", `Code generation generator (${SHORT_GENERATOR_NAMES.join(", ")})`, "typescript/sqlite").option("-o, --output <dir>", "Output directory for generated files", "./generated").option("-f, --force", "Overwrite existing files").action(async (options) => {
2509
2968
  const parentOpts = program.opts();
2510
2969
  try {
2511
2970
  await initProject(options);
@@ -2525,6 +2984,14 @@ program.command("init").description("Initialize a new SQG project with example c
2525
2984
  program.command("syntax").description("Show SQL annotation syntax reference").action(() => {
2526
2985
  console.log(SQL_SYNTAX_REFERENCE);
2527
2986
  });
2987
+ program.command("mcp").description("Start MCP (Model Context Protocol) server for AI assistants").action(async () => {
2988
+ try {
2989
+ await startMcpServer();
2990
+ } catch (error) {
2991
+ consola.error("Fatal error in MCP server:", error);
2992
+ exit(1);
2993
+ }
2994
+ });
2528
2995
  if (process.argv.length <= 2) {
2529
2996
  program.outputHelp();
2530
2997
  exit(1);