@sqg/sqg 0.5.0 → 0.7.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,30 @@ 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
+ },
58
+ "typescript/sqlite/libsql": {
59
+ language: "typescript",
60
+ engine: "sqlite",
61
+ driver: "libsql",
62
+ description: "TypeScript with @libsql/client (Turso)",
63
+ extension: ".ts",
64
+ template: "libsql.hbs"
65
+ },
66
+ "typescript/sqlite/turso": {
67
+ language: "typescript",
68
+ engine: "sqlite",
69
+ driver: "turso",
70
+ description: "TypeScript with Turso (limbo) native driver",
71
+ extension: ".ts",
72
+ template: "turso.hbs"
73
+ },
34
74
  "typescript/duckdb/node-api": {
35
75
  language: "typescript",
36
76
  engine: "duckdb",
@@ -315,246 +355,513 @@ function formatErrorForOutput(err) {
315
355
  }
316
356
 
317
357
  //#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
- };
358
+ //#region src/init.ts
418
359
  /**
419
- * Represents a TABLE annotation for generating appenders.
420
- * TABLE annotations specify a table name for which to generate bulk insert appenders.
360
+ * SQG Project Initialization - Creates new SQG projects with example files
421
361
  */
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
- }
362
+ /**
363
+ * Get the default generator for a language preference
364
+ */
365
+ function getDefaultGenerator() {
366
+ return "typescript/sqlite";
367
+ }
368
+ /**
369
+ * Generate example SQL content based on engine
370
+ */
371
+ function getExampleSql(engine) {
372
+ return {
373
+ sqlite: `-- MIGRATE 1
374
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
375
+ CREATE TABLE users (
376
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
377
+ name TEXT NOT NULL,
378
+ email TEXT UNIQUE NOT NULL,
379
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
380
+ );
381
+
382
+ -- MIGRATE 2
383
+ CREATE TABLE posts (
384
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
385
+ user_id INTEGER NOT NULL REFERENCES users(id),
386
+ title TEXT NOT NULL,
387
+ content TEXT,
388
+ published INTEGER DEFAULT 0,
389
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
390
+ );
391
+
392
+ -- TESTDATA seed_data
393
+ INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
394
+ INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
395
+ INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', 1);
396
+ `,
397
+ duckdb: `-- MIGRATE 1
398
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
399
+ CREATE TABLE users (
400
+ id INTEGER PRIMARY KEY,
401
+ name VARCHAR NOT NULL,
402
+ email VARCHAR UNIQUE NOT NULL,
403
+ metadata STRUCT(role VARCHAR, active BOOLEAN),
404
+ tags VARCHAR[],
405
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
406
+ );
407
+
408
+ -- MIGRATE 2
409
+ CREATE TABLE posts (
410
+ id INTEGER PRIMARY KEY,
411
+ user_id INTEGER NOT NULL REFERENCES users(id),
412
+ title VARCHAR NOT NULL,
413
+ content VARCHAR,
414
+ published BOOLEAN DEFAULT FALSE,
415
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
416
+ );
417
+
418
+ -- TESTDATA seed_data
419
+ INSERT INTO users (id, name, email, metadata, tags)
420
+ VALUES (1, 'Alice', 'alice@example.com', {'role': 'admin', 'active': true}, ['developer', 'lead']);
421
+ INSERT INTO users (id, name, email, metadata, tags)
422
+ VALUES (2, 'Bob', 'bob@example.com', {'role': 'user', 'active': true}, ['developer']);
423
+ INSERT INTO posts (id, user_id, title, content, published)
424
+ VALUES (1, 1, 'Hello World', 'My first post!', TRUE);
425
+ `,
426
+ postgres: `-- MIGRATE 1
427
+ -- Create the users table (SQG Example - https://sqg.dev/guides/sql-syntax/)
428
+ CREATE TABLE users (
429
+ id SERIAL PRIMARY KEY,
430
+ name TEXT NOT NULL,
431
+ email TEXT UNIQUE NOT NULL,
432
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
433
+ );
434
+
435
+ -- MIGRATE 2
436
+ CREATE TABLE posts (
437
+ id SERIAL PRIMARY KEY,
438
+ user_id INTEGER NOT NULL REFERENCES users(id),
439
+ title TEXT NOT NULL,
440
+ content TEXT,
441
+ published BOOLEAN DEFAULT FALSE,
442
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
443
+ );
444
+
445
+ -- TESTDATA seed_data
446
+ INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
447
+ INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
448
+ INSERT INTO posts (user_id, title, content, published) VALUES (1, 'Hello World', 'My first post!', TRUE);
449
+ `
450
+ }[engine] + `
451
+ -- QUERY list_users
452
+ SELECT id, name, email, created_at
453
+ FROM users
454
+ ORDER BY created_at DESC;
455
+
456
+ -- QUERY get_user_by_id :one
457
+ @set id = 1
458
+ SELECT id, name, email, created_at
459
+ FROM users
460
+ WHERE id = \${id};
461
+
462
+ -- QUERY get_user_by_email :one
463
+ @set email = 'alice@example.com'
464
+ SELECT id, name, email, created_at
465
+ FROM users
466
+ WHERE email = \${email};
467
+
468
+ -- QUERY count_users :one :pluck
469
+ SELECT COUNT(*) FROM users;
470
+
471
+ -- QUERY list_posts_by_user
472
+ @set user_id = 1
473
+ SELECT p.id, p.title, p.content, p.published, p.created_at
474
+ FROM posts p
475
+ WHERE p.user_id = \${user_id}
476
+ ORDER BY p.created_at DESC;
477
+
478
+ -- QUERY list_published_posts
479
+ SELECT
480
+ p.id,
481
+ p.title,
482
+ p.content,
483
+ p.created_at,
484
+ u.name as author_name,
485
+ u.email as author_email
486
+ FROM posts p
487
+ JOIN users u ON p.user_id = u.id
488
+ WHERE p.published = 1
489
+ ORDER BY p.created_at DESC;
490
+ ` + {
491
+ sqlite: `
492
+ -- EXEC create_user
493
+ @set name = 'New User'
494
+ @set email = 'new@example.com'
495
+ INSERT INTO users (name, email)
496
+ VALUES (\${name}, \${email});
497
+
498
+ -- EXEC create_post
499
+ @set user_id = 1
500
+ @set title = 'New Post'
501
+ @set content = 'Post content here'
502
+ INSERT INTO posts (user_id, title, content)
503
+ VALUES (\${user_id}, \${title}, \${content});
504
+
505
+ -- EXEC publish_post
506
+ @set id = 1
507
+ UPDATE posts SET published = 1 WHERE id = \${id};
508
+
509
+ -- EXEC delete_post
510
+ @set id = 1
511
+ DELETE FROM posts WHERE id = \${id};
512
+ `,
513
+ duckdb: `
514
+ -- EXEC create_user
515
+ @set id = 100
516
+ @set name = 'New User'
517
+ @set email = 'new@example.com'
518
+ INSERT INTO users (id, name, email)
519
+ VALUES (\${id}, \${name}, \${email});
520
+
521
+ -- EXEC create_post
522
+ @set id = 100
523
+ @set user_id = 1
524
+ @set title = 'New Post'
525
+ @set content = 'Post content here'
526
+ INSERT INTO posts (id, user_id, title, content)
527
+ VALUES (\${id}, \${user_id}, \${title}, \${content});
528
+
529
+ -- EXEC publish_post
530
+ @set id = 1
531
+ UPDATE posts SET published = TRUE WHERE id = \${id};
532
+
533
+ -- EXEC delete_post
534
+ @set id = 1
535
+ DELETE FROM posts WHERE id = \${id};
536
+ `,
537
+ postgres: `
538
+ -- EXEC create_user
539
+ @set name = 'New User'
540
+ @set email = 'new@example.com'
541
+ INSERT INTO users (name, email)
542
+ VALUES (\${name}, \${email});
543
+
544
+ -- EXEC create_post
545
+ @set user_id = 1
546
+ @set title = 'New Post'
547
+ @set content = 'Post content here'
548
+ INSERT INTO posts (user_id, title, content)
549
+ VALUES (\${user_id}, \${title}, \${content});
550
+
551
+ -- EXEC publish_post
552
+ @set id = 1
553
+ UPDATE posts SET published = TRUE WHERE id = \${id};
554
+
555
+ -- EXEC delete_post
556
+ @set id = 1
557
+ DELETE FROM posts WHERE id = \${id};
558
+ `
559
+ }[engine];
560
+ }
561
+ /**
562
+ * Generate sqg.yaml configuration
563
+ */
564
+ function getConfigYaml(generator, output) {
565
+ const isJava = parseGenerator(generator).language === "java";
566
+ return `# SQG Configuration
567
+ # Generated by: sqg init
568
+ # Documentation: https://sqg.dev
569
+
570
+ version: 1
571
+ name: my-project
572
+
573
+ sql:
574
+ - files:
575
+ - queries.sql
576
+ gen:
577
+ - generator: ${generator}
578
+ output: ${output.endsWith("/") ? output : `${output}/`}${isJava ? `
579
+ config:
580
+ package: generated` : ""}
581
+ `;
582
+ }
583
+ /**
584
+ * Initialize a new SQG project
585
+ */
586
+ async function initProject(options) {
587
+ const generator = options.generator || getDefaultGenerator();
588
+ const output = options.output || "./generated";
589
+ if (!isValidGenerator(generator)) {
590
+ const similar = findSimilarGenerators(generator);
591
+ throw new InvalidGeneratorError(generator, [...SHORT_GENERATOR_NAMES, ...GENERATOR_NAMES], similar.length > 0 ? similar[0] : void 0);
592
+ }
593
+ const engine = parseGenerator(generator).engine;
594
+ const configPath = "sqg.yaml";
595
+ const sqlPath = "queries.sql";
596
+ if (!options.force) {
597
+ if (existsSync(configPath)) throw new SqgError(`File already exists: ${configPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
598
+ if (existsSync(sqlPath)) throw new SqgError(`File already exists: ${sqlPath}`, "VALIDATION_ERROR", "Use --force to overwrite existing files");
599
+ }
600
+ if (!existsSync(output)) {
601
+ mkdirSync(output, { recursive: true });
602
+ consola.success(`Created output directory: ${output}`);
603
+ }
604
+ writeFileSync(configPath, getConfigYaml(generator, output));
605
+ consola.success(`Created ${configPath}`);
606
+ writeFileSync(sqlPath, getExampleSql(engine));
607
+ consola.success(`Created ${sqlPath}`);
608
+ consola.box(`
609
+ SQG project initialized!
610
+
611
+ Generator: ${generator}
612
+ Engine: ${engine}
613
+ Output: ${output}
614
+
615
+ Next steps:
616
+ 1. Edit queries.sql to add your SQL queries
617
+ 2. Run: sqg sqg.yaml
618
+ 3. Import the generated code from ${output}
619
+
620
+ Documentation: https://sqg.dev
621
+ `);
622
+ }
623
+
624
+ //#endregion
625
+ //#region src/parser/sql-parser.ts
626
+ const parser = LRParser.deserialize({
627
+ version: 14,
628
+ 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",
629
+ 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_[~",
630
+ goto: "#prPPsPPPwP!R!VPPP!YPPP!]!a!g!q#T#ZPPP#a#ePP#ePP#iTTOUQcVSn`aRrmTgYhRusR]STj[kQUOR^UQ`VQdWTl`dQYRQaVWeYamvQm`RvuQhYRphQk[RqkTSOUTROUQ[STj[k",
631
+ nodeNames: "⚠ File QueryBlock BlockCommentStartSpecial Name Modifiers Config LineCommentStartSpecial SetVarLine Value StringLiteral StringLiteralSingle SQLText SQLBlock BlockComment LineComment VarRef BR",
632
+ maxTerm: 33,
633
+ skippedNodes: [0],
634
+ repeatNodeCount: 5,
635
+ 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",
636
+ tokenizers: [
637
+ 0,
638
+ 1,
639
+ 2
640
+ ],
641
+ topRules: { "File": [0, 1] },
642
+ tokenPrec: 245
643
+ });
644
+
645
+ //#endregion
646
+ //#region src/sql-query.ts
647
+ var ListType = class {
648
+ constructor(baseType) {
649
+ this.baseType = baseType;
650
+ }
651
+ toString() {
652
+ return `${this.baseType.toString()}[]`;
653
+ }
654
+ };
655
+ var StructType = class {
656
+ constructor(fields) {
657
+ this.fields = fields;
658
+ }
659
+ toString() {
660
+ return `STRUCT(${this.fields.map((f) => `"${f.name}" ${f.type.toString()}`).join(", ")})`;
661
+ }
662
+ };
663
+ var MapType = class {
664
+ constructor(keyType, valueType) {
665
+ this.keyType = keyType;
666
+ this.valueType = valueType;
667
+ }
668
+ toString() {
669
+ return `MAP(${this.keyType.toString()}, ${this.valueType.toString()})`;
670
+ }
671
+ };
672
+ var EnumType = class {
673
+ constructor(values) {
674
+ this.values = values;
675
+ }
676
+ toString() {
677
+ return `ENUM(${this.values.map((v) => `'${v}'`).join(", ")})`;
678
+ }
679
+ };
680
+ var SQLQuery = class {
681
+ columns;
682
+ allColumns;
683
+ constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
684
+ this.filename = filename;
685
+ this.id = id;
686
+ this.rawQuery = rawQuery;
687
+ this.queryAnonymous = queryAnonymous;
688
+ this.queryNamed = queryNamed;
689
+ this.queryPositional = queryPositional;
690
+ this.type = type;
691
+ this.isOne = isOne;
692
+ this.isPluck = isPluck;
693
+ this.variables = variables;
694
+ this.config = config;
695
+ this.columns = [];
696
+ }
697
+ get isQuery() {
698
+ return this.type === "QUERY";
699
+ }
700
+ get isExec() {
701
+ return this.type === "EXEC";
702
+ }
703
+ get isMigrate() {
704
+ return this.type === "MIGRATE";
705
+ }
706
+ get isTestdata() {
707
+ return this.type === "TESTDATA";
708
+ }
709
+ get skipGenerateFunction() {
710
+ return this.isTestdata || this.isMigrate || this.id.startsWith("_");
711
+ }
712
+ validateVariables() {
713
+ const missingVars = [];
714
+ const varRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
715
+ let match;
716
+ while (true) {
717
+ match = varRegex.exec(this.rawQuery);
718
+ if (match === null) break;
719
+ const varName = match[1];
720
+ if (!this.variables.has(varName)) missingVars.push(varName);
721
+ }
722
+ return missingVars;
723
+ }
724
+ };
725
+ /**
726
+ * Represents a TABLE annotation for generating appenders.
727
+ * TABLE annotations specify a table name for which to generate bulk insert appenders.
728
+ */
729
+ var TableInfo = class {
730
+ /** Columns introspected from the database table schema */
731
+ columns = [];
732
+ constructor(filename, id, tableName, includeColumns, hasAppender) {
733
+ this.filename = filename;
734
+ this.id = id;
735
+ this.tableName = tableName;
736
+ this.includeColumns = includeColumns;
737
+ this.hasAppender = hasAppender;
738
+ }
739
+ get skipGenerateFunction() {
740
+ return !this.hasAppender;
741
+ }
742
+ };
743
+ function parseSQLQueries(filePath, extraVariables) {
744
+ const content = readFileSync(filePath, "utf-8");
745
+ consola.info(`Parsing SQL file: ${filePath}`);
746
+ consola.debug(`File start: ${content.slice(0, 200)}`);
747
+ const queries = [];
748
+ const tables = [];
749
+ const cursor = parser.parse(content).cursor();
750
+ function getLineNumber(position) {
751
+ return content.slice(0, position).split("\n").length;
752
+ }
753
+ function getStr(nodeName, optional = false) {
754
+ const node = cursor.node.getChild(nodeName);
755
+ if (!node) {
756
+ if (optional) return;
757
+ const lineNumber = getLineNumber(cursor.node.from);
758
+ throw new Error(`Node '${nodeName}' not found at line ${lineNumber}`);
759
+ }
760
+ return nodeStr(node);
761
+ }
762
+ function nodeStr(node) {
763
+ return content.slice(node.from, node.to);
764
+ }
765
+ const queryNames = /* @__PURE__ */ new Set();
766
+ do
767
+ if (cursor.name === "QueryBlock") {
768
+ const queryType = (getStr("LineCommentStartSpecial", true) ?? getStr("BlockCommentStartSpecial")).replace("--", "").replace("/*", "").trim();
769
+ const name = getStr("Name").trim();
770
+ const modifiers = cursor.node.getChildren("Modifiers").map((node) => nodeStr(node));
771
+ const isOne = modifiers.includes(":one");
772
+ const isPluck = modifiers.includes(":pluck");
773
+ let configStr = getStr("Config", true);
774
+ if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
775
+ let config = null;
776
+ if (configStr) config = Config.fromYaml(name, filePath, configStr);
777
+ const setVars = cursor.node.getChildren("SetVarLine");
778
+ const variables = /* @__PURE__ */ new Map();
779
+ for (const setVar of setVars) {
780
+ const varName = nodeStr(setVar.getChild("Name"));
781
+ const value = nodeStr(setVar.getChild("Value"));
782
+ variables.set(varName, value.trim());
783
+ }
784
+ function getVariable(varName) {
785
+ if (variables.has(varName)) return variables.get(varName);
786
+ for (const extraVariable of extraVariables) if (extraVariable.name === varName) {
787
+ variables.set(varName, extraVariable.value);
788
+ return extraVariable.value;
789
+ }
790
+ const definedVars = Array.from(variables.keys());
791
+ const suggestion = definedVars.length > 0 ? `Add '@set ${varName} = <value>' before the query. Defined variables: ${definedVars.join(", ")}` : `Add '@set ${varName} = <value>' before the query`;
792
+ throw SqgError.inQuery(`Variable '\${${varName}}' is referenced but not defined`, "MISSING_VARIABLE", name, filePath, { suggestion });
793
+ }
794
+ const sqlNode = cursor.node.getChild("SQLBlock");
795
+ 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" });
796
+ const sqlContentStr = nodeStr(sqlNode).trim();
797
+ const sqlCursor = sqlNode.cursor();
798
+ let from = -1;
799
+ let to = -1;
800
+ class SQLQueryBuilder {
801
+ sqlParts = [];
802
+ appendSql(sql$1) {
803
+ this.sqlParts.push(sql$1);
804
+ }
805
+ appendVariable(varName, value) {
806
+ this.sqlParts.push({
807
+ name: varName,
808
+ value
809
+ });
810
+ }
811
+ trim() {
812
+ const lastPart = this.sqlParts.length > 0 ? this.sqlParts[this.sqlParts.length - 1] : null;
813
+ if (lastPart && typeof lastPart === "string") this.sqlParts[this.sqlParts.length - 1] = lastPart.trimEnd();
814
+ }
815
+ parameters() {
816
+ return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
817
+ }
818
+ toSqlWithAnonymousPlaceholders() {
819
+ let sql$1 = "";
820
+ const sqlParts = [];
821
+ for (const part of this.sqlParts) if (typeof part === "string") {
822
+ sql$1 += part;
823
+ sqlParts.push(part);
824
+ } else {
825
+ if (sql$1.length > 0) {
826
+ const last = sql$1[sql$1.length - 1];
827
+ if (last !== " " && last !== "=" && last !== ">" && last !== "<") sql$1 += " ";
828
+ }
829
+ sql$1 += "?";
830
+ if (part.name.startsWith("sources_")) sqlParts.push(part);
831
+ else sqlParts.push("?");
832
+ }
833
+ return {
834
+ parameters: this.parameters(),
835
+ sql: sql$1,
836
+ sqlParts
837
+ };
838
+ }
839
+ toSqlWithPositionalPlaceholders() {
840
+ const parameters = [];
841
+ const sqlParts = [];
842
+ for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
843
+ else {
844
+ const varName = part.name;
845
+ const value = part.value;
846
+ if (varName.startsWith("sources_")) sqlParts.push(part);
847
+ else {
848
+ let pos = parameters.findIndex((p) => p.name === varName);
849
+ if (pos < 0) {
850
+ parameters.push({
851
+ name: varName,
852
+ value
853
+ });
854
+ pos = parameters.length;
855
+ } else pos = pos + 1;
856
+ sqlParts.push(`$${pos}`);
857
+ }
858
+ }
859
+ return {
860
+ parameters,
861
+ sqlParts,
862
+ sql: sqlParts.map((part) => typeof part === "string" ? part : ` ${part.value} `).join("").trim()
863
+ };
864
+ }
558
865
  toSqlWithNamedPlaceholders() {
559
866
  const sqlParts = [];
560
867
  for (const part of this.sqlParts) if (typeof part === "string") sqlParts.push(part);
@@ -602,7 +909,7 @@ function parseSQLQueries(filePath, extraVariables) {
602
909
  if (match) includeColumns.push(...match[1].split(",").map((c) => c.trim()));
603
910
  }
604
911
  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` });
912
+ 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
913
  queryNames.add(name);
607
914
  tables.push(table);
608
915
  consola.debug(`Added table: ${name} -> ${tableName} (appender: ${hasAppender})`);
@@ -618,7 +925,7 @@ function parseSQLQueries(filePath, extraVariables) {
618
925
  config
619
926
  });
