@push.rocks/smartregistry 1.1.1
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.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.smartregistry.d.ts +45 -0
- package/dist_ts/classes.smartregistry.js +113 -0
- package/dist_ts/core/classes.authmanager.d.ts +108 -0
- package/dist_ts/core/classes.authmanager.js +315 -0
- package/dist_ts/core/classes.baseregistry.d.ts +28 -0
- package/dist_ts/core/classes.baseregistry.js +6 -0
- package/dist_ts/core/classes.registrystorage.d.ts +109 -0
- package/dist_ts/core/classes.registrystorage.js +226 -0
- package/dist_ts/core/index.d.ts +7 -0
- package/dist_ts/core/index.js +10 -0
- package/dist_ts/core/interfaces.core.d.ts +142 -0
- package/dist_ts/core/interfaces.core.js +5 -0
- package/dist_ts/index.d.ts +8 -0
- package/dist_ts/index.js +13 -0
- package/dist_ts/npm/classes.npmregistry.d.ts +36 -0
- package/dist_ts/npm/classes.npmregistry.js +717 -0
- package/dist_ts/npm/index.d.ts +5 -0
- package/dist_ts/npm/index.js +6 -0
- package/dist_ts/npm/interfaces.npm.d.ts +245 -0
- package/dist_ts/npm/interfaces.npm.js +6 -0
- package/dist_ts/oci/classes.ociregistry.d.ts +43 -0
- package/dist_ts/oci/classes.ociregistry.js +565 -0
- package/dist_ts/oci/index.d.ts +5 -0
- package/dist_ts/oci/index.js +6 -0
- package/dist_ts/oci/interfaces.oci.d.ts +103 -0
- package/dist_ts/oci/interfaces.oci.js +5 -0
- package/dist_ts/paths.d.ts +1 -0
- package/dist_ts/paths.js +3 -0
- package/dist_ts/plugins.d.ts +6 -0
- package/dist_ts/plugins.js +9 -0
- package/npmextra.json +18 -0
- package/package.json +49 -0
- package/readme.hints.md +3 -0
- package/readme.md +486 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.smartregistry.ts +129 -0
- package/ts/core/classes.authmanager.ts +388 -0
- package/ts/core/classes.baseregistry.ts +36 -0
- package/ts/core/classes.registrystorage.ts +270 -0
- package/ts/core/index.ts +11 -0
- package/ts/core/interfaces.core.ts +159 -0
- package/ts/index.ts +16 -0
- package/ts/npm/classes.npmregistry.ts +890 -0
- package/ts/npm/index.ts +6 -0
- package/ts/npm/interfaces.npm.ts +263 -0
- package/ts/oci/classes.ociregistry.ts +734 -0
- package/ts/oci/index.ts +6 -0
- package/ts/oci/interfaces.oci.ts +101 -0
- package/ts/paths.ts +5 -0
- package/ts/plugins.ts +11 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { IStorageConfig, IStorageBackend } from './interfaces.core.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Storage abstraction layer for registry
|
|
6
|
+
* Provides a unified interface over SmartBucket
|
|
7
|
+
*/
|
|
8
|
+
export class RegistryStorage implements IStorageBackend {
|
|
9
|
+
private smartBucket: plugins.smartbucket.SmartBucket;
|
|
10
|
+
private bucket: plugins.smartbucket.Bucket;
|
|
11
|
+
private bucketName: string;
|
|
12
|
+
|
|
13
|
+
constructor(private config: IStorageConfig) {
|
|
14
|
+
this.bucketName = config.bucketName;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the storage backend
|
|
19
|
+
*/
|
|
20
|
+
public async init(): Promise<void> {
|
|
21
|
+
this.smartBucket = new plugins.smartbucket.SmartBucket({
|
|
22
|
+
accessKey: this.config.accessKey,
|
|
23
|
+
accessSecret: this.config.accessSecret,
|
|
24
|
+
endpoint: this.config.endpoint,
|
|
25
|
+
port: this.config.port || 443,
|
|
26
|
+
useSsl: this.config.useSsl !== false,
|
|
27
|
+
region: this.config.region || 'us-east-1',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Ensure bucket exists
|
|
31
|
+
await this.smartBucket.createBucket(this.bucketName).catch(() => {
|
|
32
|
+
// Bucket may already exist
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.bucket = await this.smartBucket.getBucketByName(this.bucketName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get an object from storage
|
|
40
|
+
*/
|
|
41
|
+
public async getObject(key: string): Promise<Buffer | null> {
|
|
42
|
+
try {
|
|
43
|
+
return await this.bucket.fastGet({ path: key });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Store an object
|
|
51
|
+
*/
|
|
52
|
+
public async putObject(
|
|
53
|
+
key: string,
|
|
54
|
+
data: Buffer,
|
|
55
|
+
metadata?: Record<string, string>
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
// Note: SmartBucket doesn't support metadata yet
|
|
58
|
+
await this.bucket.fastPut({
|
|
59
|
+
path: key,
|
|
60
|
+
contents: data,
|
|
61
|
+
overwrite: true, // Always overwrite existing objects
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Delete an object
|
|
67
|
+
*/
|
|
68
|
+
public async deleteObject(key: string): Promise<void> {
|
|
69
|
+
await this.bucket.fastRemove({ path: key });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* List objects with a prefix (recursively)
|
|
74
|
+
*/
|
|
75
|
+
public async listObjects(prefix: string): Promise<string[]> {
|
|
76
|
+
const paths: string[] = [];
|
|
77
|
+
for await (const path of this.bucket.listAllObjects(prefix)) {
|
|
78
|
+
paths.push(path);
|
|
79
|
+
}
|
|
80
|
+
return paths;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if an object exists
|
|
85
|
+
*/
|
|
86
|
+
public async objectExists(key: string): Promise<boolean> {
|
|
87
|
+
return await this.bucket.fastExists({ path: key });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get object metadata
|
|
92
|
+
* Note: SmartBucket may not support metadata retrieval, returning empty object
|
|
93
|
+
*/
|
|
94
|
+
public async getMetadata(key: string): Promise<Record<string, string> | null> {
|
|
95
|
+
// SmartBucket doesn't expose metadata retrieval directly
|
|
96
|
+
// This is a limitation we'll document
|
|
97
|
+
const exists = await this.objectExists(key);
|
|
98
|
+
return exists ? {} : null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ========================================================================
|
|
102
|
+
// OCI-SPECIFIC HELPERS
|
|
103
|
+
// ========================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get OCI blob by digest
|
|
107
|
+
*/
|
|
108
|
+
public async getOciBlob(digest: string): Promise<Buffer | null> {
|
|
109
|
+
const path = this.getOciBlobPath(digest);
|
|
110
|
+
return this.getObject(path);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Store OCI blob
|
|
115
|
+
*/
|
|
116
|
+
public async putOciBlob(digest: string, data: Buffer): Promise<void> {
|
|
117
|
+
const path = this.getOciBlobPath(digest);
|
|
118
|
+
return this.putObject(path, data);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if OCI blob exists
|
|
123
|
+
*/
|
|
124
|
+
public async ociBlobExists(digest: string): Promise<boolean> {
|
|
125
|
+
const path = this.getOciBlobPath(digest);
|
|
126
|
+
return this.objectExists(path);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Delete OCI blob
|
|
131
|
+
*/
|
|
132
|
+
public async deleteOciBlob(digest: string): Promise<void> {
|
|
133
|
+
const path = this.getOciBlobPath(digest);
|
|
134
|
+
return this.deleteObject(path);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get OCI manifest
|
|
139
|
+
*/
|
|
140
|
+
public async getOciManifest(repository: string, digest: string): Promise<Buffer | null> {
|
|
141
|
+
const path = this.getOciManifestPath(repository, digest);
|
|
142
|
+
return this.getObject(path);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Store OCI manifest
|
|
147
|
+
*/
|
|
148
|
+
public async putOciManifest(
|
|
149
|
+
repository: string,
|
|
150
|
+
digest: string,
|
|
151
|
+
data: Buffer,
|
|
152
|
+
contentType: string
|
|
153
|
+
): Promise<void> {
|
|
154
|
+
const path = this.getOciManifestPath(repository, digest);
|
|
155
|
+
return this.putObject(path, data, { 'Content-Type': contentType });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Check if OCI manifest exists
|
|
160
|
+
*/
|
|
161
|
+
public async ociManifestExists(repository: string, digest: string): Promise<boolean> {
|
|
162
|
+
const path = this.getOciManifestPath(repository, digest);
|
|
163
|
+
return this.objectExists(path);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Delete OCI manifest
|
|
168
|
+
*/
|
|
169
|
+
public async deleteOciManifest(repository: string, digest: string): Promise<void> {
|
|
170
|
+
const path = this.getOciManifestPath(repository, digest);
|
|
171
|
+
return this.deleteObject(path);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ========================================================================
|
|
175
|
+
// NPM-SPECIFIC HELPERS
|
|
176
|
+
// ========================================================================
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get NPM packument (package document)
|
|
180
|
+
*/
|
|
181
|
+
public async getNpmPackument(packageName: string): Promise<any | null> {
|
|
182
|
+
const path = this.getNpmPackumentPath(packageName);
|
|
183
|
+
const data = await this.getObject(path);
|
|
184
|
+
return data ? JSON.parse(data.toString('utf-8')) : null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Store NPM packument
|
|
189
|
+
*/
|
|
190
|
+
public async putNpmPackument(packageName: string, packument: any): Promise<void> {
|
|
191
|
+
const path = this.getNpmPackumentPath(packageName);
|
|
192
|
+
const data = Buffer.from(JSON.stringify(packument, null, 2), 'utf-8');
|
|
193
|
+
return this.putObject(path, data, { 'Content-Type': 'application/json' });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Check if NPM packument exists
|
|
198
|
+
*/
|
|
199
|
+
public async npmPackumentExists(packageName: string): Promise<boolean> {
|
|
200
|
+
const path = this.getNpmPackumentPath(packageName);
|
|
201
|
+
return this.objectExists(path);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Delete NPM packument
|
|
206
|
+
*/
|
|
207
|
+
public async deleteNpmPackument(packageName: string): Promise<void> {
|
|
208
|
+
const path = this.getNpmPackumentPath(packageName);
|
|
209
|
+
return this.deleteObject(path);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get NPM tarball
|
|
214
|
+
*/
|
|
215
|
+
public async getNpmTarball(packageName: string, version: string): Promise<Buffer | null> {
|
|
216
|
+
const path = this.getNpmTarballPath(packageName, version);
|
|
217
|
+
return this.getObject(path);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Store NPM tarball
|
|
222
|
+
*/
|
|
223
|
+
public async putNpmTarball(
|
|
224
|
+
packageName: string,
|
|
225
|
+
version: string,
|
|
226
|
+
tarball: Buffer
|
|
227
|
+
): Promise<void> {
|
|
228
|
+
const path = this.getNpmTarballPath(packageName, version);
|
|
229
|
+
return this.putObject(path, tarball, { 'Content-Type': 'application/octet-stream' });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check if NPM tarball exists
|
|
234
|
+
*/
|
|
235
|
+
public async npmTarballExists(packageName: string, version: string): Promise<boolean> {
|
|
236
|
+
const path = this.getNpmTarballPath(packageName, version);
|
|
237
|
+
return this.objectExists(path);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Delete NPM tarball
|
|
242
|
+
*/
|
|
243
|
+
public async deleteNpmTarball(packageName: string, version: string): Promise<void> {
|
|
244
|
+
const path = this.getNpmTarballPath(packageName, version);
|
|
245
|
+
return this.deleteObject(path);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ========================================================================
|
|
249
|
+
// PATH HELPERS
|
|
250
|
+
// ========================================================================
|
|
251
|
+
|
|
252
|
+
private getOciBlobPath(digest: string): string {
|
|
253
|
+
const hash = digest.split(':')[1];
|
|
254
|
+
return `oci/blobs/sha256/${hash}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private getOciManifestPath(repository: string, digest: string): string {
|
|
258
|
+
const hash = digest.split(':')[1];
|
|
259
|
+
return `oci/manifests/${repository}/${hash}`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private getNpmPackumentPath(packageName: string): string {
|
|
263
|
+
return `npm/packages/${packageName}/index.json`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private getNpmTarballPath(packageName: string, version: string): string {
|
|
267
|
+
const safeName = packageName.replace('@', '').replace('/', '-');
|
|
268
|
+
return `npm/packages/${packageName}/${safeName}-${version}.tgz`;
|
|
269
|
+
}
|
|
270
|
+
}
|
package/ts/core/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core registry infrastructure exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Interfaces
|
|
6
|
+
export * from './interfaces.core.js';
|
|
7
|
+
|
|
8
|
+
// Classes
|
|
9
|
+
export { BaseRegistry } from './classes.baseregistry.js';
|
|
10
|
+
export { RegistryStorage } from './classes.registrystorage.js';
|
|
11
|
+
export { AuthManager } from './classes.authmanager.js';
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core interfaces for the composable registry system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Registry protocol types
|
|
7
|
+
*/
|
|
8
|
+
export type TRegistryProtocol = 'oci' | 'npm';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Unified action types across protocols
|
|
12
|
+
*/
|
|
13
|
+
export type TRegistryAction = 'pull' | 'push' | 'delete' | 'read' | 'write' | '*';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Unified authentication token
|
|
17
|
+
*/
|
|
18
|
+
export interface IAuthToken {
|
|
19
|
+
/** Token type/protocol */
|
|
20
|
+
type: TRegistryProtocol;
|
|
21
|
+
/** User ID */
|
|
22
|
+
userId: string;
|
|
23
|
+
/** Permission scopes (e.g., "npm:package:foo:write", "oci:repository:bar:push") */
|
|
24
|
+
scopes: string[];
|
|
25
|
+
/** Token expiration */
|
|
26
|
+
expiresAt?: Date;
|
|
27
|
+
/** Read-only flag */
|
|
28
|
+
readonly?: boolean;
|
|
29
|
+
/** Additional metadata */
|
|
30
|
+
metadata?: Record<string, any>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Credentials for authentication
|
|
35
|
+
*/
|
|
36
|
+
export interface ICredentials {
|
|
37
|
+
username: string;
|
|
38
|
+
password: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Storage backend configuration
|
|
43
|
+
*/
|
|
44
|
+
export interface IStorageConfig {
|
|
45
|
+
accessKey: string;
|
|
46
|
+
accessSecret: string;
|
|
47
|
+
endpoint: string;
|
|
48
|
+
port?: number;
|
|
49
|
+
useSsl?: boolean;
|
|
50
|
+
region?: string;
|
|
51
|
+
bucketName: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Authentication configuration
|
|
56
|
+
*/
|
|
57
|
+
export interface IAuthConfig {
|
|
58
|
+
/** JWT secret for OCI tokens */
|
|
59
|
+
jwtSecret: string;
|
|
60
|
+
/** Token storage type */
|
|
61
|
+
tokenStore: 'memory' | 'redis' | 'database';
|
|
62
|
+
/** NPM token settings */
|
|
63
|
+
npmTokens: {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
defaultReadonly?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** OCI token settings */
|
|
68
|
+
ociTokens: {
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
realm: string;
|
|
71
|
+
service: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Protocol-specific configuration
|
|
77
|
+
*/
|
|
78
|
+
export interface IProtocolConfig {
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
basePath: string;
|
|
81
|
+
features?: Record<string, boolean>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Main registry configuration
|
|
86
|
+
*/
|
|
87
|
+
export interface IRegistryConfig {
|
|
88
|
+
storage: IStorageConfig;
|
|
89
|
+
auth: IAuthConfig;
|
|
90
|
+
oci?: IProtocolConfig;
|
|
91
|
+
npm?: IProtocolConfig;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Storage backend interface
|
|
96
|
+
*/
|
|
97
|
+
export interface IStorageBackend {
|
|
98
|
+
/**
|
|
99
|
+
* Get an object from storage
|
|
100
|
+
*/
|
|
101
|
+
getObject(key: string): Promise<Buffer | null>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Store an object
|
|
105
|
+
*/
|
|
106
|
+
putObject(key: string, data: Buffer, metadata?: Record<string, string>): Promise<void>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Delete an object
|
|
110
|
+
*/
|
|
111
|
+
deleteObject(key: string): Promise<void>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* List objects with a prefix
|
|
115
|
+
*/
|
|
116
|
+
listObjects(prefix: string): Promise<string[]>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if an object exists
|
|
120
|
+
*/
|
|
121
|
+
objectExists(key: string): Promise<boolean>;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get object metadata
|
|
125
|
+
*/
|
|
126
|
+
getMetadata(key: string): Promise<Record<string, string> | null>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Error response structure
|
|
131
|
+
*/
|
|
132
|
+
export interface IRegistryError {
|
|
133
|
+
errors: Array<{
|
|
134
|
+
code: string;
|
|
135
|
+
message: string;
|
|
136
|
+
detail?: any;
|
|
137
|
+
}>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Base request context
|
|
142
|
+
*/
|
|
143
|
+
export interface IRequestContext {
|
|
144
|
+
method: string;
|
|
145
|
+
path: string;
|
|
146
|
+
headers: Record<string, string>;
|
|
147
|
+
query: Record<string, string>;
|
|
148
|
+
body?: any;
|
|
149
|
+
token?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Base response structure
|
|
154
|
+
*/
|
|
155
|
+
export interface IResponse {
|
|
156
|
+
status: number;
|
|
157
|
+
headers: Record<string, string>;
|
|
158
|
+
body?: any;
|
|
159
|
+
}
|
package/ts/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @push.rocks/smartregistry
|
|
3
|
+
* Composable registry supporting OCI and NPM protocols
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Main orchestrator
|
|
7
|
+
export { SmartRegistry } from './classes.smartregistry.js';
|
|
8
|
+
|
|
9
|
+
// Core infrastructure
|
|
10
|
+
export * from './core/index.js';
|
|
11
|
+
|
|
12
|
+
// OCI Registry
|
|
13
|
+
export * from './oci/index.js';
|
|
14
|
+
|
|
15
|
+
// NPM Registry
|
|
16
|
+
export * from './npm/index.js';
|