@sqg/sqg 0.19.1 → 0.21.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/README.md +4 -0
- package/dist/index.mjs +329 -36
- package/dist/sqg.mjs +336 -37
- package/dist/templates/better-sqlite3.hbs +1 -1
- package/dist/templates/java-duckdb-arrow.hbs +3 -1
- package/dist/templates/java-jdbc.hbs +9 -0
- package/dist/templates/python.hbs +15 -9
- package/dist/templates/typescript-duckdb.hbs +8 -1
- package/package.json +1 -1
package/dist/sqg.mjs
CHANGED
|
@@ -221,6 +221,7 @@ SQL Annotation Syntax:
|
|
|
221
221
|
-- QUERY <name> [:one] [:pluck] Select query (returns rows)
|
|
222
222
|
-- EXEC <name> [:batch] Execute statement (INSERT/UPDATE/DELETE)
|
|
223
223
|
-- BASELINE <name> Schema created outside SQG (runs before migrations, not tracked)
|
|
224
|
+
-- BASELINE <name> :source=<src> Schema of a 'type: postgres' source (applied to a managed testcontainer, attached into DuckDB)
|
|
224
225
|
-- MIGRATE <name> Schema migration (runs in source order; name is any identifier, e.g. "1" or "add_email")
|
|
225
226
|
-- TESTDATA <name> Test data setup (not generated)
|
|
226
227
|
-- TABLE <name> :appender Table for bulk insert appender (DuckDB, PostgreSQL)
|
|
@@ -229,11 +230,19 @@ SQL Annotation Syntax:
|
|
|
229
230
|
\${varName} Reference a variable in SQL
|
|
230
231
|
|
|
231
232
|
Modifiers:
|
|
232
|
-
:one
|
|
233
|
-
:pluck
|
|
234
|
-
:all
|
|
235
|
-
:batch
|
|
236
|
-
:appender
|
|
233
|
+
:one Return single row (or null) instead of array
|
|
234
|
+
:pluck Return single column value (requires exactly 1 column)
|
|
235
|
+
:all Return all rows (default)
|
|
236
|
+
:batch Generate a JDBC batch method for an EXEC (Java only)
|
|
237
|
+
:appender Generate bulk insert appender for TABLE annotation
|
|
238
|
+
:source=Name Mark a BASELINE block as the schema of a postgres source (Name)
|
|
239
|
+
:result=Name Name (and share) the row type (Java only). Annotate ONE query
|
|
240
|
+
with :result=Name — every other query in the same file with the
|
|
241
|
+
same column shape automatically gets the same name; you do NOT
|
|
242
|
+
need to repeat the modifier on each query.
|
|
243
|
+
Without :result=, each query keeps its own per-query row type.
|
|
244
|
+
Exception: SELECT * matching a -- TABLE schema (same columns,
|
|
245
|
+
same order) auto-uses the table's row type with no annotation.
|
|
237
246
|
|
|
238
247
|
Example:
|
|
239
248
|
-- MIGRATE 1
|
|
@@ -724,7 +733,7 @@ const parser = LRParser.deserialize({
|
|
|
724
733
|
maxTerm: 34,
|
|
725
734
|
skippedNodes: [0],
|
|
726
735
|
repeatNodeCount: 6,
|
|
727
|
-
tokenData: "$KR~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#2P!Q![$A]![!]$Bu!]!_#Y!_!`$Eu!`!b#Y!b!c$Fu!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#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%XOnSS%[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&jVnS_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~'|Xj~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTk~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+VT]QOz$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,gWnSOY-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-qO]QQ-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]]Q_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_nS_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<%lO2dQ3oV]Q_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{TaQOz$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;dT^QOz$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<tWnSOY=^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>OO^QQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sW^QOY=^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@^Z^QOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]^Q_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_nS_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{V^Q_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_^Q_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[UJZb^Q_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#YVLthQR_QOXN`XY!&|YZ$WZ]N`]^! c^pN`pq!&|qtN`tu! cuzN`z{!#q{!dN`!d!e!=`!e!gN`!g!h!Ft!h!oN`!o!p!Ja!p!sN`!s!t#!`!t!vN`!v!w#'U!w;'SN`;'S;=`!&v<%lON`VNg^QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V! hVQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!!SXQROY! cYZ$WZz! cz{! }{!P! c!P!Q!!o!Q;'S! c;'S;=`!#k<%lO! cV!!vSnSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#XSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#hP;=`<%l!#SV!#nP;=`<%l! cV!#x`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!PN`!P!Q!$z!Q;'SN`;'S;=`!&v<%lON`V!%TZnSQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!%}ZQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!&sP;=`<%l!%vV!&yP;=`<%lN`V!'RdQROX! cXY!&|YZ$WZp! cpq!&|qz! cz{! }{!d! c!d!e!(a!e!g! c!g!h!.l!h!o! c!o!p!0p!p!s! c!s!t!4W!t!v! c!v!w!6|!w;'S! c;'S;=`!#k<%lO! cV!(fXQROY! cYZ$WZz! cz{! }{!c! c!c!d!)R!d;'S! c;'S;=`!#k<%lO! cV!)WXQROY! cYZ$WZz! cz{! }{!u! c!u!v!)s!v;'S! c;'S;=`!#k<%lO! cV!)xXQROY! cYZ$WZz! cz{! }{!g! c!g!h!*e!h;'S! c;'S;=`!#k<%lO! cV!*jXQROY! cYZ$WZz! cz{! }{!n! c!n!o!+V!o;'S! c;'S;=`!#k<%lO! cV!+[XQROY! cYZ$WZz! cz{! }{!k! c!k!l!+w!l;'S! c;'S;=`!#k<%lO! cV!+|XQROY! cYZ$WZz! cz{! }{!p! c!p!q!,i!q;'S! c;'S;=`!#k<%lO! cV!,nXQROY! cYZ$WZz! cz{! }{!g! c!g!h!-Z!h;'S! c;'S;=`!#k<%lO! cV!-`YQROX! cXY!.OYZ$WZp! cpq!.Oqz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.VVYRQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.qXQROY! cYZ$WZz! cz{! }{!z! c!z!{!/^!{;'S! c;'S;=`!#k<%lO! cV!/cXQROY! cYZ$WZz! cz{! }{!g! c!g!h!0O!h;'S! c;'S;=`!#k<%lO! cV!0TXQROY! cYZ$WZz! cz{! }{!e! c!e!f!-Z!f;'S! c;'S;=`!#k<%lO! cV!0uXQROY! cYZ$WZz! cz{! }{!k! c!k!l!1b!l;'S! c;'S;=`!#k<%lO! cV!1gXQROY! cYZ$WZz! cz{! }{!i! c!i!j!2S!j;'S! c;'S;=`!#k<%lO! cV!2XXQROY! cYZ$WZz! cz{! }{!t! c!t!u!2t!u;'S! c;'S;=`!#k<%lO! cV!2yXQROY! cYZ$WZz! cz{! }{!c! c!c!d!3f!d;'S! c;'S;=`!#k<%lO! cV!3kXQROY! cYZ$WZz! cz{! }{!v! c!v!w!,i!w;'S! c;'S;=`!#k<%lO! cV!4]XQROY! cYZ$WZz! cz{! }{!w! c!w!x!4x!x;'S! c;'S;=`!#k<%lO! cV!4}XQROY! cYZ$WZz! cz{! }{!g! c!g!h!5j!h;'S! c;'S;=`!#k<%lO! cV!5oXQROY! cYZ$WZz! cz{! }{!t! c!t!u!6[!u;'S! c;'S;=`!#k<%lO! cV!6aXQROY! cYZ$WZz! cz{! }{!{! c!{!|!-Z!|;'S! c;'S;=`!#k<%lO! cV!7RZQROY! cYZ$WZz! cz{! }{!c! c!c!d!7t!d!g! c!g!h!9W!h;'S! c;'S;=`!#k<%lO! cV!7yXQROY! cYZ$WZz! cz{! }{!d! c!d!e!8f!e;'S! c;'S;=`!#k<%lO! cV!8kXQROY! cYZ$WZz! cz{! }{!n! c!n!o!,i!o;'S! c;'S;=`!#k<%lO! cV!9]XQROY! cYZ$WZz! cz{! }{!u! c!u!v!9x!v;'S! c;'S;=`!#k<%lO! cV!9}XQROY! cYZ$WZz! cz{! }{!v! c!v!w!:j!w;'S! c;'S;=`!#k<%lO! cV!:oXQROY! cYZ$WZz! cz{! }{!f! c!f!g!;[!g;'S! c;'S;=`!#k<%lO! cV!;aXQROY! cYZ$WZz! cz{! }{!c! c!c!d!;|!d;'S! c;'S;=`!#k<%lO! cV!<RXQROY! cYZ$WZz! cz{! }{!v! c!v!w!<n!w;'S! c;'S;=`!#k<%lO! cV!<sXQROY! cYZ$WZz! cz{! }{!c! c!c!d!-Z!d;'S! c;'S;=`!#k<%lO! cV!=g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!>i!d;'SN`;'S;=`!&v<%lON`V!>p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v!?r!v;'SN`;'S;=`!&v<%lON`V!?y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!@{!h;'SN`;'S;=`!&v<%lON`V!AS`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!BU!o;'SN`;'S;=`!&v<%lON`V!B]`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!C_!l;'SN`;'S;=`!&v<%lON`V!Cf`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!pN`!p!q!Dh!q;'SN`;'S;=`!&v<%lON`V!Do`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!Eq!h;'SN`;'S;=`!&v<%lON`V!Ex^QR_QOXN`XY!.OYZ$WZ]N`]^! c^pN`pq!.OqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V!F{`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!zN`!z!{!G}!{;'SN`;'S;=`!&v<%lON`V!HU`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!IW!h;'SN`;'S;=`!&v<%lON`V!I_`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!eN`!e!f!Eq!f;'SN`;'S;=`!&v<%lON`V!Jh`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!Kj!l;'SN`;'S;=`!&v<%lON`V!Kq`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!iN`!i!j!Ls!j;'SN`;'S;=`!&v<%lON`V!Lz`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u!M|!u;'SN`;'S;=`!&v<%lON`V!NT`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d# V!d;'SN`;'S;=`!&v<%lON`V# ^`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w!Dh!w;'SN`;'S;=`!&v<%lON`V#!g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!wN`!w!x##i!x;'SN`;'S;=`!&v<%lON`V##p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h#$r!h;'SN`;'S;=`!&v<%lON`V#$y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u#%{!u;'SN`;'S;=`!&v<%lON`V#&S`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!{N`!{!|!Eq!|;'SN`;'S;=`!&v<%lON`V#']bQR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#(e!d!gN`!g!h#*w!h;'SN`;'S;=`!&v<%lON`V#(l`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!dN`!d!e#)n!e;'SN`;'S;=`!&v<%lON`V#)u`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!Dh!o;'SN`;'S;=`!&v<%lON`V#+O`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v#,Q!v;'SN`;'S;=`!&v<%lON`V#,X`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#-Z!w;'SN`;'S;=`!&v<%lON`V#-b`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!fN`!f!g#.d!g;'SN`;'S;=`!&v<%lON`V#.k`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#/m!d;'SN`;'S;=`!&v<%lON`V#/t`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#0v!w;'SN`;'S;=`!&v<%lON`V#0}`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!Eq!d;'SN`;'S;=`!&v<%lON`V#2U]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#2}{;'S#Y;'S;=`'q<%lO#YV#3Sj_QOX#4tXY#8uYZ#8uZ]#4t]^#5r^p#4tpq#8uqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#Js!Q!d#4t!d!e$ p!e!g#4t!g!h$*_!h!o#4t!o!p$-k!p!s#4t!s!t$3P!t!v#4t!v!w$7a!w;'S#4t;'S;=`#8o<%lO#4tV#4y]_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV#5uTOz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#6XVOz#5rz{#6U{!P#5r!P!Q#6n!Q;'S#5r;'S;=`#6u<%lO#5rV#6uORRnSV#6xP;=`<%l#5rV#7Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#8P!Q;'S#4t;'S;=`#8o<%lO#4tV#8YVRRnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PV#8rP;=`<%l#4tV#8xdOX#5rXY#8uYZ#8uZp#5rpq#8uqz#5rz{#6U{!d#5r!d!e#:W!e!g#5r!g!h#?o!h!o#5r!o!p#AZ!p!s#5r!s!t#Cw!t!v#5r!v!w#E{!w;'S#5r;'S;=`#6u<%lO#5rV#:ZVOz#5rz{#6U{!c#5r!c!d#:p!d;'S#5r;'S;=`#6u<%lO#5rV#:sVOz#5rz{#6U{!u#5r!u!v#;Y!v;'S#5r;'S;=`#6u<%lO#5rV#;]VOz#5rz{#6U{!g#5r!g!h#;r!h;'S#5r;'S;=`#6u<%lO#5rV#;uVOz#5rz{#6U{!n#5r!n!o#<[!o;'S#5r;'S;=`#6u<%lO#5rV#<_VOz#5rz{#6U{!k#5r!k!l#<t!l;'S#5r;'S;=`#6u<%lO#5rV#<wVOz#5rz{#6U{!p#5r!p!q#=^!q;'S#5r;'S;=`#6u<%lO#5rV#=aVOz#5rz{#6U{!g#5r!g!h#=v!h;'S#5r;'S;=`#6u<%lO#5rV#=yYOX#5rXY#>iYZ#?ZZp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#>nXUROX#5rXY#>iYp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?`TUROz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?rVOz#5rz{#6U{!z#5r!z!{#@X!{;'S#5r;'S;=`#6u<%lO#5rV#@[VOz#5rz{#6U{!g#5r!g!h#@q!h;'S#5r;'S;=`#6u<%lO#5rV#@tVOz#5rz{#6U{!e#5r!e!f#=v!f;'S#5r;'S;=`#6u<%lO#5rV#A^VOz#5rz{#6U{!k#5r!k!l#As!l;'S#5r;'S;=`#6u<%lO#5rV#AvVOz#5rz{#6U{!i#5r!i!j#B]!j;'S#5r;'S;=`#6u<%lO#5rV#B`VOz#5rz{#6U{!t#5r!t!u#Bu!u;'S#5r;'S;=`#6u<%lO#5rV#BxVOz#5rz{#6U{!c#5r!c!d#C_!d;'S#5r;'S;=`#6u<%lO#5rV#CbVOz#5rz{#6U{!v#5r!v!w#=^!w;'S#5r;'S;=`#6u<%lO#5rV#CzVOz#5rz{#6U{!w#5r!w!x#Da!x;'S#5r;'S;=`#6u<%lO#5rV#DdVOz#5rz{#6U{!g#5r!g!h#Dy!h;'S#5r;'S;=`#6u<%lO#5rV#D|VOz#5rz{#6U{!t#5r!t!u#Ec!u;'S#5r;'S;=`#6u<%lO#5rV#EfVOz#5rz{#6U{!{#5r!{!|#=v!|;'S#5r;'S;=`#6u<%lO#5rV#FOXOz#5rz{#6U{!c#5r!c!d#Fk!d!g#5r!g!h#Gm!h;'S#5r;'S;=`#6u<%lO#5rV#FnVOz#5rz{#6U{!d#5r!d!e#GT!e;'S#5r;'S;=`#6u<%lO#5rV#GWVOz#5rz{#6U{!n#5r!n!o#=^!o;'S#5r;'S;=`#6u<%lO#5rV#GpVOz#5rz{#6U{!u#5r!u!v#HV!v;'S#5r;'S;=`#6u<%lO#5rV#HYVOz#5rz{#6U{!v#5r!v!w#Ho!w;'S#5r;'S;=`#6u<%lO#5rV#HrVOz#5rz{#6U{!f#5r!f!g#IX!g;'S#5r;'S;=`#6u<%lO#5rV#I[VOz#5rz{#6U{!c#5r!c!d#Iq!d;'S#5r;'S;=`#6u<%lO#5rV#ItVOz#5rz{#6U{!v#5r!v!w#JZ!w;'S#5r;'S;=`#6u<%lO#5rV#J^VOz#5rz{#6U{!c#5r!c!d#=v!d;'S#5r;'S;=`#6u<%lO#5rV#Jz]nS_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#Kx]_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#LtTOz#Lqz{#MT{;'S#Lq;'S;=`#Mr<%lO#LqR#MWVOz#Lqz{#MT{!P#Lq!P!Q#Mm!Q;'S#Lq;'S;=`#Mr<%lO#LqR#MrORRR#MuP;=`<%l#LqR#M}__QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{!P#Ks!P!Q#N|!Q;'S#Ks;'S;=`$ j<%lO#KsR$ TVRR_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PR$ mP;=`<%l#KsV$ u__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$!t!d;'S#4t;'S;=`#8o<%lO#4tV$!y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$#x!v;'S#4t;'S;=`#8o<%lO#4tV$#}__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$$|!h;'S#4t;'S;=`#8o<%lO#4tV$%R__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$&Q!o;'S#4t;'S;=`#8o<%lO#4tV$&V__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$'U!l;'S#4t;'S;=`#8o<%lO#4tV$'Z__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!p#4t!p!q$(Y!q;'S#4t;'S;=`#8o<%lO#4tV$(___QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$)^!h;'S#4t;'S;=`#8o<%lO#4tV$)c^_QOX#4tXY#>iYZ#?ZZ]#4t]^#5r^p#4tpq#>iqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV$*d__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!z#4t!z!{$+c!{;'S#4t;'S;=`#8o<%lO#4tV$+h__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$,g!h;'S#4t;'S;=`#8o<%lO#4tV$,l__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!e#4t!e!f$)^!f;'S#4t;'S;=`#8o<%lO#4tV$-p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$.o!l;'S#4t;'S;=`#8o<%lO#4tV$.t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!i#4t!i!j$/s!j;'S#4t;'S;=`#8o<%lO#4tV$/x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$0w!u;'S#4t;'S;=`#8o<%lO#4tV$0|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$1{!d;'S#4t;'S;=`#8o<%lO#4tV$2Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$(Y!w;'S#4t;'S;=`#8o<%lO#4tV$3U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!w#4t!w!x$4T!x;'S#4t;'S;=`#8o<%lO#4tV$4Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$5X!h;'S#4t;'S;=`#8o<%lO#4tV$5^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$6]!u;'S#4t;'S;=`#8o<%lO#4tV$6b__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!{#4t!{!|$)^!|;'S#4t;'S;=`#8o<%lO#4tV$7fa_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$8k!d!g#4t!g!h$:s!h;'S#4t;'S;=`#8o<%lO#4tV$8p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!d#4t!d!e$9o!e;'S#4t;'S;=`#8o<%lO#4tV$9t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$(Y!o;'S#4t;'S;=`#8o<%lO#4tV$:x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$;w!v;'S#4t;'S;=`#8o<%lO#4tV$;|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$<{!w;'S#4t;'S;=`#8o<%lO#4tV$=Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!f#4t!f!g$>P!g;'S#4t;'S;=`#8o<%lO#4tV$>U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$?T!d;'S#4t;'S;=`#8o<%lO#4tV$?Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$@X!w;'S#4t;'S;=`#8o<%lO#4tV$@^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$)^!d;'S#4t;'S;=`#8o<%lO#4tV$AdeVP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$A]![!c#Y!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#Y~$Bze_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$DdeW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#YV$E|]qP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$Fz__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$Gy#h;'S#Y;'S;=`'q<%lO#YU$HO__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$H}#Y;'S#Y;'S;=`'q<%lO#YU$IS__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$JR#i;'S#Y;'S;=`'q<%lO#YU$JY]pQ_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
|
|
736
|
+
tokenData: "$NX~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#2P!Q![$A]![!]$Bu!]!_#Y!_!`$H{!`!b#Y!b!c$I{!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#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%XOnSS%[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&jVnS_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~'|Xj~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTk~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+VT]QOz$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,gWnSOY-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-qO]QQ-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]]Q_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_nS_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<%lO2dQ3oV]Q_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{TaQOz$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;dT^QOz$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<tWnSOY=^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>OO^QQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sW^QOY=^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@^Z^QOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]^Q_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_nS_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{V^Q_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_^Q_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[UJZb^Q_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#YVLthQR_QOXN`XY!&|YZ$WZ]N`]^! c^pN`pq!&|qtN`tu! cuzN`z{!#q{!dN`!d!e!=`!e!gN`!g!h!Ft!h!oN`!o!p!Ja!p!sN`!s!t#!`!t!vN`!v!w#'U!w;'SN`;'S;=`!&v<%lON`VNg^QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V! hVQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!!SXQROY! cYZ$WZz! cz{! }{!P! c!P!Q!!o!Q;'S! c;'S;=`!#k<%lO! cV!!vSnSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#XSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#hP;=`<%l!#SV!#nP;=`<%l! cV!#x`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!PN`!P!Q!$z!Q;'SN`;'S;=`!&v<%lON`V!%TZnSQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!%}ZQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!&sP;=`<%l!%vV!&yP;=`<%lN`V!'RdQROX! cXY!&|YZ$WZp! cpq!&|qz! cz{! }{!d! c!d!e!(a!e!g! c!g!h!.l!h!o! c!o!p!0p!p!s! c!s!t!4W!t!v! c!v!w!6|!w;'S! c;'S;=`!#k<%lO! cV!(fXQROY! cYZ$WZz! cz{! }{!c! c!c!d!)R!d;'S! c;'S;=`!#k<%lO! cV!)WXQROY! cYZ$WZz! cz{! }{!u! c!u!v!)s!v;'S! c;'S;=`!#k<%lO! cV!)xXQROY! cYZ$WZz! cz{! }{!g! c!g!h!*e!h;'S! c;'S;=`!#k<%lO! cV!*jXQROY! cYZ$WZz! cz{! }{!n! c!n!o!+V!o;'S! c;'S;=`!#k<%lO! cV!+[XQROY! cYZ$WZz! cz{! }{!k! c!k!l!+w!l;'S! c;'S;=`!#k<%lO! cV!+|XQROY! cYZ$WZz! cz{! }{!p! c!p!q!,i!q;'S! c;'S;=`!#k<%lO! cV!,nXQROY! cYZ$WZz! cz{! }{!g! c!g!h!-Z!h;'S! c;'S;=`!#k<%lO! cV!-`YQROX! cXY!.OYZ$WZp! cpq!.Oqz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.VVYRQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.qXQROY! cYZ$WZz! cz{! }{!z! c!z!{!/^!{;'S! c;'S;=`!#k<%lO! cV!/cXQROY! cYZ$WZz! cz{! }{!g! c!g!h!0O!h;'S! c;'S;=`!#k<%lO! cV!0TXQROY! cYZ$WZz! cz{! }{!e! c!e!f!-Z!f;'S! c;'S;=`!#k<%lO! cV!0uXQROY! cYZ$WZz! cz{! }{!k! c!k!l!1b!l;'S! c;'S;=`!#k<%lO! cV!1gXQROY! cYZ$WZz! cz{! }{!i! c!i!j!2S!j;'S! c;'S;=`!#k<%lO! cV!2XXQROY! cYZ$WZz! cz{! }{!t! c!t!u!2t!u;'S! c;'S;=`!#k<%lO! cV!2yXQROY! cYZ$WZz! cz{! }{!c! c!c!d!3f!d;'S! c;'S;=`!#k<%lO! cV!3kXQROY! cYZ$WZz! cz{! }{!v! c!v!w!,i!w;'S! c;'S;=`!#k<%lO! cV!4]XQROY! cYZ$WZz! cz{! }{!w! c!w!x!4x!x;'S! c;'S;=`!#k<%lO! cV!4}XQROY! cYZ$WZz! cz{! }{!g! c!g!h!5j!h;'S! c;'S;=`!#k<%lO! cV!5oXQROY! cYZ$WZz! cz{! }{!t! c!t!u!6[!u;'S! c;'S;=`!#k<%lO! cV!6aXQROY! cYZ$WZz! cz{! }{!{! c!{!|!-Z!|;'S! c;'S;=`!#k<%lO! cV!7RZQROY! cYZ$WZz! cz{! }{!c! c!c!d!7t!d!g! c!g!h!9W!h;'S! c;'S;=`!#k<%lO! cV!7yXQROY! cYZ$WZz! cz{! }{!d! c!d!e!8f!e;'S! c;'S;=`!#k<%lO! cV!8kXQROY! cYZ$WZz! cz{! }{!n! c!n!o!,i!o;'S! c;'S;=`!#k<%lO! cV!9]XQROY! cYZ$WZz! cz{! }{!u! c!u!v!9x!v;'S! c;'S;=`!#k<%lO! cV!9}XQROY! cYZ$WZz! cz{! }{!v! c!v!w!:j!w;'S! c;'S;=`!#k<%lO! cV!:oXQROY! cYZ$WZz! cz{! }{!f! c!f!g!;[!g;'S! c;'S;=`!#k<%lO! cV!;aXQROY! cYZ$WZz! cz{! }{!c! c!c!d!;|!d;'S! c;'S;=`!#k<%lO! cV!<RXQROY! cYZ$WZz! cz{! }{!v! c!v!w!<n!w;'S! c;'S;=`!#k<%lO! cV!<sXQROY! cYZ$WZz! cz{! }{!c! c!c!d!-Z!d;'S! c;'S;=`!#k<%lO! cV!=g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!>i!d;'SN`;'S;=`!&v<%lON`V!>p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v!?r!v;'SN`;'S;=`!&v<%lON`V!?y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!@{!h;'SN`;'S;=`!&v<%lON`V!AS`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!BU!o;'SN`;'S;=`!&v<%lON`V!B]`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!C_!l;'SN`;'S;=`!&v<%lON`V!Cf`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!pN`!p!q!Dh!q;'SN`;'S;=`!&v<%lON`V!Do`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!Eq!h;'SN`;'S;=`!&v<%lON`V!Ex^QR_QOXN`XY!.OYZ$WZ]N`]^! c^pN`pq!.OqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V!F{`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!zN`!z!{!G}!{;'SN`;'S;=`!&v<%lON`V!HU`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!IW!h;'SN`;'S;=`!&v<%lON`V!I_`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!eN`!e!f!Eq!f;'SN`;'S;=`!&v<%lON`V!Jh`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!Kj!l;'SN`;'S;=`!&v<%lON`V!Kq`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!iN`!i!j!Ls!j;'SN`;'S;=`!&v<%lON`V!Lz`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u!M|!u;'SN`;'S;=`!&v<%lON`V!NT`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d# V!d;'SN`;'S;=`!&v<%lON`V# ^`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w!Dh!w;'SN`;'S;=`!&v<%lON`V#!g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!wN`!w!x##i!x;'SN`;'S;=`!&v<%lON`V##p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h#$r!h;'SN`;'S;=`!&v<%lON`V#$y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u#%{!u;'SN`;'S;=`!&v<%lON`V#&S`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!{N`!{!|!Eq!|;'SN`;'S;=`!&v<%lON`V#']bQR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#(e!d!gN`!g!h#*w!h;'SN`;'S;=`!&v<%lON`V#(l`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!dN`!d!e#)n!e;'SN`;'S;=`!&v<%lON`V#)u`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!Dh!o;'SN`;'S;=`!&v<%lON`V#+O`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v#,Q!v;'SN`;'S;=`!&v<%lON`V#,X`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#-Z!w;'SN`;'S;=`!&v<%lON`V#-b`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!fN`!f!g#.d!g;'SN`;'S;=`!&v<%lON`V#.k`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#/m!d;'SN`;'S;=`!&v<%lON`V#/t`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#0v!w;'SN`;'S;=`!&v<%lON`V#0}`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!Eq!d;'SN`;'S;=`!&v<%lON`V#2U]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#2}{;'S#Y;'S;=`'q<%lO#YV#3Sj_QOX#4tXY#8uYZ#8uZ]#4t]^#5r^p#4tpq#8uqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#Js!Q!d#4t!d!e$ p!e!g#4t!g!h$*_!h!o#4t!o!p$-k!p!s#4t!s!t$3P!t!v#4t!v!w$7a!w;'S#4t;'S;=`#8o<%lO#4tV#4y]_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV#5uTOz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#6XVOz#5rz{#6U{!P#5r!P!Q#6n!Q;'S#5r;'S;=`#6u<%lO#5rV#6uORRnSV#6xP;=`<%l#5rV#7Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#8P!Q;'S#4t;'S;=`#8o<%lO#4tV#8YVRRnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PV#8rP;=`<%l#4tV#8xdOX#5rXY#8uYZ#8uZp#5rpq#8uqz#5rz{#6U{!d#5r!d!e#:W!e!g#5r!g!h#?o!h!o#5r!o!p#AZ!p!s#5r!s!t#Cw!t!v#5r!v!w#E{!w;'S#5r;'S;=`#6u<%lO#5rV#:ZVOz#5rz{#6U{!c#5r!c!d#:p!d;'S#5r;'S;=`#6u<%lO#5rV#:sVOz#5rz{#6U{!u#5r!u!v#;Y!v;'S#5r;'S;=`#6u<%lO#5rV#;]VOz#5rz{#6U{!g#5r!g!h#;r!h;'S#5r;'S;=`#6u<%lO#5rV#;uVOz#5rz{#6U{!n#5r!n!o#<[!o;'S#5r;'S;=`#6u<%lO#5rV#<_VOz#5rz{#6U{!k#5r!k!l#<t!l;'S#5r;'S;=`#6u<%lO#5rV#<wVOz#5rz{#6U{!p#5r!p!q#=^!q;'S#5r;'S;=`#6u<%lO#5rV#=aVOz#5rz{#6U{!g#5r!g!h#=v!h;'S#5r;'S;=`#6u<%lO#5rV#=yYOX#5rXY#>iYZ#?ZZp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#>nXUROX#5rXY#>iYp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?`TUROz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?rVOz#5rz{#6U{!z#5r!z!{#@X!{;'S#5r;'S;=`#6u<%lO#5rV#@[VOz#5rz{#6U{!g#5r!g!h#@q!h;'S#5r;'S;=`#6u<%lO#5rV#@tVOz#5rz{#6U{!e#5r!e!f#=v!f;'S#5r;'S;=`#6u<%lO#5rV#A^VOz#5rz{#6U{!k#5r!k!l#As!l;'S#5r;'S;=`#6u<%lO#5rV#AvVOz#5rz{#6U{!i#5r!i!j#B]!j;'S#5r;'S;=`#6u<%lO#5rV#B`VOz#5rz{#6U{!t#5r!t!u#Bu!u;'S#5r;'S;=`#6u<%lO#5rV#BxVOz#5rz{#6U{!c#5r!c!d#C_!d;'S#5r;'S;=`#6u<%lO#5rV#CbVOz#5rz{#6U{!v#5r!v!w#=^!w;'S#5r;'S;=`#6u<%lO#5rV#CzVOz#5rz{#6U{!w#5r!w!x#Da!x;'S#5r;'S;=`#6u<%lO#5rV#DdVOz#5rz{#6U{!g#5r!g!h#Dy!h;'S#5r;'S;=`#6u<%lO#5rV#D|VOz#5rz{#6U{!t#5r!t!u#Ec!u;'S#5r;'S;=`#6u<%lO#5rV#EfVOz#5rz{#6U{!{#5r!{!|#=v!|;'S#5r;'S;=`#6u<%lO#5rV#FOXOz#5rz{#6U{!c#5r!c!d#Fk!d!g#5r!g!h#Gm!h;'S#5r;'S;=`#6u<%lO#5rV#FnVOz#5rz{#6U{!d#5r!d!e#GT!e;'S#5r;'S;=`#6u<%lO#5rV#GWVOz#5rz{#6U{!n#5r!n!o#=^!o;'S#5r;'S;=`#6u<%lO#5rV#GpVOz#5rz{#6U{!u#5r!u!v#HV!v;'S#5r;'S;=`#6u<%lO#5rV#HYVOz#5rz{#6U{!v#5r!v!w#Ho!w;'S#5r;'S;=`#6u<%lO#5rV#HrVOz#5rz{#6U{!f#5r!f!g#IX!g;'S#5r;'S;=`#6u<%lO#5rV#I[VOz#5rz{#6U{!c#5r!c!d#Iq!d;'S#5r;'S;=`#6u<%lO#5rV#ItVOz#5rz{#6U{!v#5r!v!w#JZ!w;'S#5r;'S;=`#6u<%lO#5rV#J^VOz#5rz{#6U{!c#5r!c!d#=v!d;'S#5r;'S;=`#6u<%lO#5rV#Jz]nS_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#Kx]_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#LtTOz#Lqz{#MT{;'S#Lq;'S;=`#Mr<%lO#LqR#MWVOz#Lqz{#MT{!P#Lq!P!Q#Mm!Q;'S#Lq;'S;=`#Mr<%lO#LqR#MrORRR#MuP;=`<%l#LqR#M}__QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{!P#Ks!P!Q#N|!Q;'S#Ks;'S;=`$ j<%lO#KsR$ TVRR_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PR$ mP;=`<%l#KsV$ u__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$!t!d;'S#4t;'S;=`#8o<%lO#4tV$!y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$#x!v;'S#4t;'S;=`#8o<%lO#4tV$#}__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$$|!h;'S#4t;'S;=`#8o<%lO#4tV$%R__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$&Q!o;'S#4t;'S;=`#8o<%lO#4tV$&V__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$'U!l;'S#4t;'S;=`#8o<%lO#4tV$'Z__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!p#4t!p!q$(Y!q;'S#4t;'S;=`#8o<%lO#4tV$(___QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$)^!h;'S#4t;'S;=`#8o<%lO#4tV$)c^_QOX#4tXY#>iYZ#?ZZ]#4t]^#5r^p#4tpq#>iqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV$*d__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!z#4t!z!{$+c!{;'S#4t;'S;=`#8o<%lO#4tV$+h__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$,g!h;'S#4t;'S;=`#8o<%lO#4tV$,l__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!e#4t!e!f$)^!f;'S#4t;'S;=`#8o<%lO#4tV$-p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$.o!l;'S#4t;'S;=`#8o<%lO#4tV$.t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!i#4t!i!j$/s!j;'S#4t;'S;=`#8o<%lO#4tV$/x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$0w!u;'S#4t;'S;=`#8o<%lO#4tV$0|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$1{!d;'S#4t;'S;=`#8o<%lO#4tV$2Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$(Y!w;'S#4t;'S;=`#8o<%lO#4tV$3U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!w#4t!w!x$4T!x;'S#4t;'S;=`#8o<%lO#4tV$4Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$5X!h;'S#4t;'S;=`#8o<%lO#4tV$5^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$6]!u;'S#4t;'S;=`#8o<%lO#4tV$6b__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!{#4t!{!|$)^!|;'S#4t;'S;=`#8o<%lO#4tV$7fa_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$8k!d!g#4t!g!h$:s!h;'S#4t;'S;=`#8o<%lO#4tV$8p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!d#4t!d!e$9o!e;'S#4t;'S;=`#8o<%lO#4tV$9t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$(Y!o;'S#4t;'S;=`#8o<%lO#4tV$:x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$;w!v;'S#4t;'S;=`#8o<%lO#4tV$;|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$<{!w;'S#4t;'S;=`#8o<%lO#4tV$=Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!f#4t!f!g$>P!g;'S#4t;'S;=`#8o<%lO#4tV$>U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$?T!d;'S#4t;'S;=`#8o<%lO#4tV$?Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$@X!w;'S#4t;'S;=`#8o<%lO#4tV$@^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$)^!d;'S#4t;'S;=`#8o<%lO#4tV$AdeVP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$A]![!c#Y!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#Y~$Bze_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$DdgW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!_#Y!_!`$E{!`!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$FQe_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$Gc![!c#Y!c!}$Gc!}#R#Y#R#S$Gc#S#T#Y#T#o$Gc#o;'S#Y;'S;=`'q<%lO#Y~$GjeW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$Gc![!c#Y!c!}$Gc!}#R#Y#R#S$Gc#S#T#Y#T#o$Gc#o;'S#Y;'S;=`'q<%lO#YV$IS]qP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$JQ__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$KP#h;'S#Y;'S;=`'q<%lO#YU$KU__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$LT#Y;'S#Y;'S;=`'q<%lO#YU$LY__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$MX#i;'S#Y;'S;=`'q<%lO#YU$M`]pQ_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
|
|
728
737
|
tokenizers: [
|
|
729
738
|
0,
|
|
730
739
|
1,
|
|
@@ -774,6 +783,17 @@ var SQLQuery = class {
|
|
|
774
783
|
allColumns;
|
|
775
784
|
/** Database-reported parameter types (variable name → SQL type), set by database adapters */
|
|
776
785
|
parameterTypes;
|
|
786
|
+
/** Explicit row-type name from `:result=Name` modifier, when present. */
|
|
787
|
+
resultTypeOverride;
|
|
788
|
+
/**
|
|
789
|
+
* Name of the postgres `source` this block targets, from a `:source=name`
|
|
790
|
+
* modifier on a BASELINE block. Such blocks define the schema of an external
|
|
791
|
+
* postgres source and run natively against that source's container during
|
|
792
|
+
* generation (not against the DuckDB introspection connection).
|
|
793
|
+
*/
|
|
794
|
+
sourceTarget;
|
|
795
|
+
/** Final row-type identifier assigned by assignResultTypeNames (after introspection). */
|
|
796
|
+
resultTypeName;
|
|
777
797
|
constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, isBatch, variables, config, line) {
|
|
778
798
|
this.filename = filename;
|
|
779
799
|
this.id = id;
|
|
@@ -887,6 +907,9 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
887
907
|
const isOne = modifiers.includes(":one");
|
|
888
908
|
const isPluck = modifiers.includes(":pluck");
|
|
889
909
|
const isBatch = modifiers.includes(":batch");
|
|
910
|
+
const resultTypeOverride = modifiers.find((m) => m.startsWith(":result="))?.slice(8);
|
|
911
|
+
const sourceTarget = modifiers.find((m) => m.startsWith(":source="))?.slice(8);
|
|
912
|
+
if (sourceTarget && queryType !== "BASELINE") throw SqgError.inQuery(`The ':source=' modifier is only valid on BASELINE blocks, not ${queryType}`, "SQL_PARSE_ERROR", name, filePath, { suggestion: "Move the schema for a postgres source into a '-- BASELINE <name> :source=<source>' block" });
|
|
890
913
|
let configStr = getStr("Config", true);
|
|
891
914
|
if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
|
|
892
915
|
let config = null;
|
|
@@ -1041,6 +1064,8 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
1041
1064
|
config
|
|
1042
1065
|
});
|
|
1043
1066
|
const query = new SQLQuery(filePath, name, sqlContentStr, sql.toSqlWithAnonymousPlaceholders(), sql.toSqlWithNamedPlaceholders(), sql.toSqlWithPositionalPlaceholders(), queryType, isOne, isPluck, isBatch, variables, config, annotationLine);
|
|
1067
|
+
query.resultTypeOverride = resultTypeOverride;
|
|
1068
|
+
query.sourceTarget = sourceTarget;
|
|
1044
1069
|
checkDuplicate(queryType, name);
|
|
1045
1070
|
queries.push(query);
|
|
1046
1071
|
consola.debug(`Added query: ${name} (${queryType})`);
|
|
@@ -1057,7 +1082,7 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
1057
1082
|
//#endregion
|
|
1058
1083
|
//#region src/db/types.ts
|
|
1059
1084
|
async function initializeDatabase(queries, execQueries, reporter) {
|
|
1060
|
-
const baselineQueries = queries.filter((q) => q.isBaseline);
|
|
1085
|
+
const baselineQueries = queries.filter((q) => q.isBaseline && !q.sourceTarget);
|
|
1061
1086
|
for (const query of baselineQueries) try {
|
|
1062
1087
|
await execQueries(query);
|
|
1063
1088
|
} catch (error) {
|
|
@@ -1113,9 +1138,17 @@ function convertType(type) {
|
|
|
1113
1138
|
const duckdb = new class {
|
|
1114
1139
|
db;
|
|
1115
1140
|
connection;
|
|
1116
|
-
async initializeDatabase(queries, reporter) {
|
|
1141
|
+
async initializeDatabase(queries, reporter, options) {
|
|
1117
1142
|
this.db = await DuckDBInstance.create(":memory:");
|
|
1118
1143
|
this.connection = await this.db.connect();
|
|
1144
|
+
for (const attachment of options?.attachments ?? []) {
|
|
1145
|
+
const sql = `ATTACH '${attachment.connectionUri}' AS ${attachment.alias} (TYPE postgres)`;
|
|
1146
|
+
try {
|
|
1147
|
+
await this.connection.run(sql);
|
|
1148
|
+
} catch (e) {
|
|
1149
|
+
throw new DatabaseError(`Failed to attach source '${attachment.alias}': ${e.message}`, "duckdb", "Check that the postgres source container started and is reachable.");
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1119
1152
|
await initializeDatabase(queries, async (query) => {
|
|
1120
1153
|
try {
|
|
1121
1154
|
await this.connection.run(query.rawQuery);
|
|
@@ -1325,6 +1358,7 @@ const postgres = new class {
|
|
|
1325
1358
|
mode;
|
|
1326
1359
|
dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
1327
1360
|
enumTypeCache = /* @__PURE__ */ new Map();
|
|
1361
|
+
tableOidCache = /* @__PURE__ */ new Map();
|
|
1328
1362
|
async startContainer(reporter) {
|
|
1329
1363
|
reporter?.onContainerStarting?.();
|
|
1330
1364
|
const container = await new PostgreSqlContainer("postgres:16-alpine").withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
@@ -1375,10 +1409,17 @@ const postgres = new class {
|
|
|
1375
1409
|
if (enumType) return enumType;
|
|
1376
1410
|
return this.getTypeName(dataTypeID);
|
|
1377
1411
|
}
|
|
1412
|
+
async resolveTableOids(db, oids) {
|
|
1413
|
+
const missing = oids.filter((oid) => !this.tableOidCache.has(oid));
|
|
1414
|
+
if (missing.length === 0) return;
|
|
1415
|
+
const res = await db.query("SELECT oid::int AS oid, relname FROM pg_class WHERE oid = ANY($1)", [missing]);
|
|
1416
|
+
for (const row of res.rows) this.tableOidCache.set(Number(row.oid), row.relname);
|
|
1417
|
+
}
|
|
1378
1418
|
async initializeDatabase(queries, reporter) {
|
|
1379
1419
|
const externalUrl = process.env.SQG_POSTGRES_URL;
|
|
1380
1420
|
this.dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
1381
1421
|
this.enumTypeCache = /* @__PURE__ */ new Map();
|
|
1422
|
+
this.tableOidCache = /* @__PURE__ */ new Map();
|
|
1382
1423
|
if (externalUrl) this.mode = new ExternalDbMode(externalUrl);
|
|
1383
1424
|
else {
|
|
1384
1425
|
const { connectionUri, container } = await this.startContainer(reporter);
|
|
@@ -1444,11 +1485,18 @@ const postgres = new class {
|
|
|
1444
1485
|
});
|
|
1445
1486
|
consola.debug("Columns:", columnNames);
|
|
1446
1487
|
consola.debug("Types:", columnTypes.map((t) => t.toString()));
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1488
|
+
const tableOids = Array.from(new Set(result.fields.map((f) => f.tableID).filter((oid) => oid && oid > 0)));
|
|
1489
|
+
await this.resolveTableOids(db, tableOids);
|
|
1490
|
+
query.columns = columnNames.map((name, index) => {
|
|
1491
|
+
const field = result.fields[index];
|
|
1492
|
+
const sourceTable = field.tableID && field.tableID > 0 ? this.tableOidCache.get(field.tableID) : void 0;
|
|
1493
|
+
return {
|
|
1494
|
+
name,
|
|
1495
|
+
type: columnTypes[index],
|
|
1496
|
+
nullable: true,
|
|
1497
|
+
sourceTable
|
|
1498
|
+
};
|
|
1499
|
+
});
|
|
1452
1500
|
}
|
|
1453
1501
|
if (query.isQuery) {
|
|
1454
1502
|
if (query.isOne) return result.rows[0] || null;
|
|
@@ -1486,6 +1534,7 @@ const postgres = new class {
|
|
|
1486
1534
|
await this.mode.close(this.db);
|
|
1487
1535
|
this.dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
1488
1536
|
this.enumTypeCache = /* @__PURE__ */ new Map();
|
|
1537
|
+
this.tableOidCache = /* @__PURE__ */ new Map();
|
|
1489
1538
|
}
|
|
1490
1539
|
}();
|
|
1491
1540
|
//#endregion
|
|
@@ -1596,7 +1645,8 @@ const sqlite = new class {
|
|
|
1596
1645
|
return {
|
|
1597
1646
|
name: col.name,
|
|
1598
1647
|
type: col.type || probed.type || "unknown",
|
|
1599
|
-
nullable: col.table ? colInfo?.pk === 0 && colInfo?.notnull === 0 : probed.nullable
|
|
1648
|
+
nullable: col.table ? colInfo?.pk === 0 && colInfo?.notnull === 0 : probed.nullable,
|
|
1649
|
+
sourceTable: col.table ?? void 0
|
|
1600
1650
|
};
|
|
1601
1651
|
});
|
|
1602
1652
|
}
|
|
@@ -1666,13 +1716,13 @@ var TypeMapper = class {
|
|
|
1666
1716
|
* @param path - Optional prefix path for nested type references
|
|
1667
1717
|
* @returns Generated type declaration code, or empty string for primitive types
|
|
1668
1718
|
*/
|
|
1669
|
-
getDeclarations(column, path = "") {
|
|
1670
|
-
if (column.type instanceof StructType) return this.generateStructDeclaration(column, path);
|
|
1719
|
+
getDeclarations(column, path = "", nameOverride) {
|
|
1720
|
+
if (column.type instanceof StructType) return this.generateStructDeclaration(column, path, nameOverride);
|
|
1671
1721
|
if (column.type instanceof ListType) return this.getDeclarations({
|
|
1672
1722
|
name: column.name,
|
|
1673
1723
|
type: column.type.baseType,
|
|
1674
1724
|
nullable: true
|
|
1675
|
-
}, path);
|
|
1725
|
+
}, path, nameOverride);
|
|
1676
1726
|
return "";
|
|
1677
1727
|
}
|
|
1678
1728
|
};
|
|
@@ -1829,9 +1879,9 @@ var JavaTypeMapper = class JavaTypeMapper extends TypeMapper {
|
|
|
1829
1879
|
formatStructTypeName(fieldName) {
|
|
1830
1880
|
return `${pascalCase(fieldName)}Result`;
|
|
1831
1881
|
}
|
|
1832
|
-
generateStructDeclaration(column, path = "") {
|
|
1882
|
+
generateStructDeclaration(column, path = "", nameOverride) {
|
|
1833
1883
|
if (!(column.type instanceof StructType)) throw new Error(`Expected StructType ${column}`);
|
|
1834
|
-
const structName = this.formatStructTypeName(column.name);
|
|
1884
|
+
const structName = nameOverride ?? this.formatStructTypeName(column.name);
|
|
1835
1885
|
const newPath = `${path}${structName}.`;
|
|
1836
1886
|
const children = column.type.fields.map((field) => {
|
|
1837
1887
|
return this.getDeclarations({
|
|
@@ -2025,7 +2075,7 @@ var TypeScriptTypeMapper = class extends TypeMapper {
|
|
|
2025
2075
|
formatMapTypeName(fieldName) {
|
|
2026
2076
|
return "Map";
|
|
2027
2077
|
}
|
|
2028
|
-
generateStructDeclaration(column) {
|
|
2078
|
+
generateStructDeclaration(column, _path, _nameOverride) {
|
|
2029
2079
|
if (!(column.type instanceof StructType)) throw new Error("Expected StructType");
|
|
2030
2080
|
return `interface ${this.formatStructTypeName(column.name)} {\n entries: {\n${column.type.fields.map((field) => {
|
|
2031
2081
|
const fieldType = this.getTypeName({
|
|
@@ -2160,7 +2210,7 @@ var PythonTypeMapper = class PythonTypeMapper extends TypeMapper {
|
|
|
2160
2210
|
formatMapTypeName(_fieldName) {
|
|
2161
2211
|
return "dict";
|
|
2162
2212
|
}
|
|
2163
|
-
generateStructDeclaration(column, path = "") {
|
|
2213
|
+
generateStructDeclaration(column, path = "", _nameOverride) {
|
|
2164
2214
|
if (!(column.type instanceof StructType)) throw new Error(`Expected StructType ${column}`);
|
|
2165
2215
|
const structName = this.formatStructTypeName(column.name);
|
|
2166
2216
|
const newPath = `${path}${structName}.`;
|
|
@@ -2231,7 +2281,7 @@ var BaseGenerator = class {
|
|
|
2231
2281
|
...query.columns[0],
|
|
2232
2282
|
name: query.id
|
|
2233
2283
|
});
|
|
2234
|
-
return this.getClassName(`${query.id}_Result`);
|
|
2284
|
+
return this.getClassName(query.resultTypeName ?? `${query.id}_Result`);
|
|
2235
2285
|
}
|
|
2236
2286
|
getStatement(q) {
|
|
2237
2287
|
return q.queryAnonymous;
|
|
@@ -2241,6 +2291,8 @@ var BaseGenerator = class {
|
|
|
2241
2291
|
//#region src/generators/java-generator.ts
|
|
2242
2292
|
var JavaGenerator = class extends BaseGenerator {
|
|
2243
2293
|
engine;
|
|
2294
|
+
/** Query ids that should emit their row-type record declaration (set per render). */
|
|
2295
|
+
declareTypeOwners = /* @__PURE__ */ new Set();
|
|
2244
2296
|
constructor(template, engine = "duckdb") {
|
|
2245
2297
|
super(template, new JavaTypeMapper());
|
|
2246
2298
|
this.template = template;
|
|
@@ -2291,7 +2343,21 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
2291
2343
|
if (readExpr !== null) return readExpr;
|
|
2292
2344
|
return this.typeMapper.parseValue(column, `rs.getObject(${idx})`, path);
|
|
2293
2345
|
}
|
|
2294
|
-
async beforeGenerate(_projectDir, _gen,
|
|
2346
|
+
async beforeGenerate(_projectDir, _gen, queries, tables) {
|
|
2347
|
+
this.declareTypeOwners = /* @__PURE__ */ new Set();
|
|
2348
|
+
const seenRowTypes = /* @__PURE__ */ new Set();
|
|
2349
|
+
const tableRowTypes = new Set(this.supportsAppenders(this.engine) ? tables.filter((t) => t.hasAppender).map((t) => this.getClassName(`${t.id}_row`)) : []);
|
|
2350
|
+
for (const q of queries) {
|
|
2351
|
+
if (q.skipGenerateFunction) continue;
|
|
2352
|
+
if (!q.isQuery || q.isPluck) continue;
|
|
2353
|
+
if (q.columns.length === 0) continue;
|
|
2354
|
+
const rowTypeName = this.rowType(q);
|
|
2355
|
+
if (tableRowTypes.has(rowTypeName)) continue;
|
|
2356
|
+
if (seenRowTypes.has(rowTypeName)) continue;
|
|
2357
|
+
seenRowTypes.add(rowTypeName);
|
|
2358
|
+
this.declareTypeOwners.add(q.id);
|
|
2359
|
+
}
|
|
2360
|
+
Handlebars.registerHelper("shouldDeclareType", (queryHelper) => this.declareTypeOwners.has(queryHelper.id));
|
|
2295
2361
|
Handlebars.registerHelper("isDuckDB", () => this.engine === "duckdb");
|
|
2296
2362
|
Handlebars.registerHelper("isPostgres", () => this.engine === "postgres");
|
|
2297
2363
|
Handlebars.registerHelper("pgBulkType", (column) => {
|
|
@@ -2314,7 +2380,9 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
2314
2380
|
...query.columns[0],
|
|
2315
2381
|
name: query.id
|
|
2316
2382
|
}, " ");
|
|
2317
|
-
|
|
2383
|
+
if (!this.declareTypeOwners.has(query.id)) return "";
|
|
2384
|
+
const structName = this.rowType(query);
|
|
2385
|
+
return queryHelper.typeMapper.getDeclarations(query.allColumns, "", structName);
|
|
2318
2386
|
});
|
|
2319
2387
|
Handlebars.registerHelper("appenderType", (column) => {
|
|
2320
2388
|
return this.typeMapper.getUnboxedTypeName(column);
|
|
@@ -2538,13 +2606,25 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
2538
2606
|
async beforeGenerate(projectDir, gen, queries, tables) {
|
|
2539
2607
|
const q = queries.filter((q) => q.isQuery && q.isOne || q.isMigrate);
|
|
2540
2608
|
const name = `${gen.name}-jdbc`;
|
|
2541
|
-
writeGeneratedFile(projectDir, {
|
|
2609
|
+
await writeGeneratedFile(projectDir, {
|
|
2542
2610
|
name,
|
|
2543
2611
|
generator: "java/duckdb/jdbc",
|
|
2544
2612
|
output: gen.output,
|
|
2545
2613
|
config: gen.config,
|
|
2546
2614
|
projectName: gen.projectName
|
|
2547
2615
|
}, this.javaGenerator, name, q, tables, "duckdb");
|
|
2616
|
+
const owners = /* @__PURE__ */ new Set();
|
|
2617
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2618
|
+
for (const q of queries) {
|
|
2619
|
+
if (q.skipGenerateFunction) continue;
|
|
2620
|
+
if (!q.isQuery || q.isOne) continue;
|
|
2621
|
+
if (q.columns.length === 0) continue;
|
|
2622
|
+
const rowTypeName = this.rowType(q);
|
|
2623
|
+
if (seen.has(rowTypeName)) continue;
|
|
2624
|
+
seen.add(rowTypeName);
|
|
2625
|
+
owners.add(q.id);
|
|
2626
|
+
}
|
|
2627
|
+
Handlebars.registerHelper("shouldDeclareType", (queryHelper) => owners.has(queryHelper.id));
|
|
2548
2628
|
}
|
|
2549
2629
|
isCompatibleWith(engine) {
|
|
2550
2630
|
return engine === "duckdb";
|
|
@@ -2606,7 +2686,7 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
2606
2686
|
}
|
|
2607
2687
|
rowType(query) {
|
|
2608
2688
|
if (query.isOne) return this.javaGenerator.rowType(query);
|
|
2609
|
-
return this.getClassName(`${query.id}_Result`);
|
|
2689
|
+
return this.getClassName(query.resultTypeName ?? `${query.id}_Result`);
|
|
2610
2690
|
}
|
|
2611
2691
|
};
|
|
2612
2692
|
//#endregion
|
|
@@ -2666,6 +2746,19 @@ var PythonGenerator = class extends BaseGenerator {
|
|
|
2666
2746
|
if (value.includes("\n") || value.includes("'") || value.includes("\"")) return `"""\\\n${value}"""`;
|
|
2667
2747
|
return `"${value}"`;
|
|
2668
2748
|
});
|
|
2749
|
+
Handlebars.registerHelper("sqlExpr", (queryHelper) => {
|
|
2750
|
+
const parts = queryHelper.sqlQueryParts;
|
|
2751
|
+
if (!parts.some((p) => typeof p !== "string" && p.name.startsWith("sources_"))) {
|
|
2752
|
+
const value = queryHelper.sqlQuery;
|
|
2753
|
+
if (value.includes("\n") || value.includes("'") || value.includes("\"")) return `"""\\\n${value}"""`;
|
|
2754
|
+
return `"${value}"`;
|
|
2755
|
+
}
|
|
2756
|
+
let out = "f\"\"\"";
|
|
2757
|
+
for (const part of parts) if (typeof part === "string") out += part.replace(/\\/g, "\\\\").replace(/\{/g, "{{").replace(/\}/g, "}}");
|
|
2758
|
+
else if (part.name.startsWith("sources_")) out += ` '{${part.name}}'`;
|
|
2759
|
+
else out += "?";
|
|
2760
|
+
return `${out}"""`;
|
|
2761
|
+
});
|
|
2669
2762
|
Handlebars.registerHelper("pyType", (column) => {
|
|
2670
2763
|
return this.getPyType(column);
|
|
2671
2764
|
});
|
|
@@ -2790,6 +2883,7 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
2790
2883
|
async beforeGenerate(_projectDir, gen, _queries, _tables) {
|
|
2791
2884
|
if (this.typeMapper instanceof TypeScriptTypeMapper) this.typeMapper.safeIntegers = !!gen.config?.safeIntegers;
|
|
2792
2885
|
Handlebars.registerHelper("quote", (value) => this.quote(value));
|
|
2886
|
+
Handlebars.registerHelper("sqlExpr", (queryHelper) => this.sqlExpr(queryHelper));
|
|
2793
2887
|
Handlebars.registerHelper("appendMethod", (column) => {
|
|
2794
2888
|
if (column.type instanceof ListType) return "List";
|
|
2795
2889
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
@@ -2942,6 +3036,22 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
2942
3036
|
quote(value) {
|
|
2943
3037
|
return value.includes("\n") || value.includes("'") ? `\`${value}\`` : `'${value}'`;
|
|
2944
3038
|
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Build the SQL expression for a query. Without file `sources`, this is the
|
|
3041
|
+
* plain quoted SQL string (unchanged). When a `${sources_x}` reference is
|
|
3042
|
+
* present it is interpolated into a template literal as a runtime argument
|
|
3043
|
+
* (the value is supplied by the caller, not bound), mirroring the Java
|
|
3044
|
+
* generator — `ATTACH '` + arg + `'` style.
|
|
3045
|
+
*/
|
|
3046
|
+
sqlExpr(queryHelper) {
|
|
3047
|
+
const parts = queryHelper.sqlQueryParts;
|
|
3048
|
+
if (!parts.some((p) => typeof p !== "string" && p.name.startsWith("sources_"))) return this.quote(queryHelper.sqlQuery);
|
|
3049
|
+
let out = "`";
|
|
3050
|
+
for (const part of parts) if (typeof part === "string") out += part.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
3051
|
+
else if (part.name.startsWith("sources_")) out += ` '\${${part.name}}'`;
|
|
3052
|
+
else out += "?";
|
|
3053
|
+
return `${out}\``;
|
|
3054
|
+
}
|
|
2945
3055
|
};
|
|
2946
3056
|
//#endregion
|
|
2947
3057
|
//#region src/generators/typescript-duckdb-generator.ts
|
|
@@ -3048,6 +3158,57 @@ function getGenerator(generator) {
|
|
|
3048
3158
|
}
|
|
3049
3159
|
}
|
|
3050
3160
|
//#endregion
|
|
3161
|
+
//#region src/sources.ts
|
|
3162
|
+
/**
|
|
3163
|
+
* For each `type: postgres` source: start a throwaway Postgres testcontainer,
|
|
3164
|
+
* run the source's schema (the BASELINE blocks tagged `:source=<name>`) natively
|
|
3165
|
+
* against it so introspection sees true Postgres types, and return the connection
|
|
3166
|
+
* info to ATTACH into DuckDB.
|
|
3167
|
+
*
|
|
3168
|
+
* The synthesized ATTACH (done by the DuckDB adapter) and these schema blocks are
|
|
3169
|
+
* not emitted into generated code — at runtime the application attaches the real
|
|
3170
|
+
* production database under the same alias.
|
|
3171
|
+
*/
|
|
3172
|
+
async function preparePostgresSources(sources, queries, reporter) {
|
|
3173
|
+
const containers = [];
|
|
3174
|
+
const attachments = [];
|
|
3175
|
+
const teardown = async () => {
|
|
3176
|
+
for (const container of containers) try {
|
|
3177
|
+
await container.stop();
|
|
3178
|
+
} catch {}
|
|
3179
|
+
};
|
|
3180
|
+
try {
|
|
3181
|
+
for (const source of sources) {
|
|
3182
|
+
reporter?.onContainerStarting?.();
|
|
3183
|
+
const container = await new PostgreSqlContainer(source.image).withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
3184
|
+
containers.push(container);
|
|
3185
|
+
const connectionUri = container.getConnectionUri();
|
|
3186
|
+
reporter?.onContainerStarted?.(connectionUri);
|
|
3187
|
+
const schemaBlocks = queries.filter((q) => q.isBaseline && q.sourceTarget === source.name);
|
|
3188
|
+
const client = new Client({ connectionString: connectionUri });
|
|
3189
|
+
await client.connect();
|
|
3190
|
+
try {
|
|
3191
|
+
for (const block of schemaBlocks) await client.query(block.rawQuery);
|
|
3192
|
+
} catch (e) {
|
|
3193
|
+
throw new DatabaseError(`Failed to apply schema for postgres source '${source.name}': ${e.message}`, "postgres", `Check that the BASELINE blocks tagged ':source=${source.name}' are valid PostgreSQL.`);
|
|
3194
|
+
} finally {
|
|
3195
|
+
await client.end();
|
|
3196
|
+
}
|
|
3197
|
+
attachments.push({
|
|
3198
|
+
alias: source.name,
|
|
3199
|
+
connectionUri
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
} catch (e) {
|
|
3203
|
+
await teardown();
|
|
3204
|
+
throw e;
|
|
3205
|
+
}
|
|
3206
|
+
return {
|
|
3207
|
+
attachments,
|
|
3208
|
+
teardown
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
//#endregion
|
|
3051
3212
|
//#region src/sqltool.ts
|
|
3052
3213
|
const GENERATED_FILE_COMMENT = "This file is generated by SQG (https://sqg.dev). Do not edit manually.";
|
|
3053
3214
|
const configSchema = z.object({ result: z.record(z.string(), z.string()).optional() });
|
|
@@ -3224,18 +3385,23 @@ var TableHelper = class {
|
|
|
3224
3385
|
return this.generator.typeMapper;
|
|
3225
3386
|
}
|
|
3226
3387
|
};
|
|
3227
|
-
function generateSourceFile(name, queries, tables, templatePath, generator, engine, projectName, config) {
|
|
3388
|
+
function generateSourceFile(name, queries, tables, templatePath, generator, engine, projectName, config, postgresSourceNames = []) {
|
|
3228
3389
|
const templateSrc = readFileSync(templatePath, "utf-8");
|
|
3229
3390
|
const template = Handlebars.compile(templateSrc);
|
|
3230
3391
|
Handlebars.registerHelper("mapType", (column) => generator.mapType(column));
|
|
3231
3392
|
Handlebars.registerHelper("plusOne", (value) => value + 1);
|
|
3232
3393
|
const migrations = queries.filter((q) => q.isMigrate).map((q) => new SqlQueryHelper(q, generator, generator.getStatement(q)));
|
|
3233
3394
|
const tableHelpers = generator.supportsAppenders(engine) ? tables.filter((t) => !t.skipGenerateFunction).map((t) => new TableHelper(t, generator)) : [];
|
|
3395
|
+
const attachers = engine === "duckdb" ? postgresSourceNames.map((sourceName) => ({
|
|
3396
|
+
alias: sourceName,
|
|
3397
|
+
functionName: generator.getFunctionName(`attach_${sourceName}`)
|
|
3398
|
+
})) : [];
|
|
3234
3399
|
return template({
|
|
3235
3400
|
generatedComment: GENERATED_FILE_COMMENT,
|
|
3236
3401
|
migrations,
|
|
3237
3402
|
queries: queries.map((q) => new SqlQueryHelper(q, generator, generator.getStatement(q))),
|
|
3238
3403
|
tables: tableHelpers,
|
|
3404
|
+
attachers,
|
|
3239
3405
|
className: generator.getClassName(name),
|
|
3240
3406
|
projectName,
|
|
3241
3407
|
config
|
|
@@ -3263,9 +3429,11 @@ const ProjectSchema = z.object({
|
|
|
3263
3429
|
})).min(1, "At least one generator is required").describe("Code generators to run")
|
|
3264
3430
|
})).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
3265
3431
|
sources: z.array(z.object({
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3432
|
+
type: z.enum(["file", "postgres"]).optional().describe("Source type: 'file' (default) or 'postgres'"),
|
|
3433
|
+
path: z.string().optional().describe("Path to source file for file sources (supports $HOME)"),
|
|
3434
|
+
name: z.string().optional().describe("Variable name (file sources) or DuckDB attach alias (postgres sources)"),
|
|
3435
|
+
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)")
|
|
3436
|
+
}).refine((s) => s.type === "postgres" ? !!s.name : !!s.path, { message: "file sources require 'path'; postgres sources require 'name'" })).optional().describe("External sources: files inlined as variables, or postgres databases attached for introspection")
|
|
3269
3437
|
});
|
|
3270
3438
|
var ExtraVariable = class {
|
|
3271
3439
|
constructor(name, value) {
|
|
@@ -3274,7 +3442,7 @@ var ExtraVariable = class {
|
|
|
3274
3442
|
}
|
|
3275
3443
|
};
|
|
3276
3444
|
function createExtraVariables(sources, suppressLogging = false) {
|
|
3277
|
-
return sources.map((source) => {
|
|
3445
|
+
return sources.filter((source) => source.type !== "postgres").map((source) => {
|
|
3278
3446
|
const path = source.path;
|
|
3279
3447
|
const resolvedPath = path.replace("$HOME", homedir());
|
|
3280
3448
|
const varName = `sources_${(source.name ?? basename(path, extname(resolvedPath))).replace(/\s+/g, "_")}`;
|
|
@@ -3282,6 +3450,13 @@ function createExtraVariables(sources, suppressLogging = false) {
|
|
|
3282
3450
|
return new ExtraVariable(varName, `'${resolvedPath}'`);
|
|
3283
3451
|
});
|
|
3284
3452
|
}
|
|
3453
|
+
/** Postgres sources resolved from the project config (with image defaulted). */
|
|
3454
|
+
function getPostgresSources(sources) {
|
|
3455
|
+
return sources.filter((source) => source.type === "postgres").map((source) => ({
|
|
3456
|
+
name: source.name,
|
|
3457
|
+
image: source.image ?? "postgres:16-alpine"
|
|
3458
|
+
}));
|
|
3459
|
+
}
|
|
3285
3460
|
function buildProjectFromCliOptions(options) {
|
|
3286
3461
|
if (!isValidGenerator(options.generator)) {
|
|
3287
3462
|
const similar = findSimilarGenerators(options.generator);
|
|
@@ -3358,6 +3533,106 @@ function getOutputPath(projectDir, sqlFileName, gen, generator) {
|
|
|
3358
3533
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
3359
3534
|
return outputPath;
|
|
3360
3535
|
}
|
|
3536
|
+
function assignResultTypeNames(queries, tables, projectDir = process.cwd()) {
|
|
3537
|
+
const hints = [];
|
|
3538
|
+
const tablesByName = /* @__PURE__ */ new Map();
|
|
3539
|
+
for (const t of tables) tablesByName.set(t.tableName, t);
|
|
3540
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3541
|
+
const eligible = queries.filter((q) => q.isQuery && !q.isPluck && !q.skipGenerateFunction && q.columns.length > 0);
|
|
3542
|
+
for (const q of eligible) {
|
|
3543
|
+
const cols = effectiveColumns(q);
|
|
3544
|
+
const key = JSON.stringify(cols.map((c) => ({
|
|
3545
|
+
n: c.name,
|
|
3546
|
+
t: c.type.toString(),
|
|
3547
|
+
nu: c.nullable,
|
|
3548
|
+
s: c.sourceTable ?? null
|
|
3549
|
+
})));
|
|
3550
|
+
let g = groups.get(key);
|
|
3551
|
+
if (!g) {
|
|
3552
|
+
g = { queries: [] };
|
|
3553
|
+
groups.set(key, g);
|
|
3554
|
+
}
|
|
3555
|
+
g.queries.push(q);
|
|
3556
|
+
}
|
|
3557
|
+
const recordShape = (cols) => JSON.stringify(cols.map((c) => ({
|
|
3558
|
+
n: c.name,
|
|
3559
|
+
t: c.type.toString(),
|
|
3560
|
+
nu: c.nullable
|
|
3561
|
+
})));
|
|
3562
|
+
const claims = /* @__PURE__ */ new Map();
|
|
3563
|
+
for (const t of tables) {
|
|
3564
|
+
if (!t.hasAppender) continue;
|
|
3565
|
+
const cols = t.columns.filter((c) => !c.generated);
|
|
3566
|
+
claims.set(pascalCase(`${t.id}_row`), {
|
|
3567
|
+
shape: recordShape(cols),
|
|
3568
|
+
offenderId: t.id,
|
|
3569
|
+
source: `TABLE ${t.tableName}`
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3572
|
+
const claim = (name, shape, representative, source) => {
|
|
3573
|
+
const canonical = pascalCase(name);
|
|
3574
|
+
const existing = claims.get(canonical);
|
|
3575
|
+
if (existing && existing.shape !== shape) throw SqgError.inQuery(`Result type '${name}' would be emitted with two incompatible shapes: ${source} ('${representative.id}') vs ${existing.source} ('${existing.offenderId}')`, "VALIDATION_ERROR", representative.id, representative.filename, { suggestion: "Use a different `:result=` name for one of the queries, or change the queries so their result columns match" });
|
|
3576
|
+
if (!existing) claims.set(canonical, {
|
|
3577
|
+
shape,
|
|
3578
|
+
offenderId: representative.id,
|
|
3579
|
+
source
|
|
3580
|
+
});
|
|
3581
|
+
};
|
|
3582
|
+
for (const group of groups.values()) {
|
|
3583
|
+
const overrides = group.queries.map((q) => q.resultTypeOverride).filter((v) => !!v);
|
|
3584
|
+
const uniqueOverrides = new Set(overrides);
|
|
3585
|
+
if (uniqueOverrides.size > 1) {
|
|
3586
|
+
const offenders = group.queries.filter((q) => q.resultTypeOverride);
|
|
3587
|
+
throw SqgError.inQuery(`Conflicting ':result=' names for queries with identical result shapes: ${offenders.map((q) => `'${q.id}' uses '${q.resultTypeOverride}'`).join(", ")}`, "VALIDATION_ERROR", offenders[0].id, offenders[0].filename, { suggestion: "Pick a single name for this row type or change the queries so their shapes differ" });
|
|
3588
|
+
}
|
|
3589
|
+
const shape = recordShape(effectiveColumns(group.queries[0]));
|
|
3590
|
+
if (uniqueOverrides.size === 1) {
|
|
3591
|
+
const resolved = [...uniqueOverrides][0];
|
|
3592
|
+
claim(resolved, shape, group.queries.find((q) => q.resultTypeOverride === resolved), `:result=${resolved}`);
|
|
3593
|
+
for (const q of group.queries) q.resultTypeName = resolved;
|
|
3594
|
+
continue;
|
|
3595
|
+
}
|
|
3596
|
+
const fullMatch = tryFullTableMatch(group.queries[0], tablesByName);
|
|
3597
|
+
if (fullMatch) {
|
|
3598
|
+
claim(fullMatch, shape, group.queries[0], "full-table match");
|
|
3599
|
+
for (const q of group.queries) q.resultTypeName = fullMatch;
|
|
3600
|
+
continue;
|
|
3601
|
+
}
|
|
3602
|
+
if (group.queries.length >= 2) {
|
|
3603
|
+
const list = group.queries.map((q) => `'${q.id}' (${displayPath(q.filename, projectDir)}:${q.line})`).join(", ");
|
|
3604
|
+
hints.push({
|
|
3605
|
+
queryIds: group.queries.map((q) => q.id),
|
|
3606
|
+
message: `${group.queries.length} queries return the same row shape: ${list}. Add \`:result=Name\` to any one of them to share a single row type.`
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
return hints;
|
|
3611
|
+
}
|
|
3612
|
+
/** Show paths relative to the project root when it's inside the tree; otherwise fall back to absolute. */
|
|
3613
|
+
function displayPath(fullPath, projectDir) {
|
|
3614
|
+
const rel = relative(projectDir, fullPath);
|
|
3615
|
+
return rel && !rel.startsWith("..") ? rel : fullPath;
|
|
3616
|
+
}
|
|
3617
|
+
/** Resolve config-overridden column info, mirroring what validateQueries does for allColumns. */
|
|
3618
|
+
function effectiveColumns(query) {
|
|
3619
|
+
return query.columns.map((col) => query.config?.getColumnInfo(col.name) ?? col);
|
|
3620
|
+
}
|
|
3621
|
+
function tryFullTableMatch(query, tablesByName) {
|
|
3622
|
+
const cols = effectiveColumns(query);
|
|
3623
|
+
const firstSource = cols[0]?.sourceTable;
|
|
3624
|
+
if (!firstSource) return void 0;
|
|
3625
|
+
if (!cols.every((c) => c.sourceTable === firstSource)) return void 0;
|
|
3626
|
+
const table = tablesByName.get(firstSource);
|
|
3627
|
+
if (!table) return void 0;
|
|
3628
|
+
if (table.columns.some((c) => c.generated)) return void 0;
|
|
3629
|
+
if (table.columns.length !== cols.length) return void 0;
|
|
3630
|
+
for (let i = 0; i < cols.length; i++) {
|
|
3631
|
+
if (cols[i].name !== table.columns[i].name) return void 0;
|
|
3632
|
+
if (cols[i].type.toString() !== table.columns[i].type.toString()) return void 0;
|
|
3633
|
+
}
|
|
3634
|
+
return `${table.id}_row`;
|
|
3635
|
+
}
|
|
3361
3636
|
function validateQueries(queries) {
|
|
3362
3637
|
for (const query of queries) {
|
|
3363
3638
|
if (query.isQuery && query.isPluck && query.columns.length !== 1) throw SqgError.inQuery(`':pluck' modifier requires exactly 1 column, but query has ${query.columns.length} columns`, "VALIDATION_ERROR", query.id, query.filename, { suggestion: query.columns.length === 0 ? "Ensure the query returns at least one column" : `Remove ':pluck' or select only one column. Current columns: ${query.columns.map((c) => c.name).join(", ")}` });
|
|
@@ -3373,14 +3648,14 @@ function validateQueries(queries) {
|
|
|
3373
3648
|
};
|
|
3374
3649
|
}
|
|
3375
3650
|
}
|
|
3376
|
-
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine, writeToStdout = false) {
|
|
3651
|
+
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine, writeToStdout = false, postgresSourceNames = []) {
|
|
3377
3652
|
await generator.beforeGenerate(projectDir, gen, queries, tables);
|
|
3378
3653
|
const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
|
|
3379
3654
|
const name = gen.name ?? basename(file, extname(file));
|
|
3380
3655
|
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name, {
|
|
3381
3656
|
migrations: true,
|
|
3382
3657
|
...gen.config
|
|
3383
|
-
});
|
|
3658
|
+
}, postgresSourceNames);
|
|
3384
3659
|
if (writeToStdout) {
|
|
3385
3660
|
process.stdout.write(sourceFile);
|
|
3386
3661
|
if (!sourceFile.endsWith("\n")) process.stdout.write("\n");
|
|
@@ -3481,6 +3756,11 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3481
3756
|
const results = [];
|
|
3482
3757
|
try {
|
|
3483
3758
|
const extraVariables = createExtraVariables(project.sources ?? [], writeToStdout);
|
|
3759
|
+
const pgSources = getPostgresSources(project.sources ?? []);
|
|
3760
|
+
const pgSourceNames = new Set(pgSources.map((s) => s.name));
|
|
3761
|
+
if (pgSources.length > 0) {
|
|
3762
|
+
if (!project.sql.some((sql) => sql.gen.some((gen) => getGeneratorEngine(gen.generator) === "duckdb"))) throw new SqgError("Postgres sources require a DuckDB generator (they are attached into DuckDB for introspection)", "CONFIG_VALIDATION_ERROR", "Add a 'typescript/duckdb' or 'java/duckdb' generator, or remove the postgres sources");
|
|
3763
|
+
}
|
|
3484
3764
|
const files = [];
|
|
3485
3765
|
for (const sql of project.sql) {
|
|
3486
3766
|
const gensByEngine = /* @__PURE__ */ new Map();
|
|
@@ -3498,6 +3778,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3498
3778
|
const parseResult = parseSQLQueries(fullPath, extraVariables);
|
|
3499
3779
|
queries = parseResult.queries;
|
|
3500
3780
|
tables = parseResult.tables;
|
|
3781
|
+
for (const q of queries) if (q.sourceTarget && !pgSourceNames.has(q.sourceTarget)) throw SqgError.inQuery(`BASELINE block targets unknown postgres source '${q.sourceTarget}'`, "CONFIG_VALIDATION_ERROR", q.id, sqlFile, { suggestion: `Declare a source with 'type: postgres, name: ${q.sourceTarget}' in sqg.yaml` });
|
|
3501
3782
|
} catch (e) {
|
|
3502
3783
|
if (e instanceof SqgError) throw e;
|
|
3503
3784
|
throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
|
|
@@ -3506,15 +3787,25 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3506
3787
|
const executableQueries = queries.filter((q) => !q.skipGenerateFunction);
|
|
3507
3788
|
const reporterAny = reporter;
|
|
3508
3789
|
if (reporterAny?.setQueryTotal) reporterAny.setQueryTotal(executableQueries.length);
|
|
3790
|
+
let preparedSources;
|
|
3509
3791
|
try {
|
|
3510
3792
|
const dbEngine = getDatabaseEngine(engine);
|
|
3793
|
+
let attachments;
|
|
3794
|
+
if (engine === "duckdb" && pgSources.length > 0) {
|
|
3795
|
+
ui?.startPhase("Starting postgres source containers...");
|
|
3796
|
+
preparedSources = await preparePostgresSources(pgSources, queries, reporter);
|
|
3797
|
+
attachments = preparedSources.attachments;
|
|
3798
|
+
}
|
|
3511
3799
|
ui?.startPhase(`Initializing ${engine} database...`);
|
|
3512
|
-
await dbEngine.initializeDatabase(queries, reporter);
|
|
3800
|
+
await dbEngine.initializeDatabase(queries, reporter, { attachments });
|
|
3513
3801
|
ui?.startPhase(`Introspecting ${executableQueries.length} queries...`);
|
|
3514
3802
|
await dbEngine.executeQueries(queries, reporter);
|
|
3515
3803
|
if (tables.length > 0) await dbEngine.introspectTables(tables, reporter);
|
|
3516
3804
|
ui?.succeedPhase(`Introspected ${executableQueries.length} queries`);
|
|
3517
3805
|
validateQueries(queries);
|
|
3806
|
+
const hints = assignResultTypeNames(queries, tables, projectDir);
|
|
3807
|
+
for (const hint of hints) if (ui) ui.hint(hint.message);
|
|
3808
|
+
else consola.info(hint.message);
|
|
3518
3809
|
await dbEngine.close();
|
|
3519
3810
|
} catch (e) {
|
|
3520
3811
|
ui?.failPhase(`Failed to process ${sqlFile}`);
|
|
@@ -3523,6 +3814,8 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3523
3814
|
file: sqlFile,
|
|
3524
3815
|
engine
|
|
3525
3816
|
});
|
|
3817
|
+
} finally {
|
|
3818
|
+
if (preparedSources) await preparedSources.teardown();
|
|
3526
3819
|
}
|
|
3527
3820
|
ui?.startPhase("Generating code...");
|
|
3528
3821
|
const genStart = performance.now();
|
|
@@ -3531,7 +3824,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3531
3824
|
const outputPath = await writeGeneratedFile(projectDir, {
|
|
3532
3825
|
...gen,
|
|
3533
3826
|
projectName: project.name
|
|
3534
|
-
}, generator, sqlFile, queries, tables, engine, writeToStdout);
|
|
3827
|
+
}, generator, sqlFile, queries, tables, engine, writeToStdout, pgSources.map((s) => s.name));
|
|
3535
3828
|
if (outputPath !== null) {
|
|
3536
3829
|
files.push(outputPath);
|
|
3537
3830
|
results.push({
|
|
@@ -3565,7 +3858,7 @@ async function processProject(projectPath, ui) {
|
|
|
3565
3858
|
//#region src/mcp-server.ts
|
|
3566
3859
|
const server = new Server({
|
|
3567
3860
|
name: "sqg-mcp",
|
|
3568
|
-
version: process.env.npm_package_version ?? "0.
|
|
3861
|
+
version: process.env.npm_package_version ?? "0.21.0"
|
|
3569
3862
|
}, { capabilities: {
|
|
3570
3863
|
tools: {},
|
|
3571
3864
|
resources: {}
|
|
@@ -3987,6 +4280,12 @@ var UI = class {
|
|
|
3987
4280
|
this.log("");
|
|
3988
4281
|
this.log(` ${pc.green("done")} ${pc.dim(`in ${formatMs(totalMs)}`)}`);
|
|
3989
4282
|
}
|
|
4283
|
+
/** Display an info-level hint (e.g. "these queries could share a row type") */
|
|
4284
|
+
hint(message) {
|
|
4285
|
+
if (this.silent) return;
|
|
4286
|
+
this.stopSpinner();
|
|
4287
|
+
this.log(` ${pc.cyan(pc.bold("hint"))} ${message}`);
|
|
4288
|
+
}
|
|
3990
4289
|
/** Display a formatted error */
|
|
3991
4290
|
error(err) {
|
|
3992
4291
|
if (this.silent) return;
|
|
@@ -4030,7 +4329,7 @@ function formatMs(ms) {
|
|
|
4030
4329
|
}
|
|
4031
4330
|
//#endregion
|
|
4032
4331
|
//#region src/sqg.ts
|
|
4033
|
-
const version = process.env.npm_package_version ?? "0.
|
|
4332
|
+
const version = process.env.npm_package_version ?? "0.21.0";
|
|
4034
4333
|
updateNotifier({ pkg: {
|
|
4035
4334
|
name: "@sqg/sqg",
|
|
4036
4335
|
version
|