baasix 0.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.
- package/README.md +355 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +2521 -0
- package/package.json +56 -0
- package/src/commands/extension.ts +447 -0
- package/src/commands/generate.ts +485 -0
- package/src/commands/init.ts +1409 -0
- package/src/commands/migrate.ts +573 -0
- package/src/index.ts +39 -0
- package/src/utils/api-client.ts +121 -0
- package/src/utils/get-config.ts +69 -0
- package/src/utils/get-package-info.ts +12 -0
- package/src/utils/package-manager.ts +62 -0
- package/tsconfig.json +19 -0
- package/tsup.config.ts +16 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from "axios";
|
|
2
|
+
import { BaasixConfig } from "./get-config.js";
|
|
3
|
+
|
|
4
|
+
let client: AxiosInstance | null = null;
|
|
5
|
+
let authToken: string | null = null;
|
|
6
|
+
|
|
7
|
+
export async function createApiClient(config: BaasixConfig): Promise<AxiosInstance> {
|
|
8
|
+
if (client) {
|
|
9
|
+
return client;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
client = axios.create({
|
|
13
|
+
baseURL: config.url,
|
|
14
|
+
timeout: 30000,
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Authenticate if credentials provided
|
|
21
|
+
if (config.token) {
|
|
22
|
+
authToken = config.token;
|
|
23
|
+
client.defaults.headers.common["Authorization"] = `Bearer ${authToken}`;
|
|
24
|
+
} else if (config.email && config.password) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await client.post("/auth/login", {
|
|
27
|
+
email: config.email,
|
|
28
|
+
password: config.password,
|
|
29
|
+
});
|
|
30
|
+
authToken = response.data.token;
|
|
31
|
+
client.defaults.headers.common["Authorization"] = `Bearer ${authToken}`;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`Failed to authenticate: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return client;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getAuthToken(): string | null {
|
|
41
|
+
return authToken;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SchemaInfo {
|
|
45
|
+
collectionName: string;
|
|
46
|
+
schema: {
|
|
47
|
+
name: string;
|
|
48
|
+
timestamps?: boolean;
|
|
49
|
+
paranoid?: boolean;
|
|
50
|
+
fields: Record<string, FieldDefinition>;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface FieldDefinition {
|
|
55
|
+
type?: string;
|
|
56
|
+
primaryKey?: boolean;
|
|
57
|
+
allowNull?: boolean;
|
|
58
|
+
unique?: boolean;
|
|
59
|
+
defaultValue?: unknown;
|
|
60
|
+
values?: Record<string, unknown> | string[]; // Can be object with config or array for enums
|
|
61
|
+
validate?: {
|
|
62
|
+
min?: number;
|
|
63
|
+
max?: number;
|
|
64
|
+
len?: [number, number];
|
|
65
|
+
isEmail?: boolean;
|
|
66
|
+
isUrl?: boolean;
|
|
67
|
+
isIP?: boolean;
|
|
68
|
+
isUUID?: number;
|
|
69
|
+
regex?: string;
|
|
70
|
+
[key: string]: unknown;
|
|
71
|
+
};
|
|
72
|
+
// Relation fields
|
|
73
|
+
relType?: "BelongsTo" | "HasOne" | "HasMany" | "BelongsToMany";
|
|
74
|
+
target?: string;
|
|
75
|
+
foreignKey?: string;
|
|
76
|
+
as?: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
SystemGenerated?: boolean | string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function fetchSchemas(config: BaasixConfig): Promise<SchemaInfo[]> {
|
|
82
|
+
const client = await createApiClient(config);
|
|
83
|
+
const response = await client.get("/schemas", {
|
|
84
|
+
params: { limit: -1 },
|
|
85
|
+
});
|
|
86
|
+
return response.data.data || [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface MigrationInfo {
|
|
90
|
+
id: string;
|
|
91
|
+
version: string;
|
|
92
|
+
name: string;
|
|
93
|
+
status: string;
|
|
94
|
+
type: string;
|
|
95
|
+
executedAt?: string;
|
|
96
|
+
batch?: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function fetchMigrations(config: BaasixConfig): Promise<MigrationInfo[]> {
|
|
100
|
+
const client = await createApiClient(config);
|
|
101
|
+
const response = await client.get("/migrations");
|
|
102
|
+
return response.data.data || [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function runMigrations(config: BaasixConfig, options?: {
|
|
106
|
+
dryRun?: boolean;
|
|
107
|
+
step?: number;
|
|
108
|
+
}): Promise<{ success: boolean; message: string; migrations?: MigrationInfo[] }> {
|
|
109
|
+
const client = await createApiClient(config);
|
|
110
|
+
const response = await client.post("/migrations/run", options || {});
|
|
111
|
+
return response.data;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function rollbackMigrations(config: BaasixConfig, options?: {
|
|
115
|
+
step?: number;
|
|
116
|
+
batch?: number;
|
|
117
|
+
}): Promise<{ success: boolean; message: string }> {
|
|
118
|
+
const client = await createApiClient(config);
|
|
119
|
+
const response = await client.post("/migrations/rollback", options || {});
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parse } from "dotenv";
|
|
5
|
+
|
|
6
|
+
export interface BaasixConfig {
|
|
7
|
+
url: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
token?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load Baasix configuration from .env file or environment variables
|
|
15
|
+
*/
|
|
16
|
+
export async function getConfig(cwd: string): Promise<BaasixConfig | null> {
|
|
17
|
+
// Check for .env file
|
|
18
|
+
const envPath = path.join(cwd, ".env");
|
|
19
|
+
let envVars: Record<string, string> = {};
|
|
20
|
+
|
|
21
|
+
if (existsSync(envPath)) {
|
|
22
|
+
const envContent = await fs.readFile(envPath, "utf-8");
|
|
23
|
+
envVars = parse(envContent);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Merge with process.env (process.env takes precedence)
|
|
27
|
+
const mergedEnv = { ...envVars, ...process.env };
|
|
28
|
+
|
|
29
|
+
const url = mergedEnv.BAASIX_URL || mergedEnv.API_URL || "http://localhost:8056";
|
|
30
|
+
const email = mergedEnv.BAASIX_EMAIL || mergedEnv.ADMIN_EMAIL;
|
|
31
|
+
const password = mergedEnv.BAASIX_PASSWORD || mergedEnv.ADMIN_PASSWORD;
|
|
32
|
+
const token = mergedEnv.BAASIX_TOKEN || mergedEnv.BAASIX_AUTH_TOKEN;
|
|
33
|
+
|
|
34
|
+
if (!url) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
url,
|
|
40
|
+
email,
|
|
41
|
+
password,
|
|
42
|
+
token,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load configuration from baasix.config.js or baasix.config.ts if exists
|
|
48
|
+
*/
|
|
49
|
+
export async function loadConfigFile(cwd: string): Promise<Record<string, unknown> | null> {
|
|
50
|
+
const possiblePaths = [
|
|
51
|
+
"baasix.config.js",
|
|
52
|
+
"baasix.config.mjs",
|
|
53
|
+
"baasix.config.ts",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const configPath of possiblePaths) {
|
|
57
|
+
const fullPath = path.join(cwd, configPath);
|
|
58
|
+
if (existsSync(fullPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const config = await import(fullPath);
|
|
61
|
+
return config.default || config;
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore import errors
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
export async function getPackageInfo(): Promise<Record<string, unknown>> {
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const packageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
10
|
+
const content = await fs.readFile(packageJsonPath, "utf-8");
|
|
11
|
+
return JSON.parse(content);
|
|
12
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export type PackageManager = "npm" | "pnpm" | "bun" | "yarn";
|
|
6
|
+
|
|
7
|
+
export function detectPackageManager(cwd: string): PackageManager {
|
|
8
|
+
if (existsSync(path.join(cwd, "bun.lockb"))) {
|
|
9
|
+
return "bun";
|
|
10
|
+
}
|
|
11
|
+
if (existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
12
|
+
return "pnpm";
|
|
13
|
+
}
|
|
14
|
+
if (existsSync(path.join(cwd, "yarn.lock"))) {
|
|
15
|
+
return "yarn";
|
|
16
|
+
}
|
|
17
|
+
return "npm";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function installDependencies({
|
|
21
|
+
dependencies,
|
|
22
|
+
packageManager,
|
|
23
|
+
cwd,
|
|
24
|
+
dev = false,
|
|
25
|
+
}: {
|
|
26
|
+
dependencies: string[];
|
|
27
|
+
packageManager: PackageManager;
|
|
28
|
+
cwd: string;
|
|
29
|
+
dev?: boolean;
|
|
30
|
+
}): Promise<boolean> {
|
|
31
|
+
let installCommand: string;
|
|
32
|
+
const devFlag = dev ? (packageManager === "npm" ? " --save-dev" : " -D") : "";
|
|
33
|
+
|
|
34
|
+
switch (packageManager) {
|
|
35
|
+
case "npm":
|
|
36
|
+
installCommand = `npm install${devFlag}`;
|
|
37
|
+
break;
|
|
38
|
+
case "pnpm":
|
|
39
|
+
installCommand = `pnpm add${devFlag}`;
|
|
40
|
+
break;
|
|
41
|
+
case "bun":
|
|
42
|
+
installCommand = `bun add${devFlag}`;
|
|
43
|
+
break;
|
|
44
|
+
case "yarn":
|
|
45
|
+
installCommand = `yarn add${devFlag}`;
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
throw new Error("Invalid package manager");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const command = `${installCommand} ${dependencies.join(" ")}`;
|
|
52
|
+
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
exec(command, { cwd }, (error, stdout, stderr) => {
|
|
55
|
+
if (error) {
|
|
56
|
+
reject(new Error(stderr || error.message));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
resolve(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"resolveJsonModule": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["esm"],
|
|
6
|
+
outExtension() {
|
|
7
|
+
return {
|
|
8
|
+
js: ".mjs",
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
dts: true,
|
|
12
|
+
clean: true,
|
|
13
|
+
banner: {
|
|
14
|
+
js: "#!/usr/bin/env node",
|
|
15
|
+
},
|
|
16
|
+
});
|