@izumisy/kyrage 0.7.0 → 1.1.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.
@@ -0,0 +1 @@
1
+ import{createConsola as r}from"consola";var s=r({stdout:process.stderr}),e={stdout:o=>console.log(o),reporter:{...s}},g={stdout:()=>{},reporter:{info:()=>{},warn:()=>{},success:()=>{},log:()=>{},error:()=>{}}};export{e as a,g as b};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { z } from 'zod';
2
1
  import { ColumnDataType } from 'kysely';
2
+ import z from 'zod';
3
3
 
4
4
  declare const columnSchema: z.ZodObject<{
5
5
  type: z.ZodString;
@@ -9,6 +9,13 @@ declare const columnSchema: z.ZodObject<{
9
9
  defaultSql: z.ZodOptional<z.ZodString>;
10
10
  }, z.core.$strip>;
11
11
  type ColumnValue = z.infer<typeof columnSchema>;
12
+ declare const indexSchema: z.ZodObject<{
13
+ columns: z.ZodReadonly<z.ZodArray<z.ZodString>>;
14
+ unique: z.ZodDefault<z.ZodBoolean>;
15
+ table: z.ZodString;
16
+ name: z.ZodString;
17
+ }, z.core.$strip>;
18
+ type IndexSchema = z.infer<typeof indexSchema>;
12
19
  declare const databaseSchema: z.ZodObject<{
13
20
  dialect: z.ZodEnum<{
14
21
  postgres: "postgres";
@@ -19,7 +26,41 @@ declare const databaseSchema: z.ZodObject<{
19
26
  connectionString: z.ZodString;
20
27
  }, z.core.$strip>;
21
28
  type DatabaseValue = z.infer<typeof databaseSchema>;
29
+ declare const devDatabaseSchema: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
30
+ container: z.ZodObject<{
31
+ image: z.ZodString;
32
+ reuse: z.ZodOptional<z.ZodBoolean>;
33
+ name: z.ZodOptional<z.ZodString>;
34
+ }, z.core.$strip>;
35
+ }, z.core.$strip>, z.ZodObject<{
36
+ connectionString: z.ZodString;
37
+ }, z.core.$strip>]>>;
38
+ type DevDatabaseValue = z.infer<typeof devDatabaseSchema>;
39
+
40
+ declare const primaryKeyConstraintSchema: z.ZodObject<{
41
+ columns: z.ZodReadonly<z.ZodArray<z.ZodString>>;
42
+ table: z.ZodString;
43
+ name: z.ZodString;
44
+ }, z.core.$strip>;
45
+ type PrimaryKeyConstraintSchema = z.infer<typeof primaryKeyConstraintSchema>;
46
+ declare const uniqueConstraintSchema: z.ZodObject<{
47
+ columns: z.ZodReadonly<z.ZodArray<z.ZodString>>;
48
+ table: z.ZodString;
49
+ name: z.ZodString;
50
+ }, z.core.$strip>;
51
+ type UniqueConstraintSchema = z.infer<typeof uniqueConstraintSchema>;
52
+ declare const referentialActionsSchema: z.ZodEnum<{
53
+ cascade: "cascade";
54
+ "set null": "set null";
55
+ "set default": "set default";
56
+ restrict: "restrict";
57
+ "no action": "no action";
58
+ }>;
59
+ type ReferentialActions = z.infer<typeof referentialActionsSchema>;
22
60
 
61
+ /**
62
+ * Defines a column in a table.
63
+ */
23
64
  declare const column: (type: ColumnDataType, options?: Partial<Omit<ColumnValue, "type">>) => {
24
65
  type: ColumnDataType;
25
66
  notNull: boolean | undefined;
@@ -27,17 +68,137 @@ declare const column: (type: ColumnDataType, options?: Partial<Omit<ColumnValue,
27
68
  unique: boolean | undefined;
28
69
  defaultSql: string | undefined;
29
70
  };
71
+ type DefinedValue<T, K extends string> = Omit<T, "table"> & {
72
+ kind: K;
73
+ };
30
74
  type DefinedColumn = ReturnType<typeof column>;
31
- declare const defineTable: <T extends Record<string, DefinedColumn>>(name: string, columns: T) => {
75
+ type DefinedIndex = DefinedValue<IndexSchema, "index">;
76
+ type DefinedPrimaryKeyConstraint = DefinedValue<PrimaryKeyConstraintSchema, "primaryKey">;
77
+ type DefinedUniqueConstraint = DefinedValue<UniqueConstraintSchema, "unique">;
78
+ type DefinedForeignKeyConstraint = {
79
+ kind: "foreignKey";
80
+ name: string;
81
+ columns: ReadonlyArray<string>;
82
+ referencedTable: string;
83
+ referencedColumns: ReadonlyArray<string>;
84
+ onDelete?: ReferentialActions;
85
+ onUpdate?: ReferentialActions;
86
+ };
87
+ type ExpressionBuilders<T extends Record<string, DefinedColumn>> = {
88
+ /**
89
+ * Creates an index on the specified columns.
90
+ *
91
+ * The default is non-unique, and name is autogenerated by joining the column names and table name.
92
+ */
93
+ index: (columns: ReadonlyArray<string & keyof T>, options?: {
94
+ name?: string;
95
+ unique?: boolean;
96
+ }) => DefinedIndex;
97
+ /**
98
+ * Creates a primary key constraint on the specified columns.
99
+ *
100
+ * The default name is autogenerated by joining the column names and table name.
101
+ */
102
+ primaryKey: (columns: ReadonlyArray<string & keyof T>, options?: {
103
+ name?: string;
104
+ }) => DefinedPrimaryKeyConstraint;
105
+ /**
106
+ * Creates a unique constraint on the specified columns.
107
+ *
108
+ * The default name is autogenerated by joining the column names and table name.
109
+ */
110
+ unique: (columns: ReadonlyArray<string & keyof T>, options?: {
111
+ name?: string;
112
+ }) => DefinedUniqueConstraint;
113
+ /**
114
+ * Creates a foreign key constraint on the specified columns.
115
+ *
116
+ * The default name is autogenerated by joining the column names and table name.
117
+ */
118
+ reference: <R extends Record<string, DefinedColumn>>(columns: ReadonlyArray<string & keyof T> | (string & keyof T), referencedTable: {
119
+ tableName: string;
120
+ columns: R;
121
+ }, referencedColumns: ReadonlyArray<string & keyof R> | (string & keyof R), options?: {
122
+ name?: string;
123
+ onDelete?: ReferentialActions;
124
+ onUpdate?: ReferentialActions;
125
+ }) => DefinedForeignKeyConstraint;
126
+ };
127
+ /**
128
+ * Defines a table with its columns and optional expression builders.
129
+ */
130
+ declare const defineTable: <T extends Record<string, DefinedColumn>>(
131
+ /**
132
+ * The name of the table.
133
+ */
134
+ name: string,
135
+ /**
136
+ * The columns in the table.
137
+ */
138
+ columns: T,
139
+ /**
140
+ * The expression builders for the table.
141
+ *
142
+ * This is where you can define additional indexes, constraints, or other
143
+ * database-specific features for the table.
144
+ */
145
+ tableExpBuilder?: (builder: ExpressionBuilders<T>) => ReadonlyArray<ReturnType<ExpressionBuilders<T>["index"]> | ReturnType<ExpressionBuilders<T>["primaryKey"]> | ReturnType<ExpressionBuilders<T>["unique"]> | ReturnType<ExpressionBuilders<T>["reference"]>>) => {
32
146
  tableName: string;
33
147
  columns: T;
148
+ indexes: DefinedIndex[];
149
+ primaryKeys: DefinedPrimaryKeyConstraint[];
150
+ uniques: DefinedUniqueConstraint[];
151
+ foreignKeys: DefinedForeignKeyConstraint[];
34
152
  };
35
- type DefinedTable = ReturnType<typeof defineTable>;
36
- type DefinedTables = Array<DefinedTable>;
153
+ type DefinedTable = ReturnType<typeof defineTable> & {
154
+ indexes: ReadonlyArray<DefinedIndex>;
155
+ primaryKeys: ReadonlyArray<DefinedPrimaryKeyConstraint>;
156
+ uniques: ReadonlyArray<DefinedUniqueConstraint>;
157
+ foreignKeys: ReadonlyArray<DefinedForeignKeyConstraint>;
158
+ };
159
+ type DefinedTables = ReadonlyArray<DefinedTable>;
37
160
  type DefineConfigProp = {
38
161
  database: DatabaseValue;
162
+ dev?: DevDatabaseValue;
39
163
  tables: DefinedTables;
40
164
  };
41
- declare const defineConfig: (config: DefineConfigProp) => DefineConfigProp;
165
+ declare const defineConfig: (config: DefineConfigProp) => {
166
+ readonly dev: {
167
+ container: {
168
+ image: string;
169
+ reuse?: boolean | undefined;
170
+ name?: string | undefined;
171
+ };
172
+ } | {
173
+ connectionString: string;
174
+ } | undefined;
175
+ readonly indexes: {
176
+ columns: readonly string[];
177
+ unique: boolean;
178
+ table: string;
179
+ name: string;
180
+ }[];
181
+ readonly primaryKeyConstraints: {
182
+ columns: readonly string[];
183
+ table: string;
184
+ name: string;
185
+ }[];
186
+ readonly uniqueConstraints: {
187
+ columns: readonly string[];
188
+ table: string;
189
+ name: string;
190
+ }[];
191
+ readonly foreignKeyConstraints: {
192
+ columns: readonly string[];
193
+ referencedTable: string;
194
+ referencedColumns: readonly string[];
195
+ table: string;
196
+ name: string;
197
+ onDelete?: "cascade" | "set null" | "set default" | "restrict" | "no action" | undefined;
198
+ onUpdate?: "cascade" | "set null" | "set default" | "restrict" | "no action" | undefined;
199
+ }[];
200
+ readonly database: DatabaseValue;
201
+ readonly tables: DefinedTables;
202
+ };
42
203
 
43
204
  export { type DefineConfigProp, type DefinedTable, type DefinedTables, column, defineConfig, defineTable };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import"./chunk-EY62Q3Z2.js";var a=(t,e)=>({type:t,notNull:e?.notNull,primaryKey:e?.primaryKey,unique:e?.unique,defaultSql:e?.defaultSql}),n=(t,e)=>({tableName:t,columns:e}),l=t=>t;export{a as column,l as defineConfig,n as defineTable};
1
+ import"./chunk-EY62Q3Z2.js";var f=(t,r)=>r.length===1?`${t}_${r[0]}_primary_key`:`pk_${t}_${r.join("_")}`,c=(t,r)=>r.length===1?`${t}_${r[0]}_unique`:`uq_${t}_${r.join("_")}`,p=(t,r)=>`idx_${t}_${r.join("_")}`,g=(t,r)=>`fk_${t}_${r.join("_")}`,m={primaryKey:f,unique:c,index:p,foreignKey:g};var D=(t,r)=>({type:t,notNull:r?.notNull,primaryKey:r?.primaryKey,unique:r?.unique,defaultSql:r?.defaultSql}),K=(t,r,y)=>{let i=e=>{if(e.length===0)throw new Error("At least one column must be specified.")},l=(e,n)=>`${e}_${t}_${n.join("_")}`,a=y?y({index:(e,n)=>(i(e),{kind:"index",name:n?.name??l("idx",e),columns:e,unique:n?.unique??!1}),primaryKey:(e,n)=>(i(e),{kind:"primaryKey",name:n?.name??l("pk",e),columns:e}),unique:(e,n)=>(i(e),{kind:"unique",name:n?.name??l("uq",e),columns:e}),reference:(e,n,u,o)=>{let s=Array.isArray(e)?e:[e],d=Array.isArray(u)?u:[u];return i(s),i(d),{kind:"foreignKey",name:o?.name??m.foreignKey(t,s),columns:s,referencedTable:n.tableName,referencedColumns:d,onDelete:o?.onDelete,onUpdate:o?.onUpdate}}}):[];return{tableName:t,columns:r,indexes:a.filter(e=>e.kind==="index"),primaryKeys:a.filter(e=>e.kind==="primaryKey"),uniques:a.filter(e=>e.kind==="unique"),foreignKeys:a.filter(e=>e.kind==="foreignKey")}},R=t=>{let r=new Map,y=new Map,i=new Map,l=new Map;for(let a of t.tables){let e=a.tableName;for(let n of a.indexes??[])r.set(`${e}:${n.name}`,{table:e,name:n.name,columns:n.columns,unique:n.unique});for(let n of a.primaryKeys??[])y.set(`${e}:${n.name}`,{table:e,name:n.name,columns:n.columns});for(let n of a.uniques??[])i.set(`${e}:${n.name}`,{table:e,name:n.name,columns:n.columns});for(let n of a.foreignKeys??[])l.set(`${e}:${n.name}`,{table:e,name:n.name,columns:n.columns,referencedTable:n.referencedTable,referencedColumns:n.referencedColumns,onDelete:n.onDelete,onUpdate:n.onUpdate});for(let[n,u]of Object.entries(a.columns)){if(u.primaryKey){let o=m.primaryKey(e,[n]),s=`${e}:${o}`;y.has(s)||y.set(s,{table:e,name:o,columns:[n]})}if(u.unique){let o=m.unique(e,[n]),s=`${e}:${o}`;i.has(s)||i.set(s,{table:e,name:o,columns:[n]})}}}return{...t,dev:t.dev,indexes:Array.from(r.values()),primaryKeyConstraints:Array.from(y.values()),uniqueConstraints:Array.from(i.values()),foreignKeyConstraints:Array.from(l.values())}};export{D as column,R as defineConfig,K as defineTable};
@@ -0,0 +1 @@
1
+ import{a,b}from"./chunk-3M6BYRKC.js";import"./chunk-EY62Q3Z2.js";export{a as defaultConsolaLogger,b as nullLogger};
package/dist/main.js CHANGED
@@ -1,25 +1,101 @@
1
- import{a as y,b}from"./chunk-EY62Q3Z2.js";import{defineCommand as M,runMain as Pe}from"citty";import{Kysely as X,MysqlDialect as Z,PostgresDialect as ee,SqliteDialect as te}from"kysely";import{Pool as R}from"pg";import{createPool as ae}from"mysql2";import ne from"better-sqlite3";import{PostgresAdapter as G,PostgresDialect as W}from"kysely";var P=class extends G{async acquireMigrationLock(n,t){await n.selectFrom(t.lockTable).selectAll().forUpdate().execute()}},x=class extends W{createAdapter(){return new P}};import{DummyDriver as H}from"kysely";var _=class extends H{constructor(t){super();this.queries=t}async acquireConnection(){return new E(this.queries)}},E=class{constructor(n){this.queries=n}async executeQuery(n){let t=n.query.kind;if(t==="CreateTableNode"||t==="DropTableNode"||t==="AlterTableNode"){if(n.query.table.table.identifier.name.startsWith("kysely_"))return{rows:[]};this.queries.push(n)}return{rows:[]}}async*streamQuery(){}};var re=e=>{switch(e.dialect){case"cockroachdb":return new x({pool:new R({connectionString:e.connectionString})});case"postgres":return new ee({pool:new R({connectionString:e.connectionString})});case"mysql":return new Z({pool:ae(e.connectionString)});case"sqlite":return new te({database:new ne(e.connectionString)});default:throw new Error(`Unsupported dialect: ${e.dialect}`)}},S=e=>new q({databaseProps:e.database,options:e.options}),q=class{constructor(n){this.constructorProps=n}getDB(n){let t=re(this.constructorProps.databaseProps);return new k({dialect:{createAdapter:()=>t.createAdapter(),createDriver:()=>t.createDriver(),createIntrospector:a=>t.createIntrospector(a),createQueryCompiler:()=>t.createQueryCompiler()}},{isPlan:n?.plan===!0})}getDialect(){return this.constructorProps.databaseProps.dialect}},k=class extends X{constructor(t,a){let r=[];super({dialect:{...t.dialect,createDriver:()=>a.isPlan?new _(r):t.dialect.createDriver()}});this.plannedQueries=[];this.plannedQueries=r}getPlannedQueries(){return this.plannedQueries}};var O="0.7.0";import{z as m}from"zod";var se=m.object({type:m.string(),primaryKey:m.boolean().optional().default(!1),notNull:m.boolean().optional().default(!1),unique:m.boolean().optional().default(!1),defaultSql:m.string().optional()}),le=m.object({tableName:m.string(),columns:m.record(m.string(),se)}),ce=m.object({table:m.string(),columns:m.array(m.string()),unique:m.boolean().optional()}),me=m.enum(["postgres","cockroachdb","mysql","sqlite"]),ue=m.object({dialect:me,connectionString:m.string()}),K=m.object({database:ue,tables:m.array(le),indexes:m.array(ce).optional()}),j=m.object({id:m.string(),version:m.string(),diff:m.any()});import{Migrator as be}from"kysely";import{DEFAULT_MIGRATION_TABLE as Q,isColumnDataType as pe,sql as B}from"kysely";import{readdir as de,readFile as ge}from"fs/promises";import{join as fe}from"path";var w="migrations",I=async()=>{try{let n=(await de(w)).filter(t=>t.endsWith(".json")).map(async t=>j.parse(JSON.parse(await ge(fe(w,t),"utf-8"))));return await Promise.all(n)}catch(e){if(e instanceof Object&&"code"in e&&e.code==="ENOENT")return[];throw e}},D=async e=>{var s=[];try{let n=y(s,e.getDB(),!0);let t=await I();let a=(await n.introspection.getTables({withInternalKyselyTables:!0})).map(g=>g.name);if(!a.includes(Q))return t;let r=await n.selectFrom(Q).select(["name","timestamp"]).$narrowType().execute();return t.filter(g=>!r.some(o=>o.name===g.id))}catch(u){var p=u,i=!0}finally{var d=b(s,p,i);d&&await d}},L=e=>({getMigrations:async()=>{let n=await e.migrationsResolver(),t={};return n.forEach(a=>{t[a.id]={up:async()=>{await ye(e.db,a.diff)}}}),t}});async function ye(e,n){for(let t of n.addedTables){let a=e.schema.createTable(t.table);for(let[r,s]of Object.entries(t.columns)){let u=s.type;$(u),a=a.addColumn(r,u,p=>{let i=p;return s.notNull&&(i=i.notNull()),s.defaultSql&&(i=i.defaultTo(B.raw(s.defaultSql))),i}),s.primaryKey&&(a=a.addPrimaryKeyConstraint(`${t.table}_${r}_primary_key`,[r])),s.unique&&(a=a.addUniqueConstraint(`${t.table}_${r}_unique`,[r]))}await a.execute()}for(let t of n.removedTables)await e.schema.dropTable(t).execute();for(let t of n.changedTables){for(let a of t.addedColumns){let r=a.attributes.type;$(r),await e.schema.alterTable(t.table).addColumn(a.column,r,s=>{let u=s;return a.attributes.notNull&&(u=u.notNull()),a.attributes.defaultSql&&(u=u.defaultTo(B.raw(a.attributes.defaultSql))),u}).execute(),a.attributes.primaryKey&&await e.schema.alterTable(t.table).addPrimaryKeyConstraint(`${t.table}_${a.column}_primary_key`,[a.column]).execute(),a.attributes.unique&&await e.schema.alterTable(t.table).addUniqueConstraint(`${t.table}_${a.column}_unique`,[a.column]).execute()}for(let a of t.removedColumns)await e.schema.alterTable(t.table).dropColumn(a.column).execute();for(let a of t.changedColumns){if(a.before.type!==a.after.type){let r=a.after.type;$(r),await e.schema.alterTable(t.table).alterColumn(a.column,s=>s.setDataType(r)).execute()}a.after.notNull!==a.before.notNull&&(a.after.notNull?await e.schema.alterTable(t.table).alterColumn(a.column,r=>r.setNotNull()).execute():await e.schema.alterTable(t.table).alterColumn(a.column,r=>r.dropNotNull()).execute()),a.after.primaryKey!==a.before.primaryKey&&(a.after.primaryKey?await e.schema.alterTable(t.table).addPrimaryKeyConstraint(`${t.table}_${a.column}_primary_key`,[a.column]).execute():await e.schema.alterTable(t.table).dropConstraint(`${t.table}_${a.column}_primary_key`).execute()),a.after.unique!==a.before.unique&&(a.after.unique?await e.schema.alterTable(t.table).addUniqueConstraint(`${t.table}_${a.column}_unique`,[a.column]).execute():await e.schema.alterTable(t.table).dropConstraint(`${t.table}_${a.column}_unique`).execute())}}}var $=e=>{if(!pe(e))throw new Error(`Unsupported data type: ${e}`)};import{format as he}from"sql-formatter";var N=async e=>{var p=[];try{let{reporter:n}=e.logger;let t=y(p,e.client.getDB({plan:e.options.plan}),!0);let a=new be({db:t,provider:L({db:t,migrationsResolver:async()=>e.options.plan?await D(e.client):await I(),options:{plan:e.options.plan}})});let{results:r,error:s}=await a.migrateToLatest();let u=t.getPlannedQueries();if(u.length>0){u.forEach(l=>{e.logger.stdout(e.options.pretty?he(l.sql):l.sql)});return}r&&r.length>0?r.forEach(l=>{l.status==="Error"?n.error(`Migration failed: ${l.migrationName}`):l.status==="Success"&&n.success(`Migration applied: ${l.migrationName}`)}):n.info("No migrations to run");s&&(n.error(`Migration error: ${s}`),process.exit(1))}catch(i){var d=i,g=!0}finally{var o=b(p,d,g);o&&await o}};import{createConsola as Ce}from"consola";var Te=Ce({stdout:process.stderr}),U={stdout:e=>console.log(e),reporter:{...Te}};import{mkdir as _e,writeFile as De}from"fs/promises";function z(e){let{current:n,ideal:t}=e,a=n.map(i=>i.name),r=t.map(i=>i.name),s=t.filter(i=>!a.includes(i.name)).map(i=>({table:i.name,columns:i.columns})),u=n.filter(i=>!r.includes(i.name)).map(i=>i.name),p=n.reduce((i,d)=>{let g=t.find(c=>c.name===d.name);if(!g)return i;let o=d.columns,l=g.columns,f=Object.keys(o),C=Object.keys(l),T=C.filter(c=>!f.includes(c)).map(c=>({column:c,attributes:l[c]})),h=f.filter(c=>!C.includes(c)).map(c=>({column:c,attributes:o[c]})),A=f.filter(c=>C.includes(c)&&(o[c].type!==l[c].type||o[c].notNull!==l[c].notNull||o[c].primaryKey!==l[c].primaryKey||o[c].unique!==l[c].unique)).map(c=>({column:c,before:o[c],after:l[c]}));return(T.length>0||h.length>0||A.length>0)&&i.push({table:d.name,addedColumns:T,removedColumns:h,changedColumns:A}),i},[]);return{addedTables:s,removedTables:u,changedTables:p}}import{sql as we}from"kysely";var F=e=>{let n=async()=>{var p=[];try{let r=e.client;let s=y(p,r.getDB(),!0);let{rows:u}=await we`
1
+ import{a as ne,b as ae}from"./chunk-3M6BYRKC.js";import{a as _,b as x}from"./chunk-EY62Q3Z2.js";import{defineCommand as Mt,runMain as jt}from"citty";var re="1.1.0";import{defineCommand as lt}from"citty";import y from"zod";import{loadConfig as Ee}from"c12";import u from"zod";var w=u.object({type:u.string()}).catchall(u.any()),N=u.object({table:u.string(),name:u.string()}),j=u.object({table:u.string(),column:u.string()}),L=u.object({...N.shape,columns:u.array(u.string()).readonly()}),F=u.object({...N.shape,columns:u.array(u.string()).readonly()}),oe=u.enum(["cascade","set null","set default","restrict","no action"]),B=u.object({...N.shape,columns:u.array(u.string()).readonly(),referencedTable:u.string(),referencedColumns:u.array(u.string()).readonly(),onDelete:oe.optional(),onUpdate:oe.optional()}),ie=u.discriminatedUnion("type",[u.object({type:u.literal("create_table"),table:u.string(),columns:u.record(u.string(),w)}),u.object({type:u.literal("drop_table"),table:u.string()}),u.object({...j.shape,type:u.literal("add_column"),attributes:w}),u.object({...j.shape,type:u.literal("drop_column"),attributes:w}),u.object({...j.shape,type:u.literal("alter_column"),before:w,after:w}),u.object({...N.shape,type:u.literal("create_index"),columns:u.array(u.string()).readonly(),unique:u.boolean()}),u.object({...N.shape,type:u.literal("drop_index")}),u.object({...L.shape,type:u.literal("create_primary_key_constraint")}),u.object({...N.shape,type:u.literal("drop_primary_key_constraint")}),u.object({...F.shape,type:u.literal("create_unique_constraint")}),u.object({...N.shape,type:u.literal("drop_unique_constraint")}),u.object({...B.shape,type:u.literal("create_foreign_key_constraint")}),u.object({...N.shape,type:u.literal("drop_foreign_key_constraint")})]),f={createTable:(e,t)=>({type:"create_table",table:e,columns:t}),dropTable:e=>({type:"drop_table",table:e}),addColumn:(e,t)=>({...e,type:"add_column",attributes:t}),dropColumn:(e,t)=>({...e,type:"drop_column",attributes:t}),alterColumn:(e,t,n)=>({...e,type:"alter_column",before:t,after:n}),createIndex:e=>({...e,type:"create_index"}),dropIndex:e=>({...e,type:"drop_index"}),createPrimaryKeyConstraint:e=>({...e,type:"create_primary_key_constraint"}),dropPrimaryKeyConstraint:e=>({...e,type:"drop_primary_key_constraint"}),createUniqueConstraint:e=>({...e,type:"create_unique_constraint"}),dropUniqueConstraint:e=>({...e,type:"drop_unique_constraint"}),createForeignKeyConstraint:(e,t)=>({...e,...t,type:"create_foreign_key_constraint"}),dropForeignKeyConstraint:e=>({...e,type:"drop_foreign_key_constraint"})};var Se=y.object({type:y.string(),primaryKey:y.boolean().optional().default(!1),notNull:y.boolean().optional().default(!1),unique:y.boolean().optional().default(!1),defaultSql:y.string().optional()}),De=y.object({tableName:y.string(),columns:y.record(y.string(),Se)}),we=y.object({...N.shape,columns:y.array(y.string()).readonly(),unique:y.boolean().default(!1)}),Oe=y.enum(["postgres","cockroachdb","mysql","sqlite"]),Te=y.object({dialect:Oe,connectionString:y.string()}),Ae=y.union([y.object({container:y.object({image:y.string(),reuse:y.boolean().optional(),name:y.string().optional()})}),y.object({connectionString:y.string()})]).optional(),Ke=y.object({database:Te,dev:Ae,tables:y.array(De).readonly(),indexes:y.array(we).readonly(),primaryKeyConstraints:y.array(L).readonly(),uniqueConstraints:y.array(F).readonly(),foreignKeyConstraints:y.array(B).readonly()}),se=async()=>{let e=await Ee({name:"kyrage"});return Ke.parse(e.config)};import{Kysely as qe,MysqlDialect as Pe,PostgresDialect as ke,SqliteDialect as $e}from"kysely";import{Pool as ce}from"pg";import{createPool as Ue}from"mysql2";import Me from"better-sqlite3";import{PostgresAdapter as ve,PostgresDialect as Re}from"kysely";var H=class extends ve{async acquireMigrationLock(t,n){await t.selectFrom(n.lockTable).selectAll().forUpdate().execute()}},K=class extends Re{createAdapter(){return new H}};import{DummyDriver as Ie}from"kysely";var v=class extends Ie{constructor(n){super();this.queries=n}async acquireConnection(){return new W(this.queries)}},W=class{constructor(t){this.queries=t}async executeQuery(t){let n=t.query.kind;if(n==="CreateTableNode"||n==="DropTableNode"||n==="AlterTableNode"){if(t.query.table.table.identifier.name.startsWith("kysely_"))return{rows:[]};this.queries.push(t)}else(n==="CreateIndexNode"||n==="DropIndexNode")&&this.queries.push(t);return{rows:[]}}async*streamQuery(){}};var je=e=>{switch(e.dialect){case"cockroachdb":return new K({pool:new ce({connectionString:e.connectionString})});case"postgres":return new ke({pool:new ce({connectionString:e.connectionString})});case"mysql":return new Pe({pool:Ue(e.connectionString)});case"sqlite":return new $e({database:new Me(e.connectionString)});default:throw new Error(`Unsupported dialect: ${e.dialect}`)}},R=e=>new Q({databaseProps:e.database,options:e.options}),Q=class{constructor(t){this.constructorProps=t}getDB(t){let n=je(this.constructorProps.databaseProps);return new V({dialect:{createAdapter:()=>n.createAdapter(),createDriver:()=>n.createDriver(),createIntrospector:a=>n.createIntrospector(a),createQueryCompiler:()=>n.createQueryCompiler()}},{isPlan:t?.plan===!0})}getDialect(){return this.constructorProps.databaseProps.dialect}},V=class extends qe{constructor(n,a){let s=[];super({dialect:{...n.dialect,createDriver:()=>a.isPlan?new v(s):n.dialect.createDriver()}});this.plannedQueries=[];this.plannedQueries=s}getPlannedQueries(){return this.plannedQueries}};async function S(){let e=await se();return{client:R({database:e.database}),logger:ne,config:e}}import{Migrator as mt}from"kysely";import{DEFAULT_MIGRATION_TABLE as le,isColumnDataType as Le,sql as ue}from"kysely";import{readdir as Fe,readFile as Be}from"fs/promises";import{join as He}from"path";import O from"zod";var T="migrations",We=O.object({operations:O.array(ie).readonly()}),Qe=O.object({id:O.string(),version:O.string(),diff:We}),z=async()=>{try{let t=(await Fe(T)).filter(n=>n.endsWith(".json")).map(async n=>Qe.parse(JSON.parse(await Be(He(T,n),"utf-8"))));return await Promise.all(t)}catch(e){if(e instanceof Object&&"code"in e&&e.code==="ENOENT")return[];throw e}},I=async e=>{var l=[];try{let t=_(l,e.getDB(),!0);let n=await z();let a=(await t.introspection.getTables({withInternalKyselyTables:!0})).map(r=>r.name);if(!a.includes(le))return n;let s=await t.selectFrom(le).select(["name","timestamp"]).$narrowType().execute();return n.filter(r=>!s.some(p=>p.name===r.id))}catch(c){var o=c,i=!0}finally{var d=x(l,o,i);d&&await d}},pe=e=>({getMigrations:async()=>{let t=await e.migrationsResolver(),n={};return t.forEach(a=>{n[a.id]={up:async()=>{await Ve(e.db,a.diff)}}}),n}});async function Ve(e,t){let n=ct(t.operations);for(let a of n)await ze(e,a)}async function ze(e,t){switch(t.type){case"create_table":return Je(e,t);case"drop_table":return Ye(e,t);case"add_column":return Ge(e,t);case"drop_column":return Xe(e,t);case"alter_column":return Ze(e,t);case"create_index":return et(e,t);case"drop_index":return tt(e,t);case"create_primary_key_constraint":return nt(e,t);case"drop_primary_key_constraint":return at(e,t);case"create_unique_constraint":return rt(e,t);case"drop_unique_constraint":return ot(e,t);case"create_foreign_key_constraint":return it(e,t);case"drop_foreign_key_constraint":return st(e,t);default:throw new Error(`Unknown operation type: ${t.type}`)}}async function Je(e,t){let n=e.schema.createTable(t.table);for(let[a,s]of Object.entries(t.columns)){let l=s.type;J(l),n=n.addColumn(a,l,c=>{let o=c;return s.notNull&&(o=o.notNull()),typeof s.defaultSql=="string"&&(o=o.defaultTo(ue.raw(s.defaultSql))),o})}await n.execute()}async function Ye(e,t){await e.schema.dropTable(t.table).execute()}async function Ge(e,t){let n=t.attributes.type;J(n),await e.schema.alterTable(t.table).addColumn(t.column,n,a=>{let s=a;return t.attributes.notNull&&(s=s.notNull()),typeof t.attributes.defaultSql=="string"&&(s=s.defaultTo(ue.raw(t.attributes.defaultSql))),s}).execute()}async function Xe(e,t){await e.schema.alterTable(t.table).dropColumn(t.column).execute()}async function Ze(e,t){let{table:n,column:a,before:s,after:l}=t;if(s.type!==l.type){let c=l.type;J(c),await e.schema.alterTable(n).alterColumn(a,o=>o.setDataType(c)).execute()}l.notNull!==s.notNull&&(l.notNull?await e.schema.alterTable(n).alterColumn(a,c=>c.setNotNull()).execute():await e.schema.alterTable(n).alterColumn(a,c=>c.dropNotNull()).execute())}async function et(e,t){let n=e.schema.createIndex(t.name).on(t.table);for(let a of t.columns)n=n.column(a);t.unique&&(n=n.unique()),await n.execute()}async function tt(e,t){await e.schema.dropIndex(t.name).execute()}async function nt(e,t){await e.schema.alterTable(t.table).addPrimaryKeyConstraint(t.name,t.columns).execute()}async function at(e,t){await e.schema.alterTable(t.table).dropConstraint(t.name).execute()}async function rt(e,t){await e.schema.alterTable(t.table).addUniqueConstraint(t.name,t.columns).execute()}async function ot(e,t){await e.schema.alterTable(t.table).dropConstraint(t.name).execute()}async function it(e,t){let n=e.schema.alterTable(t.table).addForeignKeyConstraint(t.name,t.columns,t.referencedTable,t.referencedColumns);t.onDelete&&(n=n.onDelete(t.onDelete)),t.onUpdate&&(n=n.onUpdate(t.onUpdate)),await n.execute()}async function st(e,t){await e.schema.alterTable(t.table).dropConstraint(t.name).execute()}var J=e=>{if(!Le(e))throw new Error(`Unsupported data type: ${e}`)},me={drop_foreign_key_constraint:0,drop_unique_constraint:1,drop_primary_key_constraint:2,drop_index:3,drop_column:4,drop_table:5,create_table:6,add_column:7,alter_column:8,create_index:9,create_primary_key_constraint:10,create_unique_constraint:11,create_foreign_key_constraint:12},ct=e=>e.slice().sort((t,n)=>{let a=me[t.type],s=me[n.type];return a!==s?a-s:t.table.localeCompare(n.table)});import{format as ut}from"sql-formatter";async function q(e,t){var r=[];try{let{client:n,logger:a}=e;let{reporter:s}=a;let l=_(r,n.getDB({plan:t.plan}),!0);let c=new mt({db:l,provider:pe({db:l,migrationsResolver:async()=>t.plan?await I(n):await z(),options:{plan:t.plan}})});let{results:o,error:i}=await c.migrateToLatest();let d=l.getPlannedQueries();if(d.length>0){d.forEach(b=>{a.stdout(t.pretty?ut(b.sql):b.sql)});return}o&&o.length>0?o.forEach(b=>{b.status==="Error"?s.error(`Migration failed: ${b.migrationName}`):b.status==="Success"&&s.success(`Migration applied: ${b.migrationName}`)}):s.info("No migrations to run");if(i)throw i instanceof Error?i:new Error(`Migration error: ${i}`)}catch(p){var m=p,h=!0}finally{var C=x(r,m,h);C&&await C}}var de=lt({meta:{name:"apply",description:"Run migrations to sync database schema"},args:{plan:{type:"boolean",description:"Plan the migration without applying it",default:!1},pretty:{type:"boolean",description:"Pretty print the migration SQL (only for --plan)",default:!1}},run:async e=>{try{let t=await S();await q(t,{plan:e.args.plan,pretty:e.args.pretty})}catch(t){let{defaultConsolaLogger:n}=await import("./logger-SE7G5AMI.js");n.reporter.error(t),process.exit(1)}}});import{defineCommand as wt}from"citty";import{mkdir as Ot,writeFile as Tt}from"fs/promises";import*as g from"ramda";var D=()=>({added:(e,t,n)=>g.pipe(g.difference(t),g.map(n))(e),removed:(e,t,n)=>g.pipe(g.difference(e),g.map(n))(t),changed:(e,t,n,a)=>g.pipe(g.intersection(e),g.filter(n),g.map(a))(t)}),P=g.prop("name"),pt=g.eqBy(e=>[e.type,e.notNull??!1,e.primaryKey??!1,e.unique??!1]);function dt(e,t){let n=[],a=D(),s=e.columns,l=t.columns,c=Object.keys(s),o=Object.keys(l);return a.added(c,o,i=>({column:i,attributes:l[i]})).forEach(i=>{n.push(f.addColumn({table:e.name,column:i.column},i.attributes))}),a.removed(c,o,i=>({column:i,attributes:s[i]})).forEach(i=>{n.push(f.dropColumn({table:e.name,column:i.column},i.attributes))}),a.changed(c,o,i=>!pt(s[i],l[i]),i=>({column:i,before:s[i],after:l[i]})).forEach(i=>{n.push(f.alterColumn({table:e.name,column:i.column},i.before,i.after))}),n}function yt(e){let{current:t,ideal:n}=e,a=[],s=D(),l=g.map(P,t),c=g.map(P,n);return s.added(l,c,o=>n.find(d=>d.name===o)).forEach(o=>{a.push(f.createTable(o.name,o.columns))}),s.removed(l,c,g.identity).forEach(o=>{a.push(f.dropTable(o))}),g.filter(o=>n.some(i=>i.name===o.name))(t).forEach(o=>{let i=n.find(d=>d.name===o.name);a.push(...dt(o,i))}),a}function gt(e){let{current:t,ideal:n}=e,a=[],s=D(),l=r=>`${r.table}:${r.name}`,c=new Map(t.map(r=>[l(r),r])),o=new Map(n.map(r=>[l(r),r])),i=Array.from(c.keys()),d=Array.from(o.keys());return s.added(i,d,r=>o.get(r)).forEach(r=>{a.push(f.createIndex(r))}),s.removed(i,d,r=>{let p=c.get(r);return{table:p.table,name:p.name}}).forEach(r=>{a.push(f.dropIndex(r))}),s.changed(i,d,r=>{let p=c.get(r),m=o.get(r);return!g.equals(p.columns,m.columns)||p.unique!==m.unique},r=>{let p=c.get(r),m=o.get(r);return{current:p,ideal:m}}).forEach(({current:r,ideal:p})=>{a.push(f.dropIndex(r)),a.push(f.createIndex(p))}),a}function ft(e){let{current:t,ideal:n}=e,a=[],s=D(),l=r=>`${r.table}:${r.name}`,c=new Map(t.map(r=>[l(r),r])),o=new Map(n.map(r=>[l(r),r])),i=Array.from(c.keys()),d=Array.from(o.keys());return s.added(i,d,r=>o.get(r)).forEach(r=>{a.push(f.createPrimaryKeyConstraint(r))}),s.removed(i,d,r=>{let p=c.get(r);return{table:p.table,name:p.name}}).forEach(r=>{a.push(f.dropPrimaryKeyConstraint(r))}),s.changed(i,d,r=>{let p=c.get(r),m=o.get(r);return!g.equals(p.columns,m.columns)},r=>{let p=c.get(r),m=o.get(r);return{current:p,ideal:m}}).forEach(({current:r,ideal:p})=>{a.push(f.dropPrimaryKeyConstraint(r)),a.push(f.createPrimaryKeyConstraint(p))}),a}function bt(e){let{current:t,ideal:n}=e,a=[],s=D(),l=r=>`${r.table}:${r.name}`,c=new Map(t.map(r=>[l(r),r])),o=new Map(n.map(r=>[l(r),r])),i=Array.from(c.keys()),d=Array.from(o.keys());return s.added(i,d,r=>o.get(r)).forEach(r=>{a.push(f.createUniqueConstraint(r))}),s.removed(i,d,r=>{let p=c.get(r);return{table:p.table,name:p.name}}).forEach(r=>{a.push(f.dropUniqueConstraint(r))}),s.changed(i,d,r=>{let p=c.get(r),m=o.get(r);return!g.equals(p.columns,m.columns)},r=>{let p=c.get(r),m=o.get(r);return{current:p,ideal:m}}).forEach(({current:r,ideal:p})=>{a.push(f.dropUniqueConstraint(r)),a.push(f.createUniqueConstraint(p))}),a}function Ct(e){let t=[],n=D(),a=e.current.map(P),s=e.ideal.map(P);t.push(...n.added(a,s,c=>{let o=e.ideal.find(p=>p.name===c),{table:i,name:d,...r}=o;return f.createForeignKeyConstraint({table:i,name:d},r)})),t.push(...n.removed(a,s,c=>{let o=e.current.find(i=>i.name===c);return f.dropForeignKeyConstraint(o)}));let l=c=>{let o=e.current.find(d=>d.name===c),i=e.ideal.find(d=>d.name===c);return!o||!i?!1:!(g.equals(o.columns,i.columns)&&o.referencedTable===i.referencedTable&&g.equals(o.referencedColumns,i.referencedColumns)&&o.onDelete===i.onDelete&&o.onUpdate===i.onUpdate)};return t.push(...n.changed(a,s,l,c=>{let o=e.current.find(m=>m.name===c),i=e.ideal.find(m=>m.name===c),{table:d,name:r,...p}=i;return[f.dropForeignKeyConstraint(o),f.createForeignKeyConstraint({table:d,name:r},p)]}).flat()),t}function ye(e){let t=yt({current:e.current.tables,ideal:e.ideal.tables}),n=gt({current:e.current.indexes,ideal:e.ideal.indexes}),a=ft({current:e.current.primaryKeyConstraints,ideal:e.ideal.primaryKeyConstraints}),s=bt({current:e.current.uniqueConstraints,ideal:e.ideal.uniqueConstraints}),l=Ct({current:e.current.foreignKeyConstraints,ideal:e.ideal.foreignKeyConstraints});return{operations:[...t,...n,...a,...s,...l]}}import{sql as Y}from"kysely";var ht={bool:"boolean",int2:"smallint",int4:"integer",int8:"bigint"},_t=e=>ht[e]??e,ge=e=>({introspectTables:async()=>{var o=[];try{let s=e.client;let l=_(o,s.getDB(),!0);let{rows:c}=await Y`
2
2
  SELECT
3
- c.table_schema,
4
- c.table_name,
5
- c.column_name,
6
- c.column_default,
7
- c.character_maximum_length,
8
- tc.constraint_name,
9
- tc.constraint_type
10
- FROM information_schema.columns c
11
- JOIN information_schema.key_column_usage kcu
12
- ON c.table_name = kcu.table_name
13
- AND c.column_name = kcu.column_name
14
- AND c.table_schema = kcu.table_schema
15
- JOIN information_schema.table_constraints tc
16
- ON kcu.constraint_name = tc.constraint_name
17
- AND kcu.table_schema = tc.table_schema
18
- AND tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY')
19
- WHERE c.table_schema = 'public'
20
- ORDER BY c.table_name, c.ordinal_position;
21
- `.$castTo().execute(s);return u.map(l=>({schema:l.table_schema,table:l.table_name,name:l.column_name,default:l.column_default,characterMaximumLength:l.character_maximum_length,constraint:{name:l.constraint_name,type:l.constraint_type}}))}catch(i){var d=i,g=!0}finally{var o=b(p,d,g);o&&await o}},t={bool:"boolean",int2:"smallint",int4:"integer",int8:"bigint"};return{introspect:n,convertTypeName:r=>t[r]??r}};var xe=e=>{let n=e.getDialect();switch(n){case"postgres":case"cockroachdb":return F({client:e});default:throw new Error(`Unsupported dialect: ${n}`)}},V=e=>{let n=xe(e);return{getTables:async()=>{var u=[];try{let a=y(u,e.getDB(),!0);let r=await a.introspection.getTables();let s=await n.introspect();return r.map(o=>{let l=Object.fromEntries(o.columns.map(f=>{let C=s.filter(h=>h.table===o.name&&h.name===f.name),T=C[0]||{};return[f.name,{schema:o.schema,table:o.name,name:f.name,dataType:n.convertTypeName(f.dataType),default:T.default??null,characterMaximumLength:T.characterMaximumLength??null,notNull:!f.isNullable,constraints:C.map(h=>h.constraint)}]}));return{schema:o.schema,name:o.name,columns:l}})}catch(p){var i=p,d=!0}finally{var g=b(u,i,d);g&&await g}}}};var J=async e=>{let{reporter:n}=e.logger,t=e.config;if(!e.options.ignorePending){let s=await D(e.client);if(s.length>0){n.warn([`There are pending migrations: ${s.map(u=>u.id).join(", ")}`,"Please apply them first before generating a new migration.","Otherwise, use --ignore-pending to skip this check."].join(`
22
- `));return}}let a=await Ne({client:e.client,config:t});if(!a){n.info("No changes detected, no migration needed.");return}ve(e.logger,a.diff);let r=`${w}/${a.id}.json`;await _e(w,{recursive:!0}),await De(r,JSON.stringify(a,null,2)),n.success(`Migration file generated: ${r}`),e.options.apply&&await N({client:e.client,logger:e.logger,options:{plan:e.options.plan,pretty:!1}})},Ne=async e=>{let{client:n,config:t}=e,s=(await V(n).getTables()).map(d=>({name:d.name,columns:Object.fromEntries(Object.entries(d.columns).map(([g,o])=>[g,{type:o.dataType,notNull:o.notNull,primaryKey:!!o.constraints.find(l=>l.type==="PRIMARY KEY"),unique:!!o.constraints.find(l=>l.type==="UNIQUE"),defaultSql:o.default??void 0}]))})),u=t.tables.map(d=>({name:d.tableName,columns:Object.fromEntries(Object.entries(d.columns).map(([g,o])=>[g,{...o,notNull:o.primaryKey||o.notNull}]))})),p=z({current:s,ideal:u});return p.addedTables.length===0&&p.removedTables.length===0&&p.changedTables.length===0?null:{version:"1",id:Date.now()+"",diff:p}},ve=(e,n)=>{let t=[];n.addedTables.length>0&&n.addedTables.forEach(a=>{t.push(`-- create_table: ${a.table}`),Object.entries(a.columns).forEach(([r,s])=>{t.push(` -> column: ${r} (${JSON.stringify(s)})`)})}),n.removedTables.length>0&&n.removedTables.forEach(a=>{t.push(`-- remove_table: ${a}`)}),n.changedTables.length>0&&n.changedTables.forEach(a=>{a.addedColumns.forEach(r=>{t.push([`-- add_column: ${a.table}.${r.column}`,` -> to: ${JSON.stringify(r.attributes)}`].join(`
23
- `))}),a.removedColumns.forEach(r=>{t.push(`-- remove_column: ${a.table}.${r.column}`)}),a.changedColumns.forEach(r=>{t.push([`-- change_column: ${a.table}.${r.column}`,` -> from: ${JSON.stringify(r.before)}`,` -> to: ${JSON.stringify(r.after)}`].join(`
24
- `))})}),e.reporter.log(t.join(`
25
- `))};import{loadConfig as Ee}from"c12";var Y=async()=>{let e=await Ee({name:"kyrage"});return K.parse(e.config)},v=U,qe=M({meta:{name:"generate",description:"Generate migration files based on the current schema"},args:{apply:{type:"boolean",description:"Apply the migration after generating it",default:!1},plan:{type:"boolean",description:"Plan the migration without applying it (only for --apply)",default:!1},"ignore-pending":{type:"boolean",description:"Ignore pending migrations and generate a new one",default:!1}},run:async e=>{try{let n=await Y(),t=S({database:n.database});await J({client:t,logger:v,config:n,options:{ignorePending:e.args["ignore-pending"],apply:e.args.apply,plan:e.args.plan}})}catch(n){v.reporter.error(n),process.exit(1)}}}),ke=M({meta:{name:"apply",description:"Run migrations to sync database schema"},args:{plan:{type:"boolean",description:"Plan the migration without applying it",default:!1},pretty:{type:"boolean",description:"Pretty print the migration SQL (only for --plan)",default:!1}},run:async e=>{try{let n=await Y(),t=S({database:n.database});await N({client:t,logger:v,options:{plan:e.args.plan,pretty:e.args.pretty}})}catch(n){v.reporter.error(n),process.exit(1)}}}),Se=M({meta:{name:"kyrage",version:O,description:"Kysely migration CLI with declarative schema"},subCommands:{apply:ke,generate:qe}});Pe(Se);
3
+ n.nspname AS table_schema,
4
+ c.relname AS table_name,
5
+ a.attname AS column_name,
6
+ pg_get_expr(d.adbin, d.adrelid) AS column_default,
7
+ CASE
8
+ WHEN t.typname = 'varchar' OR t.typname = 'char' THEN a.atttypmod - 4
9
+ ELSE NULL
10
+ END AS character_maximum_length
11
+ FROM pg_class c
12
+ JOIN pg_namespace n ON n.oid = c.relnamespace
13
+ JOIN pg_attribute a ON a.attrelid = c.oid
14
+ LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = a.attnum
15
+ JOIN pg_type t ON t.oid = a.atttypid
16
+ WHERE c.relkind = 'r'
17
+ AND n.nspname = 'public'
18
+ AND a.attnum > 0
19
+ AND NOT a.attisdropped
20
+ ORDER BY c.relname, a.attnum;
21
+ `.$castTo().execute(l);return c.map(m=>({schema:m.table_schema,table:m.table_name,name:m.column_name,default:m.column_default,characterMaximumLength:m.character_maximum_length}))}catch(i){var d=i,r=!0}finally{var p=x(o,d,r);p&&await p}},introspectIndexes:async()=>{var o=[];try{let s=e.client;let l=_(o,s.getDB(),!0);let{rows:c}=await Y`
22
+ SELECT
23
+ t.relname AS table_name,
24
+ i.relname AS index_name,
25
+ pg_index.indisunique AS is_unique,
26
+ jsonb_agg(a.attname ORDER BY array_position(pg_index.indkey, a.attnum)) AS column_names
27
+ FROM pg_class t
28
+ JOIN pg_namespace n ON n.oid = t.relnamespace
29
+ JOIN pg_index ON pg_index.indrelid = t.oid
30
+ JOIN pg_class i ON i.oid = pg_index.indexrelid
31
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY (pg_index.indkey)
32
+ WHERE t.relkind = 'r'
33
+ AND n.nspname = 'public'
34
+ AND NOT pg_index.indisprimary
35
+ AND NOT EXISTS (
36
+ SELECT 1 FROM pg_constraint con
37
+ WHERE con.conindid = i.oid
38
+ AND con.contype IN ('p', 'u')
39
+ )
40
+ GROUP BY t.relname, i.relname, pg_index.indisunique, pg_index.indisprimary, i.oid;
41
+ `.$castTo().execute(l);return c.map(m=>({table:m.table_name,name:m.index_name,columns:m.column_names,unique:m.is_unique}))}catch(i){var d=i,r=!0}finally{var p=x(o,d,r);p&&await p}},introspectConstraints:async()=>{var o=[];try{let s=e.client;let l=_(o,s.getDB(),!0);let{rows:c}=await Y`
42
+ SELECT
43
+ n.nspname AS schema,
44
+ t.relname AS table,
45
+ c.conname AS name,
46
+ CASE c.contype
47
+ WHEN 'p' THEN 'PRIMARY KEY'
48
+ WHEN 'u' THEN 'UNIQUE'
49
+ WHEN 'f' THEN 'FOREIGN KEY'
50
+ END AS type,
51
+ jsonb_agg(a.attname ORDER BY array_position(c.conkey, a.attnum)) AS columns,
52
+ -- Foreign Key専用の情報
53
+ CASE
54
+ WHEN c.contype = 'f' THEN rt.relname
55
+ ELSE NULL
56
+ END AS referenced_table,
57
+ CASE
58
+ WHEN c.contype = 'f' THEN jsonb_agg(ra.attname ORDER BY array_position(c.confkey, ra.attnum))
59
+ ELSE NULL
60
+ END AS referenced_columns,
61
+ CASE
62
+ WHEN c.contype = 'f' THEN
63
+ CASE c.confdeltype
64
+ WHEN 'c' THEN 'cascade'
65
+ WHEN 'n' THEN 'set null'
66
+ WHEN 'd' THEN 'set default'
67
+ WHEN 'r' THEN 'restrict'
68
+ WHEN 'a' THEN 'no action'
69
+ END
70
+ ELSE NULL
71
+ END AS on_delete,
72
+ CASE
73
+ WHEN c.contype = 'f' THEN
74
+ CASE c.confupdtype
75
+ WHEN 'c' THEN 'cascade'
76
+ WHEN 'n' THEN 'set null'
77
+ WHEN 'd' THEN 'set default'
78
+ WHEN 'r' THEN 'restrict'
79
+ WHEN 'a' THEN 'no action'
80
+ END
81
+ ELSE NULL
82
+ END AS on_update
83
+ FROM pg_constraint c
84
+ JOIN pg_class t ON c.conrelid = t.oid
85
+ JOIN pg_namespace n ON t.relnamespace = n.oid
86
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey)
87
+ -- Foreign Key用のJOIN
88
+ LEFT JOIN pg_class rt ON c.confrelid = rt.oid
89
+ LEFT JOIN pg_attribute ra ON ra.attrelid = c.confrelid AND ra.attnum = ANY(c.confkey)
90
+ WHERE c.contype IN ('p', 'u', 'f') -- primary key, unique, foreign key constraints
91
+ AND n.nspname = 'public'
92
+ AND NOT a.attisdropped
93
+ AND (c.contype != 'f' OR NOT ra.attisdropped) -- Foreign Key用の条件
94
+ GROUP BY n.nspname, t.relname, c.conname, c.contype, c.oid, c.conkey, t.oid,
95
+ rt.relname, c.confkey, c.confdeltype, c.confupdtype
96
+ ORDER BY t.relname, c.conname;
97
+ `.$castTo().execute(l);return{primaryKey:c.filter(m=>m.type==="PRIMARY KEY"),unique:c.filter(m=>m.type==="UNIQUE"),foreignKey:c.filter(m=>m.type==="FOREIGN KEY").map(m=>{let{referenced_table:h,referenced_columns:C,on_delete:b,on_update:A,...E}=m;return{...E,referencedTable:h,referencedColumns:C,onDelete:b,onUpdate:A}})}}catch(i){var d=i,r=!0}finally{var p=x(o,d,r);p&&await p}},convertTypeName:_t});var xt=e=>{let t=e.getDialect();switch(t){case"postgres":case"cockroachdb":return ge({client:e});default:throw new Error(`Unsupported dialect: ${t}`)}},fe=e=>{let t=xt(e);return{getTables:async()=>{var l=[];try{let n=_(l,e.getDB(),!0);let a=await n.introspection.getTables();let s=await t.introspectTables();return a.map(r=>{let p=Object.fromEntries(r.columns.map(m=>{let C=s.filter(b=>b.table===r.name&&b.name===m.name)[0]||{};return[m.name,{schema:r.schema,table:r.name,name:m.name,dataType:t.convertTypeName(m.dataType),default:C.default??null,characterMaximumLength:C.characterMaximumLength??null,notNull:!m.isNullable}]}));return{schema:r.schema,name:r.name,columns:p}})}catch(c){var o=c,i=!0}finally{var d=x(l,o,i);d&&await d}},getIndexes:async()=>(await t.introspectIndexes()).filter(n=>!n.table.startsWith("kysely_")),getConstraints:async()=>{let n=await t.introspectConstraints(),a=n.primaryKey.filter(c=>!c.table.startsWith("kysely_")),s=n.unique.filter(c=>!c.table.startsWith("kysely_")),l=n.foreignKey.filter(c=>!c.table.startsWith("kysely_"));return{primaryKey:a,unique:s,foreignKey:l}}}};import{PostgreSqlContainer as Nt}from"@testcontainers/postgresql";import{CockroachDbContainer as Et}from"@testcontainers/cockroachdb";import{getContainerRuntimeClient as ee}from"testcontainers";var be="kyrage.dialect",Ce="kyrage.managed",St=async()=>(await(await ee()).container.list()).filter(n=>n.Labels[Ce]==="true").map(n=>n.Id),Dt=async e=>{let t=await ee();await Promise.allSettled(e.map(async n=>t.container.getById(n).remove({force:!0})))},k=class{constructor(t,n){this.options=t;this.startedContainer=null;let a=n();a.withLabels({[be]:this.options.dialect,[Ce]:"true"}),this.options.reuse&&a.withReuse(),this.options.containerName&&a.withName(this.options.containerName),this.container=a}async findRunningContainer(){return(await ee()).container.fetchByLabel(be,this.options.dialect,{status:["running"]})}async exists(){return!!await this.findRunningContainer()}async start(){this.startedContainer=await this.container.start()}getConnectionString(){return this.startedContainer?this.startedContainer.getConnectionUri():null}async stop(){this.startedContainer&&(await this.startedContainer.stop(),this.startedContainer=null)}async remove(){let t=await St();await Dt(t),this.startedContainer=null}async getStatus(){let t=await this.findRunningContainer();if(!t)return null;let n=await t.inspect();return{type:"container",imageName:n.Config.Image,containerID:n.Id}}},G=class extends k{constructor(t){super(t,()=>new Nt(t.image))}},X=class extends k{constructor(t){super(t,()=>new Et(t.image))}},Z=class{constructor(t){this.connectionString=t}async start(){}async stop(){}async remove(){}async exists(){return!0}getConnectionString(){return this.connectionString}async getStatus(){return{type:"connectionString"}}},$=(e,t)=>{if("connectionString"in e)return new Z(e.connectionString);if("container"in e){let n={dialect:t,image:e.container.image,reuse:e.container.reuse||!1,containerName:e.container.name};switch(t){case"postgres":return new G(n);case"cockroachdb":return new X(n);default:throw new Error(`Unsupported dialect for container: ${t}`)}}else throw new Error("Invalid dev database configuration")};async function At(e,t){let{client:n,logger:a,config:s}=e,{reporter:l}=a,{client:c,cleanup:o}=await Kt(e,t);try{if(!t.dev&&!t.ignorePending){let r=await I(n);if(r.length>0){l.warn([`There are pending migrations: ${r.map(p=>p.id).join(", ")}`,"Please apply them first before generating a new migration.","Otherwise, use --ignore-pending to skip this check."].join(`
98
+ `));return}}let i=await vt({client:c,config:s});if(!i){l.info("No changes detected, no migration needed.");return}Rt(a,i.diff);let d=`${T}/${i.id}.json`;await Ot(T,{recursive:!0}),await Tt(d,JSON.stringify(i,null,2)),l.success(`Migration file generated: ${d}`),t.apply&&(t.dev?l.warn("--apply flag is ignored when using --dev. Use 'kyrage apply' to apply to production database."):await q(e,{plan:t.plan,pretty:!1}))}finally{await o()}}var Kt=async(e,t)=>{let{client:n,logger:a,config:s}=e,{reporter:l}=a;if(!t.dev)return{client:n,cleanup:async()=>{}};if(!s.dev)throw new Error("Dev database configuration is required when using --dev flag");let c=s.database.dialect,o=$(s.dev,c),i="container"in s.dev&&s.dev.container.reuse;i&&await o.exists()?l.info("\u{1F504} Reusing existing dev database..."):l.info("\u{1F680} Starting dev database for migration generation..."),await o.start(),l.success(`Dev database started: ${c}`);let d=o.getConnectionString();if(!d)throw new Error("Failed to get connection string for dev database");let r=R({database:{dialect:c,connectionString:d}});return await q({client:r,logger:ae,config:s},{plan:!1,pretty:!1}),{client:r,cleanup:async()=>{i?l.success("\u2728 Persistent dev database ready: "+c):(await o.stop(),l.success("Dev database stopped"))}}},vt=async e=>{let{client:t,config:n}=e,a=fe(t),s=await a.getTables(),l=await a.getConstraints(),c=(m,h)=>C=>C.some(b=>b.table===m&&b.columns.length===1&&b.columns[0]===h),o=s.map(m=>({name:m.name,columns:Object.fromEntries(Object.entries(m.columns).map(([h,C])=>{let b=c(m.name,h),E=l.primaryKey.some(M=>M.table===m.name&&M.columns.length>1&&M.columns.includes(h))?!0:C.notNull;return[h,{type:C.dataType,notNull:E,primaryKey:b(l.primaryKey),unique:b(l.unique),defaultSql:C.default??void 0}]}))})),i=n.tables.map(m=>({name:m.tableName,columns:Object.fromEntries(Object.entries(m.columns).map(([h,C])=>{let b=(n.primaryKeyConstraints??[]).some(E=>E.table===m.tableName&&E.columns.includes(h)),A=C.primaryKey||C.notNull||b;return[h,{...C,notNull:A}]}))})),d=await a.getIndexes(),r=ye({current:{tables:o,indexes:d,primaryKeyConstraints:l.primaryKey,uniqueConstraints:l.unique,foreignKeyConstraints:l.foreignKey},ideal:{tables:i,indexes:n.indexes.map(m=>({table:m.table,name:m.name,columns:m.columns,unique:m.unique})),primaryKeyConstraints:n.primaryKeyConstraints||[],uniqueConstraints:n.uniqueConstraints||[],foreignKeyConstraints:n.foreignKeyConstraints||[]}});return r.operations.length===0?null:{version:"1",id:Date.now()+"",diff:r}},Rt=(e,t)=>{let n=[];t.operations.forEach(a=>{switch(a.type){case"create_table":n.push(`-- create_table: ${a.table}`),Object.entries(a.columns).forEach(([s,l])=>{n.push(` -> column: ${s} (${JSON.stringify(l)})`)});break;case"drop_table":n.push(`-- remove_table: ${a.table}`);break;case"add_column":n.push([`-- add_column: ${a.table}.${a.column}`,` -> to: ${JSON.stringify(a.attributes)}`].join(`
99
+ `));break;case"drop_column":n.push(`-- remove_column: ${a.table}.${a.column}`);break;case"alter_column":n.push([`-- change_column: ${a.table}.${a.column}`,` -> from: ${JSON.stringify(a.before)}`,` -> to: ${JSON.stringify(a.after)}`].join(`
100
+ `));break;case"create_index":n.push(`-- create_index: ${a.table}.${a.name} (${a.columns.join(", ")})${a.unique?" [unique]":""}`);break;case"drop_index":n.push(`-- drop_index: ${a.table}.${a.name}`);break;case"create_primary_key_constraint":n.push(`-- create_primary_key_constraint: ${a.table}.${a.name} (${a.columns.join(", ")})`);break;case"drop_primary_key_constraint":n.push(`-- drop_primary_key_constraint: ${a.table}.${a.name}`);break;case"create_unique_constraint":n.push(`-- create_unique_constraint: ${a.table}.${a.name} (${a.columns.join(", ")})`);break;case"drop_unique_constraint":n.push(`-- drop_unique_constraint: ${a.table}.${a.name}`);break;case"create_foreign_key_constraint":n.push(`-- create_foreign_key_constraint: ${a.table}.${a.name} (${a.columns.join(", ")}) -> ${a.referencedTable} (${a.referencedColumns.join(", ")})${a.onDelete?` ON DELETE ${a.onDelete.toUpperCase()}`:""}${a.onUpdate?` ON UPDATE ${a.onUpdate.toUpperCase()}`:""}`);break;case"drop_foreign_key_constraint":n.push(`-- drop_foreign_key_constraint: ${a.table}.${a.name}`);break;default:break}}),e.reporter.log(n.join(`
101
+ `))},he=wt({meta:{name:"generate",description:"Generate migration files based on the current schema"},args:{apply:{type:"boolean",description:"Apply the migration after generating it",default:!1},plan:{type:"boolean",description:"Plan the migration without applying it (only for --apply)",default:!1},"ignore-pending":{type:"boolean",description:"Ignore pending migrations and generate a new one",default:!1},dev:{type:"boolean",description:"Use dev database for safe migration generation",default:!1}},run:async e=>{try{let t=await S();await At(t,{ignorePending:e.args["ignore-pending"],apply:e.args.apply,plan:e.args.plan,dev:e.args.dev})}catch(t){let{defaultConsolaLogger:n}=await import("./logger-SE7G5AMI.js");n.reporter.error(t),process.exit(1)}}});import{defineCommand as U}from"citty";async function It(e){let{manager:t}=e,n=await t.getStatus();if(!n||n.type!=="container"){console.log("No running dev containers found");return}console.log(`Running: ${n.containerID} (${n.imageName})`)}async function qt(e){let{manager:t}=e;if(!await t.exists()){console.log("No running dev containers found");return}await t.start();let n=t.getConnectionString();if(n)console.log(n);else throw new Error("Failed to get connection string")}async function Pt(e){let{manager:t,logger:n}=e;if(!await t.exists()){console.log("No dev containers found");return}await t.remove(),n.reporter.success("Cleaned up dev containers")}async function te(){let e=await S();if(!e.config.dev||!("container"in e.config.dev))throw new Error("No dev database container configuration found");let t=$(e.config.dev,e.config.database.dialect);return{...e,manager:t}}var kt=U({meta:{name:"status",description:"Show status of kyrage dev container"},run:async()=>{try{let e=await te();await It(e)}catch(e){let{defaultConsolaLogger:t}=await import("./logger-SE7G5AMI.js");t.reporter.error(e),process.exit(1)}}}),$t=U({meta:{name:"get-url",description:"Print connection URL for running dev container"},run:async()=>{try{let e=await te();await qt(e)}catch(e){let{defaultConsolaLogger:t}=await import("./logger-SE7G5AMI.js");t.reporter.error(e),process.exit(1)}}}),Ut=U({meta:{name:"clean",description:"Remove all kyrage dev containers"},run:async()=>{try{let e=await te();await Pt(e)}catch(e){let{defaultConsolaLogger:t}=await import("./logger-SE7G5AMI.js");t.reporter.error(e),process.exit(1)}}}),_e=U({meta:{name:"dev",description:"Manage development database containers"},subCommands:{status:kt,"get-url":$t,clean:Ut}});var Lt=Mt({meta:{name:"kyrage",version:re,description:"Kysely migration CLI with declarative schema"},subCommands:{apply:de,generate:he,dev:_e}});jt(Lt);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@izumisy/kyrage",
3
3
  "private": false,
4
4
  "type": "module",
5
- "version": "0.7.0",
5
+ "version": "1.1.0",
6
6
  "description": "A minimal, schema-based declarative migration tool for Node.js ecosystem",
7
7
  "author": "Seiya Izumi (https://github.com/IzumiSy)",
8
8
  "license": "MIT",
@@ -30,6 +30,8 @@
30
30
  "kyrage": "./dist/main.js"
31
31
  },
32
32
  "dependencies": {
33
+ "@testcontainers/cockroachdb": "^11.5.1",
34
+ "@testcontainers/postgresql": "^11.5.1",
33
35
  "better-sqlite3": "^12.2.0",
34
36
  "c12": "^3.2.0",
35
37
  "citty": "^0.1.6",
@@ -38,19 +40,19 @@
38
40
  "kysely-codegen": "^0.18.5",
39
41
  "mysql2": "^3.14.3",
40
42
  "pg": "^8.16.3",
43
+ "ramda": "^0.31.3",
41
44
  "sql-formatter": "^15.6.6",
42
- "zod": "^4.0.17"
45
+ "testcontainers": "^11.5.1",
46
+ "zod": "^4.1.1"
43
47
  },
44
48
  "devDependencies": {
45
- "@swc/core": "^1.13.3",
46
- "@testcontainers/cockroachdb": "^11.5.1",
47
- "@testcontainers/postgresql": "^11.5.1",
49
+ "@swc/core": "^1.13.5",
48
50
  "@types/better-sqlite3": "^7.6.13",
49
- "@types/node": "^24.2.1",
51
+ "@types/node": "^24.3.0",
50
52
  "@types/pg": "^8.15.5",
51
- "memfs": "^4.36.0",
53
+ "@types/ramda": "^0.31.0",
54
+ "memfs": "^4.38.1",
52
55
  "prettier": "^3.6.2",
53
- "testcontainers": "^11.5.1",
54
56
  "tsup": "^8.5.0",
55
57
  "typescript": "^5.9.2",
56
58
  "vitest": "^3.2.4"