@push.rocks/smartregistry 1.5.0 → 1.6.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/readme.hints.md CHANGED
@@ -1,6 +1,8 @@
1
- # Project Readme Hints
1
+ # Project Implementation Notes
2
2
 
3
- ## Python (PyPI) Protocol Implementation Notes
3
+ This file contains technical implementation details for PyPI and RubyGems protocols.
4
+
5
+ ## Python (PyPI) Protocol Implementation ✅
4
6
 
5
7
  ### PEP 503: Simple Repository API (HTML-based)
6
8
 
@@ -114,7 +116,7 @@ Format: `#<hashname>=<hashvalue>`
114
116
 
115
117
  ---
116
118
 
117
- ## Ruby (RubyGems) Protocol Implementation Notes
119
+ ## Ruby (RubyGems) Protocol Implementation
118
120
 
119
121
  ### Compact Index Format
120
122
 
@@ -222,7 +224,16 @@ gemname3
222
224
 
223
225
  ---
224
226
 
225
- ## Implementation Strategy
227
+ ## Implementation Details
228
+
229
+ ### Completed Protocols
230
+ - ✅ OCI Distribution Spec v1.1
231
+ - ✅ NPM Registry API
232
+ - ✅ Maven Repository
233
+ - ✅ Cargo/crates.io Registry
234
+ - ✅ Composer/Packagist
235
+ - ✅ PyPI (Python Package Index) - PEP 503/691
236
+ - ✅ RubyGems - Compact Index
226
237
 
227
238
  ### Storage Paths
228
239
 
@@ -333,3 +344,96 @@ rubygems:gem:{name}:{read|write|yank}
333
344
  6. **HTML escaping** - Prevent XSS in generated HTML
334
345
  7. **Metadata sanitization** - Clean user-provided strings
335
346
  8. **Rate limiting** - Consider upload frequency limits
