@sqg/sqg 0.5.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";
@@ -21,6 +25,18 @@ import typescriptPlugin from "prettier/parser-typescript";
21
25
  import estree from "prettier/plugins/estree";
22
26
 
23
27
  //#region src/constants.ts
28
+ /**
29
+ * SQG Constants - Centralized definitions for supported generators
30
+ *
31
+ * Generator format: <language>/<engine>/<driver>
32
+ * Short format: <language>/<engine> (uses default driver)
33
+ */
34
+ /** Supported database engines */
35
+ const DB_ENGINES = [
36
+ "sqlite",
37
+ "duckdb",
38
+ "postgres"
39
+ ];
24
40
  /** All supported generators with their full specification */
25
41
  const GENERATORS = {
26
42
  "typescript/sqlite/better-sqlite3": {
@@ -31,6 +47,14 @@ const GENERATORS = {
31
47
  extension: ".ts",
32
48
  template: "better-sqlite3.hbs"
33
49
  },
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
+ },
34
58
  "typescript/duckdb/node-api": {
35
59
  language: "typescript",
36
60
  engine: "duckdb",
@@ -315,254 +339,521 @@ function formatErrorForOutput(err) {
315
339
  }
316
340
 
317
341
  //#endregion
318
- //#region src/parser/sql-parser.ts
319
- const parser = LRParser.deserialize({
320
- version: 14,
321
- 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",
322
- 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_[~",
323
- goto: "#prPPsPPPwP!R!VPPP!YPPP!]!a!g!q#T#ZPPP#a#ePP#ePP#iTTOUQcVSn`aRrmTgYhRusR]STj[kQUOR^UQ`VQdWTl`dQYRQaVWeYamvQm`RvuQhYRphQk[RqkTSOUTROUQ[STj[k",
324
- nodeNames: "⚠ File QueryBlock BlockCommentStartSpecial Name Modifiers Config LineCommentStartSpecial SetVarLine Value StringLiteral StringLiteralSingle SQLText SQLBlock BlockComment LineComment VarRef BR",
325
- maxTerm: 33,
326
- skippedNodes: [0],
327
- repeatNodeCount: 5,
328
- 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",
329
- tokenizers: [
330
- 0,
331
- 1,
332
- 2
333
- ],
334
- topRules: { "File": [0, 1] },
335
- tokenPrec: 245
336
- });
337
-
338
- //#endregion
339
- //#region src/sql-query.ts
340
- var ListType = class {
341
- constructor(baseType) {
342
- this.baseType = baseType;
343
- }
344
- toString() {
345
- return `${this.baseType.toString()}[]`;
346
- }
347
- };
348
- var StructType = class {
349
- constructor(fields) {
350
- this.fields = fields;
351
- }
352
- toString() {
353
- return `STRUCT(${this.fields.map((f) => `"${f.name}" ${f.type.toString()}`).join(", ")})`;
354
- }
355
- };
356
- var MapType = class {
357
- constructor(keyType, valueType) {
358
- this.keyType = keyType;
359
- this.valueType = valueType;
360
- }
361
- toString() {
362
- return `MAP(${this.keyType.toString()}, ${this.valueType.toString()})`;
363
- }
364
- };
365
- var EnumType = class {
366
- constructor(values) {
367
- this.values = values;
368
- }
369
- toString() {
370
- return `ENUM(${this.values.map((v) => `'${v}'`).join(", ")})`;
371
- }
372
- };
373
- var SQLQuery = class {
374
- columns;
375
- allColumns;
376
- constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
377
- this.filename = filename;
378
- this.id = id;
379
- this.rawQuery = rawQuery;
380
- this.queryAnonymous = queryAnonymous;
381
- this.queryNamed = queryNamed;
382
- this.queryPositional = queryPositional;
383
- this.type = type;
384
- this.isOne = isOne;
385
- this.isPluck = isPluck;
386
- this.variables = variables;
387
- this.config = config;
388
- this.columns = [];
389
- }
390
- get isQuery() {
391
- return this.type === "QUERY";
392
- }
393
- get isExec() {
394
- return this.type === "EXEC";
395
- }
396
- get isMigrate() {
397
- return this.type === "MIGRATE";
398
- }
399
- get isTestdata() {
400
- return this.type === "TESTDATA";
401
- }
402
- get skipGenerateFunction() {
403
- return this.isTestdata || this.isMigrate || this.id.startsWith("_");
404
- }
405
- validateVariables() {
406
- const missingVars = [];
407
- const varRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
408
- let match;
409
- while (true) {
410
- match = varRegex.exec(this.rawQuery);
411
- if (match === null) break;
412
- const varName = match[1];
413
- if (!this.variables.has(varName)) missingVars.push(varName);
414
- }
415
- return missingVars;
416
- }
417
- };
342
+ //#region src/init.ts
418
343
  /**
419
- * Represents a TABLE annotation for generating appenders.
420
- * TABLE annotations specify a table name for which to generate bulk insert appenders.
344
+ * SQG Project Initialization - Creates new SQG projects with example files
421
345
  */
422
- var TableInfo = class {
423
- /** Columns introspected from the database table schema */
424
- columns = [];
425
- constructor(filename, id, tableName, includeColumns, hasAppender) {
426
- this.filename = filename;
427
- this.id = id;
428
- this.tableName = tableName;
429
- this.includeColumns = includeColumns;
430
- this.hasAppender = hasAppender;
431
- }
432
- get skipGenerateFunction() {
433
- return !this.hasAppender;
434
- }
435
- };
436
- function parseSQLQueries(filePath, extraVariables) {
437
- const content = readFileSync(filePath, "utf-8");
438
- consola.info(`Parsing SQL file: ${filePath}`);
439
- consola.debug(`File start: ${content.slice(0, 200)}`);
440
- const queries = [];
441
- const tables = [];
442
- const cursor = parser.parse(content).cursor();
443
- function getLineNumber(position) {
444
- return content.slice(0, position).split("\n").length;
445
- }
446
- function getStr(nodeName, optional = false) {
447
- const node = cursor.node.getChild(nodeName);
448
- if (!node) {
449
- if (optional) return;
450
- const lineNumber = getLineNumber(cursor.node.from);
451
- throw new Error(`Node '${nodeName}' not found at line ${lineNumber}`);
452
- }
453
- return nodeStr(node);
454
- }
455
- function nodeStr(node) {
456
- return content.slice(node.from, node.to);
457
- }
458
- const queryNames = /* @__PURE__ */ new Set();
459
- do
460
- if (cursor.name === "QueryBlock") {
461
- const queryType = (getStr("LineCommentStartSpecial", true) ?? getStr("BlockCommentStartSpecial")).replace("--", "").replace("/*", "").trim();
462
- const name = getStr("Name").trim();
463
- const modifiers = cursor.node.getChildren("Modifiers").map((node) => nodeStr(node));
464
- const isOne = modifiers.includes(":one");
465
- const isPluck = modifiers.includes(":pluck");
466
- let configStr = getStr("Config", true);
467
- if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
468
- let config = null;
469
- if (configStr) config = Config.fromYaml(name, filePath, configStr);
470
- const setVars = cursor.node.getChildren("SetVarLine");
471
- const variables = /* @__PURE__ */ new Map();
472
- for (const setVar of setVars) {
473
- const varName = nodeStr(setVar.getChild("Name"));
474
- const value = nodeStr(setVar.getChild("Value"));
475
- variables.set(varName, value.trim());
476
- }
477
- function getVariable(varName) {
478
- if (variables.has(varName)) return variables.get(varName);
479
- for (const extraVariable of extraVariables) if (extraVariable.name === varName) {
480
- variables.set(varName, extraVariable.value);
481
- return extraVariable.value;
482
- }
483
- const definedVars = Array.from(variables.keys());
484
- const suggestion = definedVars.length > 0 ? `Add '@set ${varName} = <value>' before the query. Defined variables: ${definedVars.join(", ")}` : `Add '@set ${varName} = <value>' before the query`;
485
- throw SqgError.inQuery(`Variable '\${${varName}}' is referenced but not defined`, "MISSING_VARIABLE", name, filePath, { suggestion });
486
- }
487
- const sqlNode = cursor.node.getChild("SQLBlock");
488
- 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" });
489
- const sqlContentStr = nodeStr(sqlNode).trim();
490
- const sqlCursor = sqlNode.cursor();
491
- let from = -1;
492
- let to = -1;
493
- class SQLQueryBuilder {
494
- sqlParts = [];
495
- appendSql(sql$1) {
496
- this.sqlParts.push(sql$1);
497
- }
498
- appendVariable(varName, value) {
499
- this.sqlParts.push({
500
- name: varName,
501
- value
502
- });
503
- }
504
- trim() {
505
- const lastPart = this.sqlParts.length > 0 ? this.sqlParts[this.sqlParts.length - 1] : null;
506
- if (lastPart && typeof lastPart === "string") this.sqlParts[this.sqlParts.length - 1] = lastPart.trimEnd();
507
- }
508
- parameters() {
509
- return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
510
- }
511
- toSqlWithAnonymousPlaceholders() {
512
- let sql$1 = "";
513
- const sqlParts = [];
514
- for (const part of this.sqlParts) if (typeof part === "string") {
515
- sql$1 += part;
516
- sqlParts.push(part);
517
- } else {
518
- if (sql$1.length > 0) {
519
- const last = sql$1[sql$1.length - 1];
520
- if (last !== " " && last !== "=" && last !== ">" && last !== "<") sql$1 += " ";
521
- }
522
- sql$1 += "?";
523
- if (part.name.startsWith("sources_")) sqlParts.push(part);
524
- else sqlParts.push("?");
525
- }
526
- return {
527
- parameters: this.parameters(),
528
- sql: sql$1,
529
- sqlParts
530
- };
531
- }
532
- toSqlWithPositionalPlaceholders() {
533
- const parameters = [];
534
- const sqlParts = [];
535
- for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
536
- else {
537
- const varName = part.name;
538
- const value = part.value;
539
- if (varName.startsWith("sources_")) sqlParts.push(part);
540
- else {
541
- let pos = parameters.findIndex((p) => p.name === varName);
542
- if (pos < 0) {
543
- parameters.push({
544
- name: varName,
545
- value
546
- });
547
- pos = parameters.length;
548
- } else pos = pos + 1;
549
- sqlParts.push(`$${pos}`);
550
- }
551
- }
552
- return {
553
- parameters,
554
- sqlParts,
555
- sql: sqlParts.map((part) => typeof part === "string" ? part : ` ${part.value} `).join("").trim()
556
- };
557
- }
558
- toSqlWithNamedPlaceholders() {
559
- const sqlParts = [];
560
- for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
561
- else if (part.name.startsWith("sources_")) sqlParts.push(part);
562
- else sqlParts.push(`$${part.name}`);
563
- return {
564
- parameters: this.parameters(),
565
- sqlParts,
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 = "";
804
+ const sqlParts = [];
805
+ for (const part of this.sqlParts) if (typeof part === "string") {
806
+ sql$1 += part;
807
+ sqlParts.push(part);
808
+ } else {
809
+ if (sql$1.length > 0) {
810
+ const last = sql$1[sql$1.length - 1];
811
+ if (last !== " " && last !== "=" && last !== ">" && last !== "<") sql$1 += " ";
812
+ }
813
+ sql$1 += "?";
814
+ if (part.name.startsWith("sources_")) sqlParts.push(part);
815
+ else sqlParts.push("?");
816
+ }
817
+ return {
818
+ parameters: this.parameters(),
819
+ sql: sql$1,
820
+ sqlParts
821
+ };
822
+ }
823
+ toSqlWithPositionalPlaceholders() {
824
+ const parameters = [];
825
+ const sqlParts = [];
826
+ for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
827
+ else {
828
+ const varName = part.name;
829
+ const value = part.value;
830
+ if (varName.startsWith("sources_")) sqlParts.push(part);
831
+ else {
832
+ let pos = parameters.findIndex((p) => p.name === varName);
833
+ if (pos < 0) {
834
+ parameters.push({
835
+ name: varName,
836
+ value
837
+ });
838
+ pos = parameters.length;
839
+ } else pos = pos + 1;
840
+ sqlParts.push(`$${pos}`);
841
+ }
842
+ }
843
+ return {
844
+ parameters,
845
+ sqlParts,
846
+ sql: sqlParts.map((part) => typeof part === "string" ? part : ` ${part.value} `).join("").trim()
847
+ };
848
+ }
849
+ toSqlWithNamedPlaceholders() {
850
+ const sqlParts = [];
851
+ for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
852
+ else if (part.name.startsWith("sources_")) sqlParts.push(part);
853
+ else sqlParts.push(`$${part.name}`);
854
+ return {
855
+ parameters: this.parameters(),
856
+ sqlParts,
566
857
  sql: sqlParts.map((part) => typeof part === "string" ? part : `$${part.name}`).join("").trim()
567
858
  };
568
859
  }
@@ -602,7 +893,7 @@ function parseSQLQueries(filePath, extraVariables) {
602
893
  if (match) includeColumns.push(...match[1].split(",").map((c) => c.trim()));
603
894
  }
604
895
  const table = new TableInfo(filePath, name, tableName, includeColumns, hasAppender);
605
- 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" });
606
897
  queryNames.add(name);
607
898
  tables.push(table);
608
899
  consola.debug(`Added table: ${name} -> ${tableName} (appender: ${hasAppender})`);
@@ -618,7 +909,7 @@ function parseSQLQueries(filePath, extraVariables) {
618
909
  config
619
910
  });
620
911
  const query = new SQLQuery(filePath, name, sqlContentStr, sql.toSqlWithAnonymousPlaceholders(), sql.toSqlWithNamedPlaceholders(), sql.toSqlWithPositionalPlaceholders(), queryType, isOne, isPluck, variables, config);
621
- 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" });
622
913
  queryNames.add(name);
623
914
  queries.push(query);
624
915
  consola.debug(`Added query: ${name} (${queryType})`);
@@ -1020,7 +1311,7 @@ var TypeMapper = class {
1020
1311
  }
1021
1312
  if (column.type instanceof StructType) return path + this.formatStructTypeName(column.name);
1022
1313
  if (column.type instanceof MapType) return path + this.formatMapTypeName(column.name);
1023
- if (!column.type) throw new TypeMappingError(`Missing type information`, column.name);
1314
+ if (!column.type) throw new TypeMappingError("Missing type information", column.name);
1024
1315
  return this.mapPrimitiveType(column.type.toString(), column.nullable);
1025
1316
  }
1026
1317
  /**
@@ -1798,7 +2089,8 @@ function getGenerator(generator) {
1798
2089
  const info = parseGenerator(generator);
1799
2090
  const key = `${info.language}/${info.driver}`;
1800
2091
  switch (key) {
1801
- case "typescript/better-sqlite3": return new TsGenerator(`templates/${info.template}`);
2092
+ case "typescript/better-sqlite3":
2093
+ case "typescript/node": return new TsGenerator(`templates/${info.template}`);
1802
2094
  case "typescript/node-api": return new TsDuckDBGenerator(`templates/${info.template}`);
1803
2095
  case "java/jdbc": return new JavaGenerator(`templates/${info.template}`);
1804
2096
  case "java/arrow": return new JavaDuckDBArrowGenerator(`templates/${info.template}`);
@@ -2255,275 +2547,339 @@ async function processProject(projectPath) {
2255
2547
  }
2256
2548
 
2257
2549
  //#endregion
2258
- //#region src/init.ts
2259
- /**
2260
- * SQG Project Initialization - Creates new SQG projects with example files
2261
- */
2262
- /**
2263
- * Get the default generator for a language preference
2264
- */
2265
- function getDefaultGenerator() {
2266
- return "typescript/sqlite";
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");
2267
2571
  }
2268
- /**
2269
- * Generate example SQL content based on engine
2270
- */
2271
- function getExampleSql(engine) {
2272
- return {
2273
- sqlite: `-- MIGRATE 1
2274
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2275
- CREATE TABLE users (
2276
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2277
- name TEXT NOT NULL,
2278
- email TEXT UNIQUE NOT NULL,
2279
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
2280
- );
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.
2281
2622
 
2282
- -- MIGRATE 2
2283
- CREATE TABLE posts (
2284
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2285
- user_id INTEGER NOT NULL REFERENCES users(id),
2286
- title TEXT NOT NULL,
2287
- content TEXT,
2288
- published INTEGER DEFAULT 0,
2289
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
2290
- );
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
2291
2627
 
2292
- -- TESTDATA seed_data
2293
- INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
2294
- INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
2295
- INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', 1);
2296
- `,
2297
- duckdb: `-- MIGRATE 1
2298
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2299
- CREATE TABLE users (
2300
- id INTEGER PRIMARY KEY,
2301
- name VARCHAR NOT NULL,
2302
- email VARCHAR UNIQUE NOT NULL,
2303
- metadata STRUCT(role VARCHAR, active BOOLEAN),
2304
- tags VARCHAR[],
2305
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2306
- );
2628
+ ${SQL_SYNTAX_REFERENCE}
2307
2629
 
2308
- -- MIGRATE 2
2309
- CREATE TABLE posts (
2310
- id INTEGER PRIMARY KEY,
2311
- user_id INTEGER NOT NULL REFERENCES users(id),
2312
- title VARCHAR NOT NULL,
2313
- content VARCHAR,
2314
- published BOOLEAN DEFAULT FALSE,
2315
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2316
- );
2630
+ VALID GENERATORS (use short form):
2631
+ ${formatGeneratorListWithDescriptions()}
2317
2632
 
2318
- -- TESTDATA seed_data
2319
- INSERT INTO users (id, name, email, metadata, tags)
2320
- VALUES (1, 'Alice', 'alice@example.com', {'role': 'admin', 'active': true}, ['developer', 'lead']);
2321
- INSERT INTO users (id, name, email, metadata, tags)
2322
- VALUES (2, 'Bob', 'bob@example.com', {'role': 'user', 'active': true}, ['developer']);
2323
- INSERT INTO posts (id, user_id, title, content, published)
2324
- VALUES (1, 1, 'Hello World', 'My first post!', TRUE);
2325
- `,
2326
- postgres: `-- MIGRATE 1
2327
- -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
2328
- CREATE TABLE users (
2329
- id SERIAL PRIMARY KEY,
2330
- name TEXT NOT NULL,
2331
- email TEXT UNIQUE NOT NULL,
2332
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2333
- );
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.
2334
2639
 
2335
- -- MIGRATE 2
2336
- CREATE TABLE posts (
2337
- id SERIAL PRIMARY KEY,
2338
- user_id INTEGER NOT NULL REFERENCES users(id),
2339
- title TEXT NOT NULL,
2340
- content TEXT,
2341
- published BOOLEAN DEFAULT FALSE,
2342
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2343
- );
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
2344
2646
 
2345
- -- TESTDATA seed_data
2346
- INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
2347
- INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
2348
- INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', TRUE);
2349
- `
2350
- }[engine] + `
2351
- -- QUERY list_users
2352
- SELECT id, name, email, created_at
2353
- FROM users
2354
- 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
2355
2704
 
2356
- -- QUERY get_user_by_id :one
2357
- @set id = 1
2358
- SELECT id, name, email, created_at
2359
- FROM users
2360
- 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).
2361
2706
 
2362
- -- QUERY get_user_by_email :one
2363
- @set email = 'alice@example.com'
2364
- SELECT id, name, email, created_at
2365
- FROM users
2366
- WHERE email = \${email};
2707
+ ## Overview
2367
2708
 
2368
- -- QUERY count_users :one :pluck
2369
- 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.
2370
2710
 
2371
- -- QUERY list_posts_by_user
2372
- @set user_id = 1
2373
- SELECT p.id, p.title, p.content, p.published, p.created_at
2374
- FROM posts p
2375
- WHERE p.user_id = \${user_id}
2376
- ORDER BY p.created_at DESC;
2711
+ **Website:** https://sqg.dev
2712
+ **Repository:** https://github.com/sqg-dev/sqg
2377
2713
 
2378
- -- QUERY list_published_posts
2379
- SELECT
2380
- p.id,
2381
- p.title,
2382
- p.content,
2383
- p.created_at,
2384
- u.name as author_name,
2385
- u.email as author_email
2386
- FROM posts p
2387
- JOIN users u ON p.user_id = u.id
2388
- WHERE p.published = 1
2389
- ORDER BY p.created_at DESC;
2390
- ` + {
2391
- sqlite: `
2392
- -- EXEC create_user
2393
- @set name = 'New User'
2394
- @set email = 'new@example.com'
2395
- INSERT INTO users (name, email)
2396
- VALUES (\${name}, \${email});
2714
+ ## Key Features
2397
2715
 
2398
- -- EXEC create_post
2399
- @set user_id = 1
2400
- @set title = 'New Post'
2401
- @set content = 'Post content here'
2402
- INSERT INTO posts (user_id, title, content)
2403
- 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
2404
2723
 
2405
- -- EXEC publish_post
2406
- @set id = 1
2407
- UPDATE posts SET published = 1 WHERE id = \${id};
2724
+ ## SQL Annotations
2408
2725
 
2409
- -- EXEC delete_post
2410
- @set id = 1
2411
- DELETE FROM posts WHERE id = \${id};
2412
- `,
2413
- duckdb: `
2414
- -- EXEC create_user
2415
- @set id = 100
2416
- @set name = 'New User'
2417
- @set email = 'new@example.com'
2418
- INSERT INTO users (id, name, email)
2419
- VALUES (\${id}, \${name}, \${email});
2726
+ ${SQL_SYNTAX_REFERENCE}
2420
2727
 
2421
- -- EXEC create_post
2422
- @set id = 100
2423
- @set user_id = 1
2424
- @set title = 'New Post'
2425
- @set content = 'Post content here'
2426
- INSERT INTO posts (id, user_id, title, content)
2427
- VALUES (\${id}, \${user_id}, \${title}, \${content});
2728
+ ## Supported Generators
2428
2729
 
2429
- -- EXEC publish_post
2430
- @set id = 1
2431
- UPDATE posts SET published = TRUE WHERE id = \${id};
2730
+ Valid generator strings:
2432
2731
 
2433
- -- EXEC delete_post
2434
- @set id = 1
2435
- DELETE FROM posts WHERE id = \${id};
2436
- `,
2437
- postgres: `
2438
- -- EXEC create_user
2439
- @set name = 'New User'
2440
- @set email = 'new@example.com'
2441
- INSERT INTO users (name, email)
2442
- VALUES (\${name}, \${email});
2732
+ ${generatorListMarkdown}
2443
2733
 
2444
- -- EXEC create_post
2445
- @set user_id = 1
2446
- @set title = 'New Post'
2447
- @set content = 'Post content here'
2448
- INSERT INTO posts (user_id, title, content)
2449
- VALUES (\${user_id}, \${title}, \${content});
2734
+ ## MCP Tools
2450
2735
 
2451
- -- EXEC publish_post
2452
- @set id = 1
2453
- UPDATE posts SET published = TRUE WHERE id = \${id};
2736
+ ### generate_code
2454
2737
 
2455
- -- EXEC delete_post
2456
- @set id = 1
2457
- DELETE FROM posts WHERE id = \${id};
2458
- `
2459
- }[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"
2460
2749
  }
2461
- /**
2462
- * Generate sqg.yaml configuration
2463
- */
2464
- function getConfigYaml(generator, output) {
2465
- const isJava = parseGenerator(generator).language === "java";
2466
- return `# SQG Configuration
2467
- # Generated by: sqg init
2468
- # Documentation: https://sqg.dev
2750
+ \`\`\`
2469
2751
 
2470
- version: 1
2471
- name: my-project
2752
+ ### validate_sql
2472
2753
 
2473
- sql:
2474
- - files:
2475
- - queries.sql
2476
- gen:
2477
- - generator: ${generator}
2478
- output: ${output.endsWith("/") ? output : `${output}/`}${isJava ? `
2479
- config:
2480
- package: generated` : ""}
2481
- `;
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"
2482
2765
  }
2483
- /**
2484
- * Initialize a new SQG project
2485
- */
2486
- async function initProject(options) {
2487
- const generator = options.generator || getDefaultGenerator();
2488
- const output = options.output || "./generated";
2489
- if (!isValidGenerator(generator)) {
2490
- const similar = findSimilarGenerators(generator);
2491
- throw new InvalidGeneratorError(generator, [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
2492
- }
2493
- const engine = parseGenerator(generator).engine;
2494
- const configPath = "sqg.yaml";
2495
- const sqlPath = "queries.sql";
2496
- if (!options.force) {
2497
- if (existsSync(configPath)) throw new SqgError(`File already exists: ${configPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
2498
- if (existsSync(sqlPath)) throw new SqgError(`File already exists: ${sqlPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
2499
- }
2500
- if (!existsSync(output)) {
2501
- mkdirSync(output, { recursive: true });
2502
- consola.success(`Created output directory: ${output}`);
2503
- }
2504
- writeFileSync(configPath, getConfigYaml(generator, output));
2505
- consola.success(`Created ${configPath}`);
2506
- writeFileSync(sqlPath, getExampleSql(engine));
2507
- consola.success(`Created ${sqlPath}`);
2508
- consola.box(`
2509
- SQG project initialized!
2766
+ \`\`\`
2510
2767
 
2511
- Generator: ${generator}
2512
- Engine: ${engine}
2513
- Output: ${output}
2768
+ ## Generator Format
2514
2769
 
2515
- Next steps:
2516
- 1. Edit queries.sql to add your SQL queries
2517
- 2. Run: sqg sqg.yaml
2518
- 3. Import the generated code from ${output}
2770
+ Generators follow the pattern \`<language>/<engine>[/<driver>]\`:
2519
2771
 
2520
- Documentation: https://sqg.dev
2521
- `);
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");
2522
2878
  }
2523
2879
 
2524
2880
  //#endregion
2525
2881
  //#region src/sqg.ts
2526
- const version = process.env.npm_package_version ?? "0.5.0";
2882
+ const version = process.env.npm_package_version ?? "0.6.0";
2527
2883
  const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
2528
2884
  consola.level = LogLevels.info;
2529
2885
  const program = new Command().name("sqg").description(`${description}
@@ -2628,6 +2984,14 @@ program.command("init").description("Initialize a new SQG project with example c
2628
2984
  program.command("syntax").description("Show SQL annotation syntax reference").action(() => {
2629
2985
  console.log(SQL_SYNTAX_REFERENCE);
2630
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
+ });
2631
2995
  if (process.argv.length <= 2) {
2632
2996
  program.outputHelp();
2633
2997
  exit(1);