@lakeql/cli 0.4.0 → 0.4.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 +1 -1
- package/package.json +8 -8
package/dist/cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import{Command as e,Option as t}from"@commander-js/extra-typings";import{readPackage as n}from"read-pkg";import{existsSync as r}from"node:fs";import{mkdir as i,readFile as a,rm as o,writeFile as s}from"node:fs/promises";import c from"node:path";import{confirm as l,multiselect as u,question as d,select as f,validators as p}from"@topcli/prompts";import{loadConfig as m}from"c12";import{generateCode as h}from"@lakeql/file-generator";import{generateConfig as g}from"@lakeql/file-generator/config";import{generateInterface as _}from"@lakeql/file-generator/interface";import{generateQuerySchema as ee}from"@lakeql/file-generator/query-schema";import{generateModel as te}from"@lakeql/schema-generator/graphql-schema";import{generateJsonSchemaFromFields as ne}from"@lakeql/schema-generator/json-schema";import{generateConfigReqistry as v}from"@lakeql/file-generator/config-registry";import{globby as y}from"globby";import{camelCase as b,upperFirst as x}from"lodash-es";import S from"typescript";import{endpointDefinitionSchema as C,findDuplicateFieldNames as re}from"@lakeql/schema-generator/endpoint-schema";import{TrinoClient as w}from"@lakeql/trino-client";import{ClimtTable as T}from"climt";import{createEnv as E}from"@t3-oss/env-core";import D from"dotenv";import{enum as O,number as ie,string as k}from"zod/v4";import{error as A}from"@lakeql/logger/console";import{Listr as ae}from"listr2";import{parseColumns as oe}from"@lakeql/column-parser";import{convertTrinoResponse as j}from"@lakeql/response-transformer";import M from"kleur";function N(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(N(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(N(i.items.fields,t+1))}else n.push(`${r}${i.name}: ${i.type}`);return n.filter(e=>e.length>0).join(`
|
|
3
3
|
`)}function P(){return process.env.INIT_CWD??process.cwd()}const se={sourcePath:`.`};async function ce(){let{config:e}=await m({name:`lakeql`,cwd:P(),defaults:se,packageJson:!1,globalRc:!1,rcFile:!1,dotenv:!1});return e}async function F(e){let t=await ce(),n=P(),r=e??t.sourcePath;return c.isAbsolute(r)?r:c.resolve(n,r)}const I=new t(`--catalog <catalog>`,`catalog to use`).env(`HIVE_CATALOG`),L=new t(`--schema <schema>`,`schema to use`),le=new t(`--table <table>`,`table to use`),ue=new t(`--type <type>`,`Show tables or views`),R=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(P(),`command invocation directory`),de=new t(`--skip-registry`,`Skip config registry generation`).default(!1);function fe(){return new e(`create-registry`).description("Generates the config registry to ensure the type-safety while using `createPermission`").addOption(R).action(async({sourcePath:e})=>{await z(e!==process.env.INIT_CWD&&e!==process.cwd()?e:void 0)})}async function z(e){let t=await F(e),n=await h({fileName:`config-registry.ts`,nodes:v({configPaths:(await y(`schemas/**/config.ts`,{cwd:t,onlyFiles:!0})).map(e=>{let t=c.parse(e);return c.join(t.dir,t.name)})})});r(c.join(t,`config-registry.ts`))&&await o(c.join(t,`config-registry.ts`),{force:!0,recursive:!0}),await s(c.join(t,`config-registry.ts`),n.text)}function pe(e,t){return{baseClassName:`${x(b(e))}_${x(b(t))}`,queryName:`${b(e)}${x(b(t))}`,mutationName:`create${b(e)}${x(b(t))}`}}const{factory:B}=S;function V(e){return B.createIdentifier(e)}function H(e){return V(e)}function U(e){return B.createStringLiteral(e)}function W(e,t){return B.createPropertyAssignment(V(e),t)}function G(e,t=!0){return B.createObjectLiteralExpression(e,t)}function me(e){return e?B.createTrue():B.createFalse()}function he(e){return B.createParameterDeclaration(void 0,void 0,V(e))}function K(e,t,n){return B.createArrowFunction(n?.async?[B.createToken(S.SyntaxKind.AsyncKeyword)]:void 0,void 0,e.map(e=>typeof e==`string`?he(e):e),void 0,B.createToken(S.SyntaxKind.EqualsGreaterThanToken),t)}function ge(e){return B.createParenthesizedExpression(e)}function _e(e,t=!0){return K([`t`],ge(G(e,t)))}function q(e,t){return B.createPropertyAccessExpression(typeof e==`string`?V(e):e,V(t))}function ve(e,t=[],n){return B.createCallExpression(e,n,t)}function J(e,t,n=[],r){return ve(q(e,t),n,r)}function ye(e,t=[],n){return J(`builder`,e,t,n)}function be(e,t){return B.createVariableStatement(void 0,B.createVariableDeclarationList([B.createVariableDeclaration(V(e),void 0,void 0,t)],S.NodeFlags.Const))}function Y(e,t,n){return B.createImportDeclaration(void 0,B.createImportClause(n?.typeOnly??!1,void 0,B.createNamedImports(t.map(e=>B.createImportSpecifier(!1,void 0,V(e))))),U(e))}function xe(e,t,n){return B.createImportDeclaration(void 0,B.createImportClause(n?.typeOnly??!1,V(t),void 0),U(e))}function Se({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=Ce(i),s=Object.values(e).filter(e=>!e.root);return[...we(r),...s.map(t=>Te(t,e,o)),Te(a,e,o),ke(t,a,r)]}function Ce(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 we(e){let t=[Y(`@lakeql/adapters`,[`executeWritePipeline`]),Y(`@lakeql/trino-client`,[`TrinoClient`]),Y(`@lakeql/api/builder`,[`builder`]),Y(`~/env`,[`env`]),Y(`./config`,[`hiveConfig`,`storageConfig`]),xe(`./json-schema.json`,`jsonSchema`)];return e&&t.push(Y(`./validations`,[`validationSchema`])),t}function Te(e,t,n){let r=`${e.modelName}Input`,i=Object.values(e.fields).map(e=>Ee(e,t,n));return be(r,ye(`inputType`,[U(r),G([W(`fields`,_e(i))])]))}function Ee(e,t,n){let r=W(`required`,me(n.get(e.name)??!0));if(e.isArray){let t=e.graphqlType.replace(/^\[/u,``).replace(/\]$/u,``),n=Oe(t);if(n)return W(e.name,J(`t`,n,[G([r],!1)]));let i=`${t}Input`;return W(e.name,J(`t`,`field`,[G([W(`type`,S.factory.createArrayLiteralExpression([H(i)])),r],!1)]))}let i=De(e.graphqlType);if(i)return W(e.name,J(`t`,i,[G([r],!1)]));if(Object.keys(t).includes(e.graphqlType)){let t=`${e.graphqlType}Input`;return W(e.name,J(`t`,`field`,[G([W(`type`,H(t)),r],!1)]))}return W(e.name,J(`t`,`string`,[G([r],!1)]))}function De(e){switch(e){case`String`:return`string`;case`Int`:return`int`;case`Float`:return`float`;case`Boolean`:return`boolean`;default:return}}function Oe(e){switch(e){case`String`:return`stringList`;case`Int`:return`intList`;case`Float`:return`floatList`;case`Boolean`:return`booleanList`;default:return}}function ke(e,t,n){let r=`${t.modelName}Input`,i=[];n&&i.push(S.factory.createExpressionStatement(J(`validationSchema`,`parse`,[H(`input`)]))),i.push(S.factory.createVariableStatement(void 0,S.factory.createVariableDeclarationList([S.factory.createVariableDeclaration(H(`trinoClient`),void 0,void 0,S.factory.createNewExpression(H(`TrinoClient`),void 0,[G([W(`host`,q(`env`,`HIVE_HOST`)),W(`port`,q(`env`,`HIVE_PORT`)),W(`auth`,G([W(`type`,U(`basic`)),W(`username`,q(`env`,`HIVE_USERNAME`)),W(`password`,q(`env`,`HIVE_PASSWORD`))],!1)),W(`catalog`,q(`env`,`HIVE_CATALOG`)),W(`source`,q(`env`,`HIVE_SOURCE`))])]))],S.NodeFlags.Const)));let a=S.factory.createAwaitExpression(ve(H(`executeWritePipeline`),[G([W(`records`,H(`input`)),W(`jsonSchema`,S.factory.createAsExpression(H(`jsonSchema`),S.factory.createKeywordTypeNode(S.SyntaxKind.AnyKeyword))),W(`config`,G([W(`loadStrategy`,q(`storageConfig`,`loadStrategy`)),W(`bucket`,q(`storageConfig`,`bucket`)),W(`basePath`,q(`storageConfig`,`basePath`)),W(`table`,G([W(`catalog`,q(`hiveConfig`,`catalog`)),W(`schema`,q(`hiveConfig`,`schema`)),W(`tableName`,q(`hiveConfig`,`tableName`))],!1)),W(`trinoClient`,H(`trinoClient`))]))])]));i.push(S.factory.createExpressionStatement(a)),i.push(S.factory.createReturnStatement(S.factory.createTrue()));let o=S.factory.createBlock(i,!0),s=K([he(`_root`),S.factory.createParameterDeclaration(void 0,void 0,S.factory.createObjectBindingPattern([S.factory.createBindingElement(void 0,void 0,S.factory.createIdentifier(`input`))]))],o,{async:!0}),c=W(e,J(`t`,`boolean`,[G([W(`args`,G([W(`input`,J(`t`,`arg`,[G([W(`type`,H(r)),W(`required`,me(!0))],!1)]))],!1)),W(`resolve`,s)])]));return S.factory.createExpressionStatement(ye(`mutationFields`,[_e([c])]))}function X(e){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(X);let t={},n=Object.keys(e).toSorted();for(let r of n)t[r]=X(e[r]);return t}function Ae(e){let t=X(e);return`${JSON.stringify(t,null,2).replaceAll(`\r
|
|
4
4
|
`,`
|
|
5
|
-
`)}\n`}async function je(e){let{definition:t,outputDir:n,skipRegistry:a,sourcePathOverride:l}=e,{catalog:u,schema:d,tableName:f,fields:p}=t,{baseClassName:m,queryName:v,mutationName:y}=pe(d,f),b=ne(p),x=te({isRoot:!0,models:{},name:m,source:b}),S=Object.values(x).find(e=>e.root===!0),C=S?.fields?Object.values(S.fields).filter(e=>e.filter===!0).map(e=>({name:e.name,type:e.graphqlType})):[],re=[...new Set(C.map(e=>e.type))],w=[...new Set(Object.values(x).filter(e=>e.transformFields.length>0).flatMap(e=>e.transformFields))],T=[...new Set(Object.values(x).filter(e=>e.dateTimeFields.length>0).flatMap(e=>e.dateTimeFields))],E=await h({fileName:`config.ts`,nodes:g({catalog:u,queryName:v,schema:d,tableName:f,mutationName:[y],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}:void 0})}),D=await h({fileName:`interface.ts`,nodes:_(x)}),O=await h({fileName:`query-schema.ts`,nodes:ee({dateTimeFields:T,filterFields:C,filterTypes:re,models:x,queryName:v,transformFields:w})}),ie=p.some(e=>e.options?.validations&&e.options.validations.length>0),k=Se({models:x,mutationName:y,mutationConfig:t.mutation,hasValidations:ie,fieldDefinitions:p}),A=``;k.length>0&&(A=(await h({fileName:`mutation-schema.ts`,nodes:k})).text);let ae=Ae(b),oe=Ae(t),j=[{fileName:`config.ts`,content:E.text},{fileName:`interface.ts`,content:D.text},{fileName:`query-schema.ts`,content:O.text},{fileName:`json-schema.json`,content:ae},{fileName:`endpoint.json`,content:oe}];A&&j.push({fileName:`mutation-schema.ts`,content:A}),r(n)&&await o(n,{force:!0,recursive:!0}),await i(n,{recursive:!0});for(let e of j)await s(c.join(n,e.fileName),e.content);return a||await z(l),{files:j,outputDir:n}}function Me(){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(R).addOption(de).addOption(new t(`--force`,`Overwrite existing files without prompting`).default(!1)).action(async e=>{let{fromFile:t,sourcePath:n,skipRegistry:i,force:o}=e,s=c.isAbsolute(t)?t:c.resolve(process.cwd(),t);r(s)||(console.error(`Error: File not found: ${s}`),process.exit(1));let u;try{u=await a(s,`utf-8`)}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`Error: Cannot read file: ${s}: ${t}`),process.exit(1)}let d;try{d=JSON.parse(u)}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`Error: Invalid JSON in ${s}: ${t}`),process.exit(1)}let f=C.safeParse(d);if(!f.success){let e=f.error.issues.map(e=>` - ${e.path.length>0?e.path.join(`.`):`root`}: ${e.message}`).join(`
|
|
5
|
+
`)}\n`}async function je(e){let{definition:t,outputDir:n,skipRegistry:a,sourcePathOverride:l}=e,{catalog:u,schema:d,tableName:f,fields:p}=t,{baseClassName:m,queryName:v,mutationName:y}=pe(d,f),b=ne(p),x=te({isRoot:!0,models:{},name:m,source:b}),S=Object.values(x).find(e=>e.root===!0),C=S?.fields?Object.values(S.fields).filter(e=>e.filter===!0).map(e=>({name:e.name,type:e.graphqlType})):[],re=[...new Set(C.map(e=>e.type))],w=[...new Set(Object.values(x).filter(e=>e.transformFields.length>0).flatMap(e=>e.transformFields))],T=[...new Set(Object.values(x).filter(e=>e.dateTimeFields.length>0).flatMap(e=>e.dateTimeFields))],E=await h({fileName:`config.ts`,nodes:g({catalog:u,queryName:v,schema:d,tableName:f,mutationName:[y],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})}),D=await h({fileName:`interface.ts`,nodes:_(x)}),O=await h({fileName:`query-schema.ts`,nodes:ee({dateTimeFields:T,filterFields:C,filterTypes:re,models:x,queryName:v,transformFields:w})}),ie=p.some(e=>e.options?.validations&&e.options.validations.length>0),k=Se({models:x,mutationName:y,mutationConfig:t.mutation,hasValidations:ie,fieldDefinitions:p}),A=``;k.length>0&&(A=(await h({fileName:`mutation-schema.ts`,nodes:k})).text);let ae=Ae(b),oe=Ae(t),j=[{fileName:`config.ts`,content:E.text},{fileName:`interface.ts`,content:D.text},{fileName:`query-schema.ts`,content:O.text},{fileName:`json-schema.json`,content:ae},{fileName:`endpoint.json`,content:oe}];A&&j.push({fileName:`mutation-schema.ts`,content:A}),r(n)&&await o(n,{force:!0,recursive:!0}),await i(n,{recursive:!0});for(let e of j)await s(c.join(n,e.fileName),e.content);return a||await z(l),{files:j,outputDir:n}}function Me(){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(R).addOption(de).addOption(new t(`--force`,`Overwrite existing files without prompting`).default(!1)).action(async e=>{let{fromFile:t,sourcePath:n,skipRegistry:i,force:o}=e,s=c.isAbsolute(t)?t:c.resolve(process.cwd(),t);r(s)||(console.error(`Error: File not found: ${s}`),process.exit(1));let u;try{u=await a(s,`utf-8`)}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`Error: Cannot read file: ${s}: ${t}`),process.exit(1)}let d;try{d=JSON.parse(u)}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`Error: Invalid JSON in ${s}: ${t}`),process.exit(1)}let f=C.safeParse(d);if(!f.success){let e=f.error.issues.map(e=>` - ${e.path.length>0?e.path.join(`.`):`root`}: ${e.message}`).join(`
|
|
6
6
|
`);console.error(`Error: Validation failed for ${s}:\n${e}`),process.exit(1)}let p=f.data,m=re(p.fields);if(m.length>0){let e=m.map(e=>{let t=e.path.length>0?e.path.join(`.`):`root`;return` - Duplicate field "${e.name}" at level: ${t}`}).join(`
|
|
7
7
|
`);console.error(`Error: Validation failed for ${s}:\n${e}`),process.exit(1)}console.log(`
|
|
8
8
|
Loaded definition summary:`),console.log(` tableName: ${p.tableName}`),console.log(` catalog: ${p.catalog}`),console.log(` schema: ${p.schema}`);let h=p.mutation?p.mutation.loadStrategy:`disabled`;console.log(` mutation: ${h}`),console.log(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakeql/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CLI for introspecting Trino schemas and generating type-safe GraphQL endpoints",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"lodash-es": "4.18.1",
|
|
61
61
|
"read-pkg": "10.1.0",
|
|
62
62
|
"zod": "4.4.3",
|
|
63
|
-
"@lakeql/
|
|
64
|
-
"@lakeql/
|
|
63
|
+
"@lakeql/helpers": "0.1.3",
|
|
64
|
+
"@lakeql/column-parser": "0.1.3",
|
|
65
|
+
"@lakeql/file-generator": "0.1.6",
|
|
66
|
+
"@lakeql/schema-generator": "0.4.0",
|
|
67
|
+
"@lakeql/response-transformer": "0.1.3",
|
|
65
68
|
"@lakeql/logger": "0.1.2",
|
|
66
|
-
"@lakeql/
|
|
67
|
-
"@lakeql/schema-generator": "0.3.0",
|
|
68
|
-
"@lakeql/file-generator": "0.1.5",
|
|
69
|
-
"@lakeql/trino-client": "0.3.0"
|
|
69
|
+
"@lakeql/trino-client": "0.3.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/lodash-es": "4.17.12",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"tsdown": "0.22.2",
|
|
76
76
|
"tsx": "4.22.4",
|
|
77
77
|
"typescript": "6.0.3",
|
|
78
|
-
"vitest": "4.1.
|
|
78
|
+
"vitest": "4.1.9",
|
|
79
79
|
"@lakeql/tsconfig": "0.1.0"
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|