347
+
348
+ ---
349
+
350
+ ## Implementation Status (Completed)
351
+
352
+ ### PyPI Implementation ✅
353
+ - **Files Created:**
354
+ - `ts/pypi/interfaces.pypi.ts` - Type definitions (354 lines)
355
+ - `ts/pypi/helpers.pypi.ts` - Helper functions (280 lines)
356
+ - `ts/pypi/classes.pypiregistry.ts` - Main registry (650 lines)
357
+ - `ts/pypi/index.ts` - Module exports
358
+
359
+ - **Features Implemented:**
360
+ - ✅ PEP 503 Simple API (HTML)
361
+ - ✅ PEP 691 JSON API
362
+ - ✅ Content negotiation (Accept header)
363
+ - ✅ Package name normalization
364
+ - ✅ File upload with multipart/form-data
365
+ - ✅ Hash verification (SHA256, MD5, Blake2b)
366
+ - ✅ Package metadata management
367
+ - ✅ JSON API endpoints (/pypi/{package}/json)
368
+ - ✅ Token-based authentication
369
+ - ✅ Scope-based permissions (read/write/delete)
370
+
371
+ - **Security Enhancements:**
372
+ - ✅ Hash verification on upload (validates client-provided hashes)
373
+ - ✅ Package name validation (regex check)
374
+ - ✅ HTML escaping in generated pages
375
+ - ✅ Permission checks on all mutating operations
376
+
377
+ ### RubyGems Implementation ✅
378
+ - **Files Created:**
379
+ - `ts/rubygems/interfaces.rubygems.ts` - Type definitions (215 lines)
380
+ - `ts/rubygems/helpers.rubygems.ts` - Helper functions (350 lines)
381
+ - `ts/rubygems/classes.rubygemsregistry.ts` - Main registry (580 lines)
382
+ - `ts/rubygems/index.ts` - Module exports
383
+
384
+ - **Features Implemented:**
385
+ - ✅ Compact Index format (modern Bundler)
386
+ - ✅ /versions endpoint (all gems list)
387
+ - ✅ /info/{gem} endpoint (gem-specific metadata)
388
+ - ✅ /names endpoint (gem names list)
389
+ - ✅ Gem upload API
390
+ - ✅ Yank/unyank functionality
391
+ - ✅ Platform-specific gems support
392
+ - ✅ JSON API endpoints
393
+ - ✅ Legacy endpoints (specs.4.8.gz, Marshal.4.8)
394
+ - ✅ Token-based authentication
395
+ - ✅ Scope-based permissions
396
+
397
+ ### Integration ✅
398
+ - **Core Updates:**
399
+ - ✅ Updated `IRegistryConfig` interface
400
+ - ✅ Updated `TRegistryProtocol` type
401
+ - ✅ Added authentication methods to `AuthManager`
402
+ - ✅ Added 30+ storage methods to `RegistryStorage`
403
+ - ✅ Updated `SmartRegistry` initialization and routing
404
+ - ✅ Module exports from `ts/index.ts`
405
+
406
+ - **Test Coverage:**
407
+ - ✅ `test/test.pypi.ts` - 25+ tests covering all PyPI endpoints
408
+ - ✅ `test/test.rubygems.ts` - 30+ tests covering all RubyGems endpoints
409
+ - ✅ `test/test.integration.pypi-rubygems.ts` - Integration tests
410
+ - ✅ Updated test helpers with PyPI and RubyGems support
411
+
412
+ ### Known Limitations
413
+ 1. **PyPI:**
414
+ - Does not implement legacy XML-RPC API
415
+ - No support for PGP signatures (data-gpg-sig always false)
416
+ - Metadata extraction from wheel files not implemented
417
+
418
+ 2. **RubyGems:**
419
+ - Gem spec extraction from .gem files returns placeholder (Ruby Marshal parsing not implemented)
420
+ - Legacy Marshal endpoints return basic data only
421
+ - No support for gem dependencies resolution
422
+
423
+ ### Configuration Example
424
+ ```typescript
425
+ {
426
+ pypi: {
427
+ enabled: true,
428
+ basePath: '/pypi', // Also handles /simple
429
+ },
430
+ rubygems: {
431
+ enabled: true,
432
+ basePath: '/rubygems',
433
+ },
434
+ auth: {
435
+ pypiTokens: { enabled: true },
436
+ rubygemsTokens: { enabled: true },
437
+ }
438
+ }
439
+ ```
package/readme.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @push.rocks/smartregistry
2
2
 
3
- > 🚀 A composable TypeScript library implementing **OCI Distribution Specification v1.1**, **NPM Registry API**, **Maven Repository**, **Cargo/crates.io Registry**, and **Composer/Packagist** for building unified container and package registries.
3
+ > 🚀 A composable TypeScript library implementing **OCI Distribution Specification v1.1**, **NPM Registry API**, **Maven Repository**, **Cargo/crates.io Registry**, **Composer/Packagist**, **PyPI (Python Package Index)**, and **RubyGems Registry** for building unified container and package registries.
4
+
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who want to sign a contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
4
8
 
5
9
  ## ✨ Features
6
10
 
@@ -10,12 +14,14 @@
10
14
  - **Maven Repository**: Java/JVM artifact management with POM support
11
15
  - **Cargo/crates.io Registry**: Rust crate registry with sparse HTTP protocol
12
16
  - **Composer/Packagist**: PHP package registry with Composer v2 protocol
17
+ - **PyPI (Python Package Index)**: Python package registry with PEP 503/691 support
18
+ - **RubyGems Registry**: Ruby gem registry with compact index protocol
13
19
 
14
20
  ### 🏗️ Unified Architecture
15
21
  - **Composable Design**: Core infrastructure with protocol plugins
