@glpkg/config 0.2.1 → 0.3.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/cli.js CHANGED
@@ -75,6 +75,24 @@ program
75
75
  const packageCmd = program
76
76
  .command('package')
77
77
  .description('Manage package registry metadata');
78
+ // Add package manually
79
+ packageCmd
80
+ .command('add <package-name>')
81
+ .description('Add package with GitLab group lookup')
82
+ .requiredOption('-g, --group <group>', 'GitLab group name')
83
+ .option('--host <host>', 'GitLab host', 'gitlab.com')
84
+ .action(async (packageName, options) => {
85
+ try {
86
+ console.log(`Looking up group "${options.group}" on ${options.host}...`);
87
+ const info = await (0, index_1.addPackageManually)(packageName, options.group, options.host);
88
+ console.log('✓ Package added successfully');
89
+ console.log(JSON.stringify(info, null, 2));
90
+ }
91
+ catch (error) {
92
+ console.error('✗ Failed to add package:', error.message);
93
+ process.exit(1);
94
+ }
95
+ });
78
96
  // Get package info
79
97
  packageCmd
80
98
  .command('get <package-name>')
@@ -132,6 +150,83 @@ packageCmd
132
150
  process.exit(1);
133
151
  }
134
152
  });
153
+ // Scope management commands
154
+ const scopeCmd = program
155
+ .command('scope')
156
+ .description('Manage scope to GitLab group mappings');
157
+ // Set scope mapping
158
+ scopeCmd
159
+ .command('set <scope> <group>')
160
+ .description('Map a scope to a GitLab group (e.g., scope set @ist microature)')
161
+ .option('--host <host>', 'GitLab host', 'gitlab.com')
162
+ .action(async (scope, group, options) => {
163
+ try {
164
+ console.log(`Looking up group "${group}" on ${options.host}...`);
165
+ const mapping = await (0, index_1.setScopeMapping)(scope, group, options.host);
166
+ console.log('✓ Scope mapping saved');
167
+ console.log(JSON.stringify(mapping, null, 2));
168
+ }
169
+ catch (error) {
170
+ console.error('✗ Failed to set scope mapping:', error.message);
171
+ process.exit(1);
172
+ }
173
+ });
174
+ // Get scope mapping
175
+ scopeCmd
176
+ .command('get <scope>')
177
+ .description('Get scope mapping information')
178
+ .action((scope) => {
179
+ const mapping = (0, index_1.getScopeMapping)(scope);
180
+ if (mapping) {
181
+ console.log(JSON.stringify(mapping, null, 2));
182
+ }
183
+ else {
184
+ console.error('✗ Scope not found:', scope);
185
+ process.exit(1);
186
+ }
187
+ });
188
+ // List all scope mappings
189
+ scopeCmd
190
+ .command('list')
191
+ .description('List all scope mappings')
192
+ .action(() => {
193
+ const metadata = (0, index_1.getAllScopes)();
194
+ const scopes = Object.values(metadata.scopes);
195
+ if (scopes.length === 0) {
196
+ console.log('No scope mappings found');
197
+ }
198
+ else {
199
+ console.log(JSON.stringify(metadata, null, 2));
200
+ }
201
+ });
202
+ // Remove scope mapping
203
+ scopeCmd
204
+ .command('remove <scope>')
205
+ .description('Remove scope mapping')
206
+ .action((scope) => {
207
+ const removed = (0, index_1.removeScopeMapping)(scope);
208
+ if (removed) {
209
+ console.log('✓ Scope mapping removed:', scope);
210
+ }
211
+ else {
212
+ console.error('✗ Scope not found:', scope);
213
+ process.exit(1);
214
+ }
215
+ });
216
+ // Clear all scope mappings
217
+ scopeCmd
218
+ .command('clear')
219
+ .description('Clear all scope mappings')
220
+ .action(() => {
221
+ try {
222
+ (0, index_1.clearAllScopes)();
223
+ console.log('✓ All scope mappings cleared');
224
+ }
225
+ catch (error) {
226
+ console.error('✗ Failed to clear scope mappings:', error.message);
227
+ process.exit(1);
228
+ }
229
+ });
135
230
  program.parse();
136
231
  // Show help if no command provided
