git0 0.2.12 → 0.2.14

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.
Files changed (71) hide show
  1. package/dist/cli.js +27780 -0
  2. package/dist/cli.js.map +331 -0
  3. package/docs/404.html +2 -2
  4. package/docs/Footer/index.html +2 -2
  5. package/docs/assets/js/1df93b7f.1385eba0.js +2 -0
  6. package/docs/assets/js/22dd74f7.e13ac7a4.js +1 -0
  7. package/docs/assets/js/28ab763d.192aa3bb.js +1 -0
  8. package/docs/assets/js/299d276b.c9496717.js +1 -0
  9. package/docs/assets/js/{68cef36b.c312447e.js → 68cef36b.e5b1975b.js} +1 -1
  10. package/docs/assets/js/a9cef9d5.a7253e41.js +1 -0
  11. package/docs/assets/js/c3a618e1.1c56fb03.js +590 -0
  12. package/docs/assets/js/{main.cf858a7d.js → main.1f646b09.js} +2 -2
  13. package/docs/assets/js/runtime~main.f69f8f44.js +1 -0
  14. package/docs/functions/fm/index.html +26 -0
  15. package/docs/functions/git0/index.html +6 -6
  16. package/docs/functions/github-api/index.html +20 -20
  17. package/docs/functions/index.html +889 -71
  18. package/docs/functions/modules/index.html +4 -3
  19. package/docs/index.html +2 -2
  20. package/docs/lunr-index-1749760982052.json +1 -0
  21. package/docs/lunr-index.json +1 -1
  22. package/docs/search-doc-1749760982052.json +1 -0
  23. package/docs/search-doc.json +1 -1
  24. package/docs/sitemap.xml +1 -1
  25. package/docs-config/.docusaurus/client-manifest.json +36 -24
  26. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/p/index-466.json +1 -1
  27. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-fm-md-a9c.json +19 -0
  28. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-git-0-md-299.json +4 -0
  29. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-index-md-c3a.json +1 -1
  30. package/docs-config/.docusaurus/globalData.json +10 -5
  31. package/docs-config/.docusaurus/registry.js +1 -0
  32. package/docs-config/.docusaurus/routes.js +9 -3
  33. package/docs-config/.docusaurus/routesChunkNames.json +7 -3
  34. package/docs-config/src/functions/fm.md +1 -0
  35. package/docs-config/src/functions/git0.md +2 -2
  36. package/docs-config/src/functions/github-api.md +34 -17
  37. package/docs-config/src/functions/index.md +19 -30
  38. package/docs-config/src/functions/modules.md +1 -0
  39. package/docs-config/src/functions/typedoc-sidebar.cjs +5 -0
  40. package/docs-config/src/pages/index.tsx +2 -3
  41. package/package.json +20 -10
  42. package/readme.md +19 -21
  43. package/src/cli.ts +150 -0
  44. package/src/download.ts +240 -0
  45. package/src/fm.js +5 -5
  46. package/src/github-api.ts +237 -0
  47. package/src/ide.ts +141 -0
  48. package/src/install.ts +147 -0
  49. package/src/package-menu.ts +183 -0
  50. package/src/platform.ts +53 -0
  51. package/src/releases.ts +159 -0
  52. package/src/setup.ts +9 -0
  53. package/src/types.ts +97 -0
  54. package/src/utils.ts +49 -0
  55. package/test/download.test.ts +48 -0
  56. package/test/github-api.test.ts +79 -0
  57. package/test/package-menu.test.ts +134 -0
  58. package/test/platform.test.ts +64 -0
  59. package/test/releases.test.ts +169 -0
  60. package/docs/assets/js/1df93b7f.d8c05d2c.js +0 -2
  61. package/docs/assets/js/22dd74f7.237398b4.js +0 -1
  62. package/docs/assets/js/28ab763d.5714aa16.js +0 -1
  63. package/docs/assets/js/299d276b.1a1baa1c.js +0 -1
  64. package/docs/assets/js/c3a618e1.965a31da.js +0 -1
  65. package/docs/assets/js/runtime~main.7520dc36.js +0 -1
  66. package/docs/lunr-index-1749613752315.json +0 -1
  67. package/docs/search-doc-1749613752315.json +0 -1
  68. package/src/git0.js +0 -380
  69. package/src/github-api.js +0 -472
  70. /package/docs/assets/js/{1df93b7f.d8c05d2c.js.LICENSE.txt → 1df93b7f.1385eba0.js.LICENSE.txt} +0 -0
  71. /package/docs/assets/js/{main.cf858a7d.js.LICENSE.txt → main.1f646b09.js.LICENSE.txt} +0 -0
