@push.rocks/smartregistry 1.4.1 → 1.5.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.
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Helper functions for PyPI registry
3
+ * Package name normalization, HTML generation, etc.
4
+ */
5
+ /**
6
+ * Normalize package name according to PEP 503
7
+ * Lowercase and replace runs of [._-] with a single dash
8
+ * @param name - Package name
9
+ * @returns Normalized name
10
+ */
11
+ export function normalizePypiPackageName(name) {
12
+ return name
13
+ .toLowerCase()
14
+ .replace(/[-_.]+/g, '-');
15
+ }
16
+ /**
17
+ * Escape HTML special characters to prevent XSS
18
+ * @param str - String to escape
19
+ * @returns Escaped string
20
+ */
21
+ export function escapeHtml(str) {
22
+ return str
23
+ .replace(/&/g, '&')
24
+ .replace(/</g, '&lt;')
25
+ .replace(/>/g, '&gt;')
26
+ .replace(/"/g, '&quot;')
27
+ .replace(/'/g, '&#039;');
28
+ }
29
+ /**
30
+ * Generate PEP 503 compliant HTML for root index (all packages)
31
+ * @param packages - List of package names
32
+ * @returns HTML string
33
+ */
34
+ export function generateSimpleRootHtml(packages) {
35
+ const links = packages
36
+ .map(pkg => {
37
+ const normalized = normalizePypiPackageName(pkg);
38
+ return ` <a href="${escapeHtml(normalized)}/">${escapeHtml(pkg)}</a>`;
39
+ })
40
+ .join('\n');
41
+ return `<!DOCTYPE html>
42
+ <html>
43
+ <head>
44
+ <meta name="pypi:repository-version" content="1.0">
45
+ <title>Simple Index</title>
46
+ </head>
47
+ <body>
48
+ <h1>Simple Index</h1>
49
+ ${links}
50
+ </body>
51
+ </html>`;
52
+ }
53
+ /**
54
+ * Generate PEP 503 compliant HTML for package index (file list)
55
+ * @param packageName - Package name (normalized)
56
+ * @param files - List of files
57
+ * @param baseUrl - Base URL for downloads
58
+ * @returns HTML string
59
+ */
60
+ export function generateSimplePackageHtml(packageName, files, baseUrl) {
61
+ const links = files
62
+ .map(file => {
63
+ // Build URL
64
+ let url = file.url;
65
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
66
+ // Relative URL - make it absolute
67
+ url = `${baseUrl}/packages/${packageName}/${file.filename}`;
68
+ }
69
+ // Add hash fragment
70
+ const hashName = Object.keys(file.hashes)[0];
71
+ const hashValue = file.hashes[hashName];
72
+ const fragment = hashName && hashValue ? `#${hashName}=${hashValue}` : '';
73
+ // Build data attributes
74
+ const dataAttrs = [];
75
+ if (file['requires-python']) {
76
+ const escaped = escapeHtml(file['requires-python']);
77
+ dataAttrs.push(`data-requires-python="${escaped}"`);
78
+ }
79
+ if (file['gpg-sig'] !== undefined) {
80
+ dataAttrs.push(`data-gpg-sig="${file['gpg-sig'] ? 'true' : 'false'}"`);
81
+ }
82
+ if (file.yanked) {
83
+ const reason = typeof file.yanked === 'string' ? file.yanked : '';
84
+ if (reason) {
85
+ dataAttrs.push(`data-yanked="${escapeHtml(reason)}"`);
86
+ }
87
+ else {
88
+ dataAttrs.push(`data-yanked=""`);
89
+ }
90
+ }
91
+ const dataAttrStr = dataAttrs.length > 0 ? ' ' + dataAttrs.join(' ') : '';
92
+ return ` <a href="${escapeHtml(url)}${fragment}"${dataAttrStr}>${escapeHtml(file.filename)}</a>`;
93
+ })
94
+ .join('\n');
95
+ return `<!DOCTYPE html>
96
+ <html>
97
+ <head>
98
+ <meta name="pypi:repository-version" content="1.0">
99
+ <title>Links for ${escapeHtml(packageName)}</title>
100
+ </head>
101
+ <body>
102
+ <h1>Links for ${escapeHtml(packageName)}</h1>
103
+ ${links}
104
+ </body>
105
+ </html>`;
106
+ }
107
+ /**
108
+ * Parse filename to extract package info
109
+ * Supports wheel and sdist formats
110
+ * @param filename - Package filename
111
+ * @returns Parsed info or null
112
+ */
113
+ export function parsePackageFilename(filename) {
114
+ // Wheel format: {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
115
+ const wheelMatch = filename.match(/^([a-zA-Z0-9_.-]+?)-([a-zA-Z0-9_.]+?)(?:-(\d+))?-([^-]+)-([^-]+)-([^-]+)\.whl$/);
116
+ if (wheelMatch) {
117
+ return {
118
+ name: wheelMatch[1],
119
+ version: wheelMatch[2],
120
+ filetype: 'bdist_wheel',
121
+ pythonVersion: wheelMatch[4],
122
+ };
123
+ }
124
+ // Sdist tar.gz format: {name}-{version}.tar.gz
125
+ const sdistTarMatch = filename.match(/^([a-zA-Z0-9_.-]+?)-([a-zA-Z0-9_.]+)\.tar\.gz$/);
126
+ if (sdistTarMatch) {
127
+ return {
128
+ name: sdistTarMatch[1],
129
+ version: sdistTarMatch[2],
130
+ filetype: 'sdist',
131
+ pythonVersion: 'source',
132
+ };
133
+ }
134
+ // Sdist zip format: {name}-{version}.zip
135
+ const sdistZipMatch = filename.match(/^([a-zA-Z0-9_.-]+?)-([a-zA-Z0-9_.]+)\.zip$/);
136
+ if (sdistZipMatch) {
137
+ return {
138
+ name: sdistZipMatch[1],
139
+ version: sdistZipMatch[2],
140
+ filetype: 'sdist',
141
+ pythonVersion: 'source',
142
+ };
143
+ }
144
+ return null;
145
+ }
146
+ /**
147
+ * Calculate hash digest for a buffer
148
+ * @param data - Data to hash
149
+ * @param algorithm - Hash algorithm (sha256, md5, blake2b)
150
+ * @returns Hex-encoded hash
151
+ */
152
+ export async function calculateHash(data, algorithm) {
153
+ const crypto = await import('crypto');
154
+ let hash;
155
+ if (algorithm === 'blake2b') {
156
+ // Node.js uses 'blake2b512' for blake2b
157
+ hash = crypto.createHash('blake2b512');
158
+ }
159
+ else {
160
+ hash = crypto.createHash(algorithm);
161
+ }
162
+ hash.update(data);
163
+ return hash.digest('hex');
164
+ }
165
+ /**
166
+ * Validate package name
167
+ * Must contain only ASCII letters, numbers, ., -, and _
168
+ * @param name - Package name
169
+ * @returns true if valid
170
+ */
171
+ export function isValidPackageName(name) {
172
+ return /^[a-zA-Z0-9._-]+$/.test(name);
173
+ }
174
+ /**
175
+ * Validate version string (basic check)
176
+ * @param version - Version string
177
+ * @returns true if valid
178
+ */
179
+ export function isValidVersion(version) {
180
+ // Basic check - allows numbers, letters, dots, hyphens, underscores
181
+ // More strict validation would follow PEP 440
182
+ return /^[a-zA-Z0-9._-]+$/.test(version);
183
+ }
184
+ /**
185
+ * Extract metadata from package metadata
186
+ * Filters and normalizes metadata fields
187
+ * @param metadata - Raw metadata object
188
+ * @returns Filtered metadata
189
+ */
190
+ export function extractCoreMetadata(metadata) {
191
+ const coreFields = [
192
+ 'metadata-version',
193
+ 'name',
194
+ 'version',
195
+ 'platform',
196
+ 'supported-platform',
197
+ 'summary',
198
+ 'description',
199
+ 'description-content-type',
200
+ 'keywords',
201
+ 'home-page',
202
+ 'download-url',
203
+ 'author',
204
+ 'author-email',
205
+ 'maintainer',
206
+ 'maintainer-email',
207
+ 'license',
208
+ 'classifier',
209
+ 'requires-python',
210
+ 'requires-dist',
211
+ 'requires-external',
212
+ 'provides-dist',
213
+ 'project-url',
214
+ 'provides-extra',
215
+ ];
216
+ const result = {};
217
+ for (const [key, value] of Object.entries(metadata)) {
218
+ const normalizedKey = key.toLowerCase().replace(/_/g, '-');
219
+ if (coreFields.includes(normalizedKey)) {
220
+ result[normalizedKey] = value;
221
+ }
222
+ }
223
+ return result;
224
+ }
225
+ /**
226
+ * Generate JSON API response for package list (PEP 691)
227
+ * @param packages - List of package names
228
+ * @returns JSON object
229
+ */
230
+ export function generateJsonRootResponse(packages) {
231
+ return {
232
+ meta: {
233
+ 'api-version': '1.0',
234
+ },
235
+ projects: packages.map(name => ({ name })),
236
+ };
237
+ }
238
+ /**
239
+ * Generate JSON API response for package files (PEP 691)
240
+ * @param packageName - Package name (normalized)
241
+ * @param files - List of files
242
+ * @returns JSON object
243
+ */
244
+ export function generateJsonPackageResponse(packageName, files) {
245
+ return {
246
+ meta: {
247
+ 'api-version': '1.0',
248
+ },
249
+ name: packageName,
250
+ files: files.map(file => ({
251
+ filename: file.filename,
252
+ url: file.url,
253
+ hashes: file.hashes,
254
+ 'requires-python': file['requires-python'],
255
+ 'dist-info-metadata': file['dist-info-metadata'],
256
+ 'gpg-sig': file['gpg-sig'],
257
+ yanked: file.yanked,
258
+ size: file.size,
259
+ 'upload-time': file['upload-time'],
260
+ })),
261
+ };
262
+ }
263
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5weXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcHlwaS9oZWxwZXJzLnB5cGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBSUg7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsd0JBQXdCLENBQUMsSUFBWTtJQUNuRCxPQUFPLElBQUk7U0FDUixXQUFXLEVBQUU7U0FDYixPQUFPLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQzdCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLFVBQVUsQ0FBQyxHQUFXO0lBQ3BDLE9BQU8sR0FBRztTQUNQLE9BQU8sQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDO1NBQ3RCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDO1NBQ3JCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDO1NBQ3JCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDO1NBQ3ZCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDN0IsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsc0JBQXNCLENBQUMsUUFBa0I7SUFDdkQsTUFBTSxLQUFLLEdBQUcsUUFBUTtTQUNuQixHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDVCxNQUFNLFVBQVUsR0FBRyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNqRCxPQUFPLGdCQUFnQixVQUFVLENBQUMsVUFBVSxDQUFDLE1BQU0sVUFBVSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUM7SUFDM0UsQ0FBQyxDQUFDO1NBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRWQsT0FBTzs7Ozs7Ozs7RUFRUCxLQUFLOztRQUVDLENBQUM7QUFDVCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLHlCQUF5QixDQUN2QyxXQUFtQixFQUNuQixLQUFrQixFQUNsQixP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
@@ -0,0 +1,7 @@
1
+ /**
2
+ * PyPI Registry Module
3
+ * Python Package Index implementation
4
+ */
5
+ export * from './interfaces.pypi.js';
6
+ export * from './classes.pypiregistry.js';
7
+ export * as pypiHelpers from './helpers.pypi.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * PyPI Registry Module
3
+ * Python Package Index implementation
4
+ */
5
+ export * from './interfaces.pypi.js';
6
+ export * from './classes.pypiregistry.js';
7
+ export * as pypiHelpers from './helpers.pypi.js';
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9weXBpL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILGNBQWMsc0JBQXNCLENBQUM7QUFDckMsY0FBYywyQkFBMkIsQ0FBQztBQUMxQyxPQUFPLEtBQUssV0FBVyxNQUFNLG1CQUFtQixDQUFDIn0=
@@ -0,0 +1,301 @@
1
+ /**
2
+ * PyPI Registry Type Definitions
3
+ * Compliant with PEP 503 (Simple API), PEP 691 (JSON API), and PyPI upload API
4
+ */
5
+ /**
6
+ * File information for a package distribution
7
+ * Used in both PEP 503 HTML and PEP 691 JSON responses
8
+ */
9
+ export interface IPypiFile {
10
+ /** Filename (e.g., "package-1.0.0-py3-none-any.whl") */
11
+ filename: string;
12
+ /** Download URL (absolute or relative) */
13
+ url: string;
14
+ /** Hash digests (multiple algorithms supported in JSON) */
15
+ hashes: Record<string, string>;
16
+ /** Python version requirement (PEP 345 format) */
17
+ 'requires-python'?: string;
18
+ /** Whether distribution info metadata is available (PEP 658) */
19
+ 'dist-info-metadata'?: boolean | {
20
+ sha256: string;
21
+ };
22
+ /** Whether GPG signature is available */
23
+ 'gpg-sig'?: boolean;
24
+ /** Yank status: false or reason string */
25
+ yanked?: boolean | string;
26
+ /** File size in bytes */
27
+ size?: number;
28
+ /** Upload timestamp */
29
+ 'upload-time'?: string;
30
+ }
31
+ /**
32
+ * Package metadata stored internally
33
+ * Consolidated from multiple file uploads
34
+ */
35
+ export interface IPypiPackageMetadata {
36
+ /** Normalized package name */
37
+ name: string;
38
+ /** Map of version to file list */
39
+ versions: Record<string, IPypiVersionMetadata>;
40
+ /** Timestamp of last update */
41
+ 'last-modified'?: string;
42
+ }
43
+ /**
44
+ * Metadata for a specific version
45
+ */
46
+ export interface IPypiVersionMetadata {
47
+ /** Version string */
48
+ version: string;
49
+ /** Files for this version (wheels, sdists) */
50
+ files: IPypiFileMetadata[];
51
+ /** Core metadata fields */
52
+ metadata?: IPypiCoreMetadata;
53
+ /** Whether entire version is yanked */
54
+ yanked?: boolean | string;
55
+ /** Upload timestamp */
56
+ 'upload-time'?: string;
57
+ }
58
+ /**
59
+ * Internal file metadata
60
+ */
61
+ export interface IPypiFileMetadata {
62
+ filename: string;
63
+ /** Storage key/path */
64
+ path: string;
65
+ /** File type: bdist_wheel or sdist */
66
+ filetype: 'bdist_wheel' | 'sdist';
67
+ /** Python version tag */
68
+ python_version: string;
69
+ /** Hash digests */
70
+ hashes: Record<string, string>;
71
+ /** File size in bytes */
72
+ size: number;
73
+ /** Python version requirement */
74
+ 'requires-python'?: string;
75
+ /** Whether this file is yanked */
76
+ yanked?: boolean | string;
77
+ /** Upload timestamp */
78
+ 'upload-time': string;
79
+ /** Uploader user ID */
80
+ 'uploaded-by': string;
81
+ }
82
+ /**
83
+ * Core metadata fields (subset of PEP 566)
84
+ * These are extracted from package uploads
85
+ */
86
+ export interface IPypiCoreMetadata {
87
+ /** Metadata version */
88
+ 'metadata-version': string;
89
+ /** Package name */
90
+ name: string;
91
+ /** Version string */
92
+ version: string;
93
+ /** Platform compatibility */
94
+ platform?: string;
95
+ /** Supported platforms */
96
+ 'supported-platform'?: string;
97
+ /** Summary/description */
98
+ summary?: string;
99
+ /** Long description */
100
+ description?: string;
101
+ /** Description content type (text/plain, text/markdown, text/x-rst) */
102
+ 'description-content-type'?: string;
103
+ /** Keywords */
104
+ keywords?: string;
105
+ /** Homepage URL */
106
+ 'home-page'?: string;
107
+ /** Download URL */
108
+ 'download-url'?: string;
109
+ /** Author name */
110
+ author?: string;
111
+ /** Author email */
112
+ 'author-email'?: string;
113
+ /** Maintainer name */
114
+ maintainer?: string;
115
+ /** Maintainer email */
116
+ 'maintainer-email'?: string;
117
+ /** License */
118
+ license?: string;
119
+ /** Classifiers (Trove classifiers) */
120
+ classifier?: string[];
121
+ /** Python version requirement */
122
+ 'requires-python'?: string;
123
+ /** Dist name requirement */
124
+ 'requires-dist'?: string[];
125
+ /** External requirement */
126
+ 'requires-external'?: string[];
127
+ /** Provides dist */
128
+ 'provides-dist'?: string[];
129
+ /** Project URLs */
130
+ 'project-url'?: string[];
131
+ /** Provides extra */
132
+ 'provides-extra'?: string[];
133
+ }
134
+ /**
135
+ * PEP 503: Simple API root response (project list)
136
+ */
137
+ export interface IPypiSimpleRootHtml {
138
+ /** List of project names */
139
+ projects: string[];
140
+ }
141
+ /**
142
+ * PEP 503: Simple API project response (file list)
143
+ */
144
+ export interface IPypiSimpleProjectHtml {
145
+ /** Normalized project name */
146
+ name: string;
147
+ /** List of files */
148
+ files: IPypiFile[];
149
+ }
150
+ /**
151
+ * PEP 691: JSON API root response
152
+ */
153
+ export interface IPypiJsonRoot {
154
+ /** API metadata */
155
+ meta: {
156
+ /** API version (e.g., "1.0") */
157
+ 'api-version': string;
158
+ };
159
+ /** List of projects */
160
+ projects: Array<{
161
+ /** Project name */
162
+ name: string;
163
+ }>;
164
+ }
165
+ /**
166
+ * PEP 691: JSON API project response
167
+ */
168
+ export interface IPypiJsonProject {
169
+ /** Normalized project name */
170
+ name: string;
171
+ /** API metadata */
172
+ meta: {
173
+ /** API version (e.g., "1.0") */
174
+ 'api-version': string;
175
+ };
176
+ /** List of files */
177
+ files: IPypiFile[];
178
+ }
179
+ /**
180
+ * Upload form data (multipart/form-data fields)
181
+ * Based on PyPI legacy upload API
182
+ */
183
+ export interface IPypiUploadForm {
184
+ /** Action type (always "file_upload") */
185
+ ':action': 'file_upload';
186
+ /** Protocol version (always "1") */
187
+ protocol_version: '1';
188
+ /** File content (binary) */
189
+ content: Buffer;
190
+ /** File type */
191
+ filetype: 'bdist_wheel' | 'sdist';
192
+ /** Python version tag */
193
+ pyversion: string;
194
+ /** Package name */
195
+ name: string;
196
+ /** Version string */
197
+ version: string;
198
+ /** Metadata version */
199
+ metadata_version: string;
200
+ /** Hash digests (at least one required) */
201
+ md5_digest?: string;
202
+ sha256_digest?: string;
203
+ blake2_256_digest?: string;
204
+ /** Optional attestations */
205
+ attestations?: string;
206
+ /** Optional core metadata fields */
207
+ summary?: string;
208
+ description?: string;
209
+ description_content_type?: string;
210
+ author?: string;
211
+ author_email?: string;
212
+ maintainer?: string;
213
+ maintainer_email?: string;
214
+ license?: string;
215
+ keywords?: string;
216
+ home_page?: string;
217
+ download_url?: string;
218
+ requires_python?: string;
219
+ classifiers?: string[];
220
+ platform?: string;
221
+ [key: string]: any;
222
+ }
223
+ /**
224
+ * JSON API upload response
225
+ */
226
+ export interface IPypiUploadResponse {
227
+ /** Success message */
228
+ message?: string;
229
+ /** URL of uploaded file */
230
+ url?: string;
231
+ }
232
+ /**
233
+ * Error response structure
234
+ */
235
+ export interface IPypiError {
236
+ /** Error message */
237
+ message: string;
238
+ /** HTTP status code */
239
+ status?: number;
240
+ /** Additional error details */
241
+ details?: string[];
242
+ }
243
+ /**
244
+ * Search query parameters
245
+ */
246
+ export interface IPypiSearchQuery {
247
+ /** Search term */
248
+ q?: string;
249
+ /** Page number */
250
+ page?: number;
251
+ /** Results per page */
252
+ per_page?: number;
253
+ }
254
+ /**
255
+ * Search result for a single package
256
+ */
257
+ export interface IPypiSearchResult {
258
+ /** Package name */
259
+ name: string;
260
+ /** Latest version */
261
+ version: string;
262
+ /** Summary */
263
+ summary: string;
264
+ /** Description */
265
+ description?: string;
266
+ }
267
+ /**
268
+ * Search response structure
269
+ */
270
+ export interface IPypiSearchResponse {
271
+ /** Search results */
272
+ results: IPypiSearchResult[];
273
+ /** Result count */
274
+ count: number;
275
+ /** Current page */
276
+ page: number;
277
+ /** Total pages */
278
+ pages: number;
279
+ }
280
+ /**
281
+ * Yank request
282
+ */
283
+ export interface IPypiYankRequest {
284
+ /** Package name */
285
+ name: string;
286
+ /** Version to yank */
287
+ version: string;
288
+ /** Optional filename (specific file) */
289
+ filename?: string;
290
+ /** Reason for yanking */
291
+ reason?: string;
292
+ }
293
+ /**
294
+ * Yank response
295
+ */
296
+ export interface IPypiYankResponse {
297
+ /** Success indicator */
298
+ success: boolean;
299
+ /** Message */
300
+ message?: string;
301
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * PyPI Registry Type Definitions
3
+ * Compliant with PEP 503 (Simple API), PEP 691 (JSON API), and PyPI upload API
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5weXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcHlwaS9pbnRlcmZhY2VzLnB5cGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartregistry",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
5
  "description": "a registry for npm modules and oci images",
6
6
  "main": "dist_ts/index.js",