137
232
  if (!process.argv.slice(2).length) {
package/dist/index.d.ts CHANGED
@@ -35,6 +35,39 @@ export interface PackageInfo {
35
35
  export interface PackageRegistryMetadata {
36
36
  packages: Record<string, PackageInfo>;
37
37
  }
38
+ /**
39
+ * Scope mapping information
40
+ */
41
+ export interface ScopeMapping {
42
+ /** Scope name without @ (e.g., "ist") */
43
+ scope: string;
44
+ /** GitLab group name */
45
+ group: string;
46
+ /** GitLab group ID */
47
+ groupId: number;
48
+ /** GitLab host */
49
+ gitlabHost: string;
50
+ /** Registry URL for this scope */
51
+ registryUrl: string;
52
+ /** Created timestamp */
53
+ createdAt: string;
54
+ }
55
+ /**
56
+ * Scope registry metadata structure
57
+ */
58
+ export interface ScopeRegistryMetadata {
59
+ scopes: Record<string, ScopeMapping>;
60
+ }
61
+ /**
62
+ * GitLab group API response
63
+ */
64
+ interface GitLabGroupResponse {
65
+ id: number;
66
+ name: string;
67
+ path: string;
68
+ full_path: string;
69
+ web_url: string;
70
+ }
38
71
  /**
39
72
  * Save GitLab token to user config directory
40
73
  * @param token - GitLab personal access token
@@ -56,6 +89,7 @@ export declare function hasToken(): boolean;
56
89
  export declare function removeToken(): void;
57
90
  /**
58
91
  * Get package information by package name
92
+ * Falls back to scope mapping if package is not directly registered
59
93
  * @param packageName - Package name (e.g., "@another/lib-name")
60
94
  * @returns Package info or null if not found
61
95
  */
@@ -92,3 +126,49 @@ export declare function hasPackageInfo(packageName: string): boolean;
92
126
  * Clear all package registry metadata
93
127
  */
94
128
  export declare function clearAllPackages(): void;
129
+ /**
130
+ * Get scope mapping by scope name
131
+ * @param scope - Scope name (with or without @)
132
+ */
133
+ export declare function getScopeMapping(scope: string): ScopeMapping | null;
134
+ /**
135
+ * Get all scope mappings
136
+ */
137
+ export declare function getAllScopes(): ScopeRegistryMetadata;
138
+ /**
139
+ * Save scope mapping
140
+ * @param mapping - Scope mapping to save
141
+ */
142
+ export declare function saveScopeMapping(mapping: ScopeMapping): void;
143
+ /**
144
+ * Remove scope mapping
145
+ * @param scope - Scope name to remove
146
+ * @returns true if removed, false if not found
147
+ */
148
+ export declare function removeScopeMapping(scope: string): boolean;
149
+ /**
150
+ * Clear all scope mappings
151
+ */
152
+ export declare function clearAllScopes(): void;
153
+ /**
154
+ * Fetch GitLab group information by group name
155
+ * @param groupName - GitLab group/namespace name
156
+ * @param gitlabHost - GitLab host (default: gitlab.com)
157
+ * @returns Group information or throws error
158
+ */
159
+ export declare function fetchGitLabGroup(groupName: string, gitlabHost?: string): Promise<GitLabGroupResponse>;
160
+ /**
161
+ * Add package manually with GitLab group lookup
162
+ * @param packageName - Package name (e.g., "@ist/monitor-server")
163
+ * @param groupName - GitLab group name
164
+ * @param gitlabHost - GitLab host (default: gitlab.com)
165
+ */
166
+ export declare function addPackageManually(packageName: string, groupName: string, gitlabHost?: string): Promise<PackageInfo>;
167
+ /**
168
+ * Set scope mapping with GitLab group lookup
169
+ * @param scope - Scope name (e.g., "@ist" or "ist")
170
+ * @param groupName - GitLab group name
171
+ * @param gitlabHost - GitLab host (default: gitlab.com)
172
+ */
173
+ export declare function setScopeMapping(scope: string, groupName: string, gitlabHost?: string): Promise<ScopeMapping>;
174
+ export {};
package/dist/index.js CHANGED
@@ -44,12 +44,21 @@ exports.savePackageInfo = savePackageInfo;
44
44
  exports.removePackageInfo = removePackageInfo;
45
45
  exports.hasPackageInfo = hasPackageInfo;
46
46
  exports.clearAllPackages = clearAllPackages;
47
+ exports.getScopeMapping = getScopeMapping;
48
+ exports.getAllScopes = getAllScopes;
49
+ exports.saveScopeMapping = saveScopeMapping;
50
+ exports.removeScopeMapping = removeScopeMapping;
51
+ exports.clearAllScopes = clearAllScopes;
52
+ exports.fetchGitLabGroup = fetchGitLabGroup;
53
+ exports.addPackageManually = addPackageManually;
54
+ exports.setScopeMapping = setScopeMapping;
47
55
  const fs = __importStar(require("fs"));
48
56
  const path = __importStar(require("path"));
49
57
  const os = __importStar(require("os"));
50
58
  const CONFIG_DIR = path.join(os.homedir(), '.config', 'gitlab-config');
51
59
  const TOKEN_FILE = path.join(CONFIG_DIR, 'token');
52
60
  const PACKAGE_REGISTRY_FILE = path.join(CONFIG_DIR, 'package-registry.json');
61
+ const SCOPE_REGISTRY_FILE = path.join(CONFIG_DIR, 'scope-registry.json');
53
62
  /**
54
63
  * Ensure the config directory exists
55
64
  */
@@ -126,14 +135,51 @@ function savePackageRegistry(metadata) {
126
135
  ensureConfigDir();
127
136
  fs.writeFileSync(PACKAGE_REGISTRY_FILE, JSON.stringify(metadata, null, 2), { mode: 0o644 });
128
137
  }
138
+ /**
139
+ * Extract scope from package name
140
+ * @param packageName - Package name (e.g., "@ist/monitor-server")
141
+ * @returns Scope without @ or null if no scope
142
+ */
143
+ function extractScope(packageName) {
144
+ if (packageName.startsWith('@')) {
145
+ const slashIndex = packageName.indexOf('/');
146
+ if (slashIndex > 1) {
147
+ return packageName.slice(1, slashIndex);
148
+ }
149
+ }
150
+ return null;
151
+ }
129
152
  /**
130
153
  * Get package information by package name
154
+ * Falls back to scope mapping if package is not directly registered
131
155
  * @param packageName - Package name (e.g., "@another/lib-name")
132
156
  * @returns Package info or null if not found
133
157
  */
134
158
  function getPackageInfo(packageName) {
135
159
  const metadata = loadPackageRegistry();
136
- return metadata.packages[packageName] || null;
160
+ // First, check direct package registration
161
+ if (metadata.packages[packageName]) {
162
+ return metadata.packages[packageName];
163
+ }
164
+ // Fallback: check scope mapping
165
+ const scope = extractScope(packageName);
166
+ if (scope) {
167
+ const scopeMapping = getScopeMapping(scope);
168
+ if (scopeMapping) {
169
+ // Create virtual PackageInfo from scope mapping
170
+ return {
171
+ packageName,
172
+ group: scopeMapping.group,
173
+ groupId: scopeMapping.groupId,
174
+ registryType: 'group',
175
+ registryUrl: scopeMapping.registryUrl,
176
+ gitlabHost: scopeMapping.gitlabHost,
177
+ lastUpdated: scopeMapping.createdAt,
178
+ source: 'detected', // Detected via scope mapping
179
+ };
180
+ }
181
+ }
182
+ return null;
137
183
  }
138
184
  /**
139
185
  * Get all package information
@@ -193,3 +239,160 @@ function clearAllPackages() {
193
239
  fs.unlinkSync(PACKAGE_REGISTRY_FILE);
194
240
  }
195
241
  }
242
+ // ============================================================================
243
+ // Scope Registry Management
244
+ // ============================================================================
245
+ /**
246
+ * Load scope registry metadata from file
247
+ */
248
+ function loadScopeRegistry() {
249
+ if (!fs.existsSync(SCOPE_REGISTRY_FILE)) {
250
+ return { scopes: {} };
251
+ }
252
+ try {
253
+ const content = fs.readFileSync(SCOPE_REGISTRY_FILE, 'utf-8');
254
+ return JSON.parse(content);
255
+ }
256
+ catch (error) {
257
+ return { scopes: {} };
258
+ }
259
+ }
260
+ /**
261
+ * Save scope registry metadata to file
262
+ */
263
+ function saveScopeRegistry(metadata) {
264
+ ensureConfigDir();
265
+ fs.writeFileSync(SCOPE_REGISTRY_FILE, JSON.stringify(metadata, null, 2), { mode: 0o644 });
266
+ }
267
+ /**
268
+ * Normalize scope name (remove @ prefix if present)
269
+ */
270
+ function normalizeScope(scope) {
271
+ return scope.startsWith('@') ? scope.slice(1) : scope;
272
+ }
273
+ /**
274
+ * Get scope mapping by scope name
275
+ * @param scope - Scope name (with or without @)
276
+ */
277
+ function getScopeMapping(scope) {
278
+ const normalizedScope = normalizeScope(scope);
279
+ const metadata = loadScopeRegistry();
280
+ return metadata.scopes[normalizedScope] || null;
281
+ }
282
+ /**
283
+ * Get all scope mappings
284
+ */
285
+ function getAllScopes() {
286
+ return loadScopeRegistry();
287
+ }
288
+ /**
289
+ * Save scope mapping
290
+ * @param mapping - Scope mapping to save
291
+ */
292
+ function saveScopeMapping(mapping) {
293
+ const metadata = loadScopeRegistry();
294
+ mapping.scope = normalizeScope(mapping.scope);
295
+ mapping.createdAt = new Date().toISOString();
296
+ metadata.scopes[mapping.scope] = mapping;
297
+ saveScopeRegistry(metadata);
298
+ }
299
+ /**
300
+ * Remove scope mapping
301
+ * @param scope - Scope name to remove
302
+ * @returns true if removed, false if not found
303
+ */
304
+ function removeScopeMapping(scope) {
305
+ const normalizedScope = normalizeScope(scope);
306
+ const metadata = loadScopeRegistry();
307
+ if (metadata.scopes[normalizedScope]) {
308
+ delete metadata.scopes[normalizedScope];
309
+ saveScopeRegistry(metadata);
310
+ return true;
311
+ }
312
+ return false;
313
+ }
314
+ /**
315
+ * Clear all scope mappings
316
+ */
317
+ function clearAllScopes() {
318
+ if (fs.existsSync(SCOPE_REGISTRY_FILE)) {
319
+ fs.unlinkSync(SCOPE_REGISTRY_FILE);
320
+ }
321
+ }
322
+ // ============================================================================
323
+ // GitLab API Integration
324
+ // ============================================================================
325
+ /**
326
+ * Fetch GitLab group information by group name
327
+ * @param groupName - GitLab group/namespace name
328
+ * @param gitlabHost - GitLab host (default: gitlab.com)
329
+ * @returns Group information or throws error
330
+ */
331
+ async function fetchGitLabGroup(groupName, gitlabHost = 'gitlab.com') {
332
+ const token = getToken();
333
+ if (!token) {
334
+ throw new Error('GitLab token not configured. Run: gitlab-config save <token>');
335
+ }
336
+ const encodedGroup = encodeURIComponent(groupName);
337
+ const url = `https://${gitlabHost}/api/v4/groups/${encodedGroup}`;
338
+ const response = await fetch(url, {
339
+ headers: {
340
+ 'PRIVATE-TOKEN': token,
341
+ },
342
+ });
343
+ if (!response.ok) {
344
+ if (response.status === 404) {
345
+ throw new Error(`Group not found: ${groupName}`);
346
+ }
347
+ if (response.status === 401) {
348
+ throw new Error('Invalid or expired GitLab token');
349
+ }
350
+ throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
351
+ }
352
+ return response.json();
353
+ }
354
+ /**
355
+ * Add package manually with GitLab group lookup
356
+ * @param packageName - Package name (e.g., "@ist/monitor-server")
357
+ * @param groupName - GitLab group name
358
+ * @param gitlabHost - GitLab host (default: gitlab.com)
359
+ */
360
+ async function addPackageManually(packageName, groupName, gitlabHost = 'gitlab.com') {
361
+ // Fetch group info from GitLab API
362
+ const groupInfo = await fetchGitLabGroup(groupName, gitlabHost);
363
+ const registryUrl = `https://${gitlabHost}/api/v4/groups/${groupInfo.id}/-/packages/npm/`;
364
+ const packageInfo = {
365
+ packageName,
366
+ group: groupName,
367
+ groupId: groupInfo.id,
368
+ registryType: 'group',
369
+ registryUrl,
370
+ gitlabHost,
371
+ lastUpdated: new Date().toISOString(),
372
+ source: 'manual',
373
+ };
374
+ savePackageInfo(packageInfo);
375
+ return packageInfo;
376
+ }
377
+ /**
378
+ * Set scope mapping with GitLab group lookup
379
+ * @param scope - Scope name (e.g., "@ist" or "ist")
380
+ * @param groupName - GitLab group name
381
+ * @param gitlabHost - GitLab host (default: gitlab.com)
382
+ */
383
+ async function setScopeMapping(scope, groupName, gitlabHost = 'gitlab.com') {
384
+ // Fetch group info from GitLab API
385
+ const groupInfo = await fetchGitLabGroup(groupName, gitlabHost);
386
+ const normalizedScope = normalizeScope(scope);
387
+ const registryUrl = `https://${gitlabHost}/api/v4/groups/${groupInfo.id}/-/packages/npm/`;
388
+ const mapping = {
389
+ scope: normalizedScope,
390
+ group: groupName,
391
+ groupId: groupInfo.id,
392
+ gitlabHost,
393
+ registryUrl,
394
+ createdAt: new Date().toISOString(),
395
+ };
396
+ saveScopeMapping(mapping);
397
+ return mapping;
398
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glpkg/config",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "GitLab configuration management - save and load tokens from user config directory",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",