blok0 0.1.0 โ†’ 0.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.
@@ -0,0 +1,132 @@
1
+ import * as path from 'path';
2
+ import { isAuthenticated } from '../auth';
3
+ import { apiClient } from '../api';
4
+ import { isBlockRegistered, addBlockToRegistry, calculateDirectoryChecksums } from '../registry';
5
+ import {
6
+ ensureBlocksDirectory,
7
+ createBlockDirectory,
8
+ createBlockEntry,
9
+ slugToIdentifier,
10
+ validateBlockDirectory
11
+ } from '../blocks';
12
+ import { updatePageCollectionConfig, updateRenderBlocksComponent, findPagesCollection, findRenderBlocksComponent } from '../ast';
13
+
14
+ /**
15
+ * Handle add block command
16
+ */
17
+ export async function handleAddBlock(blockUrl: string, options: { force?: boolean; dryRun?: boolean } = {}): Promise<void> {
18
+ console.log('๐Ÿ“ฆ Adding Blok0 Block');
19
+ console.log('====================');
20
+ console.log('');
21
+
22
+ try {
23
+ // Step 1: Authentication check
24
+ console.log('๐Ÿ” Checking authentication...');
25
+ const authenticated = await isAuthenticated();
26
+ if (!authenticated) {
27
+ console.error('โŒ You are not logged in. Please run `blok0 login` first.');
28
+ process.exit(1);
29
+ }
30
+
31
+ // Step 2: Fetch block data from API
32
+ console.log(`๐Ÿ“ก Fetching block from: ${blockUrl}`);
33
+ const { metadata, files } = await apiClient.fetchBlockData(blockUrl);
34
+ console.log(`โœ… Found block: "${metadata.name}" (${metadata.slug})`);
35
+
36
+ // Step 3: Check if block is already registered
37
+ if (isBlockRegistered(metadata.slug)) {
38
+ if (!options.force) {
39
+ console.error(`โŒ Block "${metadata.slug}" is already installed. Use --force to reinstall.`);
40
+ process.exit(1);
41
+ }
42
+ console.log('โš ๏ธ Block already exists, reinstalling...');
43
+ }
44
+
45
+ if (options.dryRun) {
46
+ console.log('๐Ÿ” Dry run mode - would perform the following actions:');
47
+ console.log(` - Create directory: src/blocks/${metadata.slug}`);
48
+ console.log(` - Download ${files.length} files`);
49
+ console.log(' - Update Payload config');
50
+ console.log(' - Update RenderBlocks component');
51
+ console.log(' - Register block in blok0-registry.json');
52
+ return;
53
+ }
54
+
55
+ // Step 4: Ensure blocks directory exists
56
+ const blocksDir = ensureBlocksDirectory();
57
+
58
+ // Step 5: Create block directory and files
59
+ console.log('๐Ÿ“ Creating block directory and files...');
60
+ const { dir, configPath, componentPath } = createBlockDirectory(blocksDir, metadata.slug, files);
61
+ console.log(`โœ… Created block directory: ${path.relative(process.cwd(), dir)}`);
62
+
63
+ // Step 6: Validate created block
64
+ const validation = validateBlockDirectory(dir);
65
+ if (!validation.valid) {
66
+ console.error('โŒ Block validation failed:');
67
+ validation.errors.forEach(error => console.error(` - ${error}`));
68
+ // Cleanup on failure
69
+ require('fs').rmSync(dir, { recursive: true, force: true });
70
+ process.exit(1);
71
+ }
72
+
73
+ // Step 7: Calculate checksums
74
+ const checksums = calculateDirectoryChecksums(dir);
75
+
76
+ // Step 8: Create registry entry
77
+ const blockEntry = createBlockEntry(
78
+ {
79
+ id: metadata.id,
80
+ name: metadata.name,
81
+ slug: metadata.slug,
82
+ sourceUrl: blockUrl
83
+ },
84
+ dir,
85
+ configPath,
86
+ componentPath,
87
+ checksums
88
+ );
89
+
90
+ // Step 9: Update Pages collection (AST manipulation)
91
+ const pagesCollectionPath = findPagesCollection();
92
+ if (pagesCollectionPath) {
93
+ console.log('๐Ÿ”ง Updating Pages collection...');
94
+ const blockIdentifier = slugToIdentifier(metadata.slug);
95
+ const relativeConfigPath = `@/blocks/${metadata.slug}/config`;
96
+
97
+ updatePageCollectionConfig(pagesCollectionPath, relativeConfigPath, blockIdentifier);
98
+ console.log(`โœ… Added ${blockIdentifier} to Pages collection`);
99
+ } else {
100
+ console.warn('โš ๏ธ Could not find Pages collection file. You may need to manually add the block to your collections.');
101
+ }
102
+
103
+ // Step 10: Update RenderBlocks component (AST manipulation)
104
+ const renderBlocksPath = findRenderBlocksComponent();
105
+ if (renderBlocksPath) {
106
+ console.log('๐Ÿ”ง Updating RenderBlocks component...');
107
+ const relativeComponentPath = `./${metadata.slug}/Component`;
108
+
109
+ updateRenderBlocksComponent(renderBlocksPath, metadata.slug, relativeComponentPath);
110
+ console.log(`โœ… Added ${metadata.slug} component to RenderBlocks`);
111
+ } else {
112
+ console.warn('โš ๏ธ Could not find RenderBlocks component. You may need to manually add the block component.');
113
+ }
114
+
115
+ // Step 11: Register block in registry
116
+ console.log('๐Ÿ“ Registering block...');
117
+ addBlockToRegistry(blockEntry);
118
+ console.log('โœ… Block registered successfully');
119
+
120
+ console.log('');
121
+ console.log('๐ŸŽ‰ Block installation complete!');
122
+ console.log('');
123
+ console.log('Next steps:');
124
+ console.log('1. Review the installed files in src/blocks/' + metadata.slug);
125
+ console.log('2. Test your application to ensure the block works correctly');
126
+ console.log('3. Commit the changes to your repository');
127
+
128
+ } catch (error) {
129
+ console.error('โŒ Failed to add block:', (error as Error).message);
130
+ process.exit(1);
131
+ }
132
+ }
@@ -1,62 +1,62 @@
1
- import { createInterface } from 'readline';
2
- import { exec, spawn } from 'child_process';
3
- import { promisify } from 'util';
4
-
5
- const execAsync = promisify(exec);
6
-
7
- const repoUrl = 'https://github.com/blok0-payload/starter.git';
8
-
9
- function prompt(question: string): Promise<boolean> {
10
- return new Promise((resolve) => {
11
- const rl = createInterface({
12
- input: process.stdin,
13
- output: process.stdout,
14
- });
15
- rl.question(question, (answer) => {
16
- rl.close();
17
- resolve(answer.toLowerCase().startsWith('y'));
18
- });
19
- });
20
- }
21
-
22
- export async function generateStarter(): Promise<void> {
23
- console.log('Cloning starter repository...');
24
- try {
25
- await execAsync(`git clone --depth 1 ${repoUrl} .`);
26
- console.log('Repository cloned successfully.');
27
- } catch (error) {
28
- throw new Error(`Failed to clone repository: ${error}`);
29
- }
30
-
31
- // Prompt for bun install
32
- const installDeps = await prompt('Run \'bun install\' to install dependencies? (y/n): ');
33
- if (installDeps) {
34
- console.log('Installing dependencies...');
35
- try {
36
- await new Promise<void>((resolve, reject) => {
37
- const child = spawn('bun', ['install'], { stdio: 'inherit' });
38
- child.on('close', (code) => {
39
- if (code === 0) resolve();
40
- else reject(new Error('Failed to install dependencies'));
41
- });
42
- child.on('error', reject);
43
- });
44
- } catch (error) {
45
- console.error('Failed to install dependencies:', error);
46
- }
47
- }
48
-
49
- // Prompt for git init
50
- const initGit = await prompt('Initialize git repository? (y/n): ');
51
- if (initGit) {
52
- console.log('Initializing git repository...');
53
- try {
54
- await execAsync('git init');
55
- console.log('Git repository initialized.');
56
- } catch (error) {
57
- console.error('Failed to initialize git:', error);
58
- }
59
- }
60
-
61
- console.log('Blok0 starter project created successfully!');
62
- }
1
+ import { createInterface } from 'readline';
2
+ import { exec, spawn } from 'child_process';
3
+ import { promisify } from 'util';
4
+
5
+ const execAsync = promisify(exec);
6
+
7
+ const repoUrl = 'https://github.com/blok0-payload/starter.git';
8
+
9
+ function prompt(question: string): Promise<boolean> {
10
+ return new Promise((resolve) => {
11
+ const rl = createInterface({
12
+ input: process.stdin,
13
+ output: process.stdout,
14
+ });
15
+ rl.question(question, (answer) => {
16
+ rl.close();
17
+ resolve(answer.toLowerCase().startsWith('y'));
18
+ });
19
+ });
20
+ }
21
+
22
+ export async function generateStarter(): Promise<void> {
23
+ console.log('Cloning starter repository...');
24
+ try {
25
+ await execAsync(`git clone --depth 1 ${repoUrl} .`);
26
+ console.log('Repository cloned successfully.');
27
+ } catch (error) {
28
+ throw new Error(`Failed to clone repository: ${error}`);
29
+ }
30
+
31
+ // Prompt for bun install
32
+ const installDeps = await prompt('Run \'bun install\' to install dependencies? (y/n): ');
33
+ if (installDeps) {
34
+ console.log('Installing dependencies...');
35
+ try {
36
+ await new Promise<void>((resolve, reject) => {
37
+ const child = spawn('bun', ['install'], { stdio: 'inherit' });
38
+ child.on('close', (code) => {
39
+ if (code === 0) resolve();
40
+ else reject(new Error('Failed to install dependencies'));
41
+ });
42
+ child.on('error', reject);
43
+ });
44
+ } catch (error) {
45
+ console.error('Failed to install dependencies:', error);
46
+ }
47
+ }
48
+
49
+ // Prompt for git init
50
+ const initGit = await prompt('Initialize git repository? (y/n): ');
51
+ if (initGit) {
52
+ console.log('Initializing git repository...');
53
+ try {
54
+ await execAsync('git init');
55
+ console.log('Git repository initialized.');
56
+ } catch (error) {
57
+ console.error('Failed to initialize git:', error);
58
+ }
59
+ }
60
+
61
+ console.log('Blok0 starter project created successfully!');
62
+ }
@@ -0,0 +1,130 @@
1
+ import { isAuthenticated, clearCredentials, storeAccessToken, AuthCallback } from '../auth';
2
+ import { AuthServer } from '../auth/server';
3
+ import open from 'open';
4
+
5
+ // Add SIGINT handler for graceful cleanup
6
+ process.on('SIGINT', () => {
7
+ console.log('\n\nโš ๏ธ Authentication cancelled by user.');
8
+ process.exit(0);
9
+ });
10
+
11
+ /**
12
+ * Handle login command
13
+ */
14
+ export async function handleLogin(token?: string, manual?: boolean): Promise<void> {
15
+ // Direct token authentication (CI/CD)
16
+ if (token) {
17
+ try {
18
+ console.log('๐Ÿ” Saving authentication token...');
19
+ await storeAccessToken(token);
20
+ console.log('โœ… Successfully authenticated!');
21
+ console.log('');
22
+ console.log('You can now use blok0 commands that require authentication.');
23
+ } catch (error) {
24
+ console.error('โŒ Failed to save authentication token:', (error as Error).message);
25
+ process.exit(1);
26
+ }
27
+ return;
28
+ }
29
+
30
+ // Manual authentication instructions
31
+ if (manual) {
32
+ showManualInstructions();
33
+ return;
34
+ }
35
+
36
+ // Default: Browser-based authentication
37
+ try {
38
+ await handleBrowserLogin();
39
+ } catch (error) {
40
+ console.error('โŒ Browser authentication failed:', (error as Error).message);
41
+ console.log('');
42
+ console.log('๐Ÿ’ก Try manual authentication:');
43
+ console.log(' blok0 login --manual');
44
+ process.exit(1);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Handle browser-based authentication flow
50
+ */
51
+ async function handleBrowserLogin(): Promise<void> {
52
+ console.log('๐Ÿ” Blok0 Authentication');
53
+ console.log('======================');
54
+ console.log('');
55
+
56
+ // Create authentication server
57
+ const authServer = new AuthServer();
58
+
59
+ try {
60
+ // Initialize server (find available port)
61
+ console.log('๐Ÿš€ Starting authentication server...');
62
+ await authServer.initialize();
63
+
64
+ // Get the authorization URL (now port is available)
65
+ const authUrl = authServer.getAuthorizationUrl();
66
+
67
+ console.log('๐ŸŒ Opening browser for authentication...');
68
+ await open(authUrl);
69
+
70
+ console.log('๐Ÿ“ฑ Please complete authentication in your browser.');
71
+ console.log('โณ Waiting for authentication to complete...');
72
+
73
+ // Start server and wait for callback
74
+ const authCallback: AuthCallback = await authServer.start();
75
+
76
+ // Store the token
77
+ console.log('๐Ÿ” Saving authentication token...');
78
+ await storeAccessToken(authCallback.token);
79
+ console.log('โœ… Successfully authenticated!');
80
+ console.log('');
81
+ console.log('You can now use blok0 commands that require authentication.');
82
+
83
+ } catch (error) {
84
+ authServer.stop();
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Show manual authentication instructions
91
+ */
92
+ function showManualInstructions(): void {
93
+ console.log('๐Ÿ” Blok0 Manual Authentication');
94
+ console.log('==============================');
95
+ console.log('');
96
+ console.log('To authenticate with the Blok0 API, make a POST request to:');
97
+ console.log('https://www.blok0.xyz/api/customers/login');
98
+ console.log('');
99
+ console.log('Example using curl:');
100
+ console.log('curl -X POST https://www.blok0.xyz/api/customers/login \\');
101
+ console.log(' -H "Content-Type: application/json" \\');
102
+ console.log(' -d \'{"email": "your-email@example.com", "password": "your-password"}\'');
103
+ console.log('');
104
+ console.log('Then copy the access token and run:');
105
+ console.log('blok0 login --token <your-token>');
106
+ console.log('');
107
+ console.log('For CI/CD environments, set the BLOK0_TOKEN environment variable.');
108
+ console.log('');
109
+ console.log('๐Ÿ’ก For browser-based login, run: blok0 login');
110
+ }
111
+
112
+ /**
113
+ * Handle logout command
114
+ */
115
+ export async function handleLogout(): Promise<void> {
116
+ try {
117
+ const wasAuthenticated = await isAuthenticated();
118
+
119
+ if (!wasAuthenticated) {
120
+ console.log('You are not currently logged in.');
121
+ return;
122
+ }
123
+
124
+ await clearCredentials();
125
+ console.log('โœ… Successfully logged out and cleared stored credentials.');
126
+ } catch (error) {
127
+ console.error('โŒ Failed to logout:', (error as Error).message);
128
+ process.exit(1);
129
+ }
130
+ }
package/src/index.ts CHANGED
@@ -1,51 +1,212 @@
1
- #!/usr/bin/env bun
2
-
3
- import { mkdirSync } from 'fs';
4
- import { createInterface } from 'readline';
5
- import { checkEmptyDirectory } from './detectors';
6
- import { generateStarter } from './handlers/generate';
7
-
8
- function prompt(question: string): Promise<string> {
9
- return new Promise((resolve) => {
10
- const rl = createInterface({
11
- input: process.stdin,
12
- output: process.stdout,
13
- });
14
- rl.question(question, (answer) => {
15
- rl.close();
16
- resolve(answer.trim());
17
- });
18
- });
19
- }
20
-
21
- async function main() {
22
- const args = process.argv.slice(2);
23
-
24
- if (args.length < 2 || args[0] !== 'generate' || args[1] !== 'starter') {
25
- console.error('Error: Invalid command. Supported: generate starter [folder]');
26
- process.exit(1);
27
- }
28
-
29
- let targetFolder = args[2];
30
- if (!targetFolder) {
31
- targetFolder = await prompt('Enter project folder name: ');
32
- }
33
-
34
- if (targetFolder !== '.') {
35
- mkdirSync(targetFolder, { recursive: true });
36
- process.chdir(targetFolder);
37
- }
38
-
39
- if (!checkEmptyDirectory()) {
40
- process.exit(1);
41
- }
42
-
43
- try {
44
- await generateStarter();
45
- } catch (error) {
46
- console.error(`Error: ${(error as Error).message}`);
47
- process.exit(1);
48
- }
49
- }
50
-
51
- main();
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdirSync } from 'fs';
4
+ import { createInterface } from 'readline';
5
+ import { checkEmptyDirectory } from './detectors';
6
+ import { generateStarter } from './handlers/generate';
7
+ import { handleLogin, handleLogout } from './handlers/login';
8
+ import { handleAddBlock } from './handlers/add-block';
9
+ import { createEmptyRegistry } from './registry';
10
+
11
+ function prompt(question: string): Promise<string> {
12
+ return new Promise((resolve) => {
13
+ const rl = createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ });
17
+ rl.question(question, (answer) => {
18
+ rl.close();
19
+ resolve(answer.trim());
20
+ });
21
+ });
22
+ }
23
+
24
+ async function showHelp() {
25
+ console.log(`
26
+ Blok0 - PayloadCMS Block Management CLI
27
+
28
+ USAGE:
29
+ blok0 <command> [subcommand] [options]
30
+
31
+ COMMANDS:
32
+ login Authenticate via browser or token
33
+ logout Remove stored credentials
34
+ debug Show authentication debug info
35
+ generate starter [folder] Generate PayloadCMS starter project
36
+ add block <url> Add a block from remote API
37
+ update block <id> Update existing block (future)
38
+ remove block <id> Remove block and clean up (future)
39
+ registry validate Validate registry integrity (future)
40
+
41
+ OPTIONS:
42
+ --help, -h Show this help message
43
+ --version, -v Show version information
44
+ --verbose Enable verbose logging
45
+ --dry-run Preview changes without applying them
46
+
47
+ EXAMPLES:
48
+ blok0 login
49
+ blok0 generate starter my-project
50
+ blok0 add block https://api.example.com/blocks/123
51
+
52
+ For more information, visit: https://github.com/blok0-payload/cli
53
+ `);
54
+ }
55
+
56
+ async function main() {
57
+ const args = process.argv.slice(2);
58
+
59
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
60
+ showHelp();
61
+ process.exit(0);
62
+ }
63
+
64
+ if (args.includes('--version') || args.includes('-v')) {
65
+ const pkg = require('../package.json');
66
+ console.log(`blok0 v${pkg.version}`);
67
+ process.exit(0);
68
+ }
69
+
70
+ const [command, ...restArgs] = args;
71
+
72
+ try {
73
+ switch (command) {
74
+ case 'generate':
75
+ const [genSubcommand, ...genRestArgs] = restArgs;
76
+ if (genSubcommand === 'starter') {
77
+ await handleGenerateStarter(genRestArgs);
78
+ } else {
79
+ console.error('Error: Invalid subcommand. Use: blok0 generate starter [folder]');
80
+ process.exit(1);
81
+ }
82
+ break;
83
+
84
+ case 'login':
85
+ // Check for flags
86
+ const tokenIndex = restArgs.indexOf('--token');
87
+ const manualIndex = restArgs.indexOf('--manual');
88
+
89
+ if (tokenIndex !== -1 && tokenIndex + 1 < restArgs.length) {
90
+ const token = restArgs[tokenIndex + 1];
91
+ await handleLogin(token);
92
+ } else if (manualIndex !== -1) {
93
+ await handleLogin(undefined, true);
94
+ } else {
95
+ await handleLogin();
96
+ }
97
+ break;
98
+
99
+ case 'logout':
100
+ await handleLogout();
101
+ break;
102
+
103
+ case 'debug':
104
+ await handleDebug();
105
+ break;
106
+
107
+ case 'add':
108
+ const [addSubcommand, ...addRestArgs] = restArgs;
109
+ if (addSubcommand === 'block') {
110
+ const blockUrl = addRestArgs[0];
111
+ if (!blockUrl) {
112
+ console.error('Error: Block URL is required. Use: blok0 add block <url>');
113
+ process.exit(1);
114
+ }
115
+ const options = {
116
+ force: addRestArgs.includes('--force'),
117
+ dryRun: addRestArgs.includes('--dry-run')
118
+ };
119
+ await handleAddBlock(blockUrl, options);
120
+ } else {
121
+ console.error('Error: Invalid subcommand. Use: blok0 add block <url>');
122
+ process.exit(1);
123
+ }
124
+ break;
125
+
126
+ case 'update':
127
+ case 'remove':
128
+ case 'registry':
129
+ console.log(`${command} functionality coming soon...`);
130
+ break;
131
+
132
+ default:
133
+ console.error(`Error: Unknown command '${command}'`);
134
+ showHelp();
135
+ process.exit(1);
136
+ }
137
+ } catch (error) {
138
+ console.error(`Error: ${(error as Error).message}`);
139
+ process.exit(1);
140
+ }
141
+ }
142
+
143
+ async function handleGenerateStarter(args: string[]) {
144
+ let targetFolder = args[0];
145
+ if (!targetFolder) {
146
+ targetFolder = await prompt('Enter project folder name: ');
147
+ }
148
+
149
+ if (targetFolder !== '.') {
150
+ mkdirSync(targetFolder, { recursive: true });
151
+ process.chdir(targetFolder);
152
+ }
153
+
154
+ if (!checkEmptyDirectory()) {
155
+ process.exit(1);
156
+ }
157
+
158
+ await generateStarter();
159
+
160
+ // Initialize empty registry for the new project
161
+ try {
162
+ createEmptyRegistry();
163
+ console.log('๐Ÿ“ Initialized blok0-registry.json');
164
+ } catch (error) {
165
+ console.warn('โš ๏ธ Failed to initialize registry:', (error as Error).message);
166
+ }
167
+ }
168
+
169
+ async function handleDebug() {
170
+ console.log('๐Ÿ” Blok0 CLI Debug Information');
171
+ console.log('==============================');
172
+ console.log('');
173
+
174
+ // Check stored token
175
+ const { getAccessToken, isAuthenticated } = await import('./auth');
176
+ const token = await getAccessToken();
177
+ const isAuth = await isAuthenticated();
178
+
179
+ console.log('๐Ÿ” Authentication Status:');
180
+ console.log(` Authenticated: ${isAuth ? 'โœ… Yes' : 'โŒ No'}`);
181
+ console.log(` Token Stored: ${token ? 'โœ… Yes' : 'โŒ No'}`);
182
+
183
+ if (token) {
184
+ console.log(` Token Preview: ${token.substring(0, 20)}...`);
185
+ console.log(` Authorization Header: Bearer ${token}`);
186
+ }
187
+
188
+ console.log('');
189
+ console.log('๐ŸŒ API Configuration:');
190
+ console.log(' Base URL: https://www.blok0.xyz');
191
+ console.log(' User Agent: blok0-cli/1.0.0');
192
+
193
+ console.log('');
194
+ console.log('๐Ÿงช Test API Connection:');
195
+
196
+ // Test API connection
197
+ const { apiClient } = await import('./api');
198
+ try {
199
+ const connectionTest = await apiClient.testConnection();
200
+ console.log(` Connection Test: ${connectionTest ? 'โœ… Passed' : 'โŒ Failed'}`);
201
+ } catch (error) {
202
+ console.log(` Connection Test: โŒ Failed - ${(error as Error).message}`);
203
+ }
204
+
205
+ console.log('');
206
+ console.log('๐Ÿ’ก Next Steps:');
207
+ console.log(' 1. If no token, run: blok0 login');
208
+ console.log(' 2. Test API with: blok0 add block <url>');
209
+ console.log(' 3. Check server logs for detailed request info');
210
+ }
211
+
212
+ main();