@aurios/mizzling 1.0.0 → 1.1.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/package.json +5 -2
- package/src/cli.ts +88 -0
- package/src/commands/push.ts +1 -1
- package/src/config.ts +24 -1
- package/src/index.ts +1 -88
- package/tsup.config.ts +1 -1
- package/CHANGELOG.md +0 -27
- package/dist/index.js +0 -23
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurios/mizzling",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"mizzling": "./
|
|
7
|
+
"mizzling": "./src/cli.ts"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/index.ts"
|
|
8
11
|
},
|
|
9
12
|
"author": {
|
|
10
13
|
"name": "Lucas",
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import { loadConfig } from "./config";
|
|
4
|
+
import { generateCommand } from "./commands/generate";
|
|
5
|
+
import { initCommand } from "./commands/init";
|
|
6
|
+
import { pushCommand } from "./commands/push";
|
|
7
|
+
import { listCommand } from "./commands/list";
|
|
8
|
+
import { dropCommand } from "./commands/drop";
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name("mizzle")
|
|
14
|
+
.description("Mizzle Migration CLI")
|
|
15
|
+
.version("0.0.1");
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.command("init")
|
|
19
|
+
.description("Initialize Mizzle configuration")
|
|
20
|
+
.action(async () => {
|
|
21
|
+
try {
|
|
22
|
+
await initCommand();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
25
|
+
p.log.error(message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command("generate")
|
|
32
|
+
.description("Generate a new migration snapshot and script")
|
|
33
|
+
.option("-n, --name <name>", "Migration name")
|
|
34
|
+
.action(async (options) => {
|
|
35
|
+
try {
|
|
36
|
+
const config = await loadConfig();
|
|
37
|
+
await generateCommand({ config, name: options.name });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
40
|
+
p.log.error(message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
program
|
|
46
|
+
.command("push")
|
|
47
|
+
.description("Directly apply schema changes to the target DynamoDB environment")
|
|
48
|
+
.option("-y, --yes", "Skip confirmation")
|
|
49
|
+
.action(async (options) => {
|
|
50
|
+
try {
|
|
51
|
+
const config = await loadConfig();
|
|
52
|
+
await pushCommand({ config, force: options.yes });
|
|
53
|
+
} catch (e) {
|
|
54
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
55
|
+
p.log.error(message);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program
|
|
61
|
+
.command("list")
|
|
62
|
+
.description("List all existing DynamoDB tables in the environment")
|
|
63
|
+
.action(async () => {
|
|
64
|
+
try {
|
|
65
|
+
const config = await loadConfig();
|
|
66
|
+
await listCommand({ config });
|
|
67
|
+
} catch (e) {
|
|
68
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
69
|
+
p.log.error(message);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
program
|
|
75
|
+
.command("drop")
|
|
76
|
+
.description("Interactive command to select and delete DynamoDB tables")
|
|
77
|
+
.action(async () => {
|
|
78
|
+
try {
|
|
79
|
+
const config = await loadConfig();
|
|
80
|
+
await dropCommand({ config });
|
|
81
|
+
} catch (e) {
|
|
82
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
83
|
+
p.log.error(message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
program.parse();
|
package/src/commands/push.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type MizzleConfig, getClient } from "../config";
|
|
|
2
2
|
import { discoverSchema } from "../discovery";
|
|
3
3
|
import { compareSchema } from "@aurios/mizzle/diff";
|
|
4
4
|
import { getRemoteSnapshot } from "@aurios/mizzle/introspection";
|
|
5
|
-
import { DynamoDBClient, CreateTableCommand
|
|
5
|
+
import { DynamoDBClient, CreateTableCommand } from "@aws-sdk/client-dynamodb";
|
|
6
6
|
import { confirm, isCancel, cancel, intro, outro, spinner } from "@clack/prompts";
|
|
7
7
|
|
|
8
8
|
interface PushOptions {
|
package/src/config.ts
CHANGED
|
@@ -54,10 +54,31 @@ export interface MizzleConfig {
|
|
|
54
54
|
* Maximum number of retry attempts for DynamoDB requests.
|
|
55
55
|
*/
|
|
56
56
|
maxAttempts?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Print all SQL statements (or DynamoDB commands) and their execution time.
|
|
59
|
+
*/
|
|
60
|
+
verbose?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Require user confirmation before pushing any changes to the database.
|
|
63
|
+
*/
|
|
64
|
+
strict?: boolean;
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
/**
|
|
60
|
-
* Helper function to define the configuration with type safety.
|
|
68
|
+
* Helper function to define the Mizzle CLI configuration with type safety and autocompletion.
|
|
69
|
+
*
|
|
70
|
+
* Typically used in a `mizzle.config.ts` file at the root of your project.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { defineConfig } from "@aurios/mizzling";
|
|
75
|
+
*
|
|
76
|
+
* export default defineConfig({
|
|
77
|
+
* schema: "./src/schema.ts",
|
|
78
|
+
* out: "./mizzle",
|
|
79
|
+
* region: "us-east-1",
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
61
82
|
*
|
|
62
83
|
* @param config The Mizzle configuration object.
|
|
63
84
|
* @returns The same configuration object, validated by TypeScript.
|
|
@@ -151,6 +172,8 @@ export async function loadConfig(configName = "mizzle.config.ts"): Promise<Mizzl
|
|
|
151
172
|
if (process.env.MIZZLE_ENDPOINT) finalConfig.endpoint = process.env.MIZZLE_ENDPOINT;
|
|
152
173
|
if (process.env.MIZZLE_SCHEMA) finalConfig.schema = process.env.MIZZLE_SCHEMA;
|
|
153
174
|
if (process.env.MIZZLE_OUT) finalConfig.out = process.env.MIZZLE_OUT;
|
|
175
|
+
if (process.env.MIZZLE_VERBOSE) finalConfig.verbose = process.env.MIZZLE_VERBOSE === "true";
|
|
176
|
+
if (process.env.MIZZLE_STRICT) finalConfig.strict = process.env.MIZZLE_STRICT === "true";
|
|
154
177
|
|
|
155
178
|
return finalConfig;
|
|
156
179
|
} catch (error) {
|
package/src/index.ts
CHANGED
|
@@ -1,88 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as p from "@clack/prompts";
|
|
3
|
-
import { loadConfig } from "./config";
|
|
4
|
-
import { generateCommand } from "./commands/generate";
|
|
5
|
-
import { initCommand } from "./commands/init";
|
|
6
|
-
import { pushCommand } from "./commands/push";
|
|
7
|
-
import { listCommand } from "./commands/list";
|
|
8
|
-
import { dropCommand } from "./commands/drop";
|
|
9
|
-
|
|
10
|
-
const program = new Command();
|
|
11
|
-
|
|
12
|
-
program
|
|
13
|
-
.name("mizzle")
|
|
14
|
-
.description("Mizzle Migration CLI")
|
|
15
|
-
.version("0.0.1");
|
|
16
|
-
|
|
17
|
-
program
|
|
18
|
-
.command("init")
|
|
19
|
-
.description("Initialize Mizzle configuration")
|
|
20
|
-
.action(async () => {
|
|
21
|
-
try {
|
|
22
|
-
await initCommand();
|
|
23
|
-
} catch (e) {
|
|
24
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
25
|
-
p.log.error(message);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
program
|
|
31
|
-
.command("generate")
|
|
32
|
-
.description("Generate a new migration snapshot and script")
|
|
33
|
-
.option("-n, --name <name>", "Migration name")
|
|
34
|
-
.action(async (options) => {
|
|
35
|
-
try {
|
|
36
|
-
const config = await loadConfig();
|
|
37
|
-
await generateCommand({ config, name: options.name });
|
|
38
|
-
} catch (e) {
|
|
39
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
40
|
-
p.log.error(message);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
program
|
|
46
|
-
.command("push")
|
|
47
|
-
.description("Directly apply schema changes to the target DynamoDB environment")
|
|
48
|
-
.option("-y, --yes", "Skip confirmation")
|
|
49
|
-
.action(async (options) => {
|
|
50
|
-
try {
|
|
51
|
-
const config = await loadConfig();
|
|
52
|
-
await pushCommand({ config, force: options.yes });
|
|
53
|
-
} catch (e) {
|
|
54
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
55
|
-
p.log.error(message);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
program
|
|
61
|
-
.command("list")
|
|
62
|
-
.description("List all existing DynamoDB tables in the environment")
|
|
63
|
-
.action(async () => {
|
|
64
|
-
try {
|
|
65
|
-
const config = await loadConfig();
|
|
66
|
-
await listCommand({ config });
|
|
67
|
-
} catch (e) {
|
|
68
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
69
|
-
p.log.error(message);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
program
|
|
75
|
-
.command("drop")
|
|
76
|
-
.description("Interactive command to select and delete DynamoDB tables")
|
|
77
|
-
.action(async () => {
|
|
78
|
-
try {
|
|
79
|
-
const config = await loadConfig();
|
|
80
|
-
await dropCommand({ config });
|
|
81
|
-
} catch (e) {
|
|
82
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
83
|
-
p.log.error(message);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
program.parse();
|
|
1
|
+
export { defineConfig, type MizzleConfig } from "./config";
|
package/tsup.config.ts
CHANGED
package/CHANGELOG.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# mizzling
|
|
2
|
-
|
|
3
|
-
## 1.0.2
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies
|
|
8
|
-
- @mizzle/shared@0.0.1
|
|
9
|
-
|
|
10
|
-
## 1.0.1
|
|
11
|
-
|
|
12
|
-
### Patch Changes
|
|
13
|
-
|
|
14
|
-
- fix: resolve schema discovery issues in monorepos and stabilize test suite.
|
|
15
|
-
|
|
16
|
-
## 1.0.0
|
|
17
|
-
|
|
18
|
-
### Major Changes
|
|
19
|
-
|
|
20
|
-
- Initial production release of Mizzle ORM and Mizzling CLI.
|
|
21
|
-
Includes:
|
|
22
|
-
- Fluent Query Builder (Select, Insert, Update, Delete)
|
|
23
|
-
- Native Date Support with ISO 8601 storage
|
|
24
|
-
- Relational Queries with Single-Table optimization
|
|
25
|
-
- Atomic Transactions support
|
|
26
|
-
- Migration CLI with schema discovery and push/pull
|
|
27
|
-
- High-performance tsup-based bundling
|
package/dist/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{Command as He}from"commander";import*as b from"@clack/prompts";import{join as X}from"path";import{existsSync as Q}from"fs";import{DynamoDBClient as ee}from"@aws-sdk/client-dynamodb";import{fromIni as te}from"@aws-sdk/credential-provider-ini";import{NodeHttpHandler as ne}from"@smithy/node-http-handler";import oe from"http";import ae from"https";function h(e){let n={keepAlive:!0,maxSockets:1/0},t={region:e.region||"us-east-1",endpoint:e.endpoint,maxAttempts:e.maxAttempts,requestHandler:new ne({httpAgent:new oe.Agent(n),httpsAgent:new ae.Agent(n)})};return e.credentials?t.credentials=e.credentials:e.profile?t.credentials=te({profile:e.profile}):e.endpoint&&(e.endpoint.includes("localhost")||e.endpoint.includes("127.0.0.1"))&&(t.credentials={accessKeyId:"local",secretAccessKey:"local"}),new ee(t)}async function N(e="mizzle.config.ts"){let t=process.env.MIZZLE_CONFIG||X(process.cwd(),e);if(!Q(t))throw new Error(`Could not find ${e} in current directory.`);try{let o=await import(t),a=o.default||o;if(!a||typeof a!="object")throw new Error("Invalid config: default export must be an object");if(!a.schema)throw new Error("Invalid config: missing 'schema' path");if(!a.out)throw new Error("Invalid config: missing 'out' directory");let i={...a};return process.env.MIZZLE_REGION&&(i.region=process.env.MIZZLE_REGION),process.env.MIZZLE_ENDPOINT&&(i.endpoint=process.env.MIZZLE_ENDPOINT),process.env.MIZZLE_SCHEMA&&(i.schema=process.env.MIZZLE_SCHEMA),process.env.MIZZLE_OUT&&(i.out=process.env.MIZZLE_OUT),i}catch(o){if(o instanceof Error&&o.message.startsWith("Invalid config"))throw o;let a=o instanceof Error?o.message:String(o);throw new Error(`Failed to load config: ${a}`)}}var f={COLUMNS:Symbol.for("mizzle:Columns"),INDEXES:Symbol.for("mizzle:Indexes"),SORT_KEY:Symbol.for("mizzle:SortKey"),TABLE_NAME:Symbol.for("mizzle:TableName"),PARTITION_KEY:Symbol.for("mizzle:PartitionKey")},u={ENTITY_NAME:Symbol.for("mizzle:EntityName"),ENTITY_STRATEGY:Symbol.for("mizzle:EntityStrategy"),PHYSICAL_TABLE:Symbol.for("mizzle:PhysicalTable"),COLUMNS:Symbol.for("mizzle:Columns"),ENTITY_KIND:Symbol.for("mizzle:EntityKind")};var v=class{[f.TABLE_NAME]="";[f.INDEXES]=void 0;[f.PARTITION_KEY]={};[f.SORT_KEY]=void 0;static Symbol=f;constructor(e,n){this[f.TABLE_NAME]=e,this[f.PARTITION_KEY]=n.pk.build(this),this[f.SORT_KEY]=n.sk?n.sk.build(this):void 0,this[f.INDEXES]=n.indexes}},O=class{[u.ENTITY_NAME]="";[u.PHYSICAL_TABLE]={};[u.COLUMNS]={};[u.ENTITY_STRATEGY]={};static Symbol=u;constructor(e,n,t,o){this[u.ENTITY_NAME]=e,this[u.PHYSICAL_TABLE]=n,this[u.COLUMNS]=t,this[u.ENTITY_STRATEGY]=o}};var M={COLUMNS:Symbol.for("mizzle:Columns"),INDEXES:Symbol.for("mizzle:Indexes"),SORT_KEY:Symbol.for("mizzle:SortKey"),TABLE_NAME:Symbol.for("mizzle:TableName"),PARTITION_KEY:Symbol.for("mizzle:PartitionKey")},_={ENTITY_NAME:Symbol.for("mizzle:EntityName"),ENTITY_STRATEGY:Symbol.for("mizzle:EntityStrategy"),PHYSICAL_TABLE:Symbol.for("mizzle:PhysicalTable"),COLUMNS:Symbol.for("mizzle:Columns"),ENTITY_KIND:Symbol.for("mizzle:EntityKind")};import ie from"fast-glob";import{stat as re}from"fs/promises";import{resolve as se}from"path";async function w(e){let n=Array.isArray(e.schema)?e.schema:[e.schema],t=[],o=[],a=new Set,i=async r=>{let s=se(process.cwd(),r);if(!a.has(s)){a.add(s);try{let l=await import(s);for(let m in l){let c=l[m];!c||typeof c!="object"||(c instanceof v||c[M.TABLE_NAME]!==void 0?t.push(c):(c instanceof O||c[_.ENTITY_NAME]!==void 0)&&o.push(c))}}catch(l){console.warn(`Failed to import schema file: ${s}`,l)}}};for(let r of n){let s=r,l=!1;try{let c=await re(r);c.isDirectory()?s=`${r}/**/*.{ts,js,tsx,jsx}`:c.isFile()&&(l=!0)}catch{}if(l){await i(r);continue}let m=await ie(s,{absolute:!0});for(let c of m)await i(c)}return{tables:t,entities:o}}import{join as $}from"path";import{writeFile as le,readFile as ce,mkdir as me,readdir as fe}from"fs/promises";import{existsSync as k}from"fs";var j="snapshot.json";async function P(e,n){k(e)||await me(e,{recursive:!0});let t=$(e,j);await le(t,JSON.stringify(n,null,2),"utf-8")}async function Y(e){let n=$(e,j);if(!k(n))return null;let t=await ce(n,"utf-8");return JSON.parse(t)}function E(e){let n={};for(let t of e.tables){let o=e.entities.filter(i=>i[u.PHYSICAL_TABLE]===t),a=pe(t,o);n[a.TableName]=a}return{version:"1",tables:n}}async function R(e){if(!k(e))return"0000";let n=await fe(e),t=-1;for(let o of n){if(!o.endsWith(".ts"))continue;let a=o.match(/^(\d{4})_/);if(a){let i=parseInt(a[1],10);i>t&&(t=i)}}return t===-1?"0000":(t+1).toString().padStart(4,"0")}function pe(e,n){let t=e[f.TABLE_NAME],o=new Map,a=e[f.PARTITION_KEY];o.set(a.name,a.getDynamoType());let i=e[f.SORT_KEY];i&&o.set(i.name,i.getDynamoType());let r=[{AttributeName:a.name,KeyType:"HASH"}];i&&r.push({AttributeName:i.name,KeyType:"RANGE"});let s=[],l=[],m=e[f.INDEXES]||{};for(let[d,y]of Object.entries(m)){let B=y.type,p=y.config;if(B==="gsi"){if(p.pk){let T=K(p.pk,e,n);o.set(p.pk,T)}if(p.sk){let T=K(p.sk,e,n);o.set(p.sk,T)}let x={IndexName:d,KeySchema:[{AttributeName:p.pk,KeyType:"HASH"}],Projection:{ProjectionType:"ALL"}};p.sk&&x.KeySchema.push({AttributeName:p.sk,KeyType:"RANGE"}),s.push(x)}else if(B==="lsi"){if(p.sk){let T=K(p.sk,e,n);o.set(p.sk,T)}let x={IndexName:d,KeySchema:[{AttributeName:a.name,KeyType:"HASH"},{AttributeName:p.sk,KeyType:"RANGE"}],Projection:{ProjectionType:"ALL"}};l.push(x)}}let c=Array.from(o.entries()).map(([d,y])=>({AttributeName:d,AttributeType:y})).sort((d,y)=>d.AttributeName.localeCompare(y.AttributeName));s.sort((d,y)=>(d.IndexName||"").localeCompare(y.IndexName||"")),l.sort((d,y)=>(d.IndexName||"").localeCompare(y.IndexName||""));let S={TableName:t,AttributeDefinitions:c,KeySchema:r};return s.length>0&&(S.GlobalSecondaryIndexes=s),l.length>0&&(S.LocalSecondaryIndexes=l),S}function K(e,n,t){let o=n[f.PARTITION_KEY];if(o.name===e)return o.getDynamoType();let a=n[f.SORT_KEY];if(a&&a.name===e)return a.getDynamoType();for(let i of t){let r=i[u.COLUMNS];if(r){let s=r[e];if(s)return s.getDynamoType()}}throw new Error(`Could not resolve type for column '${e}' in table '${n[f.TABLE_NAME]}'. Ensure it is defined in an Entity.`)}function I(e,n){let t=[],o=E(e).tables,a=n.tables||{},i=new Set([...Object.keys(o),...Object.keys(a)]);for(let r of i){let s=o[r],l=a[r];s&&!l?t.push({type:"create",table:s}):!s&&l?t.push({type:"delete",tableName:r}):s&&l&&(ue(s,l)||t.push({type:"update",tableName:r,changes:["Changed"]}))}return t}function ue(e,n){let t=o=>{let a={...o};return a.AttributeDefinitions=[...a.AttributeDefinitions||[]].sort((i,r)=>(i.AttributeName||"").localeCompare(r.AttributeName||"")),a.GlobalSecondaryIndexes&&(a.GlobalSecondaryIndexes=[...a.GlobalSecondaryIndexes].sort((i,r)=>(i.IndexName||"").localeCompare(r.IndexName||""))),a.LocalSecondaryIndexes&&(a.LocalSecondaryIndexes=[...a.LocalSecondaryIndexes].sort((i,r)=>(i.IndexName||"").localeCompare(r.IndexName||""))),a};return JSON.stringify(t(e))===JSON.stringify(t(n))}import{join as de}from"path";import{writeFile as ye,mkdir as ge}from"fs/promises";import{existsSync as he}from"fs";import{text as be,isCancel as Se,cancel as Te,intro as Ne,outro as U}from"@clack/prompts";async function F(e){Ne("Mizzle Generate");let{config:n}=e,t=e.discoverSchema||w;try{let o=await t(n),a=n.out,i=await Y(a)||{version:"0",tables:{}},r=E(o),s=I(o,i);if(s.length===0){U("No changes detected.");return}console.log(`Detected ${s.length} changes.`);let l=await R(a),m=e.name;if(m||(m=await be({message:"Enter migration name",placeholder:"init",initialValue:"migration",validate(y){if(y.length===0)return"Name is required"}})),Se(m)){Te("Operation cancelled.");return}let c=`${l}_${m}.ts`;he(a)||await ge(a,{recursive:!0});let S=de(a,c),d=Ee(s);await ye(S,d),console.log(`Created migration: ${c}`),await P(a,r),U("Updated snapshot.json")}catch(o){console.error("Error generating migration:",o),process.exit(1)}}function Ee(e){let n=[],t=[];for(let o of e)o.type==="create"?(n.push(`// Create Table: ${o.table.TableName}`),n.push(`await db.createTable("${o.table.TableName}", ${JSON.stringify(o.table,null,2)});
|
|
3
|
-
`),t.unshift(`// Drop Table: ${o.table.TableName}`),t.unshift(`await db.deleteTable("${o.table.TableName}");
|
|
4
|
-
`)):o.type==="delete"?(n.push(`// Drop Table: ${o.tableName}`),n.push(`await db.deleteTable("${o.tableName}");
|
|
5
|
-
`),t.unshift(`// Create Table: ${o.tableName}`),t.unshift(`// TODO: Restore table definition for rollback
|
|
6
|
-
`)):o.type==="update"&&(n.push(`// Update Table: ${o.tableName}`),t.unshift(`// Revert Update Table: ${o.tableName}`));return`import { Mizzle } from "@auri/mizzle";
|
|
7
|
-
|
|
8
|
-
export async function up(db: Mizzle) {
|
|
9
|
-
${n.join(" ")}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function down(db: Mizzle) {
|
|
13
|
-
${t.join(" ")}
|
|
14
|
-
}
|
|
15
|
-
`}import{existsSync as xe,writeFileSync as we}from"fs";import{join as Ie}from"path";import{intro as Ae,outro as Ce,text as A,isCancel as C,cancel as z}from"@clack/prompts";async function G(){let e=Ie(process.cwd(),"mizzle.config.ts");if(xe(e)){console.log("mizzle.config.ts already exists in the current directory. Aborting.");return}Ae("Mizzle Initialization");let n=await A({message:"Where is your schema file or directory located?",placeholder:"./src/schema.ts",initialValue:"./src/schema.ts",validate:r=>{if(!r)return"Schema path is required"}});if(C(n)){z("Operation cancelled.");return}let t=await A({message:"Where should Mizzle store migrations and snapshots?",placeholder:"./migrations",initialValue:"./migrations",validate:r=>{if(!r)return"Output directory is required"}});if(C(t)){z("Operation cancelled.");return}let o=await A({message:"Which AWS region do you want to use?",placeholder:"us-east-1",initialValue:"us-east-1"});if(C(o)){z("Operation cancelled.");return}let a=await A({message:"Do you want to use a custom endpoint (e.g., for local development)?",placeholder:"http://localhost:8000 (optional)"});if(C(a)){z("Operation cancelled.");return}let i=`import { defineConfig } from "@auri/mizzle";
|
|
16
|
-
|
|
17
|
-
export default defineConfig({
|
|
18
|
-
schema: "${n}",
|
|
19
|
-
out: "${t}",
|
|
20
|
-
region: "${o}",
|
|
21
|
-
${a?`endpoint: "${a}",`:'// endpoint: "http://localhost:8000",'}
|
|
22
|
-
});
|
|
23
|
-
`;we(e,i),Ce("mizzle.config.ts created successfully!")}import{ListTablesCommand as ze,DescribeTableCommand as De}from"@aws-sdk/client-dynamodb";async function D(e){let n={},t,o=[];do{let a=await e.send(new ze({ExclusiveStartTableName:t}));a.TableNames&&o.push(...a.TableNames),t=a.LastEvaluatedTableName}while(t);for(let a of o)try{let i=await e.send(new De({TableName:a}));if(i.Table){let r=Le(i.Table);n[a]=r}}catch(i){console.warn(`Failed to describe table ${a}:`,i)}return{version:"remote",tables:n}}function Le(e){let n={TableName:e.TableName,AttributeDefinitions:e.AttributeDefinitions?.map(t=>({AttributeName:t.AttributeName,AttributeType:t.AttributeType})).sort((t,o)=>t.AttributeName.localeCompare(o.AttributeName))||[],KeySchema:e.KeySchema?.map(t=>({AttributeName:t.AttributeName,KeyType:t.KeyType}))||[]};return e.GlobalSecondaryIndexes&&e.GlobalSecondaryIndexes.length>0&&(n.GlobalSecondaryIndexes=e.GlobalSecondaryIndexes.map(t=>({IndexName:t.IndexName,KeySchema:t.KeySchema?.map(o=>({AttributeName:o.AttributeName,KeyType:o.KeyType})),Projection:t.Projection?{ProjectionType:t.Projection.ProjectionType}:void 0})).sort((t,o)=>t.IndexName.localeCompare(o.IndexName))),e.LocalSecondaryIndexes&&e.LocalSecondaryIndexes.length>0&&(n.LocalSecondaryIndexes=e.LocalSecondaryIndexes.map(t=>({IndexName:t.IndexName,KeySchema:t.KeySchema?.map(o=>({AttributeName:o.AttributeName,KeyType:o.KeyType})),Projection:t.Projection?{ProjectionType:t.Projection.ProjectionType}:void 0})).sort((t,o)=>t.IndexName.localeCompare(o.IndexName))),n}import{CreateTableCommand as ve}from"@aws-sdk/client-dynamodb";import{confirm as Oe,isCancel as Me,cancel as _e,intro as Ke,outro as H,spinner as ke}from"@clack/prompts";async function Z(e){Ke("Mizzle Push");let{config:n,force:t}=e,o=e.discoverSchema||w,a=e.client||h(n);try{let i=await o(n),r=await D(a),s=I(i,r);if(s.length===0){H("Remote is up to date.");return}console.log(`Pushing ${s.length} changes to remote...`);let l=t;if(l||(l=await Oe({message:"Do you want to apply these changes?"})),Me(l)||!l){_e("Operation cancelled.");return}let m=ke();m.start("Pushing changes...");for(let c of s)c.type==="create"?(m.message(`Creating table: ${c.table.TableName}`),await a.send(new ve({TableName:c.table.TableName,AttributeDefinitions:c.table.AttributeDefinitions,KeySchema:c.table.KeySchema,GlobalSecondaryIndexes:c.table.GlobalSecondaryIndexes,LocalSecondaryIndexes:c.table.LocalSecondaryIndexes,BillingMode:"PAY_PER_REQUEST"}))):c.type==="delete"?console.log(`Untracked table found: ${c.tableName} (Skipping deletion)`):c.type==="update"&&m.message(`Updating table: ${c.tableName} (Not fully implemented)`);m.stop("Push complete."),H("Done")}catch(i){console.error("Error pushing changes:",i),process.exit(1)}}import"@aws-sdk/client-dynamodb";import{intro as Pe,outro as V,spinner as Ye}from"@clack/prompts";async function W(e){Pe("Mizzle List Tables");let n=e.client||h(e.config),t=Ye();t.start("Fetching remote tables...");try{let o=await D(n);t.stop("Fetched remote tables.");let a=Object.values(o.tables);if(a.length===0){console.log("No tables found in the remote environment."),V("Done");return}console.log(`Found ${a.length} tables:`);for(let i of a){console.log(`- ${i.TableName}`);let r=i.KeySchema.find(l=>l.KeyType==="HASH")?.AttributeName,s=i.KeySchema.find(l=>l.KeyType==="RANGE")?.AttributeName;console.log(` PK: ${r}, SK: ${s||"(none)"}`),i.GlobalSecondaryIndexes&&i.GlobalSecondaryIndexes.length>0&&console.log(` GSIs: ${i.GlobalSecondaryIndexes.map(l=>l.IndexName).join(", ")}`)}V("Done")}catch(o){t.stop("Failed to fetch tables."),console.error("Error listing tables:",o),process.exit(1)}}import{ListTablesCommand as Re,DeleteTableCommand as Be}from"@aws-sdk/client-dynamodb";import{intro as $e,outro as L,multiselect as je,confirm as Ue,isCancel as q,cancel as Fe,spinner as Ge}from"@clack/prompts";async function J(e){$e("Mizzle Drop Tables");let n=e.client||h(e.config);try{let t=new Re({}),a=(await n.send(t)).TableNames||[];if(a.length===0){console.log("No tables found in the remote environment."),L("Done");return}let i=await je({message:"Select tables to DELETE (This action is irreversible!)",options:a.map(m=>({value:m,label:m}))});if(q(i)){Fe("Operation cancelled.");return}let r=i;if(r.length===0){console.log("No tables selected."),L("Done");return}let s=await Ue({message:`Are you SURE you want to delete ${r.length} table(s)?`});if(q(s)||!s){console.log("Operation cancelled."),L("Done");return}let l=Ge();l.start("Deleting tables...");for(let m of r)l.message(`Deleting ${m}...`),await n.send(new Be({TableName:m}));l.stop("All selected tables deleted."),L("Done")}catch(t){console.error("Error dropping tables:",t),process.exit(1)}}var g=new He;g.name("mizzle").description("Mizzle Migration CLI").version("0.0.1");g.command("init").description("Initialize Mizzle configuration").action(async()=>{try{await G()}catch(e){let n=e instanceof Error?e.message:String(e);b.log.error(n),process.exit(1)}});g.command("generate").description("Generate a new migration snapshot and script").option("-n, --name <name>","Migration name").action(async e=>{try{let n=await N();await F({config:n,name:e.name})}catch(n){let t=n instanceof Error?n.message:String(n);b.log.error(t),process.exit(1)}});g.command("push").description("Directly apply schema changes to the target DynamoDB environment").option("-y, --yes","Skip confirmation").action(async e=>{try{let n=await N();await Z({config:n,force:e.yes})}catch(n){let t=n instanceof Error?n.message:String(n);b.log.error(t),process.exit(1)}});g.command("list").description("List all existing DynamoDB tables in the environment").action(async()=>{try{let e=await N();await W({config:e})}catch(e){let n=e instanceof Error?e.message:String(e);b.log.error(n),process.exit(1)}});g.command("drop").description("Interactive command to select and delete DynamoDB tables").action(async()=>{try{let e=await N();await J({config:e})}catch(e){let n=e instanceof Error?e.message:String(e);b.log.error(n),process.exit(1)}});g.parse();
|