@push.rocks/smartregistry 1.4.1 → 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/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/classes.smartregistry.d.ts +2 -2
- package/dist_ts/classes.smartregistry.js +37 -2
- package/dist_ts/core/classes.authmanager.d.ts +55 -1
- package/dist_ts/core/classes.authmanager.js +138 -3
- package/dist_ts/core/classes.registrystorage.d.ts +145 -0
- package/dist_ts/core/classes.registrystorage.js +392 -1
- package/dist_ts/core/interfaces.core.d.ts +13 -1
- package/dist_ts/index.d.ts +3 -1
- package/dist_ts/index.js +6 -2
- package/dist_ts/pypi/classes.pypiregistry.d.ts +70 -0
- package/dist_ts/pypi/classes.pypiregistry.js +482 -0
- package/dist_ts/pypi/helpers.pypi.d.ts +84 -0
- package/dist_ts/pypi/helpers.pypi.js +263 -0
- package/dist_ts/pypi/index.d.ts +7 -0
- package/dist_ts/pypi/index.js +8 -0
- package/dist_ts/pypi/interfaces.pypi.d.ts +301 -0
- package/dist_ts/pypi/interfaces.pypi.js +6 -0
- package/dist_ts/rubygems/classes.rubygemsregistry.d.ts +86 -0
- package/dist_ts/rubygems/classes.rubygemsregistry.js +475 -0
- package/dist_ts/rubygems/helpers.rubygems.d.ts +143 -0
- package/dist_ts/rubygems/helpers.rubygems.js +312 -0
- package/dist_ts/rubygems/index.d.ts +7 -0
- package/dist_ts/rubygems/index.js +8 -0
- package/dist_ts/rubygems/interfaces.rubygems.d.ts +236 -0
- package/dist_ts/rubygems/interfaces.rubygems.js +6 -0
- package/package.json +2 -2
- package/readme.hints.md +438 -2
- package/readme.md +288 -13
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/classes.smartregistry.ts +41 -2
- package/ts/core/classes.authmanager.ts +161 -2
- package/ts/core/classes.registrystorage.ts +463 -0
- package/ts/core/interfaces.core.ts +13 -1
- package/ts/index.ts +7 -1
- package/ts/pypi/classes.pypiregistry.ts +580 -0
- package/ts/pypi/helpers.pypi.ts +299 -0
- package/ts/pypi/index.ts +8 -0
- package/ts/pypi/interfaces.pypi.ts +316 -0
- package/ts/rubygems/classes.rubygemsregistry.ts +598 -0
- package/ts/rubygems/helpers.rubygems.ts +398 -0
- package/ts/rubygems/index.ts +8 -0
- package/ts/rubygems/interfaces.rubygems.ts +251 -0
|
@@ -601,4 +601,467 @@ export class RegistryStorage implements IStorageBackend {
|
|
|
601
601
|
private getComposerZipPath(vendorPackage: string, reference: string): string {
|
|
602
602
|
return `composer/packages/${vendorPackage}/${reference}.zip`;
|
|
603
603
|
}
|
|
604
|
+
|
|
605
|
+
// ========================================================================
|
|
606
|
+
// PYPI STORAGE METHODS
|
|
607
|
+
// ========================================================================
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Get PyPI package metadata
|
|
611
|
+
*/
|
|
612
|
+
public async getPypiPackageMetadata(packageName: string): Promise<any | null> {
|
|
613
|
+
const path = this.getPypiMetadataPath(packageName);
|
|
614
|
+
const data = await this.getObject(path);
|
|
615
|
+
return data ? JSON.parse(data.toString('utf-8')) : null;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Store PyPI package metadata
|
|
620
|
+
*/
|
|
621
|
+
public async putPypiPackageMetadata(packageName: string, metadata: any): Promise<void> {
|
|
622
|
+
const path = this.getPypiMetadataPath(packageName);
|
|
623
|
+
const data = Buffer.from(JSON.stringify(metadata, null, 2), 'utf-8');
|
|
624
|
+
return this.putObject(path, data, { 'Content-Type': 'application/json' });
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Check if PyPI package metadata exists
|
|
629
|
+
*/
|
|
630
|
+
public async pypiPackageMetadataExists(packageName: string): Promise<boolean> {
|
|
631
|
+
const path = this.getPypiMetadataPath(packageName);
|
|
632
|
+
return this.objectExists(path);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Delete PyPI package metadata
|
|
637
|
+
*/
|
|
638
|
+
public async deletePypiPackageMetadata(packageName: string): Promise<void> {
|
|
639
|
+
const path = this.getPypiMetadataPath(packageName);
|
|
640
|
+
return this.deleteObject(path);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Get PyPI Simple API index (HTML)
|
|
645
|
+
*/
|
|
646
|
+
public async getPypiSimpleIndex(packageName: string): Promise<string | null> {
|
|
647
|
+
const path = this.getPypiSimpleIndexPath(packageName);
|
|
648
|
+
const data = await this.getObject(path);
|
|
649
|
+
return data ? data.toString('utf-8') : null;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Store PyPI Simple API index (HTML)
|
|
654
|
+
*/
|
|
655
|
+
public async putPypiSimpleIndex(packageName: string, html: string): Promise<void> {
|
|
656
|
+
const path = this.getPypiSimpleIndexPath(packageName);
|
|
657
|
+
const data = Buffer.from(html, 'utf-8');
|
|
658
|
+
return this.putObject(path, data, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Get PyPI root Simple API index (HTML)
|
|
663
|
+
*/
|
|
664
|
+
public async getPypiSimpleRootIndex(): Promise<string | null> {
|
|
665
|
+
const path = this.getPypiSimpleRootIndexPath();
|
|
666
|
+
const data = await this.getObject(path);
|
|
667
|
+
return data ? data.toString('utf-8') : null;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Store PyPI root Simple API index (HTML)
|
|
672
|
+
*/
|
|
673
|
+
public async putPypiSimpleRootIndex(html: string): Promise<void> {
|
|
674
|
+
const path = this.getPypiSimpleRootIndexPath();
|
|
675
|
+
const data = Buffer.from(html, 'utf-8');
|
|
676
|
+
return this.putObject(path, data, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Get PyPI package file (wheel, sdist)
|
|
681
|
+
*/
|
|
682
|
+
public async getPypiPackageFile(packageName: string, filename: string): Promise<Buffer | null> {
|
|
683
|
+
const path = this.getPypiPackageFilePath(packageName, filename);
|
|
684
|
+
return this.getObject(path);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Store PyPI package file (wheel, sdist)
|
|
689
|
+
*/
|
|
690
|
+
public async putPypiPackageFile(
|
|
691
|
+
packageName: string,
|
|
692
|
+
filename: string,
|
|
693
|
+
data: Buffer
|
|
694
|
+
): Promise<void> {
|
|
695
|
+
const path = this.getPypiPackageFilePath(packageName, filename);
|
|
696
|
+
return this.putObject(path, data, { 'Content-Type': 'application/octet-stream' });
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Check if PyPI package file exists
|
|
701
|
+
*/
|
|
702
|
+
public async pypiPackageFileExists(packageName: string, filename: string): Promise<boolean> {
|
|
703
|
+
const path = this.getPypiPackageFilePath(packageName, filename);
|
|
704
|
+
return this.objectExists(path);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Delete PyPI package file
|
|
709
|
+
*/
|
|
710
|
+
public async deletePypiPackageFile(packageName: string, filename: string): Promise<void> {
|
|
711
|
+
const path = this.getPypiPackageFilePath(packageName, filename);
|
|
712
|
+
return this.deleteObject(path);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* List all PyPI packages
|
|
717
|
+
*/
|
|
718
|
+
public async listPypiPackages(): Promise<string[]> {
|
|
719
|
+
const prefix = 'pypi/metadata/';
|
|
720
|
+
const objects = await this.listObjects(prefix);
|
|
721
|
+
const packages = new Set<string>();
|
|
722
|
+
|
|
723
|
+
// Extract package names from paths like: pypi/metadata/package-name/metadata.json
|
|
724
|
+
for (const obj of objects) {
|
|
725
|
+
const match = obj.match(/^pypi\/metadata\/([^\/]+)\/metadata\.json$/);
|
|
726
|
+
if (match) {
|
|
727
|
+
packages.add(match[1]);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return Array.from(packages).sort();
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* List all versions of a PyPI package
|
|
736
|
+
*/
|
|
737
|
+
public async listPypiPackageVersions(packageName: string): Promise<string[]> {
|
|
738
|
+
const prefix = `pypi/packages/${packageName}/`;
|
|
739
|
+
const objects = await this.listObjects(prefix);
|
|
740
|
+
const versions = new Set<string>();
|
|
741
|
+
|
|
742
|
+
// Extract versions from filenames
|
|
743
|
+
for (const obj of objects) {
|
|
744
|
+
const filename = obj.split('/').pop();
|
|
745
|
+
if (!filename) continue;
|
|
746
|
+
|
|
747
|
+
// Extract version from wheel filename: package-1.0.0-py3-none-any.whl
|
|
748
|
+
// or sdist filename: package-1.0.0.tar.gz
|
|
749
|
+
const wheelMatch = filename.match(/^[^-]+-([^-]+)-.*\.whl$/);
|
|
750
|
+
const sdistMatch = filename.match(/^[^-]+-([^.]+)\.(tar\.gz|zip)$/);
|
|
751
|
+
|
|
752
|
+
if (wheelMatch) versions.add(wheelMatch[1]);
|
|
753
|
+
else if (sdistMatch) versions.add(sdistMatch[1]);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return Array.from(versions).sort();
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Delete entire PyPI package (all versions and files)
|
|
761
|
+
*/
|
|
762
|
+
public async deletePypiPackage(packageName: string): Promise<void> {
|
|
763
|
+
// Delete metadata
|
|
764
|
+
await this.deletePypiPackageMetadata(packageName);
|
|
765
|
+
|
|
766
|
+
// Delete Simple API index
|
|
767
|
+
const simpleIndexPath = this.getPypiSimpleIndexPath(packageName);
|
|
768
|
+
try {
|
|
769
|
+
await this.deleteObject(simpleIndexPath);
|
|
770
|
+
} catch (error) {
|
|
771
|
+
// Ignore if doesn't exist
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Delete all package files
|
|
775
|
+
const prefix = `pypi/packages/${packageName}/`;
|
|
776
|
+
const objects = await this.listObjects(prefix);
|
|
777
|
+
for (const obj of objects) {
|
|
778
|
+
await this.deleteObject(obj);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Delete specific version of a PyPI package
|
|
784
|
+
*/
|
|
785
|
+
public async deletePypiPackageVersion(packageName: string, version: string): Promise<void> {
|
|
786
|
+
const prefix = `pypi/packages/${packageName}/`;
|
|
787
|
+
const objects = await this.listObjects(prefix);
|
|
788
|
+
|
|
789
|
+
// Delete all files matching this version
|
|
790
|
+
for (const obj of objects) {
|
|
791
|
+
const filename = obj.split('/').pop();
|
|
792
|
+
if (!filename) continue;
|
|
793
|
+
|
|
794
|
+
// Check if filename contains this version
|
|
795
|
+
const wheelMatch = filename.match(/^[^-]+-([^-]+)-.*\.whl$/);
|
|
796
|
+
const sdistMatch = filename.match(/^[^-]+-([^.]+)\.(tar\.gz|zip)$/);
|
|
797
|
+
|
|
798
|
+
const fileVersion = wheelMatch?.[1] || sdistMatch?.[1];
|
|
799
|
+
if (fileVersion === version) {
|
|
800
|
+
await this.deleteObject(obj);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Update metadata to remove this version
|
|
805
|
+
const metadata = await this.getPypiPackageMetadata(packageName);
|
|
806
|
+
if (metadata && metadata.versions) {
|
|
807
|
+
delete metadata.versions[version];
|
|
808
|
+
await this.putPypiPackageMetadata(packageName, metadata);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ========================================================================
|
|
813
|
+
// PYPI PATH HELPERS
|
|
814
|
+
// ========================================================================
|
|
815
|
+
|
|
816
|
+
private getPypiMetadataPath(packageName: string): string {
|
|
817
|
+
return `pypi/metadata/${packageName}/metadata.json`;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
private getPypiSimpleIndexPath(packageName: string): string {
|
|
821
|
+
return `pypi/simple/${packageName}/index.html`;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
private getPypiSimpleRootIndexPath(): string {
|
|
825
|
+
return `pypi/simple/index.html`;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
private getPypiPackageFilePath(packageName: string, filename: string): string {
|
|
829
|
+
return `pypi/packages/${packageName}/${filename}`;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// ========================================================================
|
|
833
|
+
// RUBYGEMS STORAGE METHODS
|
|
834
|
+
// ========================================================================
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Get RubyGems versions file (compact index)
|
|
838
|
+
*/
|
|
839
|
+
public async getRubyGemsVersions(): Promise<string | null> {
|
|
840
|
+
const path = this.getRubyGemsVersionsPath();
|
|
841
|
+
const data = await this.getObject(path);
|
|
842
|
+
return data ? data.toString('utf-8') : null;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Store RubyGems versions file (compact index)
|
|
847
|
+
*/
|
|
848
|
+
public async putRubyGemsVersions(content: string): Promise<void> {
|
|
849
|
+
const path = this.getRubyGemsVersionsPath();
|
|
850
|
+
const data = Buffer.from(content, 'utf-8');
|
|
851
|
+
return this.putObject(path, data, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Get RubyGems info file for a gem (compact index)
|
|
856
|
+
*/
|
|
857
|
+
public async getRubyGemsInfo(gemName: string): Promise<string | null> {
|
|
858
|
+
const path = this.getRubyGemsInfoPath(gemName);
|
|
859
|
+
const data = await this.getObject(path);
|
|
860
|
+
return data ? data.toString('utf-8') : null;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Store RubyGems info file for a gem (compact index)
|
|
865
|
+
*/
|
|
866
|
+
public async putRubyGemsInfo(gemName: string, content: string): Promise<void> {
|
|
867
|
+
const path = this.getRubyGemsInfoPath(gemName);
|
|
868
|
+
const data = Buffer.from(content, 'utf-8');
|
|
869
|
+
return this.putObject(path, data, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Get RubyGems names file
|
|
874
|
+
*/
|
|
875
|
+
public async getRubyGemsNames(): Promise<string | null> {
|
|
876
|
+
const path = this.getRubyGemsNamesPath();
|
|
877
|
+
const data = await this.getObject(path);
|
|
878
|
+
return data ? data.toString('utf-8') : null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Store RubyGems names file
|
|
883
|
+
*/
|
|
884
|
+
public async putRubyGemsNames(content: string): Promise<void> {
|
|
885
|
+
const path = this.getRubyGemsNamesPath();
|
|
886
|
+
const data = Buffer.from(content, 'utf-8');
|
|
887
|
+
return this.putObject(path, data, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Get RubyGems .gem file
|
|
892
|
+
*/
|
|
893
|
+
public async getRubyGemsGem(gemName: string, version: string, platform?: string): Promise<Buffer | null> {
|
|
894
|
+
const path = this.getRubyGemsGemPath(gemName, version, platform);
|
|
895
|
+
return this.getObject(path);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Store RubyGems .gem file
|
|
900
|
+
*/
|
|
901
|
+
public async putRubyGemsGem(
|
|
902
|
+
gemName: string,
|
|
903
|
+
version: string,
|
|
904
|
+
data: Buffer,
|
|
905
|
+
platform?: string
|
|
906
|
+
): Promise<void> {
|
|
907
|
+
const path = this.getRubyGemsGemPath(gemName, version, platform);
|
|
908
|
+
return this.putObject(path, data, { 'Content-Type': 'application/octet-stream' });
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Check if RubyGems .gem file exists
|
|
913
|
+
*/
|
|
914
|
+
public async rubyGemsGemExists(gemName: string, version: string, platform?: string): Promise<boolean> {
|
|
915
|
+
const path = this.getRubyGemsGemPath(gemName, version, platform);
|
|
916
|
+
return this.objectExists(path);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Delete RubyGems .gem file
|
|
921
|
+
*/
|
|
922
|
+
public async deleteRubyGemsGem(gemName: string, version: string, platform?: string): Promise<void> {
|
|
923
|
+
const path = this.getRubyGemsGemPath(gemName, version, platform);
|
|
924
|
+
return this.deleteObject(path);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Get RubyGems metadata
|
|
929
|
+
*/
|
|
930
|
+
public async getRubyGemsMetadata(gemName: string): Promise<any | null> {
|
|
931
|
+
const path = this.getRubyGemsMetadataPath(gemName);
|
|
932
|
+
const data = await this.getObject(path);
|
|
933
|
+
return data ? JSON.parse(data.toString('utf-8')) : null;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Store RubyGems metadata
|
|
938
|
+
*/
|
|
939
|
+
public async putRubyGemsMetadata(gemName: string, metadata: any): Promise<void> {
|
|
940
|
+
const path = this.getRubyGemsMetadataPath(gemName);
|
|
941
|
+
const data = Buffer.from(JSON.stringify(metadata, null, 2), 'utf-8');
|
|
942
|
+
return this.putObject(path, data, { 'Content-Type': 'application/json' });
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Check if RubyGems metadata exists
|
|
947
|
+
*/
|
|
948
|
+
public async rubyGemsMetadataExists(gemName: string): Promise<boolean> {
|
|
949
|
+
const path = this.getRubyGemsMetadataPath(gemName);
|
|
950
|
+
return this.objectExists(path);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Delete RubyGems metadata
|
|
955
|
+
*/
|
|
956
|
+
public async deleteRubyGemsMetadata(gemName: string): Promise<void> {
|
|
957
|
+
const path = this.getRubyGemsMetadataPath(gemName);
|
|
958
|
+
return this.deleteObject(path);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* List all RubyGems
|
|
963
|
+
*/
|
|
964
|
+
public async listRubyGems(): Promise<string[]> {
|
|
965
|
+
const prefix = 'rubygems/metadata/';
|
|
966
|
+
const objects = await this.listObjects(prefix);
|
|
967
|
+
const gems = new Set<string>();
|
|
968
|
+
|
|
969
|
+
// Extract gem names from paths like: rubygems/metadata/gem-name/metadata.json
|
|
970
|
+
for (const obj of objects) {
|
|
971
|
+
const match = obj.match(/^rubygems\/metadata\/([^\/]+)\/metadata\.json$/);
|
|
972
|
+
if (match) {
|
|
973
|
+
gems.add(match[1]);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return Array.from(gems).sort();
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* List all versions of a RubyGem
|
|
982
|
+
*/
|
|
983
|
+
public async listRubyGemsVersions(gemName: string): Promise<string[]> {
|
|
984
|
+
const prefix = `rubygems/gems/`;
|
|
985
|
+
const objects = await this.listObjects(prefix);
|
|
986
|
+
const versions = new Set<string>();
|
|
987
|
+
|
|
988
|
+
// Extract versions from filenames: gem-name-version[-platform].gem
|
|
989
|
+
const gemPrefix = `${gemName}-`;
|
|
990
|
+
for (const obj of objects) {
|
|
991
|
+
const filename = obj.split('/').pop();
|
|
992
|
+
if (!filename || !filename.startsWith(gemPrefix) || !filename.endsWith('.gem')) continue;
|
|
993
|
+
|
|
994
|
+
// Remove gem name prefix and .gem suffix
|
|
995
|
+
const versionPart = filename.substring(gemPrefix.length, filename.length - 4);
|
|
996
|
+
|
|
997
|
+
// Split on last hyphen to separate version from platform
|
|
998
|
+
const lastHyphen = versionPart.lastIndexOf('-');
|
|
999
|
+
const version = lastHyphen > 0 ? versionPart.substring(0, lastHyphen) : versionPart;
|
|
1000
|
+
|
|
1001
|
+
versions.add(version);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return Array.from(versions).sort();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Delete entire RubyGem (all versions and files)
|
|
1009
|
+
*/
|
|
1010
|
+
public async deleteRubyGem(gemName: string): Promise<void> {
|
|
1011
|
+
// Delete metadata
|
|
1012
|
+
await this.deleteRubyGemsMetadata(gemName);
|
|
1013
|
+
|
|
1014
|
+
// Delete all gem files
|
|
1015
|
+
const prefix = `rubygems/gems/`;
|
|
1016
|
+
const objects = await this.listObjects(prefix);
|
|
1017
|
+
const gemPrefix = `${gemName}-`;
|
|
1018
|
+
|
|
1019
|
+
for (const obj of objects) {
|
|
1020
|
+
const filename = obj.split('/').pop();
|
|
1021
|
+
if (filename && filename.startsWith(gemPrefix) && filename.endsWith('.gem')) {
|
|
1022
|
+
await this.deleteObject(obj);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Delete specific version of a RubyGem
|
|
1029
|
+
*/
|
|
1030
|
+
public async deleteRubyGemsVersion(gemName: string, version: string, platform?: string): Promise<void> {
|
|
1031
|
+
// Delete gem file
|
|
1032
|
+
await this.deleteRubyGemsGem(gemName, version, platform);
|
|
1033
|
+
|
|
1034
|
+
// Update metadata to remove this version
|
|
1035
|
+
const metadata = await this.getRubyGemsMetadata(gemName);
|
|
1036
|
+
if (metadata && metadata.versions) {
|
|
1037
|
+
const versionKey = platform ? `${version}-${platform}` : version;
|
|
1038
|
+
delete metadata.versions[versionKey];
|
|
1039
|
+
await this.putRubyGemsMetadata(gemName, metadata);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// ========================================================================
|
|
1044
|
+
// RUBYGEMS PATH HELPERS
|
|
1045
|
+
// ========================================================================
|
|
1046
|
+
|
|
1047
|
+
private getRubyGemsVersionsPath(): string {
|
|
1048
|
+
return 'rubygems/versions';
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
private getRubyGemsInfoPath(gemName: string): string {
|
|
1052
|
+
return `rubygems/info/${gemName}`;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
private getRubyGemsNamesPath(): string {
|
|
1056
|
+
return 'rubygems/names';
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
private getRubyGemsGemPath(gemName: string, version: string, platform?: string): string {
|
|
1060
|
+
const filename = platform ? `${gemName}-${version}-${platform}.gem` : `${gemName}-${version}.gem`;
|
|
1061
|
+
return `rubygems/gems/${filename}`;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
private getRubyGemsMetadataPath(gemName: string): string {
|
|
1065
|
+
return `rubygems/metadata/${gemName}/metadata.json`;
|
|
1066
|
+
}
|
|
604
1067
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Registry protocol types
|
|
7
7
|
*/
|
|
8
|
-
export type TRegistryProtocol = 'oci' | 'npm' | 'maven' | 'cargo' | 'composer';
|
|
8
|
+
export type TRegistryProtocol = 'oci' | 'npm' | 'maven' | 'cargo' | 'composer' | 'pypi' | 'rubygems';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Unified action types across protocols
|
|
@@ -70,6 +70,16 @@ export interface IAuthConfig {
|
|
|
70
70
|
realm: string;
|
|
71
71
|
service: string;
|
|
72
72
|
};
|
|
73
|
+
/** PyPI token settings */
|
|
74
|
+
pypiTokens?: {
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
defaultReadonly?: boolean;
|
|
77
|
+
};
|
|
78
|
+
/** RubyGems token settings */
|
|
79
|
+
rubygemsTokens?: {
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
defaultReadonly?: boolean;
|
|
82
|
+
};
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
@@ -92,6 +102,8 @@ export interface IRegistryConfig {
|
|
|
92
102
|
maven?: IProtocolConfig;
|
|
93
103
|
cargo?: IProtocolConfig;
|
|
94
104
|
composer?: IProtocolConfig;
|
|
105
|
+
pypi?: IProtocolConfig;
|
|
106
|
+
rubygems?: IProtocolConfig;
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
/**
|
package/ts/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @push.rocks/smartregistry
|
|
3
|
-
* Composable registry supporting OCI, NPM, Maven, Cargo, and
|
|
3
|
+
* Composable registry supporting OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems protocols
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Main orchestrator
|
|
@@ -23,3 +23,9 @@ export * from './cargo/index.js';
|
|
|
23
23
|
|
|
24
24
|
// Composer Registry
|
|
25
25
|
export * from './composer/index.js';
|
|
26
|
+
|
|
27
|
+
// PyPI Registry
|
|
28
|
+
export * from './pypi/index.js';
|
|
29
|
+
|
|
30
|
+
// RubyGems Registry
|
|
31
|
+
export * from './rubygems/index.js';
|