620
927
  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` });
928
+ 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
929
  queryNames.add(name);
623
930
  queries.push(query);
624
931
  consola.debug(`Added query: ${name} (${queryType})`);
@@ -1020,7 +1327,7 @@ var TypeMapper = class {
1020
1327
  }
1021
1328
  if (column.type instanceof StructType) return path + this.formatStructTypeName(column.name);
1022
1329
  if (column.type instanceof MapType) return path + this.formatMapTypeName(column.name);
1023
- if (!column.type) throw new TypeMappingError(`Missing type information`, column.name);
1330
+ if (!column.type) throw new TypeMappingError("Missing type information", column.name);
1024
1331
  return this.mapPrimitiveType(column.type.toString(), column.nullable);
1025
1332
  }
1026
1333
  /**
@@ -1798,7 +2105,10 @@ function getGenerator(generator) {
1798
2105
  const info = parseGenerator(generator);
1799
2106
  const key = `${info.language}/${info.driver}`;
1800
2107
  switch (key) {
1801
- case "typescript/better-sqlite3": return new TsGenerator(`templates/${info.template}`);
2108
+ case "typescript/better-sqlite3":
2109
+ case "typescript/node":
2110
+ case "typescript/libsql":
2111
+ case "typescript/turso": return new TsGenerator(`templates/${info.template}`);
1802
2112
  case "typescript/node-api": return new TsDuckDBGenerator(`templates/${info.template}`);
1803
2113
  case "java/jdbc": return new JavaGenerator(`templates/${info.template}`);
1804
2114
  case "java/arrow": return new JavaDuckDBArrowGenerator(`templates/${info.template}`);
@@ -2255,275 +2565,339 @@ async function processProject(projectPath) {
2255
2565
  }
2256
2566
 
2257
2567
  //#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";
2568
+ //#region src/mcp-server.ts
2569
+ const server = new Server({
2570
+ name: "sqg-mcp",
2571
+ version: process.env.npm_package_version ?? "0.7.0"
2572
+ }, { capabilities: {
2573
+ tools: {},
2574
+ resources: {}
2575
+ } });
2576
+ function formatGeneratorListWithDescriptions() {
2577
+ const lines = [];
2578
+ const seen = /* @__PURE__ */ new Set();
2579
+ for (const shortName of SHORT_GENERATOR_NAMES) {
2580
+ const fullName = Object.keys(GENERATORS).find((g) => g.startsWith(`${shortName}/`));
2581
+ if (fullName) {
2582
+ const info = GENERATORS[fullName];
2583
+ lines.push(`- ${shortName} - ${info.description}`);
2584
+ seen.add(fullName);
2585
+ }
2586
+ }
2587
+ for (const [fullName, info] of Object.entries(GENERATORS)) if (!seen.has(fullName)) lines.push(`- ${fullName} - ${info.description}`);
2588
+ return lines.join("\n");
2267
2589
  }
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
- );
2590
+ function formatGeneratorListSimple() {
2591
+ const generators = [...SHORT_GENERATOR_NAMES];
2592
+ const seen = new Set(SHORT_GENERATOR_NAMES.map((s) => Object.keys(GENERATORS).find((g) => g.startsWith(`${s}/`))));
2593
+ for (const fullName of Object.keys(GENERATORS)) if (!seen.has(fullName)) generators.push(fullName);
2594
+ return generators.join(", ");
2595
+ }
2596
+ async function generateCode(sql, generator) {
2597
+ const engine = getGeneratorEngine(generator);
2598
+ const tempDir = join(tmpdir(), `sqg-mcp-${randomUUID()}`);
2599
+ const sqlFile = join(tempDir, "queries.sql");
2600
+ const configFile = join(tempDir, "sqg.yaml");
2601
+ try {
2602
+ mkdirSync(tempDir, { recursive: true });
2603
+ writeFileSync(sqlFile, sql, "utf-8");
2604
+ const genConfig = {
2605
+ generator,
2606
+ output: "./generated/"
2607
+ };
2608
+ if (generator.startsWith("java/")) genConfig.config = { package: "sqg.generated" };
2609
+ const projectYaml = {
2610
+ version: 1,
2611
+ name: "generated",
2612
+ sql: [{
2613
+ engine,
2614
+ files: ["queries.sql"],
2615
+ gen: [genConfig]
2616
+ }]
2617
+ };
2618
+ writeFileSync(configFile, YAML.stringify(projectYaml), "utf-8");
2619
+ const files = await processProject(configFile);
2620
+ if (files.length === 0) return {
2621
+ code: "",
2622
+ error: "No files were generated"
2623
+ };
2624
+ return { code: readFileSync(files[0], "utf-8") };
2625
+ } catch (error) {
2626
+ return {
2627
+ code: "",
2628
+ error: error instanceof Error ? error.message : String(error)
2629
+ };
2630
+ } finally {
2631
+ try {
2632
+ rmSync(tempDir, {
2633
+ recursive: true,
2634
+ force: true
2635
+ });
2636
+ } catch (error) {}
2637
+ }
2638
+ }
2639
+ const GENERATE_CODE_DESCRIPTION = `Generate type-safe database access code from annotated SQL queries.
2281
2640
 
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
- );
2641
+ CRITICAL REQUIREMENTS:
2642
+ 1. MIGRATE statements MUST come BEFORE any QUERY/EXEC that references those tables
2643
+ 2. Each query block needs a unique name
2644
+ 3. Parameters require @set declarations with sample values
2291
2645
 
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
- );
2646
+ ${SQL_SYNTAX_REFERENCE}
2307
2647
 
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
- );
2648
+ VALID GENERATORS (use short form):
2649
+ ${formatGeneratorListWithDescriptions()}
2317
2650
 
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
- );
2651
+ COMMON MISTAKES TO AVOID:
2652
+ - Missing MIGRATE before QUERY (causes "no such table" error)
2653
+ - Missing @set for parameters (causes "undefined variable" error)
2654
+ - Duplicate query names (causes "duplicate query" error)
2655
+ - Using :pluck with multiple columns (only works with 1 column)`;
2656
+ const VALIDATE_SQL_DESCRIPTION = `Validate SQL queries with SQG annotations without generating code. Use this to check for errors before generating.
2334
2657
 
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
- );
2658
+ Returns JSON with validation results including:
2659
+ - valid: boolean indicating success
2660
+ - project: project metadata if valid
2661
+ - sqlFiles: list of SQL files processed
2662
+ - generators: list of generators used
2663
+ - errors: array of error messages if invalid
2344
2664
 
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;
2665
+ See generate_code tool description for complete syntax reference.`;
2666
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
2667
+ return { tools: [{
2668
+ name: "generate_code",
2669
+ description: GENERATE_CODE_DESCRIPTION,
2670
+ inputSchema: {
2671
+ type: "object",
2672
+ properties: {
2673
+ sql: {
2674
+ type: "string",
2675
+ description: "Complete SQL file content with SQG annotations. IMPORTANT: Include MIGRATE statements first to create tables before QUERY statements that use them."
2676
+ },
2677
+ generator: {
2678
+ type: "string",
2679
+ description: `Code generator to use. Valid options: ${formatGeneratorListSimple()}`
2680
+ }
2681
+ },
2682
+ required: ["sql", "generator"]
2683
+ }
2684
+ }, {
2685
+ name: "validate_sql",
2686
+ description: VALIDATE_SQL_DESCRIPTION,
2687
+ inputSchema: {
2688
+ type: "object",
2689
+ properties: {
2690
+ sql: {
2691
+ type: "string",
2692
+ description: "Complete SQL file content with SQG annotations to validate. Include MIGRATE statements before QUERY statements."
2693
+ },
2694
+ generator: {
2695
+ type: "string",
2696
+ description: `Code generator for validation context. Valid options: ${formatGeneratorListSimple()}`
2697
+ }
2698
+ },
2699
+ required: ["sql", "generator"]
2700
+ }
2701
+ }] };
2702
+ });
2703
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
2704
+ return { resources: [{
2705
+ uri: "sqg://documentation",
2706
+ name: "SQG Documentation",
2707
+ description: "Complete documentation for SQG (SQL Query Generator) including syntax, generators, and usage examples",
2708
+ mimeType: "text/markdown"
2709
+ }] };
2710
+ });
2711
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2712
+ const { uri } = request.params;
2713
+ if (uri === "sqg://documentation") {
2714
+ const generatorListMarkdown = Object.entries(GENERATORS).map(([name, info]) => {
2715
+ const shortName = `${info.language}/${info.engine}`;
2716
+ return `- \`${Object.keys(GENERATORS).find((g) => g.startsWith(`${shortName}/`)) === name ? shortName : name}\` - ${info.description}`;
2717
+ }).filter((line, index, arr) => arr.indexOf(line) === index).join("\n");
2718
+ return { contents: [{
2719
+ uri,
2720
+ mimeType: "text/markdown",
2721
+ text: `# SQG - SQL Query Generator
2355
2722
 
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};
2723
+ 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
2724
 
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};
2725
+ ## Overview
2367
2726
 
2368
- -- QUERY count_users :one :pluck
2369
- SELECT COUNT(*) FROM users;
2727
+ SQG introspects SQL queries at build time against real database engines to determine column types and generates strongly-typed wrapper functions.
2370
2728
 
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;
2729
+ **Website:** https://sqg.dev
2730
+ **Repository:** https://github.com/sqg-dev/sqg
2377
2731
 
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});
2732
+ ## Key Features
2397
2733
 
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});
2734
+ - **Type-safe by design** - Generates fully-typed code with accurate column types inferred from your database
2735
+ - **Multiple database engines** - Supports ${DB_ENGINES.join(", ")}
2736
+ - **Multiple language targets** - Generate TypeScript or Java code from the same SQL files
2737
+ - **Arrow API support** - Can generate Apache Arrow API bindings for DuckDB (Java)
2738
+ - **DBeaver compatible** - Works seamlessly with DBeaver for database development and testing
2739
+ - **Complex type support** - DuckDB: Handles structs, lists, and maps
2740
+ - **Migration management** - Built-in support for schema migrations and test data
2404
2741
 
2405
- -- EXEC publish_post
2406
- @set id = 1
2407
- UPDATE posts SET published = 1 WHERE id = \${id};
2742
+ ## SQL Annotations
2408
2743
 
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});
2744
+ ${SQL_SYNTAX_REFERENCE}
2420
2745
 
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});
2746
+ ## Supported Generators
2428
2747
 
2429
- -- EXEC publish_post
2430
- @set id = 1
2431
- UPDATE posts SET published = TRUE WHERE id = \${id};
2748
+ Valid generator strings:
2432
2749
 
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});
2750
+ ${generatorListMarkdown}
2443
2751
 
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});
2752
+ ## MCP Tools
2450
2753
 
2451
- -- EXEC publish_post
2452
- @set id = 1
2453
- UPDATE posts SET published = TRUE WHERE id = \${id};
2754
+ ### generate_code
2454
2755
 
2455
- -- EXEC delete_post
2456
- @set id = 1
2457
- DELETE FROM posts WHERE id = \${id};
2458
- `
2459
- }[engine];
2756
+ Generate type-safe database access code from SQL queries with SQG annotations.
2757
+
2758
+ **Parameters:**
2759
+ - \`sql\` (string, required): SQL queries with SQG annotations
2760
+ - \`generator\` (string, required): Code generation generator (see supported generators above)
2761
+
2762
+ **Example:**
2763
+ \`\`\`json
2764
+ {
2765
+ "sql": "-- MIGRATE 1\\nCREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);\\n\\n-- QUERY getUsers\\nSELECT * FROM users;",
2766
+ "generator": "typescript/sqlite"
2460
2767
  }
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
2768
+ \`\`\`
2469
2769
 
2470
- version: 1
2471
- name: my-project
2770
+ ### validate_sql
2472
2771
 
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
- `;
2772
+ Validate SQL queries with SQG annotations without generating code.
2773
+
2774
+ **Parameters:**
2775
+ - \`sql\` (string, required): SQL queries with SQG annotations to validate
2776
+ - \`generator\` (string, required): Code generation generator to use for validation
2777
+
2778
+ **Example:**
2779
+ \`\`\`json
2780
+ {
2781
+ "sql": "-- QUERY getUsers\\nSELECT * FROM users;",
2782
+ "generator": "typescript/sqlite"
2482
2783
  }
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!
2784
+ \`\`\`
2510
2785
 
2511
- Generator: ${generator}
2512
- Engine: ${engine}
2513
- Output: ${output}
2786
+ ## Generator Format
2514
2787
 
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}
2788
+ Generators follow the pattern \`<language>/<engine>[/<driver>]\`:
2519
2789
 
2520
- Documentation: https://sqg.dev
2521
- `);
2790
+ - **Short form**: \`typescript/sqlite\`, \`java/duckdb\` (uses default driver)
2791
+ - **Full form**: \`typescript/sqlite/better-sqlite3\`, \`java/duckdb/arrow\` (specifies driver)
2792
+
2793
+ The MCP server accepts both short and full forms, but short forms are recommended.
2794
+
2795
+ ## More Information
2796
+
2797
+ - Full documentation: https://sqg.dev
2798
+ - GitHub: https://github.com/sqg-dev/sqg
2799
+ - SQL Syntax Reference: Run \`sqg syntax\` command
2800
+ `
2801
+ }] };
2802
+ }
2803
+ return {
2804
+ contents: [],
2805
+ isError: true
2806
+ };
2807
+ });
2808
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2809
+ const { name, arguments: args } = request.params;
2810
+ if (name === "generate_code") {
2811
+ const { sql, generator } = args;
2812
+ const result = await generateCode(sql, generator);
2813
+ if (result.error) return {
2814
+ content: [{
2815
+ type: "text",
2816
+ text: `Error generating code: ${result.error}`
2817
+ }],
2818
+ isError: true
2819
+ };
2820
+ return { content: [{
2821
+ type: "text",
2822
+ text: result.code
2823
+ }] };
2824
+ }
2825
+ if (name === "validate_sql") {
2826
+ const { sql, generator } = args;
2827
+ const engine = getGeneratorEngine(generator);
2828
+ const tempDir = join(tmpdir(), `sqg-mcp-validate-${randomUUID()}`);
2829
+ const sqlFile = join(tempDir, "queries.sql");
2830
+ const configFile = join(tempDir, "sqg.yaml");
2831
+ try {
2832
+ mkdirSync(tempDir, { recursive: true });
2833
+ writeFileSync(sqlFile, sql, "utf-8");
2834
+ const projectYaml = {
2835
+ version: 1,
2836
+ name: "validation",
2837
+ sql: [{
2838
+ engine,
2839
+ files: ["queries.sql"],
2840
+ gen: [{
2841
+ generator,
2842
+ output: "./generated/"
2843
+ }]
2844
+ }]
2845
+ };
2846
+ writeFileSync(configFile, YAML.stringify(projectYaml), "utf-8");
2847
+ const validation = await validateProject(configFile);
2848
+ if (validation.valid) return { content: [{
2849
+ type: "text",
2850
+ text: JSON.stringify({
2851
+ valid: true,
2852
+ project: validation.project,
2853
+ sqlFiles: validation.sqlFiles,
2854
+ generators: validation.generators
2855
+ }, null, 2)
2856
+ }] };
2857
+ return {
2858
+ content: [{
2859
+ type: "text",
2860
+ text: JSON.stringify({
2861
+ valid: false,
2862
+ errors: validation.errors
2863
+ }, null, 2)
2864
+ }],
2865
+ isError: true
2866
+ };
2867
+ } catch (error) {
2868
+ return {
2869
+ content: [{
2870
+ type: "text",
2871
+ text: `Error validating SQL: ${error instanceof Error ? error.message : String(error)}`
2872
+ }],
2873
+ isError: true
2874
+ };
2875
+ } finally {
2876
+ try {
2877
+ rmSync(tempDir, {
2878
+ recursive: true,
2879
+ force: true
2880
+ });
2881
+ } catch (error) {}
2882
+ }
2883
+ }
2884
+ return {
2885
+ content: [{
2886
+ type: "text",
2887
+ text: `Unknown tool: ${name}`
2888
+ }],
2889
+ isError: true
2890
+ };
2891
+ });
2892
+ async function startMcpServer() {
2893
+ const transport = new StdioServerTransport();
2894
+ await server.connect(transport);
2895
+ console.error("SQG MCP server running on stdio");
2522
2896
  }
2523
2897
 
2524
2898
  //#endregion
2525
2899
  //#region src/sqg.ts
2526
- const version = process.env.npm_package_version ?? "0.5.0";
2900
+ const version = process.env.npm_package_version ?? "0.7.0";
2527
2901
  const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
2528
2902
  consola.level = LogLevels.info;
2529
2903
  const program = new Command().name("sqg").description(`${description}
@@ -2628,6 +3002,14 @@ program.command("init").description("Initialize a new SQG project with example c
2628
3002
  program.command("syntax").description("Show SQL annotation syntax reference").action(() => {
2629
3003
  console.log(SQL_SYNTAX_REFERENCE);
2630
3004
  });
3005
+ program.command("mcp").description("Start MCP (Model Context Protocol) server for AI assistants").action(async () => {
3006
+ try {
3007
+ await startMcpServer();
3008
+ } catch (error) {
3009
+ consola.error("Fatal error in MCP server:", error);
3010
+ exit(1);
3011
+ }
3012
+ });
2631
3013
  if (process.argv.length <= 2) {
2632
3014
  program.outputHelp();
2633
3015
  exit(1);