@snowieedev/shipkit 1.0.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/dist/commands/init.js +67 -0
- package/dist/commands/login.js +60 -0
- package/dist/commands/logout.js +15 -0
- package/dist/commands/providers.js +44 -0
- package/dist/index.js +38 -0
- package/dist/lib/api.js +32 -0
- package/dist/lib/storage.js +27 -0
- package/dist/registry/features/index.js +39 -0
- package/dist/registry/templates/index.js +25 -0
- package/package.json +49 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { api } from '../lib/api.js';
|
|
8
|
+
import { getAuth } from '../lib/storage.js';
|
|
9
|
+
export const initCommand = new Command('init')
|
|
10
|
+
.description('Create a ShipKit project')
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const auth = getAuth();
|
|
13
|
+
if (!auth || !auth.token) {
|
|
14
|
+
console.log(`\n${chalk.red('✗')} Unauthorized\n\nPlease run ${chalk.cyan('shipkit login')} first.\n`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
console.log('');
|
|
18
|
+
const answers = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: 'input',
|
|
21
|
+
name: 'projectName',
|
|
22
|
+
message: 'Project name\n›',
|
|
23
|
+
default: path.basename(process.cwd()),
|
|
24
|
+
validate: (input) => {
|
|
25
|
+
if (!input.trim())
|
|
26
|
+
return 'Project name is required';
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'list',
|
|
32
|
+
name: 'framework',
|
|
33
|
+
message: 'Framework\n›',
|
|
34
|
+
choices: ['Next.js', 'React (Vite)', 'Vue', 'SvelteKit', 'Node.js Backend'],
|
|
35
|
+
default: 'Next.js'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'checkbox',
|
|
39
|
+
name: 'providers',
|
|
40
|
+
message: 'Providers\n›',
|
|
41
|
+
choices: ['Supabase', 'Resend', 'Stripe', 'PostHog', 'Vercel KV']
|
|
42
|
+
}
|
|
43
|
+
]);
|
|
44
|
+
console.log('');
|
|
45
|
+
const spinner = ora('Creating project...').start();
|
|
46
|
+
try {
|
|
47
|
+
const project = await api.createProject(answers.projectName, `A ${answers.framework} project created via CLI`);
|
|
48
|
+
const config = {
|
|
49
|
+
projectId: project.id,
|
|
50
|
+
framework: answers.framework,
|
|
51
|
+
providers: answers.providers
|
|
52
|
+
};
|
|
53
|
+
fs.writeFileSync(path.join(process.cwd(), 'shipkit.config.json'), JSON.stringify(config, null, 2), 'utf-8');
|
|
54
|
+
spinner.stop();
|
|
55
|
+
console.log(`${chalk.green('✓')} Project created\n`);
|
|
56
|
+
console.log(`Project ID\n${chalk.dim(project.id)}\n`);
|
|
57
|
+
console.log(`Configuration written\n${chalk.dim('shipkit.config.json')}\n`);
|
|
58
|
+
console.log(`${chalk.dim('Ready to build.')}\n`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
spinner.stop();
|
|
62
|
+
const errorMessage = error.response?.data?.error || error.message || 'Unknown error occurred';
|
|
63
|
+
console.log(`${chalk.red('✗')} Project creation failed\n`);
|
|
64
|
+
console.log(`${errorMessage}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { api } from '../lib/api.js';
|
|
6
|
+
import { setAuth } from '../lib/storage.js';
|
|
7
|
+
export const loginCommand = new Command('login')
|
|
8
|
+
.description('Authenticate with ShipKit')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
console.log(`\n● Welcome to ShipKit\n`);
|
|
11
|
+
const answers = await inquirer.prompt([
|
|
12
|
+
{
|
|
13
|
+
type: 'input',
|
|
14
|
+
name: 'email',
|
|
15
|
+
message: 'Email\n›',
|
|
16
|
+
validate: (input) => {
|
|
17
|
+
if (!input || !input.includes('@'))
|
|
18
|
+
return 'Please enter a valid email address';
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'password',
|
|
24
|
+
name: 'password',
|
|
25
|
+
message: 'Password\n›',
|
|
26
|
+
mask: '*',
|
|
27
|
+
validate: (input) => {
|
|
28
|
+
if (!input)
|
|
29
|
+
return 'Password is required';
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]);
|
|
34
|
+
console.log('');
|
|
35
|
+
const spinner = ora('Authenticating...').start();
|
|
36
|
+
try {
|
|
37
|
+
const response = await api.login(answers.email, answers.password);
|
|
38
|
+
if (response.success && response.session) {
|
|
39
|
+
setAuth({
|
|
40
|
+
token: response.session.access_token,
|
|
41
|
+
user: response.user
|
|
42
|
+
});
|
|
43
|
+
spinner.stop();
|
|
44
|
+
console.log(`${chalk.green('✓')} Authenticated successfully\n`);
|
|
45
|
+
console.log(`Connected as:\n${chalk.cyan(response.user.email)}\n`);
|
|
46
|
+
console.log(`${chalk.dim('Account ready.')}\n`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
throw new Error('Invalid response from server');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
spinner.stop();
|
|
54
|
+
const errorMessage = error.response?.data?.error || error.message || 'Unknown error occurred';
|
|
55
|
+
console.log(`${chalk.red('✗')} Authentication failed\n`);
|
|
56
|
+
console.log(`${errorMessage}\n`);
|
|
57
|
+
console.log(`${chalk.dim('Try again or reset your password from the dashboard.')}\n`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { clearAuth } from '../lib/storage.js';
|
|
4
|
+
export const logoutCommand = new Command('logout')
|
|
5
|
+
.description('Remove local session')
|
|
6
|
+
.action(() => {
|
|
7
|
+
try {
|
|
8
|
+
clearAuth();
|
|
9
|
+
console.log(`\n${chalk.green('✓')} Logged out successfully\n\n${chalk.dim('Local credentials removed.')}\n`);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.log(`\n${chalk.red('✗')} Failed to log out\n`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { api } from '../lib/api.js';
|
|
5
|
+
import { getAuth } from '../lib/storage.js';
|
|
6
|
+
export const providersCommand = new Command('providers')
|
|
7
|
+
.description('View connected providers')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const auth = getAuth();
|
|
10
|
+
if (!auth || !auth.token) {
|
|
11
|
+
console.log(`\n${chalk.red('✗')} Unauthorized\n\nPlease run ${chalk.cyan('shipkit login')} first.\n`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const spinner = ora('Fetching provider status...').start();
|
|
15
|
+
try {
|
|
16
|
+
const providers = await api.getProviders();
|
|
17
|
+
spinner.stop();
|
|
18
|
+
const allProviders = ['Supabase', 'Resend', 'PostHog', 'Razorpay', 'Stripe'];
|
|
19
|
+
const connectedNames = providers.map((p) => p.provider_name.toLowerCase());
|
|
20
|
+
console.log(`\nProviders\n`);
|
|
21
|
+
console.log(`${'Name'.padEnd(12)}Status`);
|
|
22
|
+
let connectedCount = 0;
|
|
23
|
+
for (const pName of allProviders) {
|
|
24
|
+
const isConnected = connectedNames.includes(pName.toLowerCase());
|
|
25
|
+
if (isConnected) {
|
|
26
|
+
console.log(`${pName.padEnd(12)}${chalk.green('Connected')}`);
|
|
27
|
+
connectedCount++;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(`${chalk.dim(pName.padEnd(12))}${chalk.dim('Missing')}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
console.log(`\n${chalk.dim(`${allProviders.length} Providers`)}`);
|
|
34
|
+
console.log(`${chalk.dim(`${connectedCount} Connected`)}`);
|
|
35
|
+
console.log(`${chalk.dim(`${allProviders.length - connectedCount} Missing`)}\n`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
spinner.stop();
|
|
39
|
+
const errorMessage = error.response?.data?.error || error.message || 'Unknown error occurred';
|
|
40
|
+
console.log(`\n${chalk.red('✗')} Fetch failed\n`);
|
|
41
|
+
console.log(`${errorMessage}\n`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import figlet from 'figlet';
|
|
5
|
+
import gradient from 'gradient-string';
|
|
6
|
+
import { loginCommand } from './commands/login.js';
|
|
7
|
+
import { logoutCommand } from './commands/logout.js';
|
|
8
|
+
import { initCommand } from './commands/init.js';
|
|
9
|
+
import { providersCommand } from './commands/providers.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
const displayBanner = () => {
|
|
12
|
+
console.log('\n');
|
|
13
|
+
console.log(gradient.pastel.multiline(figlet.textSync('ShipKit', {
|
|
14
|
+
font: 'Standard',
|
|
15
|
+
horizontalLayout: 'default',
|
|
16
|
+
verticalLayout: 'default',
|
|
17
|
+
width: 80,
|
|
18
|
+
whitespaceBreak: true
|
|
19
|
+
})));
|
|
20
|
+
console.log(chalk.bold.cyan('Build Faster. Ship Smarter.\n'));
|
|
21
|
+
};
|
|
22
|
+
program
|
|
23
|
+
.name('shipkit')
|
|
24
|
+
.description('ShipKit CLI\n\nBuild Faster. Ship Smarter.')
|
|
25
|
+
.version('1.0.0', '-v, --version', 'Display CLI version')
|
|
26
|
+
.helpOption('-h, --help', 'Display help information');
|
|
27
|
+
program.addHelpText('beforeAll', () => {
|
|
28
|
+
displayBanner();
|
|
29
|
+
return '';
|
|
30
|
+
});
|
|
31
|
+
program.addHelpText('afterAll', () => {
|
|
32
|
+
return `\nDocumentation\n\n https://docs.shipkit.dev\n`;
|
|
33
|
+
});
|
|
34
|
+
program.addCommand(loginCommand);
|
|
35
|
+
program.addCommand(logoutCommand);
|
|
36
|
+
program.addCommand(initCommand);
|
|
37
|
+
program.addCommand(providersCommand);
|
|
38
|
+
program.parse(process.argv);
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getAuth } from './storage.js';
|
|
3
|
+
const API_URL = process.env.SHIPKIT_API_URL || 'http://localhost:3000';
|
|
4
|
+
export const apiClient = axios.create({
|
|
5
|
+
baseURL: `${API_URL}/api`,
|
|
6
|
+
timeout: 10000,
|
|
7
|
+
});
|
|
8
|
+
apiClient.interceptors.request.use((config) => {
|
|
9
|
+
const auth = getAuth();
|
|
10
|
+
if (auth?.token) {
|
|
11
|
+
config.headers.Authorization = `Bearer ${auth.token}`;
|
|
12
|
+
}
|
|
13
|
+
return config;
|
|
14
|
+
});
|
|
15
|
+
export const api = {
|
|
16
|
+
login: async (email, password) => {
|
|
17
|
+
const { data } = await apiClient.post('/cli/login', { email, password });
|
|
18
|
+
return data;
|
|
19
|
+
},
|
|
20
|
+
getProjects: async () => {
|
|
21
|
+
const { data } = await apiClient.get('/projects');
|
|
22
|
+
return data.projects;
|
|
23
|
+
},
|
|
24
|
+
createProject: async (name, description) => {
|
|
25
|
+
const { data } = await apiClient.post('/projects', { name, description });
|
|
26
|
+
return data.project;
|
|
27
|
+
},
|
|
28
|
+
getProviders: async () => {
|
|
29
|
+
const { data } = await apiClient.get('/providers');
|
|
30
|
+
return data.providers;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
const authConf = new Conf({
|
|
3
|
+
projectName: 'shipkit',
|
|
4
|
+
configName: 'auth',
|
|
5
|
+
});
|
|
6
|
+
const projectConf = new Conf({
|
|
7
|
+
projectName: 'shipkit',
|
|
8
|
+
configName: 'config',
|
|
9
|
+
});
|
|
10
|
+
export const getAuth = () => {
|
|
11
|
+
return authConf.get('auth');
|
|
12
|
+
};
|
|
13
|
+
export const setAuth = (auth) => {
|
|
14
|
+
authConf.set('auth', auth);
|
|
15
|
+
};
|
|
16
|
+
export const clearAuth = () => {
|
|
17
|
+
authConf.delete('auth');
|
|
18
|
+
};
|
|
19
|
+
export const getProjectConfig = () => {
|
|
20
|
+
return (projectConf.get('project') || {});
|
|
21
|
+
};
|
|
22
|
+
export const setProjectConfig = (config) => {
|
|
23
|
+
projectConf.set('project', config);
|
|
24
|
+
};
|
|
25
|
+
export const clearProjectConfig = () => {
|
|
26
|
+
projectConf.delete('project');
|
|
27
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const featuresRegistry = [
|
|
2
|
+
{
|
|
3
|
+
id: 'auth-supabase',
|
|
4
|
+
name: 'Authentication',
|
|
5
|
+
description: 'Supabase Authentication integration',
|
|
6
|
+
category: 'auth',
|
|
7
|
+
status: 'stable'
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: 'payments-stripe',
|
|
11
|
+
name: 'Payments',
|
|
12
|
+
description: 'Stripe Payments integration',
|
|
13
|
+
category: 'payments',
|
|
14
|
+
status: 'stable'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'analytics-posthog',
|
|
18
|
+
name: 'Analytics',
|
|
19
|
+
description: 'PostHog Analytics integration',
|
|
20
|
+
category: 'analytics',
|
|
21
|
+
status: 'stable'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'email-resend',
|
|
25
|
+
name: 'Email',
|
|
26
|
+
description: 'Resend transactional email integration',
|
|
27
|
+
category: 'email',
|
|
28
|
+
status: 'stable'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'storage-s3',
|
|
32
|
+
name: 'Storage',
|
|
33
|
+
description: 'AWS S3 / Supabase Storage integration',
|
|
34
|
+
category: 'storage',
|
|
35
|
+
status: 'stable'
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
export const getFeatures = () => featuresRegistry;
|
|
39
|
+
export const getFeatureById = (id) => featuresRegistry.find(f => f.id === id);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const templatesRegistry = [
|
|
2
|
+
{
|
|
3
|
+
id: 'nextjs-saas',
|
|
4
|
+
name: 'Next.js SaaS',
|
|
5
|
+
description: 'A complete SaaS starter with authentication, billing, and dashboard',
|
|
6
|
+
framework: 'Next.js',
|
|
7
|
+
features: ['auth-supabase', 'payments-stripe', 'email-resend']
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: 'nextjs-starter',
|
|
11
|
+
name: 'Next.js Starter',
|
|
12
|
+
description: 'A minimal Next.js starter with authentication',
|
|
13
|
+
framework: 'Next.js',
|
|
14
|
+
features: ['auth-supabase']
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'api-backend',
|
|
18
|
+
name: 'API Backend',
|
|
19
|
+
description: 'A robust Node.js API backend template',
|
|
20
|
+
framework: 'Node.js',
|
|
21
|
+
features: ['auth-supabase', 'storage-s3']
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
export const getTemplates = () => templatesRegistry;
|
|
25
|
+
export const getTemplateById = (id) => templatesRegistry.find(t => t.id === id);
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@snowieedev/shipkit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The command line interface for ShipKit - Build Faster. Ship Smarter.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:coverage": "vitest run --coverage",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node ./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"shipkit",
|
|
17
|
+
"cli",
|
|
18
|
+
"deployment",
|
|
19
|
+
"saas",
|
|
20
|
+
"boilerplate",
|
|
21
|
+
"developer-tools"
|
|
22
|
+
],
|
|
23
|
+
"author": "ShipKit",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"axios": "^1.18.0",
|
|
28
|
+
"chalk": "^5.6.2",
|
|
29
|
+
"commander": "^15.0.0",
|
|
30
|
+
"conf": "^15.1.0",
|
|
31
|
+
"figlet": "^1.11.0",
|
|
32
|
+
"gradient-string": "^3.0.0",
|
|
33
|
+
"inquirer": "^14.0.2",
|
|
34
|
+
"ora": "^9.4.0",
|
|
35
|
+
"zod": "^4.4.3"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/figlet": "^1.7.0",
|
|
39
|
+
"@types/gradient-string": "^1.1.6",
|
|
40
|
+
"@types/inquirer": "^9.0.10",
|
|
41
|
+
"@types/node": "^20.19.43",
|
|
42
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"vitest": "^4.1.9"
|
|
45
|
+
},
|
|
46
|
+
"bin": {
|
|
47
|
+
"shipkit": "dist/index.js"
|
|
48
|
+
}
|
|
49
|
+
}
|