@aurios/mizzling 1.1.1 → 1.1.3
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/.turbo/turbo-build.log +15 -0
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/chunk-DKDRM5WU.js +2 -0
- package/dist/cli.js +23 -0
- package/dist/index.js +2 -0
- package/package.json +38 -19
- package/src/cli.ts +26 -29
- package/src/commands/drop.ts +51 -51
- package/src/commands/generate.ts +88 -83
- package/src/commands/init.ts +1 -1
- package/src/commands/list.ts +39 -37
- package/src/commands/push.ts +54 -54
- package/src/config.ts +14 -14
- package/src/discovery.ts +33 -25
- package/test/config-expansion.test.ts +164 -0
- package/test/config.test.ts +72 -0
- package/test/define-config-integration.test.ts +49 -0
- package/test/discovery.test.ts +117 -0
- package/test/drop.test.ts +145 -0
- package/test/e2e-env.test.ts +87 -0
- package/test/e2e.test.ts +135 -0
- package/test/generate.test.ts +116 -0
- package/test/init-command.test.ts +78 -0
- package/test/init.test.ts +22 -0
- package/test/interactive.test.ts +96 -0
- package/test/list.test.ts +58 -0
- package/test/mizzling-exports.test.ts +18 -0
- package/test/push.test.ts +88 -0
- package/tsconfig.json +33 -11
- package/tsup.config.ts +5 -5
- package/vitest.config.ts +8 -0
package/src/commands/generate.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type MizzleConfig } from "../config";
|
|
2
2
|
import { discoverSchema } from "../discovery";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
loadSnapshot,
|
|
5
|
+
saveSnapshot,
|
|
6
|
+
generateSnapshot,
|
|
7
|
+
getNextMigrationVersion,
|
|
8
|
+
} from "@aurios/mizzle/snapshot";
|
|
4
9
|
import { compareSchema, type SchemaChange } from "@aurios/mizzle/diff";
|
|
5
10
|
import { join } from "path";
|
|
6
11
|
import { writeFile, mkdir } from "fs/promises";
|
|
@@ -8,102 +13,102 @@ import { existsSync } from "fs";
|
|
|
8
13
|
import { text, isCancel, cancel, intro, outro } from "@clack/prompts";
|
|
9
14
|
|
|
10
15
|
interface GenerateOptions {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
config: MizzleConfig;
|
|
17
|
+
name?: string;
|
|
18
|
+
discoverSchema?: typeof discoverSchema;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export async function generateCommand(options: GenerateOptions) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (isCancel(name)) {
|
|
56
|
-
cancel("Operation cancelled.");
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const filename = `${version}_${name}.ts`;
|
|
61
|
-
if (!existsSync(migrationsDir)) {
|
|
62
|
-
await mkdir(migrationsDir, { recursive: true });
|
|
63
|
-
}
|
|
64
|
-
const filePath = join(migrationsDir, filename);
|
|
65
|
-
|
|
66
|
-
const scriptContent = generateMigrationScript(changes);
|
|
67
|
-
await writeFile(filePath, scriptContent);
|
|
68
|
-
console.log(`Created migration: ${filename}`);
|
|
69
|
-
|
|
70
|
-
// 5. Save new Snapshot
|
|
71
|
-
await saveSnapshot(migrationsDir, currentSnapshot);
|
|
72
|
-
outro("Updated snapshot.json");
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error("Error generating migration:", error);
|
|
75
|
-
process.exit(1);
|
|
22
|
+
intro("Mizzle Generate");
|
|
23
|
+
const { config } = options;
|
|
24
|
+
const discover = options.discoverSchema || discoverSchema;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 1. Discover Schema
|
|
28
|
+
const schema = await discover(config); // Returns { tables, entities }
|
|
29
|
+
|
|
30
|
+
// 2. Load Snapshot
|
|
31
|
+
const migrationsDir = config.out;
|
|
32
|
+
const existingSnapshot = (await loadSnapshot(migrationsDir)) || { version: "0", tables: {} };
|
|
33
|
+
|
|
34
|
+
// 3. Compare
|
|
35
|
+
const currentSnapshot = generateSnapshot(schema);
|
|
36
|
+
const changes = compareSchema(schema, existingSnapshot);
|
|
37
|
+
|
|
38
|
+
if (changes.length === 0) {
|
|
39
|
+
outro("No changes detected.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`Detected ${changes.length} changes.`);
|
|
44
|
+
|
|
45
|
+
// 4. Generate Migration Script
|
|
46
|
+
const version = await getNextMigrationVersion(migrationsDir);
|
|
47
|
+
|
|
48
|
+
let name = options.name;
|
|
49
|
+
if (!name) {
|
|
50
|
+
name = (await text({
|
|
51
|
+
message: "Enter migration name",
|
|
52
|
+
placeholder: "init",
|
|
53
|
+
initialValue: "migration",
|
|
54
|
+
validate(value) {
|
|
55
|
+
if (value.length === 0) return "Name is required";
|
|
56
|
+
},
|
|
57
|
+
})) as string;
|
|
76
58
|
}
|
|
59
|
+
|
|
60
|
+
if (isCancel(name)) {
|
|
61
|
+
cancel("Operation cancelled.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const filename = `${version}_${name}.ts`;
|
|
66
|
+
if (!existsSync(migrationsDir)) {
|
|
67
|
+
await mkdir(migrationsDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
const filePath = join(migrationsDir, filename);
|
|
70
|
+
|
|
71
|
+
const scriptContent = generateMigrationScript(changes);
|
|
72
|
+
await writeFile(filePath, scriptContent);
|
|
73
|
+
console.log(`Created migration: ${filename}`);
|
|
74
|
+
|
|
75
|
+
// 5. Save new Snapshot
|
|
76
|
+
await saveSnapshot(migrationsDir, currentSnapshot);
|
|
77
|
+
outro("Updated snapshot.json");
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error("Error generating migration:", error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
function generateMigrationScript(changes: SchemaChange[]): string {
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
const upSteps: string[] = [];
|
|
86
|
+
const downSteps: string[] = [];
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
for (const change of changes) {
|
|
89
|
+
if (change.type === "create") {
|
|
90
|
+
upSteps.push(`// Create Table: ${change.table.TableName}`);
|
|
91
|
+
upSteps.push(`await db.createTable("${change.table.TableName}", ${JSON.stringify(change.table, null, 2)});
|
|
87
92
|
`);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
|
|
94
|
+
downSteps.unshift(`// Drop Table: ${change.table.TableName}`);
|
|
95
|
+
downSteps.unshift(`await db.deleteTable("${change.table.TableName}");
|
|
91
96
|
`);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
} else if (change.type === "delete") {
|
|
98
|
+
upSteps.push(`// Drop Table: ${change.tableName}`);
|
|
99
|
+
upSteps.push(`await db.deleteTable("${change.tableName}");
|
|
95
100
|
`);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
|
|
102
|
+
downSteps.unshift(`// Create Table: ${change.tableName}`);
|
|
103
|
+
downSteps.unshift(`// TODO: Restore table definition for rollback
|
|
99
104
|
`);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
105
|
+
} else if (change.type === "update") {
|
|
106
|
+
upSteps.push(`// Update Table: ${change.tableName}`);
|
|
107
|
+
downSteps.unshift(`// Revert Update Table: ${change.tableName}`);
|
|
104
108
|
}
|
|
109
|
+
}
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
return `import { Mizzle } from "@aurios/mizzle";
|
|
107
112
|
|
|
108
113
|
export async function up(db: Mizzle) {
|
|
109
114
|
${upSteps.join(" ")}
|
package/src/commands/init.ts
CHANGED
|
@@ -67,7 +67,7 @@ export default defineConfig({
|
|
|
67
67
|
schema: "${schema}",
|
|
68
68
|
out: "${out}",
|
|
69
69
|
region: "${region}",
|
|
70
|
-
${endpoint ? `endpoint: "${endpoint}",` :
|
|
70
|
+
${endpoint ? `endpoint: "${endpoint}",` : '// endpoint: "http://localhost:8000",'}
|
|
71
71
|
});
|
|
72
72
|
`;
|
|
73
73
|
|
package/src/commands/list.ts
CHANGED
|
@@ -4,45 +4,47 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
|
4
4
|
import { intro, outro, spinner } from "@clack/prompts";
|
|
5
5
|
|
|
6
6
|
interface ListOptions {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
config: MizzleConfig;
|
|
8
|
+
client?: DynamoDBClient;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export async function listCommand(options: ListOptions) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log(`Found ${tables.length} tables:`);
|
|
31
|
-
for (const table of tables) {
|
|
32
|
-
console.log(`- ${table.TableName}`);
|
|
33
|
-
const pk = table.KeySchema.find(k => k.KeyType === "HASH")?.AttributeName;
|
|
34
|
-
const sk = table.KeySchema.find(k => k.KeyType === "RANGE")?.AttributeName;
|
|
35
|
-
console.log(` PK: ${pk}, SK: ${sk || "(none)"}`);
|
|
36
|
-
|
|
37
|
-
if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
|
|
38
|
-
console.log(` GSIs: ${table.GlobalSecondaryIndexes.map(g => g.IndexName).join(", ")}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
outro("Done");
|
|
43
|
-
} catch (error) {
|
|
44
|
-
s.stop("Failed to fetch tables.");
|
|
45
|
-
console.error("Error listing tables:", error);
|
|
46
|
-
process.exit(1);
|
|
12
|
+
intro("Mizzle List Tables");
|
|
13
|
+
|
|
14
|
+
const client = options.client || getClient(options.config);
|
|
15
|
+
const s = spinner();
|
|
16
|
+
s.start("Fetching remote tables...");
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const snapshot = await getRemoteSnapshot(client);
|
|
20
|
+
s.stop("Fetched remote tables.");
|
|
21
|
+
|
|
22
|
+
const tables = Object.values(snapshot.tables);
|
|
23
|
+
|
|
24
|
+
if (tables.length === 0) {
|
|
25
|
+
console.log("No tables found in the remote environment.");
|
|
26
|
+
outro("Done");
|
|
27
|
+
return;
|
|
47
28
|
}
|
|
29
|
+
|
|
30
|
+
console.log(`Found ${tables.length} tables:`);
|
|
31
|
+
for (const table of tables) {
|
|
32
|
+
console.log(`- ${table.TableName}`);
|
|
33
|
+
const pk = table.KeySchema.find((k: any) => k.KeyType === "HASH")?.AttributeName;
|
|
34
|
+
const sk = table.KeySchema.find((k: any) => k.KeyType === "RANGE")?.AttributeName;
|
|
35
|
+
console.log(` PK: ${pk}, SK: ${sk || "(none)"}`);
|
|
36
|
+
|
|
37
|
+
if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) {
|
|
38
|
+
console.log(
|
|
39
|
+
` GSIs: ${table.GlobalSecondaryIndexes.map((g: any) => g.IndexName).join(", ")}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
outro("Done");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
s.stop("Failed to fetch tables.");
|
|
47
|
+
console.error("Error listing tables:", error);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
48
50
|
}
|
package/src/commands/push.ts
CHANGED
|
@@ -6,70 +6,70 @@ 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 {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
config: MizzleConfig;
|
|
10
|
+
force?: boolean;
|
|
11
|
+
discoverSchema?: typeof discoverSchema;
|
|
12
|
+
client?: DynamoDBClient;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export async function pushCommand(options: PushOptions) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const client = options.client || getClient(config);
|
|
16
|
+
intro("Mizzle Push");
|
|
17
|
+
const { config, force } = options;
|
|
18
|
+
const discover = options.discoverSchema || discoverSchema;
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
const schema = await discover(config);
|
|
24
|
-
const remoteSnapshot = await getRemoteSnapshot(client);
|
|
20
|
+
const client = options.client || getClient(config);
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
try {
|
|
23
|
+
const schema = await discover(config);
|
|
24
|
+
const remoteSnapshot = await getRemoteSnapshot(client);
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
outro("Remote is up to date.");
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
26
|
+
const changes = compareSchema(schema, remoteSnapshot);
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
if (changes.length === 0) {
|
|
29
|
+
outro("Remote is up to date.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`Pushing ${changes.length} changes to remote...`);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
let shouldContinue = force;
|
|
36
|
+
if (!shouldContinue) {
|
|
37
|
+
shouldContinue = (await confirm({
|
|
38
|
+
message: "Do you want to apply these changes?",
|
|
39
|
+
})) as boolean;
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (isCancel(shouldContinue) || !shouldContinue) {
|
|
43
|
+
cancel("Operation cancelled.");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const s = spinner();
|
|
48
|
+
s.start("Pushing changes...");
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
s.stop("Push complete.");
|
|
70
|
-
outro("Done");
|
|
71
|
-
} catch (error) {
|
|
72
|
-
console.error("Error pushing changes:", error);
|
|
73
|
-
process.exit(1);
|
|
50
|
+
for (const change of changes) {
|
|
51
|
+
if (change.type === "create") {
|
|
52
|
+
s.message(`Creating table: ${change.table.TableName}`);
|
|
53
|
+
await client.send(
|
|
54
|
+
new CreateTableCommand({
|
|
55
|
+
TableName: change.table.TableName,
|
|
56
|
+
AttributeDefinitions: change.table.AttributeDefinitions,
|
|
57
|
+
KeySchema: change.table.KeySchema,
|
|
58
|
+
GlobalSecondaryIndexes: change.table.GlobalSecondaryIndexes,
|
|
59
|
+
LocalSecondaryIndexes: change.table.LocalSecondaryIndexes,
|
|
60
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
} else if (change.type === "delete") {
|
|
64
|
+
console.log(`Untracked table found: ${change.tableName} (Skipping deletion)`);
|
|
65
|
+
} else if (change.type === "update") {
|
|
66
|
+
s.message(`Updating table: ${change.tableName} (Not fully implemented)`);
|
|
67
|
+
}
|
|
74
68
|
}
|
|
69
|
+
s.stop("Push complete.");
|
|
70
|
+
outro("Done");
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error("Error pushing changes:", error);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
75
|
}
|
package/src/config.ts
CHANGED
|
@@ -66,20 +66,20 @@ export interface MizzleConfig {
|
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Helper function to define the Mizzle CLI configuration with type safety and autocompletion.
|
|
69
|
-
*
|
|
69
|
+
*
|
|
70
70
|
* Typically used in a `mizzle.config.ts` file at the root of your project.
|
|
71
|
-
*
|
|
71
|
+
*
|
|
72
72
|
* @example
|
|
73
73
|
* ```ts
|
|
74
74
|
* import { defineConfig } from "@aurios/mizzling";
|
|
75
|
-
*
|
|
75
|
+
*
|
|
76
76
|
* export default defineConfig({
|
|
77
77
|
* schema: "./src/schema.ts",
|
|
78
78
|
* out: "./mizzle",
|
|
79
79
|
* region: "us-east-1",
|
|
80
80
|
* });
|
|
81
81
|
* ```
|
|
82
|
-
*
|
|
82
|
+
*
|
|
83
83
|
* @param config The Mizzle configuration object.
|
|
84
84
|
* @returns The same configuration object, validated by TypeScript.
|
|
85
85
|
*/
|
|
@@ -89,20 +89,20 @@ export function defineConfig(config: MizzleConfig): MizzleConfig {
|
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Creates a configured DynamoDBClient instance based on the provided configuration.
|
|
92
|
-
*
|
|
92
|
+
*
|
|
93
93
|
* It prioritizes credentials in the following order:
|
|
94
94
|
* 1. Explicitly provided `credentials` object.
|
|
95
95
|
* 2. Explicitly provided AWS `profile`.
|
|
96
96
|
* 3. Default "local" credentials if the endpoint is localhost/127.0.0.1.
|
|
97
97
|
* 4. Default AWS SDK credential provider chain (environment variables, IAM roles, etc.).
|
|
98
|
-
*
|
|
98
|
+
*
|
|
99
99
|
* @param config The Mizzle configuration.
|
|
100
100
|
* @returns A configured DynamoDBClient instance.
|
|
101
101
|
*/
|
|
102
102
|
export function getClient(config: MizzleConfig): DynamoDBClient {
|
|
103
103
|
const agentOptions = {
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
keepAlive: true,
|
|
105
|
+
maxSockets: Infinity,
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
const clientConfig: DynamoDBClientConfig = {
|
|
@@ -110,8 +110,8 @@ export function getClient(config: MizzleConfig): DynamoDBClient {
|
|
|
110
110
|
endpoint: config.endpoint,
|
|
111
111
|
maxAttempts: config.maxAttempts,
|
|
112
112
|
requestHandler: new NodeHttpHandler({
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
httpAgent: new http.Agent(agentOptions),
|
|
114
|
+
httpsAgent: new https.Agent(agentOptions),
|
|
115
115
|
}),
|
|
116
116
|
};
|
|
117
117
|
|
|
@@ -134,10 +134,10 @@ export function getClient(config: MizzleConfig): DynamoDBClient {
|
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
136
|
* Loads the Mizzle configuration from a file (defaulting to mizzle.config.ts).
|
|
137
|
-
*
|
|
137
|
+
*
|
|
138
138
|
* Environment variables (MIZZLE_REGION, MIZZLE_ENDPOINT, MIZZLE_SCHEMA, MIZZLE_OUT)
|
|
139
139
|
* will override values provided in the configuration file.
|
|
140
|
-
*
|
|
140
|
+
*
|
|
141
141
|
* @param configName The name of the config file to load.
|
|
142
142
|
* @returns A promise that resolves to the loaded and overridden configuration.
|
|
143
143
|
* @throws Error if the configuration file is missing or invalid.
|
|
@@ -155,7 +155,7 @@ export async function loadConfig(configName = "mizzle.config.ts"): Promise<Mizzl
|
|
|
155
155
|
const config = imported.default || imported;
|
|
156
156
|
|
|
157
157
|
if (!config || typeof config !== "object") {
|
|
158
|
-
|
|
158
|
+
throw new Error("Invalid config: default export must be an object");
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (!config.schema) {
|
|
@@ -178,7 +178,7 @@ export async function loadConfig(configName = "mizzle.config.ts"): Promise<Mizzl
|
|
|
178
178
|
return finalConfig;
|
|
179
179
|
} catch (error) {
|
|
180
180
|
if (error instanceof Error && error.message.startsWith("Invalid config")) {
|
|
181
|
-
|
|
181
|
+
throw error;
|
|
182
182
|
}
|
|
183
183
|
const message = error instanceof Error ? error.message : String(error);
|
|
184
184
|
throw new Error(`Failed to load config: ${message}`);
|
package/src/discovery.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type MizzleConfig } from "./config";
|
|
2
2
|
import { PhysicalTable, Entity } from "@aurios/mizzle/table";
|
|
3
|
-
import { TABLE_SYMBOLS, ENTITY_SYMBOLS } from "@
|
|
3
|
+
import { TABLE_SYMBOLS, ENTITY_SYMBOLS } from "@repo/shared";
|
|
4
4
|
import fg from "fast-glob";
|
|
5
5
|
import { stat } from "fs/promises";
|
|
6
6
|
import { resolve } from "path";
|
|
7
7
|
|
|
8
|
-
export async function discoverSchema(
|
|
8
|
+
export async function discoverSchema(
|
|
9
|
+
config: MizzleConfig,
|
|
10
|
+
): Promise<{ tables: PhysicalTable[]; entities: Entity[] }> {
|
|
9
11
|
const schemaPatterns = Array.isArray(config.schema) ? config.schema : [config.schema];
|
|
10
12
|
const tables: PhysicalTable[] = [];
|
|
11
13
|
const entities: Entity[] = [];
|
|
@@ -15,47 +17,53 @@ export async function discoverSchema(config: MizzleConfig): Promise<{ tables: Ph
|
|
|
15
17
|
const absolutePath = resolve(process.cwd(), file);
|
|
16
18
|
if (scannedFiles.has(absolutePath)) return;
|
|
17
19
|
scannedFiles.add(absolutePath);
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const imported = await import(absolutePath);
|
|
23
|
+
for (const key in imported) {
|
|
24
|
+
const exportVal = imported[key];
|
|
25
|
+
if (!exportVal || typeof exportVal !== "object") continue;
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
exportVal instanceof PhysicalTable ||
|
|
29
|
+
exportVal[TABLE_SYMBOLS.TABLE_NAME] !== undefined
|
|
30
|
+
) {
|
|
31
|
+
tables.push(exportVal as PhysicalTable);
|
|
32
|
+
} else if (
|
|
33
|
+
exportVal instanceof Entity ||
|
|
34
|
+
exportVal[ENTITY_SYMBOLS.ENTITY_NAME] !== undefined
|
|
35
|
+
) {
|
|
36
|
+
entities.push(exportVal as Entity);
|
|
30
37
|
}
|
|
38
|
+
}
|
|
31
39
|
} catch (e) {
|
|
32
|
-
|
|
40
|
+
console.warn(`Failed to import schema file: ${absolutePath}`, e);
|
|
33
41
|
}
|
|
34
42
|
};
|
|
35
43
|
|
|
36
44
|
for (const pattern of schemaPatterns) {
|
|
37
45
|
let searchPattern = pattern;
|
|
38
46
|
let isDirectFile = false;
|
|
39
|
-
|
|
47
|
+
|
|
40
48
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const stats = await stat(pattern);
|
|
50
|
+
if (stats.isDirectory()) {
|
|
51
|
+
searchPattern = `${pattern}/**/*.{ts,js,tsx,jsx}`;
|
|
52
|
+
} else if (stats.isFile()) {
|
|
53
|
+
isDirectFile = true;
|
|
54
|
+
}
|
|
47
55
|
} catch {
|
|
48
|
-
|
|
56
|
+
// Not a file/dir, assume it's a glob pattern
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
if (isDirectFile) {
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
await processFile(pattern);
|
|
61
|
+
continue;
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
const files = await fg(searchPattern, { absolute: true });
|
|
57
65
|
for (const file of files) {
|
|
58
|
-
|
|
66
|
+
await processFile(file);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
|