prpm 0.2.0 → 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/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
package/dist/core/telemetry.js
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.telemetry = void 0;
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const os_1 = __importDefault(require("os"));
|
|
10
|
-
const posthog_node_1 = require("posthog-node");
|
|
11
|
-
const user_config_1 = require("./user-config");
|
|
12
|
-
class Telemetry {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.events = [];
|
|
15
|
-
this.maxEvents = 100; // Keep only last 100 events locally
|
|
16
|
-
this.posthog = null;
|
|
17
|
-
this.userConfigChecked = false;
|
|
18
|
-
this.userTelemetryEnabled = true; // Default to true until checked
|
|
19
|
-
this.configPath = path_1.default.join(os_1.default.homedir(), '.prpm', 'telemetry.json');
|
|
20
|
-
this.config = this.loadConfig();
|
|
21
|
-
}
|
|
22
|
-
async checkUserConfig() {
|
|
23
|
-
if (this.userConfigChecked)
|
|
24
|
-
return;
|
|
25
|
-
try {
|
|
26
|
-
const userConfig = await (0, user_config_1.getConfig)();
|
|
27
|
-
this.userTelemetryEnabled = userConfig.telemetryEnabled ?? true;
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
// If we can't load user config, default to enabled
|
|
31
|
-
this.userTelemetryEnabled = true;
|
|
32
|
-
}
|
|
33
|
-
this.userConfigChecked = true;
|
|
34
|
-
}
|
|
35
|
-
async initializePostHog() {
|
|
36
|
-
// Check user config first
|
|
37
|
-
await this.checkUserConfig();
|
|
38
|
-
// Only initialize if telemetry is enabled in user config
|
|
39
|
-
if (!this.userTelemetryEnabled) {
|
|
40
|
-
this.posthog = null;
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
this.posthog = new posthog_node_1.PostHog('phc_aO5lXLILeylHfb1ynszVwKbQKSzO91UGdXNhN5Q0Snl', {
|
|
45
|
-
host: 'https://app.posthog.com',
|
|
46
|
-
flushAt: 1, // Send events immediately
|
|
47
|
-
flushInterval: 0, // No batching
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
this.posthog = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
loadConfig() {
|
|
55
|
-
try {
|
|
56
|
-
const data = require(this.configPath);
|
|
57
|
-
return {
|
|
58
|
-
enabled: data.enabled ?? true, // Default to enabled
|
|
59
|
-
userId: data.userId,
|
|
60
|
-
sessionId: data.sessionId || this.generateSessionId(),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return {
|
|
65
|
-
enabled: true,
|
|
66
|
-
sessionId: this.generateSessionId(),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Load userId from user config and update telemetry config
|
|
72
|
-
*/
|
|
73
|
-
async loadUserIdFromConfig() {
|
|
74
|
-
try {
|
|
75
|
-
const userConfig = await (0, user_config_1.getConfig)();
|
|
76
|
-
if (userConfig.userId && userConfig.userId !== this.config.userId) {
|
|
77
|
-
this.config.userId = userConfig.userId;
|
|
78
|
-
await this.saveConfig();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
// Silently fail - telemetry shouldn't break the CLI
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
generateSessionId() {
|
|
86
|
-
return Math.random().toString(36).substring(2, 15) +
|
|
87
|
-
Math.random().toString(36).substring(2, 15);
|
|
88
|
-
}
|
|
89
|
-
async saveConfig() {
|
|
90
|
-
try {
|
|
91
|
-
await fs_1.promises.mkdir(path_1.default.dirname(this.configPath), { recursive: true });
|
|
92
|
-
await fs_1.promises.writeFile(this.configPath, JSON.stringify(this.config, null, 2));
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
// Silently fail - telemetry shouldn't break the CLI
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
async track(event) {
|
|
99
|
-
// Check user config first
|
|
100
|
-
await this.checkUserConfig();
|
|
101
|
-
// Return early if telemetry is disabled in user config
|
|
102
|
-
if (!this.userTelemetryEnabled)
|
|
103
|
-
return;
|
|
104
|
-
if (!this.config.enabled)
|
|
105
|
-
return;
|
|
106
|
-
// Load userId from user config before tracking
|
|
107
|
-
await this.loadUserIdFromConfig();
|
|
108
|
-
const fullEvent = {
|
|
109
|
-
...event,
|
|
110
|
-
timestamp: new Date().toISOString(),
|
|
111
|
-
version: process.env.npm_package_version || '0.1.0',
|
|
112
|
-
platform: os_1.default.platform(),
|
|
113
|
-
arch: os_1.default.arch(),
|
|
114
|
-
nodeVersion: process.version,
|
|
115
|
-
};
|
|
116
|
-
this.events.push(fullEvent);
|
|
117
|
-
// Keep only the last maxEvents
|
|
118
|
-
if (this.events.length > this.maxEvents) {
|
|
119
|
-
this.events = this.events.slice(-this.maxEvents);
|
|
120
|
-
}
|
|
121
|
-
// Save events locally
|
|
122
|
-
await this.saveEvents();
|
|
123
|
-
// Send to analytics service (async, non-blocking)
|
|
124
|
-
this.sendToAnalytics(fullEvent).catch(() => {
|
|
125
|
-
// Silently fail - don't break the CLI
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
async saveEvents() {
|
|
129
|
-
try {
|
|
130
|
-
const eventsPath = path_1.default.join(os_1.default.homedir(), '.prpm', 'events.json');
|
|
131
|
-
await fs_1.promises.mkdir(path_1.default.dirname(eventsPath), { recursive: true });
|
|
132
|
-
await fs_1.promises.writeFile(eventsPath, JSON.stringify(this.events, null, 2));
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
// Silently fail
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
async sendToAnalytics(event) {
|
|
139
|
-
// Initialize PostHog if needed (this will check user config)
|
|
140
|
-
if (!this.posthog && this.userTelemetryEnabled) {
|
|
141
|
-
await this.initializePostHog();
|
|
142
|
-
}
|
|
143
|
-
// Send to PostHog
|
|
144
|
-
await this.sendToPostHog(event);
|
|
145
|
-
}
|
|
146
|
-
async enable() {
|
|
147
|
-
this.config.enabled = true;
|
|
148
|
-
await this.saveConfig();
|
|
149
|
-
}
|
|
150
|
-
async disable() {
|
|
151
|
-
this.config.enabled = false;
|
|
152
|
-
await this.saveConfig();
|
|
153
|
-
}
|
|
154
|
-
async isEnabled() {
|
|
155
|
-
await this.checkUserConfig();
|
|
156
|
-
return this.userTelemetryEnabled && this.config.enabled;
|
|
157
|
-
}
|
|
158
|
-
async getStats() {
|
|
159
|
-
try {
|
|
160
|
-
const eventsPath = path_1.default.join(os_1.default.homedir(), '.prpm', 'events.json');
|
|
161
|
-
const data = await fs_1.promises.readFile(eventsPath, 'utf8');
|
|
162
|
-
const savedEvents = JSON.parse(data);
|
|
163
|
-
return {
|
|
164
|
-
totalEvents: savedEvents.length,
|
|
165
|
-
lastEvent: savedEvents[savedEvents.length - 1]?.timestamp,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
return {
|
|
170
|
-
totalEvents: this.events.length,
|
|
171
|
-
lastEvent: this.events[this.events.length - 1]?.timestamp,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async shutdown() {
|
|
176
|
-
if (this.posthog) {
|
|
177
|
-
try {
|
|
178
|
-
// Flush any pending events before shutdown
|
|
179
|
-
await this.posthog.flush();
|
|
180
|
-
await this.posthog.shutdown();
|
|
181
|
-
}
|
|
182
|
-
catch (error) {
|
|
183
|
-
// Silently fail
|
|
184
|
-
}
|
|
185
|
-
finally {
|
|
186
|
-
this.posthog = null;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Identify user in PostHog with user properties
|
|
192
|
-
* Called after successful login to set user attributes
|
|
193
|
-
*/
|
|
194
|
-
async identifyUser(userId, traits) {
|
|
195
|
-
if (!this.posthog || !this.config.enabled)
|
|
196
|
-
return;
|
|
197
|
-
try {
|
|
198
|
-
// Update local config with userId
|
|
199
|
-
this.config.userId = userId;
|
|
200
|
-
await this.saveConfig();
|
|
201
|
-
// Send $identify event to PostHog
|
|
202
|
-
this.posthog.identify({
|
|
203
|
-
distinctId: userId,
|
|
204
|
-
properties: traits,
|
|
205
|
-
});
|
|
206
|
-
// Also capture the $identify event explicitly
|
|
207
|
-
this.posthog.capture({
|
|
208
|
-
distinctId: userId,
|
|
209
|
-
event: '$identify',
|
|
210
|
-
properties: traits,
|
|
211
|
-
});
|
|
212
|
-
// Flush immediately to ensure identification happens
|
|
213
|
-
await this.posthog.flush();
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
// Silently fail - telemetry shouldn't break the CLI
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Send to PostHog
|
|
220
|
-
async sendToPostHog(event) {
|
|
221
|
-
if (!this.posthog)
|
|
222
|
-
return;
|
|
223
|
-
try {
|
|
224
|
-
const distinctId = this.config.userId || this.config.sessionId || 'anonymous';
|
|
225
|
-
this.posthog.capture({
|
|
226
|
-
distinctId,
|
|
227
|
-
event: `prpm_${event.command}`,
|
|
228
|
-
properties: {
|
|
229
|
-
// Core event data
|
|
230
|
-
command: event.command,
|
|
231
|
-
success: event.success,
|
|
232
|
-
duration: event.duration,
|
|
233
|
-
error: event.error,
|
|
234
|
-
// System information
|
|
235
|
-
version: event.version,
|
|
236
|
-
platform: event.platform,
|
|
237
|
-
arch: event.arch,
|
|
238
|
-
nodeVersion: event.nodeVersion,
|
|
239
|
-
// Command-specific data
|
|
240
|
-
...event.data,
|
|
241
|
-
// Metadata
|
|
242
|
-
timestamp: event.timestamp,
|
|
243
|
-
sessionId: this.config.sessionId,
|
|
244
|
-
},
|
|
245
|
-
});
|
|
246
|
-
// Event sent to PostHog
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
// Silently fail - don't break the CLI
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
exports.telemetry = new Telemetry();
|
package/dist/core/user-config.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* User configuration management for ~/.prpmrc and .prpmrc
|
|
4
|
-
* Stores global settings like registry URL and authentication token
|
|
5
|
-
* Supports both user-level (~/.prpmrc) and repository-level (.prpmrc) config
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.getConfig = getConfig;
|
|
9
|
-
exports.saveConfig = saveConfig;
|
|
10
|
-
exports.saveRepoConfig = saveRepoConfig;
|
|
11
|
-
exports.getRepoConfig = getRepoConfig;
|
|
12
|
-
exports.getUserConfig = getUserConfig;
|
|
13
|
-
exports.updateConfig = updateConfig;
|
|
14
|
-
exports.clearAuth = clearAuth;
|
|
15
|
-
exports.getRegistryUrl = getRegistryUrl;
|
|
16
|
-
const fs_1 = require("fs");
|
|
17
|
-
const path_1 = require("path");
|
|
18
|
-
const os_1 = require("os");
|
|
19
|
-
const USER_CONFIG_FILE = (0, path_1.join)((0, os_1.homedir)(), '.prpmrc');
|
|
20
|
-
const REPO_CONFIG_FILE = '.prpmrc';
|
|
21
|
-
const DEFAULT_REGISTRY_URL = 'https://registry.prpm.dev';
|
|
22
|
-
/**
|
|
23
|
-
* Load configuration from a file
|
|
24
|
-
*/
|
|
25
|
-
async function loadConfigFile(filePath) {
|
|
26
|
-
try {
|
|
27
|
-
const data = await fs_1.promises.readFile(filePath, 'utf-8');
|
|
28
|
-
return JSON.parse(data);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
if (error.code === 'ENOENT') {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
throw new Error(`Failed to read config from ${filePath}: ${error}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get merged configuration from user and repository levels
|
|
39
|
-
* Priority: CLI flags > environment > repo config > user config > defaults
|
|
40
|
-
*/
|
|
41
|
-
async function getConfig() {
|
|
42
|
-
// Load user-level config (~/.prpmrc)
|
|
43
|
-
const userConfig = await loadConfigFile(USER_CONFIG_FILE);
|
|
44
|
-
// Load repository-level config (./prpmrc)
|
|
45
|
-
const repoConfigPath = (0, path_1.join)(process.cwd(), REPO_CONFIG_FILE);
|
|
46
|
-
const repoConfig = await loadConfigFile(repoConfigPath);
|
|
47
|
-
// Merge configs (repo overrides user)
|
|
48
|
-
const config = {
|
|
49
|
-
...userConfig,
|
|
50
|
-
...repoConfig,
|
|
51
|
-
};
|
|
52
|
-
// Deep merge nested objects
|
|
53
|
-
if (userConfig?.cursor || repoConfig?.cursor) {
|
|
54
|
-
config.cursor = {
|
|
55
|
-
...userConfig?.cursor,
|
|
56
|
-
...repoConfig?.cursor,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
if (userConfig?.claude || repoConfig?.claude) {
|
|
60
|
-
config.claude = {
|
|
61
|
-
...userConfig?.claude,
|
|
62
|
-
...repoConfig?.claude,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (userConfig?.collections || repoConfig?.collections) {
|
|
66
|
-
config.collections = {
|
|
67
|
-
...userConfig?.collections,
|
|
68
|
-
...repoConfig?.collections,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// Allow environment variable to override registry URL
|
|
72
|
-
if (process.env.PRPM_REGISTRY_URL) {
|
|
73
|
-
config.registryUrl = process.env.PRPM_REGISTRY_URL;
|
|
74
|
-
}
|
|
75
|
-
else if (!config.registryUrl) {
|
|
76
|
-
config.registryUrl = DEFAULT_REGISTRY_URL;
|
|
77
|
-
}
|
|
78
|
-
// Set defaults
|
|
79
|
-
if (config.telemetryEnabled === undefined) {
|
|
80
|
-
config.telemetryEnabled = true;
|
|
81
|
-
}
|
|
82
|
-
return config;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Save user configuration to ~/.prpmrc
|
|
86
|
-
*/
|
|
87
|
-
async function saveConfig(config) {
|
|
88
|
-
try {
|
|
89
|
-
const data = JSON.stringify(config, null, 2);
|
|
90
|
-
await fs_1.promises.writeFile(USER_CONFIG_FILE, data, 'utf-8');
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
throw new Error(`Failed to save user config: ${error}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Save repository configuration to ./.prpmrc
|
|
98
|
-
*/
|
|
99
|
-
async function saveRepoConfig(config) {
|
|
100
|
-
try {
|
|
101
|
-
const repoConfigPath = (0, path_1.join)(process.cwd(), REPO_CONFIG_FILE);
|
|
102
|
-
const data = JSON.stringify(config, null, 2);
|
|
103
|
-
await fs_1.promises.writeFile(repoConfigPath, data, 'utf-8');
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw new Error(`Failed to save repository config: ${error}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Get repository-level configuration only
|
|
111
|
-
*/
|
|
112
|
-
async function getRepoConfig() {
|
|
113
|
-
const repoConfigPath = (0, path_1.join)(process.cwd(), REPO_CONFIG_FILE);
|
|
114
|
-
return await loadConfigFile(repoConfigPath);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get user-level configuration only
|
|
118
|
-
*/
|
|
119
|
-
async function getUserConfig() {
|
|
120
|
-
return await loadConfigFile(USER_CONFIG_FILE);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Update specific config values
|
|
124
|
-
*/
|
|
125
|
-
async function updateConfig(updates) {
|
|
126
|
-
const config = await getConfig();
|
|
127
|
-
const newConfig = { ...config, ...updates };
|
|
128
|
-
await saveConfig(newConfig);
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Clear authentication (logout)
|
|
132
|
-
*/
|
|
133
|
-
async function clearAuth() {
|
|
134
|
-
const config = await getConfig();
|
|
135
|
-
delete config.token;
|
|
136
|
-
delete config.username;
|
|
137
|
-
delete config.userId;
|
|
138
|
-
delete config.email;
|
|
139
|
-
await saveConfig(config);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Get registry URL (with fallback to default)
|
|
143
|
-
*/
|
|
144
|
-
async function getRegistryUrl() {
|
|
145
|
-
const config = await getConfig();
|
|
146
|
-
return config.registryUrl || DEFAULT_REGISTRY_URL;
|
|
147
|
-
}
|
package/dist/types/registry.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Registry API types for CLI
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isMultiPackageManifest = isMultiPackageManifest;
|
|
7
|
-
/**
|
|
8
|
-
* Type guard to check if manifest is multi-package
|
|
9
|
-
*/
|
|
10
|
-
function isMultiPackageManifest(manifest) {
|
|
11
|
-
return 'packages' in manifest && Array.isArray(manifest.packages);
|
|
12
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Core types for the Prompt Package Manager
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SUBTYPES = exports.FORMATS = void 0;
|
|
7
|
-
var types_1 = require("@pr-pm/types");
|
|
8
|
-
Object.defineProperty(exports, "FORMATS", { enumerable: true, get: function () { return types_1.FORMATS; } });
|
|
9
|
-
Object.defineProperty(exports, "SUBTYPES", { enumerable: true, get: function () { return types_1.SUBTYPES; } });
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* License extraction utilities
|
|
4
|
-
* Extracts license information from LICENSE files for proper attribution
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.extractLicenseInfo = extractLicenseInfo;
|
|
8
|
-
exports.validateLicenseInfo = validateLicenseInfo;
|
|
9
|
-
const promises_1 = require("fs/promises");
|
|
10
|
-
const path_1 = require("path");
|
|
11
|
-
const fs_1 = require("fs");
|
|
12
|
-
/**
|
|
13
|
-
* Common license file names to search for
|
|
14
|
-
*/
|
|
15
|
-
const LICENSE_FILE_PATTERNS = [
|
|
16
|
-
'LICENSE',
|
|
17
|
-
'LICENSE.md',
|
|
18
|
-
'LICENSE.txt',
|
|
19
|
-
'LICENCE',
|
|
20
|
-
'LICENCE.md',
|
|
21
|
-
'LICENCE.txt',
|
|
22
|
-
'LICENSE-MIT',
|
|
23
|
-
'LICENSE-APACHE',
|
|
24
|
-
'COPYING',
|
|
25
|
-
'COPYING.txt',
|
|
26
|
-
];
|
|
27
|
-
/**
|
|
28
|
-
* License type detection patterns
|
|
29
|
-
* Ordered by specificity - more specific patterns first
|
|
30
|
-
*/
|
|
31
|
-
const LICENSE_PATTERNS = [
|
|
32
|
-
{ pattern: /MIT License/i, type: 'MIT' },
|
|
33
|
-
{ pattern: /Apache License[\s\S]*Version 2\.0/i, type: 'Apache-2.0' },
|
|
34
|
-
{ pattern: /GNU GENERAL PUBLIC LICENSE[\s\S]*Version 3/i, type: 'GPL-3.0' },
|
|
35
|
-
{ pattern: /GNU GENERAL PUBLIC LICENSE[\s\S]*Version 2/i, type: 'GPL-2.0' },
|
|
36
|
-
{ pattern: /GNU LESSER GENERAL PUBLIC LICENSE[\s\S]*Version 3/i, type: 'LGPL-3.0' },
|
|
37
|
-
{ pattern: /GNU LESSER GENERAL PUBLIC LICENSE[\s\S]*Version 2/i, type: 'LGPL-2.1' },
|
|
38
|
-
{ pattern: /BSD 3-Clause License/i, type: 'BSD-3-Clause' },
|
|
39
|
-
{ pattern: /BSD 2-Clause License/i, type: 'BSD-2-Clause' },
|
|
40
|
-
{ pattern: /Mozilla Public License[\s\S]*Version 2\.0/i, type: 'MPL-2.0' },
|
|
41
|
-
{ pattern: /ISC License/i, type: 'ISC' },
|
|
42
|
-
{ pattern: /The Unlicense/i, type: 'Unlicense' },
|
|
43
|
-
{ pattern: /Creative Commons Zero[\s\S]*1\.0/i, type: 'CC0-1.0' },
|
|
44
|
-
];
|
|
45
|
-
/**
|
|
46
|
-
* Detect license type from license text
|
|
47
|
-
*/
|
|
48
|
-
function detectLicenseType(text) {
|
|
49
|
-
for (const { pattern, type } of LICENSE_PATTERNS) {
|
|
50
|
-
if (pattern.test(text)) {
|
|
51
|
-
return type;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Generate GitHub raw URL for license file
|
|
58
|
-
*/
|
|
59
|
-
function generateLicenseUrl(repositoryUrl, fileName) {
|
|
60
|
-
if (!repositoryUrl) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
// Extract owner/repo from GitHub URL
|
|
64
|
-
const githubMatch = repositoryUrl.match(/github\.com[/:]([\w-]+)\/([\w-]+)/);
|
|
65
|
-
if (!githubMatch) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
const [, owner, repo] = githubMatch;
|
|
69
|
-
// Use raw.githubusercontent.com for direct file access
|
|
70
|
-
return `https://raw.githubusercontent.com/${owner}/${repo}/main/${fileName}`;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Find and extract license information from the current directory
|
|
74
|
-
*/
|
|
75
|
-
async function extractLicenseInfo(repositoryUrl) {
|
|
76
|
-
const cwd = process.cwd();
|
|
77
|
-
// Try each license file pattern
|
|
78
|
-
for (const fileName of LICENSE_FILE_PATTERNS) {
|
|
79
|
-
const filePath = (0, path_1.join)(cwd, fileName);
|
|
80
|
-
try {
|
|
81
|
-
// Check if file exists
|
|
82
|
-
await (0, promises_1.access)(filePath, fs_1.constants.R_OK);
|
|
83
|
-
// Read license file
|
|
84
|
-
const text = await (0, promises_1.readFile)(filePath, 'utf-8');
|
|
85
|
-
// Detect license type
|
|
86
|
-
const type = detectLicenseType(text);
|
|
87
|
-
// Generate license URL if repository is provided
|
|
88
|
-
const url = generateLicenseUrl(repositoryUrl, fileName);
|
|
89
|
-
return {
|
|
90
|
-
type,
|
|
91
|
-
text,
|
|
92
|
-
url,
|
|
93
|
-
fileName,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
// File doesn't exist or can't be read, try next pattern
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// No license file found
|
|
102
|
-
return {
|
|
103
|
-
type: null,
|
|
104
|
-
text: null,
|
|
105
|
-
url: null,
|
|
106
|
-
fileName: null,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Display license information if found
|
|
111
|
-
*/
|
|
112
|
-
function validateLicenseInfo(licenseInfo, packageName) {
|
|
113
|
-
if (licenseInfo.text && licenseInfo.type) {
|
|
114
|
-
console.log(` License: ${licenseInfo.type} (${licenseInfo.fileName})`);
|
|
115
|
-
}
|
|
116
|
-
else if (licenseInfo.text && !licenseInfo.type) {
|
|
117
|
-
console.log(` License: Found (${licenseInfo.fileName})`);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
console.log(` License: Not found (package will be published without license)`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Multi-package manifest utilities
|
|
4
|
-
* Handles merging root-level fields with package-level fields
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.mergePackageFields = mergePackageFields;
|
|
8
|
-
exports.getPackagesWithInheritance = getPackagesWithInheritance;
|
|
9
|
-
exports.validateMultiPackageManifest = validateMultiPackageManifest;
|
|
10
|
-
exports.filterPackages = filterPackages;
|
|
11
|
-
/**
|
|
12
|
-
* Fields that can be inherited from root to packages
|
|
13
|
-
*/
|
|
14
|
-
const INHERITABLE_FIELDS = [
|
|
15
|
-
'author',
|
|
16
|
-
'license',
|
|
17
|
-
'repository',
|
|
18
|
-
'homepage',
|
|
19
|
-
'documentation',
|
|
20
|
-
'organization',
|
|
21
|
-
'keywords',
|
|
22
|
-
'tags',
|
|
23
|
-
];
|
|
24
|
-
/**
|
|
25
|
-
* Merge root-level fields into a package manifest
|
|
26
|
-
* Package-level fields take precedence over root-level fields
|
|
27
|
-
*/
|
|
28
|
-
function mergePackageFields(root, pkg) {
|
|
29
|
-
const merged = { ...pkg };
|
|
30
|
-
// Inherit each inheritable field if not defined in package
|
|
31
|
-
for (const field of INHERITABLE_FIELDS) {
|
|
32
|
-
if (merged[field] === undefined && root[field] !== undefined) {
|
|
33
|
-
// @ts-ignore - dynamic field access
|
|
34
|
-
merged[field] = root[field];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return merged;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get all packages from a multi-package manifest with inherited fields
|
|
41
|
-
*/
|
|
42
|
-
function getPackagesWithInheritance(manifest) {
|
|
43
|
-
return manifest.packages.map(pkg => mergePackageFields(manifest, pkg));
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Validate multi-package manifest
|
|
47
|
-
*/
|
|
48
|
-
function validateMultiPackageManifest(manifest) {
|
|
49
|
-
const errors = [];
|
|
50
|
-
// Check packages array exists and is not empty
|
|
51
|
-
if (!manifest.packages || !Array.isArray(manifest.packages)) {
|
|
52
|
-
errors.push('packages field must be an array');
|
|
53
|
-
return { valid: false, errors };
|
|
54
|
-
}
|
|
55
|
-
if (manifest.packages.length === 0) {
|
|
56
|
-
errors.push('packages array must contain at least one package');
|
|
57
|
-
return { valid: false, errors };
|
|
58
|
-
}
|
|
59
|
-
// Check for duplicate package names
|
|
60
|
-
const names = new Set();
|
|
61
|
-
for (let i = 0; i < manifest.packages.length; i++) {
|
|
62
|
-
const pkg = manifest.packages[i];
|
|
63
|
-
if (names.has(pkg.name)) {
|
|
64
|
-
errors.push(`Duplicate package name: ${pkg.name}`);
|
|
65
|
-
}
|
|
66
|
-
names.add(pkg.name);
|
|
67
|
-
}
|
|
68
|
-
// Validate each package has required fields
|
|
69
|
-
for (let i = 0; i < manifest.packages.length; i++) {
|
|
70
|
-
const pkg = manifest.packages[i];
|
|
71
|
-
const pkgPrefix = `Package ${i} (${pkg.name || 'unnamed'})`;
|
|
72
|
-
if (!pkg.name) {
|
|
73
|
-
errors.push(`${pkgPrefix}: missing required field 'name'`);
|
|
74
|
-
}
|
|
75
|
-
if (!pkg.version) {
|
|
76
|
-
errors.push(`${pkgPrefix}: missing required field 'version'`);
|
|
77
|
-
}
|
|
78
|
-
if (!pkg.description) {
|
|
79
|
-
errors.push(`${pkgPrefix}: missing required field 'description'`);
|
|
80
|
-
}
|
|
81
|
-
if (!pkg.format) {
|
|
82
|
-
errors.push(`${pkgPrefix}: missing required field 'format'`);
|
|
83
|
-
}
|
|
84
|
-
if (!pkg.files || pkg.files.length === 0) {
|
|
85
|
-
errors.push(`${pkgPrefix}: must have at least one file in 'files' array`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
valid: errors.length === 0,
|
|
90
|
-
errors,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Filter packages by name or pattern
|
|
95
|
-
*/
|
|
96
|
-
function filterPackages(packages, filter) {
|
|
97
|
-
// If filter is a number, treat as index
|
|
98
|
-
if (typeof filter === 'number') {
|
|
99
|
-
if (filter < 0 || filter >= packages.length) {
|
|
100
|
-
throw new Error(`Package index ${filter} out of range (0-${packages.length - 1})`);
|
|
101
|
-
}
|
|
102
|
-
return [packages[filter]];
|
|
103
|
-
}
|
|
104
|
-
// If exact match, return that package
|
|
105
|
-
const exactMatch = packages.find(pkg => pkg.name === filter);
|
|
106
|
-
if (exactMatch) {
|
|
107
|
-
return [exactMatch];
|
|
108
|
-
}
|
|
109
|
-
// Try as glob pattern
|
|
110
|
-
const pattern = filter.replace(/\*/g, '.*');
|
|
111
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
112
|
-
const matches = packages.filter(pkg => regex.test(pkg.name));
|
|
113
|
-
if (matches.length === 0) {
|
|
114
|
-
throw new Error(`No packages match filter: ${filter}`);
|
|
115
|
-
}
|
|
116
|
-
return matches;
|
|
117
|
-
}
|