16
22
  - **Shared Storage**: Cloud-agnostic S3-compatible backend ([@push.rocks/smartbucket](https://www.npmjs.com/package/@push.rocks/smartbucket))
17
23
  - **Unified Authentication**: Scope-based permissions across all protocols
18
- - **Path-based Routing**: `/oci/*` for containers, `/npm/*` for packages, `/maven/*` for Java artifacts, `/cargo/*` for Rust crates, `/composer/*` for PHP packages
24
+ - **Path-based Routing**: `/oci/*` for containers, `/npm/*` for packages, `/maven/*` for Java artifacts, `/cargo/*` for Rust crates, `/composer/*` for PHP packages, `/pypi/*` for Python packages, `/rubygems/*` for Ruby gems
19
25
 
20
26
  ### 🔐 Authentication & Authorization
21
27
  - NPM UUID tokens for package operations
@@ -59,6 +65,23 @@
59
65
  - ✅ Dependency resolution
60
66
  - ✅ PSR-4/PSR-0 autoloading support
61
67
 
68
+ **PyPI Features:**
69
+ - ✅ PEP 503 Simple Repository API (HTML)
70
+ - ✅ PEP 691 JSON-based Simple API
71
+ - ✅ Package upload (wheel and sdist)
72
+ - ✅ Package name normalization
73
+ - ✅ Hash verification (SHA256, MD5, Blake2b)
74
+ - ✅ Content negotiation (JSON/HTML)
75
+ - ✅ Metadata API (JSON endpoints)
76
+
77
+ **RubyGems Features:**
78
+ - ✅ Compact Index protocol (modern Bundler)
79
+ - ✅ Gem publish/download (.gem files)
80
+ - ✅ Version yank/unyank
81
+ - ✅ Platform-specific gems
82
+ - ✅ Dependency resolution
83
+ - ✅ Legacy API compatibility
84
+
62
85
  ## 📥 Installation
63
86
 
64
87
  ```bash
@@ -114,6 +137,14 @@ const config: IRegistryConfig = {
114
137
  enabled: true,
115
138
  basePath: '/composer',
116
139
  },
140
+ pypi: {
141
+ enabled: true,
142
+ basePath: '/pypi',
143
+ },
144
+ rubygems: {
145
+ enabled: true,
146
+ basePath: '/rubygems',
147
+ },
117
148
  };
118
149
 
119
150
  const registry = new SmartRegistry(config);
@@ -145,6 +176,11 @@ ts/
145
176
  ├── npm/ # NPM implementation
146
177
  │ ├── classes.npmregistry.ts
147
178
  │ └── interfaces.npm.ts
179
+ ├── maven/ # Maven implementation
180
+ ├── cargo/ # Cargo implementation
181
+ ├── composer/ # Composer implementation
182
+ ├── pypi/ # PyPI implementation
183
+ ├── rubygems/ # RubyGems implementation
148
184
  └── classes.smartregistry.ts # Main orchestrator
149
185
  ```
150
186
 
@@ -157,7 +193,12 @@ SmartRegistry (orchestrator)
157
193
 
158
194
  Path-based routing
159
195
  ├─→ /oci/* → OciRegistry
160
- └─→ /npm/* → NpmRegistry
196
+ ├─→ /npm/* → NpmRegistry
197
+ ├─→ /maven/* → MavenRegistry
198
+ ├─→ /cargo/* → CargoRegistry
199
+ ├─→ /composer/* → ComposerRegistry
200
+ ├─→ /pypi/* → PypiRegistry
201
+ └─→ /rubygems/* → RubyGemsRegistry
161
202
 
162
203
  Shared Storage & Auth
163
204
 
@@ -409,6 +450,171 @@ composer require vendor/package
409
450
  composer update
410
451
  ```
411
452
 
453
+ ### 🐍 PyPI Registry (Python Packages)
454
+
455
+ ```typescript
456
+ // Get package index (PEP 503 HTML format)
457
+ const htmlIndex = await registry.handleRequest({
458
+ method: 'GET',
459
+ path: '/simple/requests/',
460
+ headers: { 'Accept': 'text/html' },
461
+ query: {},
462
+ });
463
+
464
+ // Get package index (PEP 691 JSON format)
465
+ const jsonIndex = await registry.handleRequest({
466
+ method: 'GET',
467
+ path: '/simple/requests/',
468
+ headers: { 'Accept': 'application/vnd.pypi.simple.v1+json' },
469
+ query: {},
470
+ });
471
+
472
+ // Upload a Python package (wheel or sdist)
473
+ const formData = new FormData();
474
+ formData.append(':action', 'file_upload');
475
+ formData.append('protocol_version', '1');
476
+ formData.append('name', 'my-package');
477
+ formData.append('version', '1.0.0');
478
+ formData.append('filetype', 'bdist_wheel');
479
+ formData.append('pyversion', 'py3');
480
+ formData.append('metadata_version', '2.1');
481
+ formData.append('sha256_digest', 'abc123...');
482
+ formData.append('content', packageFile, { filename: 'my_package-1.0.0-py3-none-any.whl' });
483
+
484
+ const upload = await registry.handleRequest({
485
+ method: 'POST',
486
+ path: '/pypi/legacy/',
487
+ headers: {
488
+ 'Authorization': `Bearer <pypi-token>`,
489
+ 'Content-Type': 'multipart/form-data',
490
+ },
491
+ query: {},
492
+ body: formData,
493
+ });
494
+
495
+ // Get package metadata (PyPI JSON API)
496
+ const metadata = await registry.handleRequest({
497
+ method: 'GET',
498
+ path: '/pypi/my-package/json',
499
+ headers: {},
500
+ query: {},
501
+ });
502
+
503
+ // Download a specific version
504
+ const download = await registry.handleRequest({
505
+ method: 'GET',
506
+ path: '/packages/my-package/my_package-1.0.0-py3-none-any.whl',
507
+ headers: {},
508
+ query: {},
509
+ });
510
+ ```
511
+
512
+ **Using with pip:**
513
+
514
+ ```bash
515
+ # Install from custom registry
516
+ pip install --index-url https://registry.example.com/simple/ my-package
517
+
518
+ # Upload to custom registry
519
+ python -m twine upload --repository-url https://registry.example.com/pypi/legacy/ dist/*
520
+
521
+ # Configure in pip.conf or pip.ini
522
+ [global]
523
+ index-url = https://registry.example.com/simple/
524
+ ```
525
+
526
+ ### 💎 RubyGems Registry (Ruby Gems)
527
+
528
+ ```typescript
529
+ // Get versions file (compact index)
530
+ const versions = await registry.handleRequest({
531
+ method: 'GET',
532
+ path: '/rubygems/versions',
533
+ headers: {},
534
+ query: {},
535
+ });
536
+
537
+ // Get gem-specific info
538
+ const gemInfo = await registry.handleRequest({
539
+ method: 'GET',
540
+ path: '/rubygems/info/rails',
541
+ headers: {},
542
+ query: {},
543
+ });
544
+
545
+ // Get list of all gem names
546
+ const names = await registry.handleRequest({
547
+ method: 'GET',
548
+ path: '/rubygems/names',
549
+ headers: {},
550
+ query: {},
551
+ });
552
+
553
+ // Upload a gem file
554
+ const gemBuffer = await readFile('my-gem-1.0.0.gem');
555
+ const uploadGem = await registry.handleRequest({
556
+ method: 'POST',
557
+ path: '/rubygems/api/v1/gems',
558
+ headers: { 'Authorization': '<rubygems-api-key>' },
559
+ query: {},
560
+ body: gemBuffer,
561
+ });
562
+
563
+ // Yank a version (make unavailable for install)
564
+ const yank = await registry.handleRequest({
565
+ method: 'DELETE',
566
+ path: '/rubygems/api/v1/gems/yank',
567
+ headers: { 'Authorization': '<rubygems-api-key>' },
568
+ query: { gem_name: 'my-gem', version: '1.0.0' },
569
+ });
570
+
571
+ // Unyank a version
572
+ const unyank = await registry.handleRequest({
573
+ method: 'PUT',
574
+ path: '/rubygems/api/v1/gems/unyank',
575
+ headers: { 'Authorization': '<rubygems-api-key>' },
576
+ query: { gem_name: 'my-gem', version: '1.0.0' },
577
+ });
578
+
579
+ // Get gem version metadata
580
+ const versionMeta = await registry.handleRequest({
581
+ method: 'GET',
582
+ path: '/rubygems/api/v1/versions/rails.json',
583
+ headers: {},
584
+ query: {},
585
+ });
586
+
587
+ // Download gem file
588
+ const gemDownload = await registry.handleRequest({
589
+ method: 'GET',
590
+ path: '/rubygems/gems/rails-7.0.0.gem',
591
+ headers: {},
592
+ query: {},
593
+ });
594
+ ```
595
+
596
+ **Using with Bundler:**
597
+
598
+ ```ruby
599
+ # Gemfile
600
+ source 'https://registry.example.com/rubygems' do
601
+ gem 'my-gem'
602
+ gem 'rails'
603
+ end
604
+ ```
605
+
606
+ ```bash
607
+ # Install gems
608
+ bundle install
609
+
610
+ # Push gem to custom registry
611
+ gem push my-gem-1.0.0.gem --host https://registry.example.com/rubygems
612
+
613
+ # Configure gem source
614
+ gem sources --add https://registry.example.com/rubygems/
615
+ gem sources --remove https://rubygems.org/
616
+ ```
617
+
412
618
  ### 🔐 Authentication
413
619
 
414
620
  ```typescript
@@ -530,6 +736,20 @@ Unified storage abstraction for both OCI and NPM content.
530
736
  - `getNpmTarball(name, version)` - Get tarball
531
737
  - `putNpmTarball(name, version, data)` - Store tarball
532
738
 
739
+ **PyPI Methods:**
740
+ - `getPypiPackageMetadata(name)` - Get package metadata
741
+ - `putPypiPackageMetadata(name, data)` - Store package metadata
742
+ - `getPypiPackageFile(name, filename)` - Get package file
743
+ - `putPypiPackageFile(name, filename, data)` - Store package file
744
+
745
+ **RubyGems Methods:**
746
+ - `getRubyGemsVersions()` - Get versions index
747
+ - `putRubyGemsVersions(data)` - Store versions index
748
+ - `getRubyGemsInfo(gemName)` - Get gem info
749
+ - `putRubyGemsInfo(gemName, data)` - Store gem info
750
+ - `getRubyGem(gemName, version)` - Get .gem file
751
+ - `putRubyGem(gemName, version, data)` - Store .gem file
752
+
533
753
  #### AuthManager
534
754
 
535
755
  Unified authentication manager supporting both NPM and OCI authentication schemes.
@@ -607,11 +827,45 @@ Composer v2 repository API compliant implementation.
607
827
  - `DELETE /packages/{vendor}/{package}` - Delete entire package
608
828
  - `DELETE /packages/{vendor}/{package}/{version}` - Delete specific version
609
829
 
610
- **Package Format:**
611
- - ZIP archives with composer.json in root
612
- - SHA-1 checksums for verification
613
- - Version normalization (1.0.0 → 1.0.0.0)
614
- - PSR-4/PSR-0 autoloading configuration
830
+ #### PypiRegistry
831
+
832
+ PyPI (Python Package Index) registry implementing PEP 503 and PEP 691.
833
+
834
+ **Endpoints:**
835
+ - `GET /simple/` - List all packages (HTML or JSON)
836
+ - `GET /simple/{package}/` - List package files (HTML or JSON)
837
+ - `POST /legacy/` - Upload package (multipart/form-data)
838
+ - `GET /pypi/{package}/json` - Package metadata API
839
+ - `GET /pypi/{package}/{version}/json` - Version-specific metadata
840
+ - `GET /packages/{package}/{filename}` - Download package file
841
+
842
+ **Features:**
843
+ - PEP 503 Simple Repository API (HTML)
844
+ - PEP 691 JSON-based Simple API
845
+ - Content negotiation via Accept header
846
+ - Package name normalization
847
+ - Hash verification (SHA256, MD5, Blake2b)
848
+
849
+ #### RubyGemsRegistry
850
+
851
+ RubyGems registry with compact index protocol for modern Bundler.
852
+
853
+ **Endpoints:**
854
+ - `GET /versions` - Master versions file (all gems)
855
+ - `GET /info/{gem}` - Gem-specific info file
856
+ - `GET /names` - List of all gem names
857
+ - `POST /api/v1/gems` - Upload gem file
858
+ - `DELETE /api/v1/gems/yank` - Yank (deprecate) version
859
+ - `PUT /api/v1/gems/unyank` - Unyank version
860
+ - `GET /api/v1/versions/{gem}.json` - Version metadata
861
+ - `GET /gems/{gem}-{version}.gem` - Download gem file
862
+
863
+ **Features:**
864
+ - Compact Index format (append-only text files)
865
+ - Platform-specific gems support
866
+ - Yank/unyank functionality
867
+ - Checksum calculations (MD5 for index, SHA256 for gems)
868
+ - Legacy Marshal API compatibility
615
869
 
616
870
  ## 🗄️ Storage Structure
617
871
 
@@ -651,11 +905,24 @@ bucket/
651
905
  │ │ └── {p1}/{p2}/{name} # 4+ char (e.g., "se/rd/serde")
652
906
  │ └── crates/
653
907
  │ └── {name}/{name}-{version}.crate # Gzipped tar archives
654
- └── composer/
655
- └── packages/
656
- └── {vendor}/{package}/
657
- ├── metadata.json # All versions metadata
658
- └── {reference}.zip # Package ZIP files
908
+ ├── composer/
909
+ └── packages/
910
+ └── {vendor}/{package}/
911
+ ├── metadata.json # All versions metadata
912
+ └── {reference}.zip # Package ZIP files
913
+ ├── pypi/
914
+ │ ├── simple/ # PEP 503 HTML files
915
+ │ │ ├── index.html # All packages list
916
+ │ │ └── {package}/index.html # Package versions list
917
+ │ ├── packages/
918
+ │ │ └── {package}/{filename} # .whl and .tar.gz files
919
+ │ └── metadata/
920
+ │ └── {package}/metadata.json # Package metadata
921
+ └── rubygems/
922
+ ├── versions # Master versions file
923
+ ├── info/{gemname} # Per-gem info files
924
+ ├── names # All gem names
925
+ └── gems/{gemname}-{version}.gem # .gem files
659
926
  ```
660
927
 
661
928
  ## 🎯 Scope Format
@@ -685,6 +952,14 @@ Examples:
685
952
  composer:package:vendor/package:read # Read Composer package
686
953
  composer:package:*:write # Write any package
687
954
  composer:*:*:* # Full Composer access
955
+
956
+ pypi:package:my-package:read # Read PyPI package
957
+ pypi:package:*:write # Write any package
958
+ pypi:*:*:* # Full PyPI access
959
+
960
+ rubygems:gem:rails:read # Read RubyGems gem
961
+ rubygems:gem:*:write # Write any gem
962
+ rubygems:*:*:* # Full RubyGems access
688
963
  ```
689
964
 
690
965
  ## 🔌 Integration Examples
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartregistry',
6
- version: '1.5.0',
7
- description: 'a registry for npm modules and oci images'
6
+ version: '1.6.0',
7
+ description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
8
8
  }
@@ -7,10 +7,12 @@ import { NpmRegistry } from './npm/classes.npmregistry.js';
7
7
  import { MavenRegistry } from './maven/classes.mavenregistry.js';
8
8
  import { CargoRegistry } from './cargo/classes.cargoregistry.js';
9
9
  import { ComposerRegistry } from './composer/classes.composerregistry.js';
10
+ import { PypiRegistry } from './pypi/classes.pypiregistry.js';
11
+ import { RubyGemsRegistry } from './rubygems/classes.rubygemsregistry.js';
10
12
 
11
13
  /**
12
14
  * Main registry orchestrator
13
- * Routes requests to appropriate protocol handlers (OCI, NPM, Maven, Cargo, or Composer)
15
+ * Routes requests to appropriate protocol handlers (OCI, NPM, Maven, Cargo, Composer, PyPI, or RubyGems)
14
16
  */
15
17
  export class SmartRegistry {
16
18
  private storage: RegistryStorage;
@@ -81,6 +83,24 @@ export class SmartRegistry {
81
83
  this.registries.set('composer', composerRegistry);
82
84
  }
83
85
 
86
+ // Initialize PyPI registry if enabled
87
+ if (this.config.pypi?.enabled) {
88
+ const pypiBasePath = this.config.pypi.basePath || '/pypi';
89
+ const registryUrl = `http://localhost:5000`; // TODO: Make configurable
90
+ const pypiRegistry = new PypiRegistry(this.storage, this.authManager, pypiBasePath, registryUrl);
91
+ await pypiRegistry.init();
92
+ this.registries.set('pypi', pypiRegistry);
93
+ }
94
+
95
+ // Initialize RubyGems registry if enabled
96
+ if (this.config.rubygems?.enabled) {
97
+ const rubygemsBasePath = this.config.rubygems.basePath || '/rubygems';
98
+ const registryUrl = `http://localhost:5000${rubygemsBasePath}`; // TODO: Make configurable
99
+ const rubygemsRegistry = new RubyGemsRegistry(this.storage, this.authManager, rubygemsBasePath, registryUrl);
100
+ await rubygemsRegistry.init();
101
+ this.registries.set('rubygems', rubygemsRegistry);
102
+ }
103
+
84
104
  this.initialized = true;
85
105
  }
86
106
 
@@ -131,6 +151,25 @@ export class SmartRegistry {
131
151
  }
132
152
  }
133
153
 
154
+ // Route to PyPI registry (also handles /simple prefix)
155
+ if (this.config.pypi?.enabled) {
156
+ const pypiBasePath = this.config.pypi.basePath || '/pypi';
157
+ if (path.startsWith(pypiBasePath) || path.startsWith('/simple')) {
158
+ const pypiRegistry = this.registries.get('pypi');
159
+ if (pypiRegistry) {
160
+ return pypiRegistry.handleRequest(context);
161
+ }
162
+ }
163
+ }
164
+
165
+ // Route to RubyGems registry
166
+ if (this.config.rubygems?.enabled && path.startsWith(this.config.rubygems.basePath)) {
167
+ const rubygemsRegistry = this.registries.get('rubygems');
168
+ if (rubygemsRegistry) {
169
+ return rubygemsRegistry.handleRequest(context);
170
+ }
171
+ }
172
+
134
173
  // No matching registry
135
174
  return {
136
175
  status: 404,
@@ -159,7 +198,7 @@ export class SmartRegistry {
159
198
  /**
160
199
  * Get a specific registry handler
161
200
  */
162
- public getRegistry(protocol: 'oci' | 'npm' | 'maven' | 'cargo' | 'composer'): BaseRegistry | undefined {
201
+ public getRegistry(protocol: 'oci' | 'npm' | 'maven' | 'cargo' | 'composer' | 'pypi' | 'rubygems'): BaseRegistry | undefined {
163
202
  return this.registries.get(protocol);
164
203
  }
165
204