package/src/github-api.js DELETED
@@ -1,472 +0,0 @@
1
- import {grab, log} from 'grab-api.js';
2
- import chalk from 'chalk';
3
- import gitUrlParse from 'git-url-parse';
4
- import os from 'os';
5
- import fs from 'fs';
6
- import * as tar from 'tar';
7
- import path from 'path';
8
-
9
- /**
10
- * GitHub API client for downloading repositories, searching, and managing releases
11
- * @class GithubAPI
12
- * @example
13
- * const github = new GithubAPI();
14
- * const repos = await github.searchRepositories('nodejs');
15
- * await github.downloadRepo('user/repo', './my-downloads');
16
- */
17
- class GithubAPI {
18
- /** @constant {number} Number of results to return per page for repository searches */
19
- static DEFAULT_RESULTS_PER_PAGE = 10;
20
-
21
- /**
22
- * Creates a new GithubAPI instance
23
- * @param {Object} [options={}] - Configuration options
24
- * @param {string} [options.token] - GitHub personal access token (defaults to GITHUB_TOKEN env var)
25
- * @param {boolean} [options.debug=false] - Enable debug logging
26
- * @param {string} [options.baseURL='https://api.github.com'] - GitHub API base URL
27
- * @example
28
- * // Use default settings with environment token
29
- *
30
- * // Use custom token
31
- * const github = new GithubAPI({ token: 'ghp_xxxxxxxxxxxx' });
32
- *
33
- * // Enable debug mode
34
- * const github = new GithubAPI({ debug: true });
35
- */
36
- constructor(options = {}) {
37
- this.token = options.token || process.env.GITHUB_TOKEN;
38
- this.debug = options.debug || false;
39
- this.baseURL = options.baseURL || 'https://api.github.com';
40
-
41
- this.callGithub = grab.instance({
42
- debug: this.debug,
43
- baseURL: this.baseURL,
44
- timeout: 500,
45
- headers: this.token ? { Authorization: `token ${this.token}` } : {},
46
- onError: (error) => {
47
- if (error.includes('403')) {
48
- const githubHelpUrl = 'https://github.com/settings/personal-access-tokens/new';
49
- console.log(chalk.red('Rate limit exceeded. Please set env var GITHUB_TOKEN. Help:\n' + githubHelpUrl));
50
- process.exit(1);
51
- }
52
- }
53
- });
54
- }
55
-
56
- /**
57
- * Downloads a GitHub repository as a tarball and extracts it to a local directory
58
- * @param {string} repo - Repository URL or owner/name format
59
- * @param {string|null} [targetDir=null] - Target directory name (defaults to repo name)
60
- * @returns {Promise<string>} Path to the extracted repository directory
61
- * @throws {Error} When repository download fails
62
- * @example
63
- *
64
- * // Download repository to current directory
65
- * const repoPath = await github.downloadRepo('https://github.com/user/repo');
66
- *
67
- * // Download to specific directory
68
- * const repoPath = await github.downloadRepo('user/repo', 'my-custom-dir');
69
- */
70
- async downloadRepo(repo, targetDir = null) {
71
- const parsed = gitUrlParse(repo);
72
- const defaultDir = path.resolve(process.cwd(), targetDir?.length ? targetDir : parsed.name);
73
- const extractPath = this._getAvailableDirectoryName(defaultDir);
74
-
75
- // if it picks up a larger owner name, slice to the last part
76
- if (parsed.owner.includes('/')) {
77
- parsed.owner = parsed.owner.split('/').slice(-1).join('');
78
- }
79
-
80
- fs.mkdirSync(extractPath, { recursive: true });
81
-
82
- let url = `/repos/${parsed.owner}/${parsed.name}/tarball/${parsed.default_branch || 'master'}`;
83
-
84
- console.log(chalk.blue(`📦 Downloading ${parsed.name} into ${path.basename(extractPath)}...`));
85
-
86
- const params = {
87
- onStream: async (res) => {
88
- const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
89
- await new Promise((resolve, reject) => {
90
- nodeStream.pipe(tar.x({
91
- C: extractPath,
92
- strip: 1
93
- })).on('finish', resolve).on('error', reject);
94
- });
95
- }
96
- };
97
-
98
- let response = await this.callGithub(url, params);
99
-
100
- if (response.error) {
101
- response = await this.callGithub(url.replace("/master", "/main"), params);
102
- }
103
-
104
- return extractPath;
105
- }
106
-
107
- /**
108
- * Searches for GitHub repositories by name and enriches results with release information
109
- * @param {string} query - Search query for repository names
110
- * @param {Object} [options={}] - Search options
111
- * @param {number} [options.getReleaseInfo] - Should also check releases for each result
112
- * @param {number} [options.perPage] - Number of results per page (defaults to DEFAULT_RESULTS_PER_PAGE)
113
- * @param {string} [options.sort='stars'] - Sort field (stars, forks, updated)
114
- * @param {string} [options.order='desc'] - Sort order (asc, desc)
115
- * @returns {Promise<Array<Object>>} Array of repository objects with release information
116
- * @throws {Error} When search fails
117
- * @example
118
- *
119
- * const repos = await github.searchRepositories('nodejs');
120
- * repos.forEach(repo => {
121
- * console.log(`${repo.name}: ${repo.hasReleases ? 'Has releases' : 'No releases'}`);
122
- * });
123
- *
124
- * // Custom search options
125
- * const repos = await github.searchRepositories('react', {
126
- * perPage: 5,
127
- * sort: 'updated',
128
- * order: 'desc'
129
- * });
130
- */
131
- async searchRepositories(query, options = {}) {
132
- try {
133
- const {
134
- perPage = GithubAPI.DEFAULT_RESULTS_PER_PAGE,
135
- sort = 'stars',
136
- order = 'desc',
137
- getReleaseInfo = true
138
- } = options;
139
-
140
- const response = await this.callGithub('/search/repositories', {
141
- q: `${query} in:name`,
142
- sort,
143
- order,
144
- per_page: perPage,
145
- });
146
-
147
- if (response.error || !response.items) {
148
- console.log("No response");
149
- return [];
150
- }
151
-
152
- return !getReleaseInfo ? response :
153
- // Check for releases for each repository
154
- await Promise.all(
155
- response.items.map(async (repo) => {
156
- const releases = await this.callGithub(`/repos/${repo.owner.login}/${repo.name}/releases`);
157
-
158
- const currentPlatform = this.getCurrentPlatform();
159
- const compatibleReleases = this._filterReleasesByPlatform(releases, currentPlatform);
160
- const categorizedReleases = this._categorizeReleasesByPlatform(releases);
161
-
162
- return {
163
- ...repo,
164
- hasReleases: releases?.length > 0,
165
- hasCompatibleReleases: compatibleReleases?.length > 0,
166
- releases: compatibleReleases,
167
- allReleases: categorizedReleases
168
- };
169
- })
170
- );
171
-
172
- } catch (error) {
173
- console.error(chalk.red('Search failed:'), error.message);
174
- throw error;
175
- }
176
- }
177
-
178
- /**
179
- * Downloads a release asset from GitHub and provides installation instructions
180
- * @param {Object} packageURL - Download URL for the asset
181
- * @param {string} downloadPath - Directory to download the asset to
182
- * @returns {Promise<string>} Path to the downloaded file
183
- * @throws {Error} When download fails
184
- * @example
185
- *
186
- * const asset = 'https://github.com/user/repo/releases/download/v1.0.0/myapp-v1.0.0-linux-x64'
187
- * const downloadPath = await github.downloadPackage(asset, './downloads/myapp');
188
- */
189
- async downloadPackage(packageURL, downloadPath) {
190
- const fileName = downloadPath.split('/').slice(-1)?.[0];
191
-
192
- console.log(chalk.blue(`📦 Downloading ${fileName}...`));
193
-
194
- try {
195
- await this.callGithub(packageURL, {
196
- onStream: async (res) => {
197
- const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
198
- await new Promise((resolve, reject) => {
199
- nodeStream.pipe(fs.createWriteStream(downloadPath)).on('finish', resolve).on('error', reject);
200
- });
201
- }
202
- });
203
- log(`✅ Downloaded ${fileName} to ${downloadPath}`, true, 'color: green');
204
-
205
- // Try to make executable if it's a binary
206
- if (process.platform !== 'win32' && !fileName.includes('.')) {
207
- try {
208
- fs.chmodSync(downloadPath, '755');
209
- console.log(chalk.green(`✅ Made ${fileName} executable`));
210
- } catch (error) {
211
- console.log(chalk.yellow(`⚠️ Could not make ${fileName} executable`));
212
- }
213
- }
214
-
215
- // Provide installation instructions
216
- this._provideInstallationInstructions(downloadPath, fileName);
217
-
218
- return downloadPath;
219
- } catch (error) {
220
- console.error(chalk.red(`❌ Failed to download ${fileName}:`), error.message);
221
- throw error;
222
- }
223
- }
224
-
225
- /**
226
- * Parses a GitHub URL or shorthand repository reference
227
- * @param {string} query - GitHub URL or owner/repo format
228
- * @returns {Object|false} Parsed git URL object or false if invalid
229
- * @example
230
- *
231
- * // Parse full URL
232
- * const parsed = github.parseURL('https://github.com/user/repo');
233
- *
234
- * // Parse shorthand
235
- * const parsed = github.parseURL('user/repo');
236
- *
237
- * if (parsed) {
238
- * console.log(`Owner: ${parsed.owner}, Name: ${parsed.name}`);
239
- * }
240
- */
241
- parseURL(query) {
242
- if (query.includes('github.com') || query.startsWith('git@github.com:') ||
243
- query.startsWith('https://') || query.startsWith('git://')) {
244
- return gitUrlParse(query);
245
- } else if (/^[\w-]+\/[\w.-]+$/.test(query)) {
246
- return gitUrlParse(`https://github.com/${query}`);
247
- }
248
- return false;
249
- }
250
-
251
- /**
252
- * Detects the current operating system and architecture
253
- * @returns {Object} Platform information object
254
- * @returns {string} returns.os - Normalized OS name (windows, macos, linux)
255
- * @returns {string} returns.arch - Normalized architecture (x86_64, arm64, arm, i386)
256
- * @returns {string} returns.platform - Raw Node.js platform string
257
- * @returns {string} returns.architecture - Raw Node.js architecture string
258
- * @example
259
- * const platform = github.getCurrentPlatform();
260
- * console.log(`Running on ${platform.os} ${platform.arch}`);
261
- */
262
- getCurrentPlatform() {
263
- const platform = os.platform();
264
- const arch = os.arch();
265
-
266
- const platformMap = {
267
- 'win32': 'windows',
268
- 'darwin': 'macos',
269
- 'linux': 'linux'
270
- };
271
-
272
- const archMap = {
273
- 'x64': 'x86_64',
274
- 'arm64': 'arm64',
275
- 'arm': 'arm',
276
- 'ia32': 'i386'
277
- };
278
-
279
- return {
280
- os: platformMap[platform] || platform,
281
- arch: archMap[arch] || arch,
282
- platform,
283
- architecture: arch
284
- };
285
- }
286
-
287
- /**
288
- * Gets repository releases with platform categorization
289
- * @param {string} owner - Repository owner
290
- * @param {string} repo - Repository name
291
- * @returns {Promise<Array<Object>>} Array of categorized releases
292
- * @example
293
- * const releases = await github.getReleases('microsoft', 'vscode');
294
- * console.log(`Found ${releases.length} releases`);
295
- */
296
- async getReleases(owner, repo) {
297
- const releases = await this.callGithub(`/repos/${owner}/${repo}/releases`);
298
- return this._categorizeReleasesByPlatform(releases);
299
- }
300
-
301
- /**
302
- * Gets releases compatible with the current platform
303
- * @param {string} owner - Repository owner
304
- * @param {string} repo - Repository name
305
- * @returns {Promise<Array<Object>>} Array of compatible releases
306
- * @example
307
- * const compatible = await github.getCompatibleReleases('user', 'repo');
308
- * console.log(`Found ${compatible.length} compatible releases`);
309
- */
310
- async getCompatibleReleases(owner, repo) {
311
- const releases = await this.callGithub(`/repos/${owner}/${repo}/releases`);
312
- const currentPlatform = this.getCurrentPlatform();
313
- return this._filterReleasesByPlatform(releases, currentPlatform);
314
- }
315
-
316
- /**
317
- * Generates an available directory name by appending a counter if the base path exists
318
- * @private
319
- * @param {string} basePath - The desired base directory path
320
- * @returns {string} An available directory path
321
- */
322
- _getAvailableDirectoryName(basePath) {
323
- if (!fs.existsSync(basePath)) return basePath;
324
- let counter = 2;
325
- let newPath;
326
- while (true) {
327
- newPath = `${basePath}-${counter}`;
328
- if (!fs.existsSync(newPath)) return newPath;
329
- counter++;
330
- }
331
- }
332
-
333
- /**
334
- * Categorizes GitHub releases by platform and architecture based on asset names
335
- * @private
336
- * @param {Array<Object>} releases - Array of GitHub release objects
337
- * @returns {Array<Object>} Releases with platformAssets property containing categorized assets
338
- */
339
- _categorizeReleasesByPlatform(releases) {
340
- const platformKeywords = {
341
- windows: ['win', 'windows', 'win32', 'win64', '.exe', '.msi'],
342
- macos: ['mac', 'macos', 'darwin', 'osx', '.dmg', '.pkg'],
343
- linux: ['linux', 'ubuntu', 'debian', '.deb', '.rpm', '.tar.gz', '.AppImage']
344
- };
345
-
346
- const archKeywords = {
347
- x86_64: ['x86_64', 'x64', 'amd64', '64'],
348
- arm64: ['arm64', 'aarch64'],
349
- arm: ['arm', 'armv7'],
350
- i386: ['i386', 'x86', '32']
351
- };
352
-
353
- const categorizedReleases = [];
354
-
355
- Object.entries(releases).forEach(([key, release]) => {
356
- const platformAssets = {
357
- windows: [],
358
- macos: [],
359
- linux: [],
360
- universal: []
361
- };
362
-
363
- release?.assets?.forEach(asset => {
364
- const name = asset.name.toLowerCase();
365
- let categorized = false;
366
-
367
- // Check each platform
368
- Object.entries(platformKeywords).forEach(([platform, keywords]) => {
369
- if (keywords.some(keyword => name.includes(keyword.toLowerCase()))) {
370
- // Determine architecture
371
- let detectedArch = 'unknown';
372
- Object.entries(archKeywords).forEach(([arch, archKeys]) => {
373
- if (archKeys.some(archKey => name.includes(archKey.toLowerCase()))) {
374
- detectedArch = arch;
375
- }
376
- });
377
-
378
- platformAssets[platform].push({
379
- ...asset,
380
- detectedArch,
381
- platform
382
- });
383
- categorized = true;
384
- }
385
- });
386
-
387
- // If not categorized, check for universal binaries
388
- if (!categorized && (name.includes('universal') || name.includes('all') ||
389
- (!name.includes('win') && !name.includes('mac') && !name.includes('linux')))) {
390
- platformAssets.universal.push({
391
- ...asset,
392
- detectedArch: 'universal',
393
- platform: 'universal'
394
- });
395
- }
396
- });
397
-
398
- // Only include releases that have assets
399
- const hasAssets = Object.values(platformAssets).some(assets => assets.length > 0);
400
- if (hasAssets) {
401
- categorizedReleases.push({
402
- ...release,
403
- platformAssets
404
- });
405
- }
406
- });
407
-
408
- return categorizedReleases;
409
- }
410
-
411
- /**
412
- * Filters releases to only include those compatible with the specified platform
413
- * @private
414
- * @param {Array<Object>} releases - Array of GitHub release objects
415
- * @param {Object} currentPlatform - Platform information from getCurrentPlatform()
416
- * @returns {Array<Object>} Filtered array of compatible releases
417
- */
418
- _filterReleasesByPlatform(releases, currentPlatform) {
419
- const categorized = this._categorizeReleasesByPlatform(releases);
420
- return categorized.filter(release =>
421
- release.platformAssets[currentPlatform.os]?.length > 0 ||
422
- release.platformAssets.universal?.length > 0
423
- );
424
- }
425
-
426
- /**
427
- * Provides platform-specific installation instructions for a downloaded asset
428
- * @private
429
- * @param {string} filePath - Path to the downloaded file
430
- * @param {Object} asset - GitHub release asset object
431
- * @param {string} asset.name - Name of the asset file
432
- */
433
- _provideInstallationInstructions(filePath, fileName) {
434
- const platform = this.getCurrentPlatform();
435
-
436
- if (platform.platform === 'win32') {
437
- if (fileName.endsWith('.exe')) {
438
- console.log(chalk.white(' Run the executable:'));
439
- console.log(chalk.gray(` ${filePath}`));
440
- } else if (fileName.endsWith('.msi')) {
441
- console.log(chalk.white(' Install the MSI package:'));
442
- console.log(chalk.gray(` msiexec /i "${filePath}"`));
443
- }
444
- } else if (platform.platform === 'darwin') {
445
- if (fileName.endsWith('.dmg')) {
446
- console.log(chalk.white(' Mount and install the DMG:'));
447
- console.log(chalk.gray(` open "${filePath}"`));
448
- } else if (fileName.endsWith('.pkg')) {
449
- console.log(chalk.white(' Install the package:'));
450
- console.log(chalk.gray(` sudo installer -pkg "${filePath}" -target /`));
451
- }
452
- } else {
453
- if (fileName.endsWith('.deb')) {
454
- console.log(chalk.white(' Install the DEB package:'));
455
- console.log(chalk.gray(` sudo dpkg -i "${filePath}"`));
456
- } else if (fileName.endsWith('.rpm')) {
457
- console.log(chalk.white(' Install the RPM package:'));
458
- console.log(chalk.gray(` sudo rpm -i "${filePath}"`));
459
- } else if (fileName.endsWith('.AppImage')) {
460
- console.log(chalk.white(' Run the AppImage:'));
461
- console.log(chalk.gray(` chmod +x "${filePath}" && "${filePath}"`));
462
- } else if (!fileName.includes('.')) {
463
- console.log(chalk.white(' Binary is ready to use:'));
464
- console.log(chalk.gray(` "${filePath}"`));
465
- console.log(chalk.white(' Consider moving to PATH:'));
466
- console.log(chalk.gray(` sudo mv "${filePath}" /usr/local/bin/`));
467
- }
468
- }
469
- }
470
- }
471
-
472
- export default GithubAPI;