@push.rocks/smartregistry 1.5.0 → 1.7.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
- - **Shared Storage**: Cloud-agnostic S3-compatible backend ([@push.rocks/smartbucket](https://www.npmjs.com/package/@push.rocks/smartbucket))
22
+ - **Shared Storage**: Cloud-agnostic S3-compatible backend using [@push.rocks/smartbucket](https://www.npmjs.com/package/@push.rocks/smartbucket) with standardized `IS3Descriptor` from [@tsclass/tsclass](https://www.npmjs.com/package/@tsclass/tsclass)
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
@@ -446,15 +652,24 @@ const canWrite = await authManager.authorize(
446
652
 
447
653
  ### Storage Configuration
448
654
 
655
+ The storage configuration extends `IS3Descriptor` from `@tsclass/tsclass` for standardized S3 configuration:
656
+
449
657
  ```typescript
658
+ import type { IS3Descriptor } from '@tsclass/tsclass';
659
+
660
+ storage: IS3Descriptor & {
661
+ bucketName: string; // Bucket name for registry storage
662
+ }
663
+
664
+ // Example:
450
665
  storage: {
451
666
  accessKey: string; // S3 access key
452
667
  accessSecret: string; // S3 secret key
453
- endpoint: string; // S3 endpoint
668
+ endpoint: string; // S3 endpoint (e.g., 's3.amazonaws.com')
454
669
  port?: number; // Default: 443
455
670
  useSsl?: boolean; // Default: true
456
- region?: string; // Default: 'us-east-1'
457
- bucketName: string; // Bucket name
671
+ region?: string; // AWS region (e.g., 'us-east-1')
672
+ bucketName: string; // Bucket name for this registry
458
673
  }
459
674
  ```
460
675
 
@@ -530,6 +745,20 @@ Unified storage abstraction for both OCI and NPM content.
530
745
  - `getNpmTarball(name, version)` - Get tarball
531
746
  - `putNpmTarball(name, version, data)` - Store tarball
532
747
 
748
+ **PyPI Methods:**
749
+ - `getPypiPackageMetadata(name)` - Get package metadata
750
+ - `putPypiPackageMetadata(name, data)` - Store package metadata
751
+ - `getPypiPackageFile(name, filename)` - Get package file
752
+ - `putPypiPackageFile(name, filename, data)` - Store package file
753
+
754
+ **RubyGems Methods:**
755
+ - `getRubyGemsVersions()` - Get versions index
756
+ - `putRubyGemsVersions(data)` - Store versions index
757
+ - `getRubyGemsInfo(gemName)` - Get gem info
758
+ - `putRubyGemsInfo(gemName, data)` - Store gem info
759
+ - `getRubyGem(gemName, version)` - Get .gem file
760
+ - `putRubyGem(gemName, version, data)` - Store .gem file
761
+
533
762
  #### AuthManager
534
763
 
535
764
  Unified authentication manager supporting both NPM and OCI authentication schemes.
@@ -607,11 +836,45 @@ Composer v2 repository API compliant implementation.
607
836
  - `DELETE /packages/{vendor}/{package}` - Delete entire package
608
837
  - `DELETE /packages/{vendor}/{package}/{version}` - Delete specific version
609
838
 
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
839
+ #### PypiRegistry
840
+
841
+ PyPI (Python Package Index) registry implementing PEP 503 and PEP 691.
842
+
843
+ **Endpoints:**
844
+ - `GET /simple/` - List all packages (HTML or JSON)
845
+ - `GET /simple/{package}/` - List package files (HTML or JSON)
846
+ - `POST /legacy/` - Upload package (multipart/form-data)
847
+ - `GET /pypi/{package}/json` - Package metadata API
848
+ - `GET /pypi/{package}/{version}/json` - Version-specific metadata
849
+ - `GET /packages/{package}/{filename}` - Download package file
850
+
851
+ **Features:**
852
+ - PEP 503 Simple Repository API (HTML)
853
+ - PEP 691 JSON-based Simple API
854
+ - Content negotiation via Accept header
855
+ - Package name normalization
856
+ - Hash verification (SHA256, MD5, Blake2b)
857
+
858
+ #### RubyGemsRegistry
859
+
860
+ RubyGems registry with compact index protocol for modern Bundler.
861
+
862
+ **Endpoints:**
863
+ - `GET /versions` - Master versions file (all gems)
864
+ - `GET /info/{gem}` - Gem-specific info file
865
+ - `GET /names` - List of all gem names
866
+ - `POST /api/v1/gems` - Upload gem file
867
+ - `DELETE /api/v1/gems/yank` - Yank (deprecate) version
868
+ - `PUT /api/v1/gems/unyank` - Unyank version
869
+ - `GET /api/v1/versions/{gem}.json` - Version metadata
870
+ - `GET /gems/{gem}-{version}.gem` - Download gem file
871
+
872
+ **Features:**
873
+ - Compact Index format (append-only text files)
874
+ - Platform-specific gems support
875
+ - Yank/unyank functionality
876
+ - Checksum calculations (MD5 for index, SHA256 for gems)
877
+ - Legacy Marshal API compatibility
615
878
 
616
879
  ## 🗄️ Storage Structure
617
880
 
@@ -651,11 +914,24 @@ bucket/
651
914
  │ │ └── {p1}/{p2}/{name} # 4+ char (e.g., "se/rd/serde")
652
915
  │ └── crates/
653
916
  │ └── {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
917
+ ├── composer/
918
+ └── packages/
919
+ └── {vendor}/{package}/
920
+ ├── metadata.json # All versions metadata
921
+ └── {reference}.zip # Package ZIP files
922
+ ├── pypi/
923
+ │ ├── simple/ # PEP 503 HTML files
924
+ │ │ ├── index.html # All packages list
925
+ │ │ └── {package}/index.html # Package versions list
926
+ │ ├── packages/
927
+ │ │ └── {package}/{filename} # .whl and .tar.gz files
928
+ │ └── metadata/
929
+ │ └── {package}/metadata.json # Package metadata
930
+ └── rubygems/
931
+ ├── versions # Master versions file
932
+ ├── info/{gemname} # Per-gem info files
933
+ ├── names # All gem names
934
+ └── gems/{gemname}-{version}.gem # .gem files
659
935
  ```
660
936
 
661
937
  ## 🎯 Scope Format
@@ -685,6 +961,14 @@ Examples:
685
961
  composer:package:vendor/package:read # Read Composer package
686
962
  composer:package:*:write # Write any package
687
963
  composer:*:*:* # Full Composer access
964
+
965
+ pypi:package:my-package:read # Read PyPI package
966
+ pypi:package:*:write # Write any package
967
+ pypi:*:*:* # Full PyPI access
968
+
969
+ rubygems:gem:rails:read # Read RubyGems gem
970
+ rubygems:gem:*:write # Write any gem
971
+ rubygems:*:*:* # Full RubyGems access
688
972
  ```
689
973
 
690
974
  ## 🔌 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.7.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