prpm 0.2.0 → 1.0.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.
- package/dist/index.js +14257 -109
- package/package.json +11 -9
- package/dist/__tests__/e2e/test-helpers.js +0 -153
- package/dist/commands/buy-credits.js +0 -224
- package/dist/commands/catalog.js +0 -365
- package/dist/commands/collections.js +0 -655
- package/dist/commands/config.js +0 -161
- package/dist/commands/credits.js +0 -186
- package/dist/commands/index.js +0 -184
- package/dist/commands/info.js +0 -78
- package/dist/commands/init.js +0 -684
- package/dist/commands/install.js +0 -829
- package/dist/commands/list.js +0 -198
- package/dist/commands/login.js +0 -316
- package/dist/commands/outdated.js +0 -130
- package/dist/commands/playground.js +0 -637
- package/dist/commands/popular.js +0 -33
- package/dist/commands/publish.js +0 -803
- package/dist/commands/schema.js +0 -41
- package/dist/commands/search.js +0 -446
- package/dist/commands/starred.js +0 -147
- package/dist/commands/subscribe.js +0 -211
- package/dist/commands/telemetry.js +0 -104
- package/dist/commands/trending.js +0 -86
- package/dist/commands/uninstall.js +0 -120
- package/dist/commands/update.js +0 -121
- package/dist/commands/upgrade.js +0 -121
- package/dist/commands/whoami.js +0 -83
- package/dist/core/claude-config.js +0 -91
- package/dist/core/cursor-config.js +0 -130
- package/dist/core/downloader.js +0 -64
- package/dist/core/errors.js +0 -29
- package/dist/core/filesystem.js +0 -246
- package/dist/core/lockfile.js +0 -292
- package/dist/core/marketplace-converter.js +0 -224
- package/dist/core/prompts.js +0 -62
- package/dist/core/registry-client.js +0 -305
- package/dist/core/schema-validator.js +0 -74
- package/dist/core/telemetry.js +0 -253
- package/dist/core/user-config.js +0 -147
- package/dist/types/registry.js +0 -12
- package/dist/types.js +0 -9
- package/dist/utils/license-extractor.js +0 -122
- package/dist/utils/multi-package.js +0 -117
- package/dist/utils/parallel-publisher.js +0 -144
- package/dist/utils/script-executor.js +0 -72
- package/dist/utils/snippet-extractor.js +0 -77
- package/dist/utils/webapp-url.js +0 -44
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Parallel publishing utilities with concurrency control
|
|
4
|
-
* Optimizes multi-package publishing performance
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.publishInParallel = publishInParallel;
|
|
8
|
-
exports.withRetry = withRetry;
|
|
9
|
-
exports.formatDuration = formatDuration;
|
|
10
|
-
exports.calculateStats = calculateStats;
|
|
11
|
-
/**
|
|
12
|
-
* Execute tasks in parallel with concurrency limit
|
|
13
|
-
*/
|
|
14
|
-
async function publishInParallel(tasks, options = {}) {
|
|
15
|
-
const { concurrency = 5, continueOnError = false, onProgress, onSuccess, onError, } = options;
|
|
16
|
-
const results = new Array(tasks.length);
|
|
17
|
-
let completed = 0;
|
|
18
|
-
let hasError = false;
|
|
19
|
-
let taskIndex = 0;
|
|
20
|
-
async function executeTask(task, index) {
|
|
21
|
-
const startTime = Date.now();
|
|
22
|
-
try {
|
|
23
|
-
const result = await task.execute();
|
|
24
|
-
const duration = Date.now() - startTime;
|
|
25
|
-
results[index] = {
|
|
26
|
-
success: true,
|
|
27
|
-
name: task.name,
|
|
28
|
-
result,
|
|
29
|
-
duration,
|
|
30
|
-
};
|
|
31
|
-
completed++;
|
|
32
|
-
onProgress?.(completed, tasks.length, task.name);
|
|
33
|
-
onSuccess?.(task.name, result);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
const duration = Date.now() - startTime;
|
|
37
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
38
|
-
results[index] = {
|
|
39
|
-
success: false,
|
|
40
|
-
name: task.name,
|
|
41
|
-
error: err,
|
|
42
|
-
duration,
|
|
43
|
-
};
|
|
44
|
-
completed++;
|
|
45
|
-
hasError = true;
|
|
46
|
-
onProgress?.(completed, tasks.length, task.name);
|
|
47
|
-
onError?.(task.name, err);
|
|
48
|
-
// If not continuing on error, mark hasError to skip remaining tasks
|
|
49
|
-
if (!continueOnError) {
|
|
50
|
-
throw err;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Execute tasks with concurrency control
|
|
55
|
-
const executing = new Set();
|
|
56
|
-
while (taskIndex < tasks.length || executing.size > 0) {
|
|
57
|
-
// Fill up to concurrency limit
|
|
58
|
-
while (taskIndex < tasks.length && executing.size < concurrency) {
|
|
59
|
-
// If in strict mode and we've encountered an error, skip remaining tasks
|
|
60
|
-
if (!continueOnError && hasError) {
|
|
61
|
-
results[taskIndex] = {
|
|
62
|
-
success: false,
|
|
63
|
-
name: tasks[taskIndex].name,
|
|
64
|
-
error: new Error('Skipped due to previous failure'),
|
|
65
|
-
duration: 0,
|
|
66
|
-
};
|
|
67
|
-
taskIndex++;
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const currentIndex = taskIndex;
|
|
71
|
-
const currentTask = tasks[taskIndex];
|
|
72
|
-
taskIndex++;
|
|
73
|
-
const promise = executeTask(currentTask, currentIndex)
|
|
74
|
-
.catch(() => {
|
|
75
|
-
// Errors already handled in executeTask
|
|
76
|
-
})
|
|
77
|
-
.finally(() => {
|
|
78
|
-
executing.delete(promise);
|
|
79
|
-
});
|
|
80
|
-
executing.add(promise);
|
|
81
|
-
}
|
|
82
|
-
// Wait for at least one task to complete
|
|
83
|
-
if (executing.size > 0) {
|
|
84
|
-
await Promise.race(executing);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return results;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Retry a task with exponential backoff
|
|
91
|
-
*/
|
|
92
|
-
async function withRetry(fn, options = {}) {
|
|
93
|
-
const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffFactor = 2, } = options;
|
|
94
|
-
let lastError;
|
|
95
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
96
|
-
try {
|
|
97
|
-
return await fn();
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
101
|
-
// Don't retry on last attempt
|
|
102
|
-
if (attempt === maxRetries - 1) {
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
// Calculate delay with exponential backoff
|
|
106
|
-
const delay = Math.min(initialDelay * Math.pow(backoffFactor, attempt), maxDelay);
|
|
107
|
-
await sleep(delay);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
throw lastError || new Error('Max retries exceeded');
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Sleep utility
|
|
114
|
-
*/
|
|
115
|
-
function sleep(ms) {
|
|
116
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Format duration in human-readable format
|
|
120
|
-
*/
|
|
121
|
-
function formatDuration(ms) {
|
|
122
|
-
if (ms < 1000) {
|
|
123
|
-
return `${ms}ms`;
|
|
124
|
-
}
|
|
125
|
-
const seconds = (ms / 1000).toFixed(1);
|
|
126
|
-
return `${seconds}s`;
|
|
127
|
-
}
|
|
128
|
-
function calculateStats(results) {
|
|
129
|
-
const succeeded = results.filter(r => r.success && r.result !== undefined).length;
|
|
130
|
-
const failed = results.filter(r => !r.success && r.error && r.error.message !== 'Skipped due to previous failure').length;
|
|
131
|
-
const skipped = results.filter(r => r.error?.message === 'Skipped due to previous failure').length;
|
|
132
|
-
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
133
|
-
const completedCount = succeeded + failed;
|
|
134
|
-
const avgDuration = completedCount > 0 ? totalDuration / completedCount : 0;
|
|
135
|
-
return {
|
|
136
|
-
total: results.length,
|
|
137
|
-
succeeded,
|
|
138
|
-
failed,
|
|
139
|
-
skipped,
|
|
140
|
-
totalDuration,
|
|
141
|
-
avgDuration,
|
|
142
|
-
successRate: results.length > 0 ? succeeded / results.length : 0,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Utility for executing package lifecycle scripts
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.executeScript = executeScript;
|
|
7
|
-
exports.executePrepublishOnly = executePrepublishOnly;
|
|
8
|
-
const child_process_1 = require("child_process");
|
|
9
|
-
const util_1 = require("util");
|
|
10
|
-
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
11
|
-
/**
|
|
12
|
-
* Execute a package lifecycle script
|
|
13
|
-
* @param script - The script command to execute
|
|
14
|
-
* @param scriptName - Name of the script (for logging)
|
|
15
|
-
* @param options - Execution options
|
|
16
|
-
* @throws Error if script fails
|
|
17
|
-
*/
|
|
18
|
-
async function executeScript(script, scriptName, options = {}) {
|
|
19
|
-
const { cwd = process.cwd(), timeout = 5 * 60 * 1000, // 5 minutes default
|
|
20
|
-
maxBuffer = 10 * 1024 * 1024, // 10MB default
|
|
21
|
-
} = options;
|
|
22
|
-
console.log(`🔧 Running ${scriptName} script...`);
|
|
23
|
-
console.log(` $ ${script}\n`);
|
|
24
|
-
try {
|
|
25
|
-
const { stdout, stderr } = await execAsync(script, {
|
|
26
|
-
cwd,
|
|
27
|
-
timeout,
|
|
28
|
-
maxBuffer,
|
|
29
|
-
// Inherit environment variables
|
|
30
|
-
env: process.env,
|
|
31
|
-
});
|
|
32
|
-
// Show output in real-time
|
|
33
|
-
if (stdout) {
|
|
34
|
-
process.stdout.write(stdout);
|
|
35
|
-
}
|
|
36
|
-
if (stderr) {
|
|
37
|
-
process.stderr.write(stderr);
|
|
38
|
-
}
|
|
39
|
-
console.log(`\n✓ ${scriptName} script completed successfully\n`);
|
|
40
|
-
return {
|
|
41
|
-
stdout,
|
|
42
|
-
stderr,
|
|
43
|
-
exitCode: 0,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
// Show error output
|
|
48
|
-
if (error.stdout) {
|
|
49
|
-
process.stdout.write(error.stdout);
|
|
50
|
-
}
|
|
51
|
-
if (error.stderr) {
|
|
52
|
-
process.stderr.write(error.stderr);
|
|
53
|
-
}
|
|
54
|
-
const errorMessage = error.code === 'ETIMEDOUT'
|
|
55
|
-
? `${scriptName} script timed out after ${timeout}ms`
|
|
56
|
-
: error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'
|
|
57
|
-
? `${scriptName} script output exceeded maximum buffer size`
|
|
58
|
-
: `${scriptName} script failed with exit code ${error.code || 1}`;
|
|
59
|
-
throw new Error(errorMessage);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Execute prepublishOnly script if defined
|
|
64
|
-
* @param scripts - Package scripts object
|
|
65
|
-
* @param options - Execution options
|
|
66
|
-
*/
|
|
67
|
-
async function executePrepublishOnly(scripts, options = {}) {
|
|
68
|
-
if (!scripts?.prepublishOnly) {
|
|
69
|
-
return; // No script defined, nothing to do
|
|
70
|
-
}
|
|
71
|
-
await executeScript(scripts.prepublishOnly, 'prepublishOnly', options);
|
|
72
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Snippet extraction utilities
|
|
4
|
-
* Extracts preview content from package files for display in modals
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.extractSnippet = extractSnippet;
|
|
8
|
-
exports.validateSnippet = validateSnippet;
|
|
9
|
-
const promises_1 = require("fs/promises");
|
|
10
|
-
const path_1 = require("path");
|
|
11
|
-
const MAX_SNIPPET_LENGTH = 2000;
|
|
12
|
-
/**
|
|
13
|
-
* Extract a preview snippet from package files
|
|
14
|
-
* Uses the same path logic as the install command to determine where files will be placed
|
|
15
|
-
*/
|
|
16
|
-
async function extractSnippet(manifest) {
|
|
17
|
-
const cwd = process.cwd();
|
|
18
|
-
try {
|
|
19
|
-
// Validate manifest has required fields
|
|
20
|
-
if (!manifest.files || manifest.files.length === 0) {
|
|
21
|
-
console.warn('⚠️ Cannot extract snippet: no files specified in manifest');
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
// Prefer main file over first file if specified
|
|
25
|
-
let fileName;
|
|
26
|
-
if (manifest.main) {
|
|
27
|
-
fileName = manifest.main;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
const firstFile = manifest.files[0];
|
|
31
|
-
fileName = typeof firstFile === 'string'
|
|
32
|
-
? firstFile
|
|
33
|
-
: firstFile.path;
|
|
34
|
-
}
|
|
35
|
-
// Use the file path directly - it should be relative to project root
|
|
36
|
-
// (e.g., ".claude/skills/my-skill/SKILL.md" or ".cursor/rules/my-rule.mdc")
|
|
37
|
-
const fullPath = (0, path_1.join)(cwd, fileName);
|
|
38
|
-
// Check if path is a directory
|
|
39
|
-
const stats = await (0, promises_1.stat)(fullPath);
|
|
40
|
-
if (stats.isDirectory()) {
|
|
41
|
-
console.warn(`⚠️ Skipping snippet extraction: "${fullPath}" is a directory`);
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
// Read the file content
|
|
45
|
-
const content = await (0, promises_1.readFile)(fullPath, 'utf-8');
|
|
46
|
-
// Extract first N characters, trying to break at a reasonable point
|
|
47
|
-
if (content.length <= MAX_SNIPPET_LENGTH) {
|
|
48
|
-
return content.trim();
|
|
49
|
-
}
|
|
50
|
-
// Try to break at a newline near the limit
|
|
51
|
-
let snippet = content.substring(0, MAX_SNIPPET_LENGTH);
|
|
52
|
-
const lastNewline = snippet.lastIndexOf('\n');
|
|
53
|
-
if (lastNewline > MAX_SNIPPET_LENGTH * 0.8) {
|
|
54
|
-
// If we found a newline in the last 20%, break there
|
|
55
|
-
snippet = snippet.substring(0, lastNewline);
|
|
56
|
-
}
|
|
57
|
-
return snippet.trim() + '\n\n[... content truncated ...]';
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
// If we can't read the file, return null (snippet is optional)
|
|
61
|
-
console.warn('⚠️ Could not extract snippet:', error instanceof Error ? error.message : 'Unknown error');
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Validate snippet and warn if issues found
|
|
67
|
-
*/
|
|
68
|
-
function validateSnippet(snippet, packageName) {
|
|
69
|
-
if (!snippet) {
|
|
70
|
-
console.warn(`⚠️ Warning: No content snippet extracted for package "${packageName}"`);
|
|
71
|
-
console.warn(' A preview snippet helps users see what the prompt contains before installing.');
|
|
72
|
-
console.warn('');
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
console.log(` Snippet: ${snippet.length} characters extracted`);
|
|
76
|
-
}
|
|
77
|
-
}
|
package/dist/utils/webapp-url.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Utility to convert registry URL to webapp URL
|
|
4
|
-
* Handles localhost, production, and custom registry URLs
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.getWebappUrl = getWebappUrl;
|
|
8
|
-
/**
|
|
9
|
-
* Convert a registry URL to its corresponding webapp URL
|
|
10
|
-
*
|
|
11
|
-
* @param registryUrl - The registry URL (e.g., https://registry.prpm.dev)
|
|
12
|
-
* @returns The webapp URL (e.g., https://prpm.dev)
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // Production
|
|
16
|
-
* getWebappUrl('https://registry.prpm.dev') // => 'https://prpm.dev'
|
|
17
|
-
*
|
|
18
|
-
* // Local development
|
|
19
|
-
* getWebappUrl('http://localhost:3111') // => 'http://localhost:5173'
|
|
20
|
-
*
|
|
21
|
-
* // Custom registry
|
|
22
|
-
* getWebappUrl('https://registry.custom.com') // => 'https://custom.com'
|
|
23
|
-
*/
|
|
24
|
-
function getWebappUrl(registryUrl) {
|
|
25
|
-
const cleanUrl = registryUrl.replace(/\/$/, '').replace(/\/api\/?$/, '');
|
|
26
|
-
if (cleanUrl.includes('localhost') || cleanUrl.includes('127.0.0.1')) {
|
|
27
|
-
// Local development: registry on port 3111, webapp on port 5173
|
|
28
|
-
return cleanUrl.replace(':3111', ':5173');
|
|
29
|
-
}
|
|
30
|
-
if (cleanUrl.includes('registry.prpm.dev')) {
|
|
31
|
-
// Production: always use prpm.dev webapp
|
|
32
|
-
return 'https://prpm.dev';
|
|
33
|
-
}
|
|
34
|
-
// Custom registry: assume webapp is on same host without 'registry.' subdomain
|
|
35
|
-
try {
|
|
36
|
-
const url = new URL(cleanUrl);
|
|
37
|
-
const hostname = url.hostname.replace(/^registry\./, '');
|
|
38
|
-
return `${url.protocol}//${hostname}`;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// If URL parsing fails, return as-is
|
|
42
|
-
return cleanUrl;
|
|
43
|
-
}
|
|
44
|
-
}
|