@lakeql/cli 0.6.2 → 0.7.1
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/cli.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./pull-metadata-
|
|
2
|
+
import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c,u as l}from"./pull-metadata-B5KY-Pkr.mjs";import{Command as u}from"@commander-js/extra-typings";import d from"node:path";import{error as f,info as p,success as m,warning as h}from"@lakeql/logger/console";import{readPackage as g}from"read-pkg";import{existsSync as _}from"node:fs";import{mkdir as ee,readFile as v,readdir as te,rm as ne,writeFile as y}from"node:fs/promises";import{confirm as b,multiselect as re,question as ie,select as x,validators as ae}from"@topcli/prompts";import{loadConfig as S}from"c12";import{generateCode as C}from"@lakeql/file-generator";import{generateConfig as oe}from"@lakeql/file-generator/config";import{generateInterface as se}from"@lakeql/file-generator/interface";import{generateQuerySchema as ce}from"@lakeql/file-generator/query-schema";import{generateModel as le}from"@lakeql/schema-generator/graphql-schema";import{generateJsonSchemaFromFields as ue}from"@lakeql/schema-generator/json-schema";import{generateConfigReqistry as w}from"@lakeql/file-generator/config-registry";import{globby as de}from"globby";import{camelCase as T,upperFirst as E}from"lodash-es";import D from"typescript";import{endpointDefinitionSchema as fe,findDuplicateFieldNames as pe}from"@lakeql/schema-generator/endpoint-schema";import{TrinoClient as O}from"@lakeql/trino-client";import{ClimtTable as k}from"climt";import{createEnv as me}from"@t3-oss/env-core";import{ZodError as he,enum as ge,number as _e,string as A,z as j}from"zod/v4";import{Listr as ve}from"listr2";import{parseColumns as ye}from"@lakeql/column-parser";import{convertTrinoResponse as be}from"@lakeql/response-transformer";var M=class extends Error{code;hint;details;exitCode;constructor(e,t={}){super(e,t.cause?{cause:t.cause}:void 0),this.name=`CliError`,this.code=t.code,this.hint=t.hint,this.details=t.details,this.exitCode=t.exitCode??1}};function N(e=`Aborted by user.`){return new M(e,{code:`CLI_ABORTED`,exitCode:0})}function xe(e){if(typeof e!=`object`||!e)return!1;let t=e,n=`${t.message??``} ${t.cause?.message??``}`.toLowerCase();return t.name===`CancelError`||t.code===`ABORT_ERR`||n.includes(`cancelled`)||n.includes(`canceled`)||n.includes(`aborted`)||n.includes(`interrupted`)}function P(e,t,n){return new M(`Failed to ${e}.`,{code:`TRINO_REQUEST_FAILED`,hint:`Verify HIVE_HOST/HIVE_PORT, credentials and network reachability to Trino.`,details:[`Context: ${t}`],cause:n})}const Se={sourcePath:`.`},Ce=`lakeql.config.json`;function we(e){if(typeof e!=`object`||!e)return!1;let t=e,n=`${t.message??``} ${t.cause?.message??``}`;return t.code===`ERR_IMPORT_ATTRIBUTE_MISSING`||t.cause?.code===`ERR_IMPORT_ATTRIBUTE_MISSING`||n.includes(`needs an import attribute of "type: json"`)}function Te(e){return{...Se,...e}}async function Ee(e){let t=await v(d.join(e,Ce),`utf-8`);return Te(JSON.parse(t))}async function De(){let e=r();try{let{config:t}=await S({name:`lakeql`,cwd:e,defaults:Se,packageJson:!1,globalRc:!1,rcFile:!1,dotenv:!1});return Te(t)}catch(t){let n=d.join(e,Ce);if(we(t)&&_(n))return Ee(e);throw new M(`Failed to load LakeQL config: ${t instanceof Error?t.message:typeof t==`string`?t:`Unknown error`}`,{code:`LAKEQL_CONFIG_LOAD_FAILED`,hint:`Check lakeql.config.{ts,mjs,js,json} syntax and ensure exported values are valid.`,details:[`Context: config loading from ${e}`,`Expected files: lakeql.config.ts, lakeql.config.mjs, lakeql.config.js, lakeql.config.json`],cause:t})}}async function F(e){let t=await De(),n=r(),i=e??t.sourcePath;return d.isAbsolute(i)?i:d.resolve(n,i)}function Oe(){return s().action(async({sourcePath:e})=>{await I(e!==process.env.INIT_CWD&&e!==process.cwd()?e:void 0)})}async function I(e){let t=await F(e),n=await C({fileName:`config-registry.ts`,nodes:w({configPaths:(await de(`schemas/**/config.ts`,{cwd:t,onlyFiles:!0})).map(e=>{let t=d.parse(e);return d.join(t.dir,t.name)})})});_(d.join(t,`config-registry.ts`))&&await ne(d.join(t,`config-registry.ts`),{force:!0,recursive:!0}),await y(d.join(t,`config-registry.ts`),n.text)}function ke(e,t){return{baseClassName:`${E(T(e))}_${E(T(t))}`,queryName:`${T(e)}${E(T(t))}`,mutationName:`create${T(e)}${E(T(t))}`}}const{factory:L}=D;function R(e){return L.createIdentifier(e)}function z(e){return R(e)}function B(e){return L.createStringLiteral(e)}function V(e,t){return L.createPropertyAssignment(R(e),t)}function H(e,t=!0){return L.createObjectLiteralExpression(e,t)}function Ae(e){return e?L.createTrue():L.createFalse()}function je(e){return L.createParameterDeclaration(void 0,void 0,R(e))}function Me(e,t,n){return L.createArrowFunction(n?.async?[L.createToken(D.SyntaxKind.AsyncKeyword)]:void 0,void 0,e.map(e=>typeof e==`string`?je(e):e),void 0,L.createToken(D.SyntaxKind.EqualsGreaterThanToken),t)}function Ne(e){return L.createParenthesizedExpression(e)}function Pe(e,t=!0){return Me([`t`],Ne(H(e,t)))}function U(e,t){return L.createPropertyAccessExpression(typeof e==`string`?R(e):e,R(t))}function Fe(e,t=[],n){return L.createCallExpression(e,n,t)}function W(e,t,n=[],r){return Fe(U(e,t),n,r)}function Ie(e,t=[],n){return W(`builder`,e,t,n)}function Le(e,t){return L.createVariableStatement(void 0,L.createVariableDeclarationList([L.createVariableDeclaration(R(e),void 0,void 0,t)],D.NodeFlags.Const))}function G(e,t,n){return L.createImportDeclaration(void 0,L.createImportClause(n?.typeOnly??!1,void 0,L.createNamedImports(t.map(e=>L.createImportSpecifier(!1,void 0,R(e))))),B(e))}function Re(e,t,n){return L.createImportDeclaration(void 0,L.createImportClause(n?.typeOnly??!1,R(t),void 0),B(e))}function ze({models:e,mutationName:t,mutationConfig:n,hasValidations:r,fieldDefinitions:i}){if(!n)return[];let a=Object.values(e).find(e=>e.root);if(!a)return[];let o=Be(i),s=Ve(i),c=Object.values(e).filter(e=>!e.root);return[...He(r),...c.map(t=>Ue(t,e,o,s)),Ue(a,e,o,s),qe(t,a,r)]}function Be(e){let t=new Map;if(!e)return t;for(let n of e){let e=n.options?.required===!0;t.set(n.name,e)}return t}function Ve(e){let t=new Set;if(!e)return t;for(let n of e)n.options?.readOnly===!0&&t.add(n.name);return t}function He(e){let t=[G(`@lakeql/adapters`,[`executeWritePipeline`]),G(`@lakeql/trino-client`,[`TrinoClient`]),G(`@lakeql/api/builder`,[`builder`]),G(`~/env`,[`env`]),G(`./config`,[`hiveConfig`,`storageConfig`]),Re(`./json-schema.json`,`jsonSchema`)];return e&&t.push(G(`./validations`,[`validationSchema`])),t}function Ue(e,t,n,r){let i=`${e.modelName}Input`,a=Object.values(e.fields).filter(e=>!r.has(e.name)).map(e=>We(e,t,n));return Le(i,Ie(`inputType`,[B(i),H([V(`fields`,Pe(a))])]))}function We(e,t,n){let r=V(`required`,Ae(n.get(e.name)??!0));if(e.isArray){let t=e.graphqlType.replace(/^\[/u,``).replace(/\]$/u,``),n=Ke(t);if(n)return V(e.name,W(`t`,n,[H([r],!1)]));let i=`${t}Input`;return V(e.name,W(`t`,`field`,[H([V(`type`,D.factory.createArrayLiteralExpression([z(i)])),r],!1)]))}let i=Ge(e.graphqlType);if(i)return V(e.name,W(`t`,i,[H([r],!1)]));if(Object.keys(t).includes(e.graphqlType)){let t=`${e.graphqlType}Input`;return V(e.name,W(`t`,`field`,[H([V(`type`,z(t)),r],!1)]))}return V(e.name,W(`t`,`string`,[H([r],!1)]))}function Ge(e){switch(e){case`String`:return`string`;case`Int`:return`int`;case`Float`:return`float`;case`Boolean`:return`boolean`;default:return}}function Ke(e){switch(e){case`String`:return`stringList`;case`Int`:return`intList`;case`Float`:return`floatList`;case`Boolean`:return`booleanList`;default:return}}function qe(e,t,n){let r=`${t.modelName}Input`,i=[];n&&i.push(D.factory.createExpressionStatement(W(`validationSchema`,`parse`,[z(`input`)]))),i.push(D.factory.createVariableStatement(void 0,D.factory.createVariableDeclarationList([D.factory.createVariableDeclaration(z(`trinoClient`),void 0,void 0,D.factory.createNewExpression(z(`TrinoClient`),void 0,[H([V(`host`,U(`env`,`HIVE_HOST`)),V(`port`,U(`env`,`HIVE_PORT`)),V(`auth`,H([V(`type`,B(`basic`)),V(`username`,U(`env`,`HIVE_USERNAME`)),V(`password`,U(`env`,`HIVE_PASSWORD`))],!1)),V(`catalog`,U(`env`,`HIVE_CATALOG`)),V(`source`,U(`env`,`HIVE_SOURCE`))])]))],D.NodeFlags.Const)));let a=D.factory.createAwaitExpression(Fe(z(`executeWritePipeline`),[H([V(`records`,z(`input`)),V(`jsonSchema`,D.factory.createAsExpression(z(`jsonSchema`),D.factory.createKeywordTypeNode(D.SyntaxKind.AnyKeyword))),V(`config`,H([V(`loadStrategy`,U(`storageConfig`,`loadStrategy`)),V(`bucket`,U(`storageConfig`,`bucket`)),V(`basePath`,U(`storageConfig`,`basePath`)),V(`table`,H([V(`catalog`,U(`hiveConfig`,`catalog`)),V(`schema`,U(`hiveConfig`,`schema`)),V(`tableName`,U(`hiveConfig`,`tableName`))],!1)),V(`trinoClient`,z(`trinoClient`))]))])]));i.push(D.factory.createExpressionStatement(a)),i.push(D.factory.createReturnStatement(D.factory.createTrue()));let o=D.factory.createBlock(i,!0),s=Me([je(`_root`),D.factory.createParameterDeclaration(void 0,void 0,D.factory.createObjectBindingPattern([D.factory.createBindingElement(void 0,void 0,D.factory.createIdentifier(`input`))]))],o,{async:!0}),c=V(e,W(`t`,`boolean`,[H([V(`args`,H([V(`input`,W(`t`,`arg`,[H([V(`type`,z(r)),V(`required`,Ae(!0))],!1)]))],!1)),V(`resolve`,s)])]));return D.factory.createExpressionStatement(Ie(`mutationFields`,[Pe([c])]))}function K(e){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(K);let t={},n=Object.keys(e).toSorted();for(let r of n)t[r]=K(e[r]);return t}function Je(e){let t=K(e);return`${JSON.stringify(t,null,2).replaceAll(`\r
|
|
3
3
|
`,`
|
|
4
|
-
`)}\n`}async function
|
|
5
|
-
`)}function
|
|
6
|
-
`);throw new
|
|
7
|
-
`);throw new
|
|
8
|
-
`)
|
|
9
|
-
`);
|
|
4
|
+
`)}\n`}async function Ye(e){let{definition:t,outputDir:n,skipRegistry:r,sourcePathOverride:i}=e,{catalog:a,schema:o,tableName:s,fields:c}=t,{baseClassName:l,queryName:u,mutationName:f}=ke(o,s),p=ue(c),m=le({isRoot:!0,models:{},name:l,source:p}),h=Object.values(m).find(e=>e.root===!0),g=h?.fields?Object.values(h.fields).filter(e=>e.filter===!0).map(e=>({name:e.name,type:e.graphqlType})):[],v=[...new Set(g.map(e=>e.type))],te=[...new Set(Object.values(m).filter(e=>e.transformFields.length>0).flatMap(e=>e.transformFields))],b=[...new Set(Object.values(m).filter(e=>e.dateTimeFields.length>0).flatMap(e=>e.dateTimeFields))],re=await C({fileName:`config.ts`,nodes:oe({catalog:a,queryName:u,schema:o,tableName:s,mutationName:[f],storageConfig:t.mutation?{loadStrategy:t.mutation.loadStrategy,type:t.mutation.type,bucket:t.mutation.bucket,basePath:t.mutation.basePath,region:t.mutation.region,endpoint:t.mutation.endpoint,partitioning:t.mutation.loadStrategy===`full_load`?void 0:t.mutation.partitioning,partitioningFormat:t.mutation.loadStrategy===`full_load`?void 0:t.mutation.partitioningFormat}:void 0})}),ie=await C({fileName:`interface.ts`,nodes:se(m)}),x=await C({fileName:`query-schema.ts`,nodes:ce({dateTimeFields:b,filterFields:g,filterTypes:v,models:m,queryName:u,transformFields:te})}),ae=c.some(e=>e.options?.validations&&e.options.validations.length>0),S=ze({models:m,mutationName:f,mutationConfig:t.mutation,hasValidations:ae,fieldDefinitions:c}),w=``;S.length>0&&(w=(await C({fileName:`mutation-schema.ts`,nodes:S})).text);let de=Je(p),T=Je(t),E=[{fileName:`config.ts`,content:re.text},{fileName:`interface.ts`,content:ie.text},{fileName:`query-schema.ts`,content:x.text},{fileName:`json-schema.json`,content:de},{fileName:`endpoint.json`,content:T}];w&&E.push({fileName:`mutation-schema.ts`,content:w}),_(n)&&await ne(n,{force:!0,recursive:!0}),await ee(n,{recursive:!0});for(let e of E)await y(d.join(n,e.fileName),e.content);return r||await I(i),{files:E,outputDir:n}}function q(e,t=0){let n=[],r=` `.repeat(t+1);for(let i of e)if(i.type===`Object`)n.push(`${r}${i.name}: Object`),i.fields&&n.push(q(i.fields,t+1));else if(i.type===`Array`){let e=i.items?.type??`Unknown`;n.push(`${r}${i.name}: Array<${e}>`),i.items?.type===`Object`&&i.items.fields&&n.push(q(i.items.fields,t+1))}else n.push(`${r}${i.name}: ${i.type}`);return n.filter(e=>e.length>0).join(`
|
|
5
|
+
`)}function Xe(){return t().action(async e=>{let{fromFile:t,sourcePath:n,skipRegistry:r,force:i}=e,a=d.isAbsolute(t)?t:d.resolve(process.cwd(),t);if(!_(a))throw new M(`File not found: ${a}`,{code:`ENDPOINT_FILE_NOT_FOUND`});let o;try{o=await v(a,`utf-8`)}catch(e){throw new M(`Cannot read file: ${a}: ${e instanceof Error?e.message:String(e)}`,{code:`ENDPOINT_FILE_READ_FAILED`,cause:e})}let s;try{s=JSON.parse(o)}catch(e){throw new M(`Invalid JSON in ${a}: ${e instanceof Error?e.message:String(e)}`,{code:`ENDPOINT_INVALID_JSON`,cause:e})}let c=fe.safeParse(s);if(!c.success){let e=c.error.issues.map(e=>` - ${e.path.length>0?e.path.join(`.`):`root`}: ${e.message}`).join(`
|
|
6
|
+
`);throw new M(`Validation failed for ${a}.`,{code:`ENDPOINT_VALIDATION_FAILED`,details:[e]})}let l=c.data,u=pe(l.fields);if(u.length>0){let e=u.map(e=>{let t=e.path.length>0?e.path.join(`.`):`root`;return` - Duplicate field "${e.name}" at level: ${t}`}).join(`
|
|
7
|
+
`);throw new M(`Validation failed for ${a}.`,{code:`ENDPOINT_DUPLICATE_FIELDS`,details:[e]})}console.log(`\n${p(`Loaded definition summary:`)}`),console.log(p(`tableName: ${l.tableName}`)),console.log(p(`catalog: ${l.catalog}`)),console.log(p(`schema: ${l.schema}`));let f=l.mutation?l.mutation.loadStrategy:`disabled`;console.log(p(`mutation: ${f}`)),console.log(`\n${p(`Fields:`)}`),console.log(p(q(l.fields))),console.log(``);let h=await F(n===process.cwd()?void 0:n),g=d.join(h,`schemas/custom`,l.catalog,l.schema,l.tableName);if(_(g)&&!i&&!await b(`Directory "${g}" already exists. Overwrite?`))throw N(`Aborted: endpoint generation was not confirmed.`);await Ye({definition:l,outputDir:g,skipRegistry:r,sourcePathOverride:n===process.cwd()?void 0:n}),console.log(m(`Endpoint generated successfully at: ${g}`))})}async function J(e){try{return(await te(e,{withFileTypes:!0})).filter(e=>e.isDirectory()).map(e=>e.name).toSorted()}catch{return[]}}async function Ze(e){let t=d.join(e,`schemas`,`generated`);if(!_(t))throw new M(`No generated schemas found at "${t}".`,{code:`GENERATE_IMPORT_CONFIG_NO_SCHEMAS`,hint:"Run `lakeql-cli pull` first to generate schemas.",exitCode:1});let n=await J(t);if(n.length===0)throw new M(`No catalogs found in "${t}".`,{code:`GENERATE_IMPORT_CONFIG_EMPTY`,hint:"Run `lakeql-cli pull` first to generate schemas.",exitCode:1});return(await Promise.all(n.map(async e=>{let n=d.join(t,e),r=await J(n);return(await Promise.all(r.map(async t=>{let r=await J(d.join(n,t));return r.length>0?{catalog:e,schema:t,tables:r}:null}))).filter(e=>e!==null)}))).flat()}function Qe(e){let t=[`/** @type {import('@lakeql/cli').BulkPullConfig} */`,`export default [`];for(let n of e){let e=JSON.stringify(n.tables,null,2).split(`
|
|
8
|
+
`).map((e,t)=>t===0?e:` ${e}`).join(`
|
|
9
|
+
`);t.push(` {`),t.push(` catalog: ${JSON.stringify(n.catalog)},`),t.push(` schema: ${JSON.stringify(n.schema)},`),t.push(` tables: ${e},`),t.push(` },`)}return t.push(`]`),t.push(``),t.join(`
|
|
10
|
+
`)}function $e(){let e=a();return e.action(async e=>{let{sourcePath:t,output:n,force:i}=e,a=Qe(await Ze(await F(t===(process.env.INIT_CWD??process.cwd())?void 0:t))),o=d.isAbsolute(n)?n:d.resolve(r(),n);if(!i&&_(o)&&(console.log(h(`File "${o}" already exists.`)),!await b(`Overwrite "${o}"?`)))throw N(`Aborted: import config generation was not confirmed.`);await ee(d.dirname(o),{recursive:!0}),await y(o,a,`utf-8`),console.log(m(`Written to "${o}".`))}),e}let et;function tt(){return me({runtimeEnv:process.env,server:{HIVE_CATALOG:A().min(1),HIVE_HOST:A(),HIVE_PASSWORD:A().min(1),HIVE_PORT:A().transform(e=>Number.parseInt(e,10)).pipe(_e()),HIVE_SOURCE:A().optional(),HIVE_USERNAME:A().min(1),LOG_LEVEL:ge([`info`,`warn`,`error`,`debug`]).default(`warn`)}})}function Y(){return et||=tt(),et}function nt(){let t=e();return t.action(async({catalog:e,schema:t,table:n})=>{let r=Y(),i=e??r.HIVE_CATALOG,a=new O({auth:{password:r.HIVE_PASSWORD,type:`basic`,username:r.HIVE_USERNAME},catalog:i,host:r.HIVE_HOST,port:r.HIVE_PORT}),o=t;if(o===void 0){let e;try{e=await a.schemas({catalog:i})}catch(e){throw P(`list schemas`,`list-columns (catalog=${i})`,e)}o=await x(`Choose a schema from the ${i} catalog`,{autocomplete:!0,choices:e})}let s=n;if(s===void 0){let e;try{e=await a.tables({catalog:i,schema:o})}catch(e){throw P(`list tables`,`list-columns (catalog=${i}, schema=${o})`,e)}s=await x(`Choose a table from "${i}.${o}"`,{autocomplete:!0,choices:e})}let c;try{c=await a.columns({catalog:i,schema:o,table:s})}catch(e){throw P(`list columns`,`list-columns (catalog=${i}, schema=${o}, table=${s})`,e)}let l=c.map(([e,t,n,r])=>({description:r,extra:n,name:e,type:t})),u=new k;u.column(`Column Name`,`name`),u.column(`Type`,`type`),u.column(`Extra`,`extra`),u.column(`Description`,`description`),u.render(l)}),t}function rt(){let e=n();return e.action(async({catalog:e})=>{let t=Y(),n=e??t.HIVE_CATALOG,r=new O({auth:{password:t.HIVE_PASSWORD,type:`basic`,username:t.HIVE_USERNAME},catalog:n,host:t.HIVE_HOST,port:t.HIVE_PORT}),i;try{i=await r.schemas({catalog:n})}catch(e){throw P(`list schemas`,`list-schemas (catalog=${n})`,e)}let a=new k;a.column(`Schema Name`,`s`),a.render(i.map(e=>({s:e})))}),e}function it(){let e=o();return e.action(async({catalog:e,schema:t})=>{let n=Y(),r=e??n.HIVE_CATALOG,i=new O({auth:{password:n.HIVE_PASSWORD,type:`basic`,username:n.HIVE_USERNAME},catalog:r,host:n.HIVE_HOST,port:n.HIVE_PORT}),a=t;if(a===void 0){let e;try{e=await i.schemas({catalog:r})}catch(e){throw P(`list schemas`,`list-tables (catalog=${r})`,e)}a=await x(`Choose a schema from the ${r} catalog`,{autocomplete:!0,choices:e})}let o;try{o=await i.tables({catalog:r,schema:a})}catch(e){throw P(`list tables`,`list-tables (catalog=${r}, schema=${a})`,e)}let s=new k;s.column(`Table Name`,`t`),s.render(o.map(e=>({t:e})))}),e}function at(){let e=i();return e.action(async({catalog:e,schema:t})=>{let n=Y(),r=e??n.HIVE_CATALOG,i=new O({auth:{password:n.HIVE_PASSWORD,type:`basic`,username:n.HIVE_USERNAME},catalog:r,host:n.HIVE_HOST,port:n.HIVE_PORT}),a=t;if(a===void 0){let e;try{e=await i.schemas({catalog:r})}catch(e){throw P(`list schemas`,`list-views (catalog=${r})`,e)}a=await x(`Choose a schema from the ${r} catalog`,{choices:e})}let o;try{o=await i.views({catalog:r,schema:a})}catch(e){throw P(`list views`,`list-views (catalog=${r}, schema=${a})`,e)}let s=new k;s.column(`View Name`,`v`),s.render(o.map(e=>({v:e})))}),e}const ot=j.object({schema:j.string().min(1),catalog:j.string().min(1).optional(),tables:j.array(j.string().min(1)).optional(),views:j.array(j.string().min(1)).optional()}).superRefine((e,t)=>{let n=Array.isArray(e.tables)&&e.tables.length>0,r=Array.isArray(e.views)&&e.views.length>0;!n&&!r&&t.addIssue({code:`custom`,message:`At least one non-empty list is required: tables or views.`,path:[`tables`]})}),st=j.array(ot);function X(e){return st.parse(e)}function ct(e){switch(e.replaceAll(/[^a-zA-Z]/gu,``)){case`varchar`:return`String`;case`decimal`:case`double`:case`float`:return`Float`;case`integer`:case`bigint`:return`Integer`;case`boolean`:return`Boolean`;case`timestamp`:return`DateTime`;case`date`:return`Date`;default:return`String`}}function lt(e,t){return typeof t==`string`?{name:e,type:ct(t)}:Array.isArray(t)?{name:e,type:`Array`,items:ut(t)}:typeof t==`object`&&t?{name:e,type:`Object`,fields:Z(t)}:{name:e,type:`String`}}function ut(e){if(e.length===0)return{type:`String`};let[t]=e;return typeof t==`string`?{type:ct(t)}:typeof t==`object`&&t&&!Array.isArray(t)?{type:`Object`,fields:Z(t)}:{type:`String`}}function Z(e){let t=[];for(let[n,r]of Object.entries(e))t.push(lt(n,r));return t}function dt(e){let{tableName:t,catalog:n,schema:r,parsedColumns:i}=e;return{version:`1.0`,tableName:t,catalog:n,schema:r,fields:Z(i),mutation:!1}}async function Q(e){let{trinoClient:t,catalog:n,schema:r,tables:i,resolvedTargetPath:a,skipRegistry:o,sourcePathOverride:s}=e;for(let e of i)await Ye({definition:dt({tableName:e,catalog:n,schema:r,parsedColumns:ye((await t.columns({catalog:n,schema:r,table:e})).map(e=>be({keys:[`name`,`type`,`extra`,`description`],values:e})))}),outputDir:d.join(a,`schemas/generated`,n,r,e),skipRegistry:o,sourcePathOverride:s})}async function ft(e){let t=r(),n;if(e){let r=d.isAbsolute(e)?e:d.resolve(t,e),i=d.parse(r);n=d.join(i.dir,i.name)}let{config:i}=await S({name:`import`,cwd:t,configFile:n,defaults:{default:[]},packageJson:!1,globalRc:!1,rcFile:!1,dotenv:!1}),a=i;return Array.isArray(a)?X(a):a&&typeof a==`object`&&`default`in a&&Array.isArray(a.default)?X(a.default):X([])}async function pt(e){let{configPath:t,catalog:n,sourcePathOverride:r,skipRegistry:i}=e,a=Y(),o=await F(r),s;try{s=await ft(t)}catch(e){throw e instanceof he?new M(`Invalid bulk pull config.`,{code:`BULK_CONFIG_INVALID`,details:e.issues.map(e=>` - ${e.path.length>0?e.path.join(`.`):`root`}: ${e.message}`),hint:`Ensure each entry defines a schema and at least one non-empty list: tables or views.`,cause:e}):e}if(s.length===0){console.log(p(`No entries found in bulk config.`));return}await new ve([{title:`Pull data`,task:(e,t)=>t.newListr(s.map(e=>{let t=n??e.catalog??a.HIVE_CATALOG,i=(e.tables?.length??0)+(e.views?.length??0);return{title:`${t}/${e.schema} — ${i} item(s)`,task:async(n,s)=>{let c=new O({auth:{password:a.HIVE_PASSWORD,type:`basic`,username:a.HIVE_USERNAME},catalog:t,host:a.HIVE_HOST,port:a.HIVE_PORT});if(e.tables&&e.tables.length>0){s.output=`Pulling ${e.tables.length} table(s)...`;try{await Q({trinoClient:c,catalog:t,schema:e.schema,tables:e.tables,resolvedTargetPath:o,skipRegistry:!0,sourcePathOverride:r})}catch(n){throw P(`pull tables`,`bulk pull (catalog=${t}, schema=${e.schema}, tables=${e.tables.join(`,`)})`,n)}}if(e.views&&e.views.length>0){s.output=`Pulling ${e.views.length} view(s)...`;try{await Q({trinoClient:c,catalog:t,schema:e.schema,tables:e.views,resolvedTargetPath:o,skipRegistry:!0,sourcePathOverride:r})}catch(n){throw P(`pull views`,`bulk pull (catalog=${t}, schema=${e.schema}, views=${e.views.join(`,`)})`,n)}}s.title=`${t}/${e.schema} — ${i} item(s) pulled`}}}),{concurrent:!0,exitOnError:!1})},{title:`Create registry`,enabled:!i,task:async()=>{await I(r)}}],{concurrent:!1,exitOnError:!0}).run()}async function mt(e,t,n){try{return await n()}catch(n){throw P(e,t,n)}}function ht(){let e=c();return e.action(async e=>{let{skipRegistry:t,sourcePath:n,bulk:r,bulkConfig:i}=e,a=n===(process.env.INIT_CWD??process.cwd())?void 0:n;if(r){await pt({configPath:i,catalog:e.catalog,sourcePathOverride:a,skipRegistry:t});return}let o=Y(),s=e.catalog??o.HIVE_CATALOG,{schema:c,table:l,type:u}=e,d=await F(a),f=new O({auth:{password:o.HIVE_PASSWORD,type:`basic`,username:o.HIVE_USERNAME},catalog:s,host:o.HIVE_HOST,port:o.HIVE_PORT});if(!c){let e=await mt(`list schemas`,`pull (catalog=${s})`,()=>f.schemas({catalog:s}));c=await x(`Choose a schema from the ${s} catalog`,{autocomplete:!0,choices:e})}if(l.length===0&&(u??=await x(`What do you want to see from ${s}/${c}`,{choices:[{label:`Show tables`,value:`tables`},{label:`Show views`,value:`views`}]})),l.length===0){u??=`tables`;let e=[];if(e=await mt(u===`views`?`list views`:`list tables`,`pull (catalog=${s}, schema=${c})`,()=>u===`views`?f.views({catalog:s,schema:c}):f.tables({catalog:s,schema:c})),e.length===0)throw new M(`No ${u} found in schema '${s}.${c}'.`,{code:`PULL_NO_RESULTS`,hint:`Use a different schema or check permissions for '${s}.${c}'.`,exitCode:0});l=await re(`Choose the ${u} to pull`,{autocomplete:!0,choices:e,validators:[ae.required()]})}console.log(p(`Pulling ${l.length} item(s) from ${s}.${c} into ${d}/schemas/generated...`));let h=l.length>10;await new ve([{title:`Pull ${l.length} item(s)`,task:async(e,t)=>{if(!h)return t.newListr(l.map(e=>({title:`${s}.${c}.${e}`,task:async()=>{await Q({trinoClient:f,catalog:s,schema:c,tables:[e],resolvedTargetPath:d,skipRegistry:!0,sourcePathOverride:a})}})),{concurrent:!1,exitOnError:!0});let n=[...l],r=new Set,i=0,o=Math.min(8,l.length),u=()=>{let e=[...r].slice(0,5).map(e=>` - ${s}.${c}.${e}`).join(`
|
|
11
|
+
`),n=r.size>5?`\n ... +${r.size-5} more active`:``;t.output=`Completed ${i}/${l.length} | Active ${r.size}/${o}${e?`\n${e}${n}`:``}`};u();let p=async()=>{for(;n.length>0;){let e=n.shift();if(!e)return;r.add(e),u(),await Q({trinoClient:f,catalog:s,schema:c,tables:[e],resolvedTargetPath:d,skipRegistry:!0,sourcePathOverride:a}),r.delete(e),i+=1,u()}};await Promise.all(Array.from({length:o},()=>p())),t.output=`Completed ${i}/${l.length}`}},{title:`Create registry`,enabled:!t,task:async()=>{await I(a)}}],{concurrent:!1,exitOnError:!0}).run(),console.log(m(`Pull completed: ${l.length} item(s) generated under ${d}/schemas/generated/${s}/${c}`))}),e}const gt=[`lakeql.config.mjs`,`lakeql.config.ts`,`lakeql.config.json`];function _t(e){return`/** @type {import('@lakeql/cli').LakeQLConfig} */\nexport default {\n${Object.entries(e).map(([e,t])=>` ${e}: ${JSON.stringify(t)},`).join(`
|
|
12
|
+
`)}\n}\n`}function vt(e){for(let t of gt)if(_(d.join(e,t)))return t;return null}function yt(){return l().action(async()=>{let e=r(),t=vt(e);if(t&&await x(`${t} already exists. Overwrite?`,{choices:[{label:`Yes`,value:`yes`},{label:`No`,value:`no`}]})===`no`){console.log(p(`Aborted.`));return}let n=await x(`Config format:`,{choices:[{label:`lakeql.config.mjs (recommended)`,value:`mjs`},{label:`lakeql.config.json`,value:`json`}]}),i=_(d.join(e,`src`)),a;if(i)a=`src`,console.log(p(`Detected src/ directory - generated code will be placed in src/`));else{let e=await x(`Where should generated code be placed?`,{choices:[{label:`./ (project root)`,value:`.`},{label:`Custom path`,value:`__custom__`}]});a=e,e===`__custom__`&&(a=await ie(`Enter the source path (relative to project root):`,{defaultValue:`.`}))}let o={sourcePath:a},s=n===`mjs`?`lakeql.config.mjs`:`lakeql.config.json`,c=d.join(e,s);await y(c,n===`mjs`?_t(o):`${JSON.stringify(o,null,2)}\n`),console.log(m(`Created ${s} at ${c}`))})}function bt(e){let t=new u;return t.configureHelp({sortSubcommands:!0}),t.name(`lakeql-cli`).description(`LakeQL CLI`).version(e.version).addCommand(rt()).addCommand(it()).addCommand(at()).addCommand(nt()).addCommand(ht()).addCommand(Xe()).addCommand(Oe()).addCommand($e()).addCommand(yt()),t}async function xt(e=process.argv.slice(2),t){let n=bt(t??await g());return e.length===0?(n.outputHelp(),0):(await n.parseAsync(e,{from:`user`}),0)}function $(e){if(!(typeof e!=`object`||!e))return e}function St(e){let t=$(e),n=t?.message;for(;t?.cause;){let e=$(t.cause);if(!e)break;e.message&&(n=e.message),t=e}return n}function Ct(e){if(e instanceof M&&e.code)return e.code;let t=$(e);return t?.code?t.code:$(t?.cause)?.code}function wt(e){if(e instanceof M&&e.hint)return e.hint;let t=St(e)?.toLowerCase()??``,n=Ct(e);if(t.includes(`fetch failed`)||n===`ECONNREFUSED`||n===`ENOTFOUND`||n===`ETIMEDOUT`)return`Could not reach Trino. Verify HIVE_HOST/HIVE_PORT and that the Trino endpoint is reachable from your network.`;if(t.includes(`invalid environment variables`)||t.includes(`required`))return`Missing or invalid environment variables. Set HIVE_HOST, HIVE_PORT, HIVE_USERNAME, HIVE_PASSWORD and HIVE_CATALOG.`}function Tt(e){let t=[e instanceof M&&e.exitCode===0?`LakeQL CLI aborted.`:`LakeQL CLI failed.`],n=e instanceof Error?e.message:typeof e==`string`?e:`Unknown error`;t.push(`Reason: ${n}`),e instanceof M&&e.details?.length&&t.push(...e.details);let r=St(e);r&&r!==n&&t.push(`Root cause: ${r}`);let i=Ct(e);i&&t.push(`Error code: ${i}`);let a=wt(e);return a&&t.push(`Hint: ${a}`),t}xt().catch(e=>{let t=xe(e)?N():e,n=Tt(t),r=t instanceof M&&t.exitCode===0?h:f,i=n.map((e,t)=>t===0?r(e):p(e)).join(`
|
|
13
|
+
`);console.error(i);let a=t instanceof M?t.exitCode:1;process.exit(a)});export{};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/commands-metadata.d.ts
|
|
2
|
-
type AvailableCommand = "create-registry" | "create-endpoint" | "init" | "list-columns" | "list-schemas" | "list-tables" | "list-views" | "pull";
|
|
2
|
+
type AvailableCommand = "create-registry" | "create-endpoint" | "generate-import-config" | "init" | "list-columns" | "list-schemas" | "list-tables" | "list-views" | "pull";
|
|
3
3
|
interface CommandArgumentMeta {
|
|
4
4
|
name: string;
|
|
5
5
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Command as e,Option as t}from"@commander-js/extra-typings";function
|
|
1
|
+
import{Command as e,Option as t}from"@commander-js/extra-typings";import n from"node:path";function r(){return new e(`init`).description(`Initialize a lakeql config file`)}function i(){return process.env.INIT_CWD??process.cwd()}function a(){return new t(`--catalog <catalog>`,`catalog to use`).env(`HIVE_CATALOG`)}function o(){return new t(`--schema <schema>`,`schema to use`)}function s(){return new t(`--table <table>`,`table to use`)}function c(){return new t(`--type <type>`,`Show tables or views`)}function l(){return new t(`--source-path <path>`,"Base path for generated code (resolved from the command invocation directory). Files are created in `schemas/generated|custom` inside this path.").default(i(),`command invocation directory`)}function u(){return new t(`--skip-registry`,`Skip config registry generation`).default(!1)}function d(){return new e(`create-endpoint`).description(`Create a custom endpoint from a JSON definition file`).addOption(new t(`--from-file <path>`,`Path to a JSON definition file conforming to the Endpoint_Definition_Format`).makeOptionMandatory(!0)).addOption(l()).addOption(u()).addOption(new t(`--force`,`Overwrite existing files without prompting`).default(!1))}function f(){return new e(`create-registry`).description("Generates the config registry to ensure the type-safety while using `createPermission`").addOption(l())}function p(){return new e(`generate-import-config`).description(`Generate an import.config.mjs from already-pulled schemas in schemas/generated`).addOption(l()).option(`--output <path>`,`Output file path for the generated config`,n.join(i(),`import.config.mjs`)).option(`--force`,`Overwrite existing config without confirmation`,!1)}function m(){return new e(`list-columns`).description(`Lists the columns for the specified table`).addOption(a()).addOption(o().makeOptionMandatory()).addOption(s().makeOptionMandatory())}function h(){return new e(`list-schemas`).description(`Lists the available schemas for the configured catalog`).addOption(a())}function g(){return new e(`list-tables`).description(`Lists the available tables for the configured catalog/schema`).addOption(a()).addOption(o())}function _(){return new e(`list-views`).description(`Lists the available views for the configured catalog/schema`).addOption(a()).addOption(o())}const v=new t(`--bulk`,`Run in bulk mode using a config file`).default(!1),y=new t(`--bulk-config <path>`,`Path to the bulk import config file (default: import.config.{mjs,ts,js,json})`);function b(){return new e(`pull`).description(`Interactive query endpoint generation based on a remote table`).addOption(a()).addOption(c()).addOption(o().makeOptionMandatory(!1)).addOption(s().makeOptionMandatory(!1).default([]).argParser((e,t)=>[...t,e])).addOption(new t(`--skip-registry`,`Skip registry update`).default(!1)).addOption(l()).addOption(v).addOption(y)}function x(e){let t={name:e.name(),description:e.description,required:e.required,variadic:e.variadic};return e.defaultValue!==void 0&&(t.defaultValue=e.defaultValue),e.defaultValueDescription&&(t.defaultValueDescription=e.defaultValueDescription),t}function S(e){let t={flags:e.flags,description:e.description,required:e.mandatory,isBoolean:!e.required&&!e.optional};return e.defaultValue!==void 0&&!e.defaultValueDescription&&(t.defaultValue=e.defaultValue),e.defaultValueDescription&&(t.defaultValueDescription=e.defaultValueDescription),e.envVar&&(t.envVar=e.envVar),e.long&&(t.long=e.long),e.short&&(t.short=e.short),t}const C={"create-registry":f,"create-endpoint":d,"generate-import-config":p,init:r,"list-columns":m,"list-schemas":h,"list-tables":g,"list-views":_,pull:b};function w(e){let t=C[e];if(!t)throw Error(`No command named '${e}'`);let n=t();return{name:n.name(),description:n.description(),arguments:n.registeredArguments.map(x),options:n.options.map(S)}}const T=Object.keys(C);export{T as availableCommands,w as getCommandConfig};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s,u as c}from"./pull-metadata-B5KY-Pkr.mjs";function l(e){let t={name:e.name(),description:e.description,required:e.required,variadic:e.variadic};return e.defaultValue!==void 0&&(t.defaultValue=e.defaultValue),e.defaultValueDescription&&(t.defaultValueDescription=e.defaultValueDescription),t}function u(e){let t={flags:e.flags,description:e.description,required:e.mandatory,isBoolean:!e.required&&!e.optional};return e.defaultValue!==void 0&&!e.defaultValueDescription&&(t.defaultValue=e.defaultValue),e.defaultValueDescription&&(t.defaultValueDescription=e.defaultValueDescription),e.envVar&&(t.envVar=e.envVar),e.long&&(t.long=e.long),e.short&&(t.short=e.short),t}const d={"create-registry":o,"create-endpoint":t,"generate-import-config":i,init:c,"list-columns":e,"list-schemas":n,"list-tables":a,"list-views":r,pull:s};function f(e){let t=d[e];if(!t)throw Error(`No command named '${e}'`);let n=t();return{name:n.name(),description:n.description(),arguments:n.registeredArguments.map(l),options:n.options.map(u)}}export{f as getCommandConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Command as e,Option as t}from"@commander-js/extra-typings";import n from"node:path";function r(){return new e(`init`).description(`Initialize a lakeql config file`)}function i(){return process.env.INIT_CWD??process.cwd()}function a(){return new t(`--catalog <catalog>`,`catalog to use`).env(`HIVE_CATALOG`)}function o(){return new t(`--schema <schema>`,`schema to use`)}function s(){return new t(`--table <table>`,`table to use`)}function c(){return new t(`--type <type>`,`Show tables or views`)}function l(){return new t(`--source-path <path>`,"Base path for generated code (resolved from the command invocation directory). Files are created in `schemas/generated|custom` inside this path.").default(i(),`command invocation directory`)}function u(){return new t(`--skip-registry`,`Skip config registry generation`).default(!1)}function d(){return new e(`create-endpoint`).description(`Create a custom endpoint from a JSON definition file`).addOption(new t(`--from-file <path>`,`Path to a JSON definition file conforming to the Endpoint_Definition_Format`).makeOptionMandatory(!0)).addOption(l()).addOption(u()).addOption(new t(`--force`,`Overwrite existing files without prompting`).default(!1))}function f(){return new e(`create-registry`).description("Generates the config registry to ensure the type-safety while using `createPermission`").addOption(l())}function p(){return new e(`generate-import-config`).description(`Generate an import.config.mjs from already-pulled schemas in schemas/generated`).addOption(l()).option(`--output <path>`,`Output file path for the generated config`,n.join(i(),`import.config.mjs`)).option(`--force`,`Overwrite existing config without confirmation`,!1)}function m(){return new e(`list-columns`).description(`Lists the columns for the specified table`).addOption(a()).addOption(o().makeOptionMandatory()).addOption(s().makeOptionMandatory())}function h(){return new e(`list-schemas`).description(`Lists the available schemas for the configured catalog`).addOption(a())}function g(){return new e(`list-tables`).description(`Lists the available tables for the configured catalog/schema`).addOption(a()).addOption(o())}function _(){return new e(`list-views`).description(`Lists the available views for the configured catalog/schema`).addOption(a()).addOption(o())}const v=new t(`--bulk`,`Run in bulk mode using a config file`).default(!1),y=new t(`--bulk-config <path>`,`Path to the bulk import config file (default: import.config.{mjs,ts,js,json})`);function b(){return new e(`pull`).description(`Interactive query endpoint generation based on a remote table`).addOption(a()).addOption(c()).addOption(o().makeOptionMandatory(!1)).addOption(s().makeOptionMandatory(!1).default([]).argParser((e,t)=>[...t,e])).addOption(new t(`--skip-registry`,`Skip registry update`).default(!1)).addOption(l()).addOption(v).addOption(y)}export{m as a,d as c,h as i,i as l,_ as n,p as o,g as r,f as s,b as t,r as u};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakeql/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "CLI for introspecting Trino schemas and generating type-safe GraphQL endpoints",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -62,18 +62,19 @@
|
|
|
62
62
|
"lodash-es": "4.18.1",
|
|
63
63
|
"read-pkg": "10.1.0",
|
|
64
64
|
"zod": "4.4.3",
|
|
65
|
+
"@lakeql/file-generator": "0.1.9",
|
|
65
66
|
"@lakeql/column-parser": "0.1.4",
|
|
66
67
|
"@lakeql/helpers": "0.1.4",
|
|
67
|
-
"@lakeql/file-generator": "0.1.9",
|
|
68
68
|
"@lakeql/logger": "0.2.1",
|
|
69
|
-
"@lakeql/response-transformer": "0.1.4",
|
|
70
69
|
"@lakeql/schema-generator": "0.4.3",
|
|
71
|
-
"@lakeql/trino-client": "0.4.0"
|
|
70
|
+
"@lakeql/trino-client": "0.4.0",
|
|
71
|
+
"@lakeql/response-transformer": "0.1.4"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@types/lodash-es": "4.17.12",
|
|
75
75
|
"@types/node": "24.13.2",
|
|
76
76
|
"fast-check": "^4.8.0",
|
|
77
|
+
"memfs": "^4.57.7",
|
|
77
78
|
"tsdown": "0.22.3",
|
|
78
79
|
"tsx": "4.22.4",
|
|
79
80
|
"typescript": "6.0.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Command as e,Option as t}from"@commander-js/extra-typings";import"node:path";function n(){return new e(`init`).description(`Initialize a lakeql config file`)}function r(){return process.env.INIT_CWD??process.cwd()}function i(){return new t(`--catalog <catalog>`,`catalog to use`).env(`HIVE_CATALOG`)}function a(){return new t(`--schema <schema>`,`schema to use`)}function o(){return new t(`--table <table>`,`table to use`)}function s(){return new t(`--type <type>`,`Show tables or views`)}function c(){return new t(`--source-path <path>`,"Base path for generated code (resolved from the command invocation directory). Files are created in `schemas/generated|custom` inside this path.").default(r(),`command invocation directory`)}function l(){return new t(`--skip-registry`,`Skip config registry generation`).default(!1)}function u(){return new e(`create-endpoint`).description(`Create a custom endpoint from a JSON definition file`).addOption(new t(`--from-file <path>`,`Path to a JSON definition file conforming to the Endpoint_Definition_Format`).makeOptionMandatory(!0)).addOption(c()).addOption(l()).addOption(new t(`--force`,`Overwrite existing files without prompting`).default(!1))}function d(){return new e(`create-registry`).description("Generates the config registry to ensure the type-safety while using `createPermission`").addOption(c())}function f(){return new e(`list-columns`).description(`Lists the columns for the specified table`).addOption(i()).addOption(a().makeOptionMandatory()).addOption(o().makeOptionMandatory())}function p(){return new e(`list-schemas`).description(`Lists the available schemas for the configured catalog`).addOption(i())}function m(){return new e(`list-tables`).description(`Lists the available tables for the configured catalog/schema`).addOption(i()).addOption(a())}function h(){return new e(`list-views`).description(`Lists the available views for the configured catalog/schema`).addOption(i()).addOption(a())}const g=new t(`--bulk`,`Run in bulk mode using a config file`).default(!1),_=new t(`--bulk-config <path>`,`Path to the bulk import config file (default: import.config.{mjs,ts,js,json})`);function v(){return new e(`pull`).description(`Interactive query endpoint generation based on a remote table`).addOption(i()).addOption(s()).addOption(a().makeOptionMandatory(!1)).addOption(o().makeOptionMandatory(!1).default([]).argParser((e,t)=>[...t,e])).addOption(new t(`--skip-registry`,`Skip registry update`).default(!1)).addOption(c()).addOption(g).addOption(_)}export{f as a,r as c,p as i,n as l,h as n,d as o,m as r,u as s,v as t};
|