@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.
Files changed (43) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/classes.smartregistry.d.ts +2 -2
  3. package/dist_ts/classes.smartregistry.js +37 -2
  4. package/dist_ts/core/classes.authmanager.d.ts +55 -1
  5. package/dist_ts/core/classes.authmanager.js +138 -3
  6. package/dist_ts/core/classes.registrystorage.d.ts +145 -0
  7. package/dist_ts/core/classes.registrystorage.js +392 -1
  8. package/dist_ts/core/interfaces.core.d.ts +13 -1
  9. package/dist_ts/index.d.ts +3 -1
  10. package/dist_ts/index.js +6 -2
  11. package/dist_ts/pypi/classes.pypiregistry.d.ts +70 -0
  12. package/dist_ts/pypi/classes.pypiregistry.js +482 -0
  13. package/dist_ts/pypi/helpers.pypi.d.ts +84 -0
  14. package/dist_ts/pypi/helpers.pypi.js +263 -0
  15. package/dist_ts/pypi/index.d.ts +7 -0
  16. package/dist_ts/pypi/index.js +8 -0
  17. package/dist_ts/pypi/interfaces.pypi.d.ts +301 -0
  18. package/dist_ts/pypi/interfaces.pypi.js +6 -0
  19. package/dist_ts/rubygems/classes.rubygemsregistry.d.ts +86 -0
  20. package/dist_ts/rubygems/classes.rubygemsregistry.js +475 -0
  21. package/dist_ts/rubygems/helpers.rubygems.d.ts +143 -0
  22. package/dist_ts/rubygems/helpers.rubygems.js +312 -0
  23. package/dist_ts/rubygems/index.d.ts +7 -0
  24. package/dist_ts/rubygems/index.js +8 -0
  25. package/dist_ts/rubygems/interfaces.rubygems.d.ts +236 -0
  26. package/dist_ts/rubygems/interfaces.rubygems.js +6 -0
  27. package/package.json +2 -2
  28. package/readme.hints.md +438 -2
  29. package/readme.md +288 -13
  30. package/ts/00_commitinfo_data.ts +2 -2
  31. package/ts/classes.smartregistry.ts +41 -2
  32. package/ts/core/classes.authmanager.ts +161 -2
  33. package/ts/core/classes.registrystorage.ts +463 -0
  34. package/ts/core/interfaces.core.ts +13 -1
  35. package/ts/index.ts +7 -1
  36. package/ts/pypi/classes.pypiregistry.ts +580 -0
  37. package/ts/pypi/helpers.pypi.ts +299 -0
  38. package/ts/pypi/index.ts +8 -0
  39. package/ts/pypi/interfaces.pypi.ts +316 -0
  40. package/ts/rubygems/classes.rubygemsregistry.ts +598 -0
  41. package/ts/rubygems/helpers.rubygems.ts +398 -0
  42. package/ts/rubygems/index.ts +8 -0
  43. package/ts/rubygems/interfaces.rubygems.ts +251 -0
@@ -0,0 +1,475 @@
1
+ import { Smartlog } from '@push.rocks/smartlog';
2
+ import { BaseRegistry } from '../core/classes.baseregistry.js';
3
+ import { RegistryStorage } from '../core/classes.registrystorage.js';
4
+ import { AuthManager } from '../core/classes.authmanager.js';
5
+ import * as helpers from './helpers.rubygems.js';
6
+ /**
7
+ * RubyGems registry implementation
8
+ * Implements Compact Index API and RubyGems protocol
9
+ */
10
+ export class RubyGemsRegistry extends BaseRegistry {
11
+ storage;
12
+ authManager;
13
+ basePath = '/rubygems';
14
+ registryUrl;
15
+ logger;
16
+ constructor(storage, authManager, basePath = '/rubygems', registryUrl = 'http://localhost:5000/rubygems') {
17
+ super();
18
+ this.storage = storage;
19
+ this.authManager = authManager;
20
+ this.basePath = basePath;
21
+ this.registryUrl = registryUrl;
22
+ // Initialize logger
23
+ this.logger = new Smartlog({
24
+ logContext: {
25
+ company: 'push.rocks',
26
+ companyunit: 'smartregistry',
27
+ containerName: 'rubygems-registry',
28
+ environment: process.env.NODE_ENV || 'development',
29
+ runtime: 'node',
30
+ zone: 'rubygems'
31
+ }
32
+ });
33
+ this.logger.enableConsole();
34
+ }
35
+ async init() {
36
+ // Initialize Compact Index files if not exist
37
+ const existingVersions = await this.storage.getRubyGemsVersions();
38
+ if (!existingVersions) {
39
+ const versions = helpers.generateCompactIndexVersions([]);
40
+ await this.storage.putRubyGemsVersions(versions);
41
+ this.logger.log('info', 'Initialized RubyGems Compact Index');
42
+ }
43
+ const existingNames = await this.storage.getRubyGemsNames();
44
+ if (!existingNames) {
45
+ const names = helpers.generateNamesFile([]);
46
+ await this.storage.putRubyGemsNames(names);
47
+ this.logger.log('info', 'Initialized RubyGems names file');
48
+ }
49
+ }
50
+ getBasePath() {
51
+ return this.basePath;
52
+ }
53
+ async handleRequest(context) {
54
+ let path = context.path.replace(this.basePath, '');
55
+ // Extract token (Authorization header)
56
+ const token = await this.extractToken(context);
57
+ this.logger.log('debug', `handleRequest: ${context.method} ${path}`, {
58
+ method: context.method,
59
+ path,
60
+ hasAuth: !!token
61
+ });
62
+ // Compact Index endpoints
63
+ if (path === '/versions' && context.method === 'GET') {
64
+ return this.handleVersionsFile();
65
+ }
66
+ if (path === '/names' && context.method === 'GET') {
67
+ return this.handleNamesFile();
68
+ }
69
+ // Info file: GET /info/{gem}
70
+ const infoMatch = path.match(/^\/info\/([^\/]+)$/);
71
+ if (infoMatch && context.method === 'GET') {
72
+ return this.handleInfoFile(infoMatch[1]);
73
+ }
74
+ // Gem download: GET /gems/{gem}-{version}[-{platform}].gem
75
+ const downloadMatch = path.match(/^\/gems\/(.+\.gem)$/);
76
+ if (downloadMatch && context.method === 'GET') {
77
+ return this.handleDownload(downloadMatch[1]);
78
+ }
79
+ // API v1 endpoints
80
+ if (path.startsWith('/api/v1/')) {
81
+ return this.handleApiRequest(path.substring(8), context, token);
82
+ }
83
+ return {
84
+ status: 404,
85
+ headers: { 'Content-Type': 'application/json' },
86
+ body: Buffer.from(JSON.stringify({ message: 'Not Found' })),
87
+ };
88
+ }
89
+ /**
90
+ * Check if token has permission for resource
91
+ */
92
+ async checkPermission(token, resource, action) {
93
+ if (!token)
94
+ return false;
95
+ return this.authManager.authorize(token, `rubygems:gem:${resource}`, action);
96
+ }
97
+ /**
98
+ * Extract authentication token from request
99
+ */
100
+ async extractToken(context) {
101
+ const authHeader = context.headers['authorization'] || context.headers['Authorization'];
102
+ if (!authHeader)
103
+ return null;
104
+ // RubyGems typically uses plain API key in Authorization header
105
+ return this.authManager.validateToken(authHeader, 'rubygems');
106
+ }
107
+ /**
108
+ * Handle /versions endpoint (Compact Index)
109
+ */
110
+ async handleVersionsFile() {
111
+ const content = await this.storage.getRubyGemsVersions();
112
+ if (!content) {
113
+ return this.errorResponse(500, 'Versions file not initialized');
114
+ }
115
+ return {
116
+ status: 200,
117
+ headers: {
118
+ 'Content-Type': 'text/plain; charset=utf-8',
119
+ 'Cache-Control': 'public, max-age=60',
120
+ 'ETag': `"${await helpers.calculateMD5(content)}"`
121
+ },
122
+ body: Buffer.from(content),
123
+ };
124
+ }
125
+ /**
126
+ * Handle /names endpoint (Compact Index)
127
+ */
128
+ async handleNamesFile() {
129
+ const content = await this.storage.getRubyGemsNames();
130
+ if (!content) {
131
+ return this.errorResponse(500, 'Names file not initialized');
132
+ }
133
+ return {
134
+ status: 200,
135
+ headers: {
136
+ 'Content-Type': 'text/plain; charset=utf-8',
137
+ 'Cache-Control': 'public, max-age=300'
138
+ },
139
+ body: Buffer.from(content),
140
+ };
141
+ }
142
+ /**
143
+ * Handle /info/{gem} endpoint (Compact Index)
144
+ */
145
+ async handleInfoFile(gemName) {
146
+ const content = await this.storage.getRubyGemsInfo(gemName);
147
+ if (!content) {
148
+ return {
149
+ status: 404,
150
+ headers: { 'Content-Type': 'text/plain' },
151
+ body: Buffer.from('Not Found'),
152
+ };
153
+ }
154
+ return {
155
+ status: 200,
156
+ headers: {
157
+ 'Content-Type': 'text/plain; charset=utf-8',
158
+ 'Cache-Control': 'public, max-age=300',
159
+ 'ETag': `"${await helpers.calculateMD5(content)}"`
160
+ },
161
+ body: Buffer.from(content),
162
+ };
163
+ }
164
+ /**
165
+ * Handle gem file download
166
+ */
167
+ async handleDownload(filename) {
168
+ const parsed = helpers.parseGemFilename(filename);
169
+ if (!parsed) {
170
+ return this.errorResponse(400, 'Invalid gem filename');
171
+ }
172
+ const gemData = await this.storage.getRubyGemsGem(parsed.name, parsed.version, parsed.platform);
173
+ if (!gemData) {
174
+ return this.errorResponse(404, 'Gem not found');
175
+ }
176
+ return {
177
+ status: 200,
178
+ headers: {
179
+ 'Content-Type': 'application/octet-stream',
180
+ 'Content-Disposition': `attachment; filename="${filename}"`,
181
+ 'Content-Length': gemData.length.toString()
182
+ },
183
+ body: gemData,
184
+ };
185
+ }
186
+ /**
187
+ * Handle API v1 requests
188
+ */
189
+ async handleApiRequest(path, context, token) {
190
+ // Upload gem: POST /gems
191
+ if (path === '/gems' && context.method === 'POST') {
192
+ return this.handleUpload(context, token);
193
+ }
194
+ // Yank gem: DELETE /gems/yank
195
+ if (path === '/gems/yank' && context.method === 'DELETE') {
196
+ return this.handleYank(context, token);
197
+ }
198
+ // Unyank gem: PUT /gems/unyank
199
+ if (path === '/gems/unyank' && context.method === 'PUT') {
200
+ return this.handleUnyank(context, token);
201
+ }
202
+ // Version list: GET /versions/{gem}.json
203
+ const versionsMatch = path.match(/^\/versions\/([^\/]+)\.json$/);
204
+ if (versionsMatch && context.method === 'GET') {
205
+ return this.handleVersionsJson(versionsMatch[1]);
206
+ }
207
+ // Dependencies: GET /dependencies?gems={list}
208
+ if (path.startsWith('/dependencies') && context.method === 'GET') {
209
+ const gemsParam = context.query?.gems || '';
210
+ return this.handleDependencies(gemsParam);
211
+ }
212
+ return this.errorResponse(404, 'API endpoint not found');
213
+ }
214
+ /**
215
+ * Handle gem upload
216
+ * POST /api/v1/gems
217
+ */
218
+ async handleUpload(context, token) {
219
+ if (!token) {
220
+ return this.errorResponse(401, 'Authentication required');
221
+ }
222
+ try {
223
+ // Extract gem data from request body
224
+ const gemData = context.body;
225
+ if (!gemData || gemData.length === 0) {
226
+ return this.errorResponse(400, 'No gem file provided');
227
+ }
228
+ // For now, we expect metadata in query params or headers
229
+ // Full implementation would parse .gem file (tar + gzip + Marshal)
230
+ const gemName = context.query?.name || context.headers['x-gem-name'];
231
+ const version = context.query?.version || context.headers['x-gem-version'];
232
+ const platform = context.query?.platform || context.headers['x-gem-platform'];
233
+ if (!gemName || !version) {
234
+ return this.errorResponse(400, 'Gem name and version required');
235
+ }
236
+ // Validate gem name
237
+ if (!helpers.isValidGemName(gemName)) {
238
+ return this.errorResponse(400, 'Invalid gem name');
239
+ }
240
+ // Check permission
241
+ if (!(await this.checkPermission(token, gemName, 'write'))) {
242
+ return this.errorResponse(403, 'Insufficient permissions');
243
+ }
244
+ // Calculate checksum
245
+ const checksum = await helpers.calculateSHA256(gemData);
246
+ // Store gem file
247
+ await this.storage.putRubyGemsGem(gemName, version, gemData, platform);
248
+ // Update metadata
249
+ let metadata = await this.storage.getRubyGemsMetadata(gemName) || {
250
+ name: gemName,
251
+ versions: {},
252
+ };
253
+ const versionKey = platform ? `${version}-${platform}` : version;
254
+ metadata.versions[versionKey] = {
255
+ version,
256
+ platform,
257
+ checksum,
258
+ size: gemData.length,
259
+ 'upload-time': new Date().toISOString(),
260
+ 'uploaded-by': token.userId,
261
+ dependencies: [], // Would extract from gem spec
262
+ requirements: [],
263
+ };
264
+ metadata['last-modified'] = new Date().toISOString();
265
+ await this.storage.putRubyGemsMetadata(gemName, metadata);
266
+ // Update Compact Index info file
267
+ await this.updateCompactIndexForGem(gemName, metadata);
268
+ // Update versions file
269
+ await this.updateVersionsFile(gemName, version, platform || 'ruby', false);
270
+ // Update names file
271
+ await this.updateNamesFile(gemName);
272
+ this.logger.log('info', `Gem uploaded: ${gemName} ${version}`, {
273
+ platform,
274
+ size: gemData.length
275
+ });
276
+ return {
277
+ status: 200,
278
+ headers: { 'Content-Type': 'application/json' },
279
+ body: Buffer.from(JSON.stringify({
280
+ message: 'Gem uploaded successfully',
281
+ name: gemName,
282
+ version,
283
+ })),
284
+ };
285
+ }
286
+ catch (error) {
287
+ this.logger.log('error', 'Upload failed', { error: error.message });
288
+ return this.errorResponse(500, 'Upload failed: ' + error.message);
289
+ }
290
+ }
291
+ /**
292
+ * Handle gem yanking
293
+ * DELETE /api/v1/gems/yank
294
+ */
295
+ async handleYank(context, token) {
296
+ if (!token) {
297
+ return this.errorResponse(401, 'Authentication required');
298
+ }
299
+ const gemName = context.query?.gem_name;
300
+ const version = context.query?.version;
301
+ const platform = context.query?.platform;
302
+ if (!gemName || !version) {
303
+ return this.errorResponse(400, 'Gem name and version required');
304
+ }
305
+ if (!(await this.checkPermission(token, gemName, 'yank'))) {
306
+ return this.errorResponse(403, 'Insufficient permissions');
307
+ }
308
+ // Update metadata to mark as yanked
309
+ const metadata = await this.storage.getRubyGemsMetadata(gemName);
310
+ if (!metadata) {
311
+ return this.errorResponse(404, 'Gem not found');
312
+ }
313
+ const versionKey = platform ? `${version}-${platform}` : version;
314
+ if (!metadata.versions[versionKey]) {
315
+ return this.errorResponse(404, 'Version not found');
316
+ }
317
+ metadata.versions[versionKey].yanked = true;
318
+ await this.storage.putRubyGemsMetadata(gemName, metadata);
319
+ // Update Compact Index
320
+ await this.updateCompactIndexForGem(gemName, metadata);
321
+ await this.updateVersionsFile(gemName, version, platform || 'ruby', true);
322
+ this.logger.log('info', `Gem yanked: ${gemName} ${version}`);
323
+ return {
324
+ status: 200,
325
+ headers: { 'Content-Type': 'application/json' },
326
+ body: Buffer.from(JSON.stringify({
327
+ success: true,
328
+ message: 'Gem yanked successfully'
329
+ })),
330
+ };
331
+ }
332
+ /**
333
+ * Handle gem unyanking
334
+ * PUT /api/v1/gems/unyank
335
+ */
336
+ async handleUnyank(context, token) {
337
+ if (!token) {
338
+ return this.errorResponse(401, 'Authentication required');
339
+ }
340
+ const gemName = context.query?.gem_name;
341
+ const version = context.query?.version;
342
+ const platform = context.query?.platform;
343
+ if (!gemName || !version) {
344
+ return this.errorResponse(400, 'Gem name and version required');
345
+ }
346
+ if (!(await this.checkPermission(token, gemName, 'write'))) {
347
+ return this.errorResponse(403, 'Insufficient permissions');
348
+ }
349
+ const metadata = await this.storage.getRubyGemsMetadata(gemName);
350
+ if (!metadata) {
351
+ return this.errorResponse(404, 'Gem not found');
352
+ }
353
+ const versionKey = platform ? `${version}-${platform}` : version;
354
+ if (!metadata.versions[versionKey]) {
355
+ return this.errorResponse(404, 'Version not found');
356
+ }
357
+ metadata.versions[versionKey].yanked = false;
358
+ await this.storage.putRubyGemsMetadata(gemName, metadata);
359
+ // Update Compact Index
360
+ await this.updateCompactIndexForGem(gemName, metadata);
361
+ await this.updateVersionsFile(gemName, version, platform || 'ruby', false);
362
+ this.logger.log('info', `Gem unyanked: ${gemName} ${version}`);
363
+ return {
364
+ status: 200,
365
+ headers: { 'Content-Type': 'application/json' },
366
+ body: Buffer.from(JSON.stringify({
367
+ success: true,
368
+ message: 'Gem unyanked successfully'
369
+ })),
370
+ };
371
+ }
372
+ /**
373
+ * Handle versions JSON API
374
+ */
375
+ async handleVersionsJson(gemName) {
376
+ const metadata = await this.storage.getRubyGemsMetadata(gemName);
377
+ if (!metadata) {
378
+ return this.errorResponse(404, 'Gem not found');
379
+ }
380
+ const versions = Object.values(metadata.versions).map((v) => ({
381
+ version: v.version,
382
+ platform: v.platform,
383
+ uploadTime: v['upload-time'],
384
+ }));
385
+ const response = helpers.generateVersionsJson(gemName, versions);
386
+ return {
387
+ status: 200,
388
+ headers: {
389
+ 'Content-Type': 'application/json',
390
+ 'Cache-Control': 'public, max-age=300'
391
+ },
392
+ body: Buffer.from(JSON.stringify(response)),
393
+ };
394
+ }
395
+ /**
396
+ * Handle dependencies query
397
+ */
398
+ async handleDependencies(gemsParam) {
399
+ const gemNames = gemsParam.split(',').filter(n => n.trim());
400
+ const result = new Map();
401
+ for (const gemName of gemNames) {
402
+ const metadata = await this.storage.getRubyGemsMetadata(gemName);
403
+ if (metadata) {
404
+ const versions = Object.values(metadata.versions).map((v) => ({
405
+ version: v.version,
406
+ platform: v.platform,
407
+ dependencies: v.dependencies || [],
408
+ }));
409
+ result.set(gemName, versions);
410
+ }
411
+ }
412
+ const response = helpers.generateDependenciesJson(result);
413
+ return {
414
+ status: 200,
415
+ headers: { 'Content-Type': 'application/json' },
416
+ body: Buffer.from(JSON.stringify(response)),
417
+ };
418
+ }
419
+ /**
420
+ * Update Compact Index info file for a gem
421
+ */
422
+ async updateCompactIndexForGem(gemName, metadata) {
423
+ const entries = Object.values(metadata.versions)
424
+ .filter(v => !v.yanked) // Exclude yanked from info file
425
+ .map(v => ({
426
+ version: v.version,
427
+ platform: v.platform,
428
+ dependencies: v.dependencies || [],
429
+ requirements: v.requirements || [],
430
+ checksum: v.checksum,
431
+ }));
432
+ const content = helpers.generateCompactIndexInfo(entries);
433
+ await this.storage.putRubyGemsInfo(gemName, content);
434
+ }
435
+ /**
436
+ * Update versions file with new/updated gem
437
+ */
438
+ async updateVersionsFile(gemName, version, platform, yanked) {
439
+ const existingVersions = await this.storage.getRubyGemsVersions();
440
+ if (!existingVersions)
441
+ return;
442
+ // Calculate info file checksum
443
+ const infoContent = await this.storage.getRubyGemsInfo(gemName) || '';
444
+ const infoChecksum = await helpers.calculateMD5(infoContent);
445
+ const updated = helpers.updateCompactIndexVersions(existingVersions, gemName, { version, platform: platform !== 'ruby' ? platform : undefined, yanked }, infoChecksum);
446
+ await this.storage.putRubyGemsVersions(updated);
447
+ }
448
+ /**
449
+ * Update names file with new gem
450
+ */
451
+ async updateNamesFile(gemName) {
452
+ const existingNames = await this.storage.getRubyGemsNames();
453
+ if (!existingNames)
454
+ return;
455
+ const lines = existingNames.split('\n').filter(l => l !== '---');
456
+ if (!lines.includes(gemName)) {
457
+ lines.push(gemName);
458
+ lines.sort();
459
+ const updated = helpers.generateNamesFile(lines);
460
+ await this.storage.putRubyGemsNames(updated);
461
+ }
462
+ }
463
+ /**
464
+ * Helper: Create error response
465
+ */
466
+ errorResponse(status, message) {
467
+ const error = { message, status };
468
+ return {
469
+ status,
470
+ headers: { 'Content-Type': 'application/json' },
471
+ body: Buffer.from(JSON.stringify(error)),
472
+ };
473
+ }
474
+ }
475
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydWJ5Z2Vtc3JlZ2lzdHJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcnVieWdlbXMvY2xhc3Nlcy5ydWJ5Z2Vtc3JlZ2lzdHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQVU3RCxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBRWpEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxZQUFZO0lBQ3hDLE9BQU8sQ0FBa0I7SUFDekIsV0FBVyxDQUFjO0lBQ3pCLFFBQVEsR0FBVyxXQUFXLENBQUM7SUFDL0IsV0FBVyxDQUFTO0lBQ3BCLE1BQU0sQ0FBVztJQUV6QixZQUNFLE9BQXdCLEVBQ3hCLFdBQXdCLEVBQ3hCLFdBQW1CLFdBQVcsRUFDOUIsY0FBc0IsZ0NBQWdDO1FBRXRELEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFDL0IsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFFL0Isb0JBQW9CO1FBQ3BCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxRQUFRLENBQUM7WUFDekIsVUFBVSxFQUFFO2dCQUNWLE9BQU8sRUFBRSxZQUFZO2dCQUNyQixXQUFXLEVBQUUsZUFBZTtnQkFDNUIsYUFBYSxFQUFFLG1CQUFtQjtnQkFDbEMsV0FBVyxFQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBZ0IsSUFBSSxhQUFhO2dCQUMzRCxPQUFPLEVBQUUsTUFBTTtnQkFDZixJQUFJLEVBQUUsVUFBVTthQUNqQjtTQUNGLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDOUIsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFJO1FBQ2YsOENBQThDO1FBQzlDLE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixFQUFFLENBQUM7UUFDbEUsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDdEIsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLDRCQUE0QixDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzFELE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNqRCxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsb0NBQW9DLENBQUMsQ0FBQztRQUNoRSxDQUFDO1FBRUQsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDNUQsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ25CLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM1QyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDM0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGlDQUFpQyxDQUFDLENBQUM7UUFDN0QsQ0FBQztJQUNILENBQUM7SUFFTSxXQUFXO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRU0sS0FBSyxDQUFDLGFBQWEsQ0FBQyxPQUF3QjtRQUNqRCxJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRW5ELHVDQUF1QztRQUN2QyxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFL0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGtCQUFrQixPQUFPLENBQUMsTUFBTSxJQUFJLElBQUksRUFBRSxFQUFFO1lBQ25FLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtZQUN0QixJQUFJO1lBQ0osT0FBTyxFQUFFLENBQUMsQ0FBQyxLQUFLO1NBQ2pCLENBQUMsQ0FBQztRQUVILDBCQUEwQjtRQUMxQixJQUFJLElBQUksS0FBSyxXQUFXLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUNyRCxPQUFPLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQ25DLENBQUM7UUFFRCxJQUFJLElBQUksS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUNsRCxPQUFPLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUNoQyxDQUFDO1FBRUQsNkJBQTZCO1FBQzdCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUNuRCxJQUFJLFNBQVMsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQzFDLE9BQU8sSUFBSSxDQUFDLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzQyxDQUFDO1FBRUQsMkRBQTJEO1FBQzNELE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUN4RCxJQUFJLGFBQWEsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQzlDLE9BQU8sSUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQyxDQUFDO1FBRUQsbUJBQW1CO1FBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxPQUFPO1lBQ0wsTUFBTSxFQUFFLEdBQUc7WUFDWCxPQUFPLEVBQUUsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUU7WUFDL0MsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDO1NBQzVELENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDTyxLQUFLLENBQUMsZUFBZSxDQUM3QixLQUF3QixFQUN4QixRQUFnQixFQUNoQixNQUFjO1FBRWQsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUN6QixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsUUFBUSxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLFlBQVksQ0FBQyxPQUF3QjtRQUNqRCxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDeEYsSUFBSSxDQUFDLFVBQVU7WUFBRSxPQUFPLElBQUksQ0FBQztRQUU3QixnRUFBZ0U7UUFDaEUsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxVQUFVLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGtCQUFrQjtRQUM5QixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUV6RCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLCtCQUErQixDQUFDLENBQUM7UUFDbEUsQ0FBQztRQUVELE9BQU87WUFDTCxNQUFNLEVBQUUsR0FBRztZQUNYLE9BQU8sRUFBRTtnQkFDUCxjQUFjLEVBQUUsMkJBQTJCO2dCQUMzQyxlQUFlLEVBQUUsb0JBQW9CO2dCQUNyQyxNQUFNLEVBQUUsSUFBSSxNQUFNLE9BQU8sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLEdBQUc7YUFDbkQ7WUFDRCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7U0FDM0IsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxlQUFlO1FBQzNCLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBRXRELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsNEJBQTRCLENBQUMsQ0FBQztRQUMvRCxDQUFDO1FBRUQsT0FBTztZQUNMLE1BQU0sRUFBRSxHQUFHO1lBQ1gsT0FBTyxFQUFFO2dCQUNQLGNBQWMsRUFBRSwyQkFBMkI7Z0JBQzNDLGVBQWUsRUFBRSxxQkFBcUI7YUFDdkM7WUFDRCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7U0FDM0IsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxjQUFjLENBQUMsT0FBZTtRQUMxQyxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTVELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU87Z0JBQ0wsTUFBTSxFQUFFLEdBQUc7Z0JBQ1gsT0FBTyxFQUFFLEVBQUUsY0FBYyxFQUFFLFlBQVksRUFBRTtnQkFDekMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDO2FBQy9CLENBQUM7UUFDSixDQUFDO1FBRUQsT0FBTztZQUNMLE1BQU0sRUFBRSxHQUFHO1lBQ1gsT0FBTyxFQUFFO2dCQUNQLGNBQWMsRUFBRSwyQkFBMkI7Z0JBQzNDLGVBQWUsRUFBRSxxQkFBcUI7Z0JBQ3RDLE1BQU0sRUFBRSxJQUFJLE1BQU0sT0FBTyxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsR0FBRzthQUNuRDtZQUNELElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztTQUMzQixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGNBQWMsQ0FBQyxRQUFnQjtRQUMzQyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ1osT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO1FBQ3pELENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUMvQyxNQUFNLENBQUMsSUFBSSxFQUNYLE1BQU0sQ0FBQyxPQUFPLEVBQ2QsTUFBTSxDQUFDLFFBQVEsQ0FDaEIsQ0FBQztRQUVGLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUVELE9BQU87WUFDTCxNQUFNLEVBQUUsR0FBRztZQUNYLE9BQU8sRUFBRTtnQkFDUCxjQUFjLEVBQUUsMEJBQTBCO2dCQUMxQyxxQkFBcUIsRUFBRSx5QkFBeUIsUUFBUSxHQUFHO2dCQUMzRCxnQkFBZ0IsRUFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRTthQUM1QztZQUNELElBQUksRUFBRSxPQUFPO1NBQ2QsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxnQkFBZ0IsQ0FDNUIsSUFBWSxFQUNaLE9BQXdCLEVBQ3hCLEtBQXdCO1FBRXhCLHlCQUF5QjtRQUN6QixJQUFJLElBQUksS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNsRCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzNDLENBQUM7UUFFRCw4QkFBOEI7UUFDOUIsSUFBSSxJQUFJLEtBQUssWUFBWSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDekQsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBRUQsK0JBQStCO1FBQy9CLElBQUksSUFBSSxLQUFLLGNBQWMsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3hELE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDM0MsQ0FBQztRQUVELHlDQUF5QztRQUN6QyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFDakUsSUFBSSxhQUFhLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUM5QyxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBRUQsOENBQThDO1FBQzlDLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxlQUFlLENBQUMsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ2pFLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxLQUFLLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUM1QyxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM1QyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSx3QkFBd0IsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFRDs7O09BR0c7SUFDSyxLQUFLLENBQUMsWUFBWSxDQUFDLE9BQXdCLEVBQUUsS0FBd0I7UUFDM0UsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCxxQ0FBcUM7WUFDckMsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQWMsQ0FBQztZQUN2QyxJQUFJLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3JDLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztZQUN6RCxDQUFDO1lBRUQseURBQXlEO1lBQ3pELG1FQUFtRTtZQUNuRSxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQ3JFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxLQUFLLEVBQUUsT0FBTyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7WUFDM0UsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLEtBQUssRUFBRSxRQUFRLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1lBRTlFLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDekIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSwrQkFBK0IsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFFRCxvQkFBb0I7WUFDcEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDckMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1lBQ3JELENBQUM7WUFFRCxtQkFBbUI7WUFDbkIsSUFBSSxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUMzRCxPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLDBCQUEwQixDQUFDLENBQUM7WUFDN0QsQ0FBQztZQUVELHFCQUFxQjtZQUNyQixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFeEQsaUJBQWlCO1lBQ2pCLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFFdkUsa0JBQWtCO1lBQ2xCLElBQUksUUFBUSxHQUFzQixNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLElBQUk7Z0JBQ25GLElBQUksRUFBRSxPQUFPO2dCQUNiLFFBQVEsRUFBRSxFQUFFO2FBQ2IsQ0FBQztZQUVGLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLElBQUksUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztZQUNqRSxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxHQUFHO2dCQUM5QixPQUFPO2dCQUNQLFFBQVE7Z0JBQ1IsUUFBUTtnQkFDUixJQUFJLEVBQUUsT0FBTyxDQUFDLE1BQU07Z0JBQ3BCLGFBQWEsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtnQkFDdkMsYUFBYSxFQUFFLEtBQUssQ0FBQyxNQUFNO2dCQUMzQixZQUFZLEVBQUUsRUFBRSxFQUFFLDhCQUE4QjtnQkFDaEQsWUFBWSxFQUFFLEVBQUU7YUFDakIsQ0FBQztZQUVGLFFBQVEsQ0FBQyxlQUFlLENBQUMsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3JELE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFFMUQsaUNBQWlDO1lBQ2pDLE1BQU0sSUFBSSxDQUFDLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztZQUV2RCx1QkFBdUI7WUFDdkIsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLElBQUksTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBRTNFLG9CQUFvQjtZQUNwQixNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFcEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGlCQUFpQixPQUFPLElBQUksT0FBTyxFQUFFLEVBQUU7Z0JBQzdELFFBQVE7Z0JBQ1IsSUFBSSxFQUFFLE9BQU8sQ0FBQyxNQUFNO2FBQ3JCLENBQUMsQ0FBQztZQUVILE9BQU87Z0JBQ0wsTUFBTSxFQUFFLEdBQUc7Z0JBQ1gsT0FBTyxFQUFFLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFO2dCQUMvQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO29CQUMvQixPQUFPLEVBQUUsMkJBQTJCO29CQUNwQyxJQUFJLEVBQUUsT0FBTztvQkFDYixPQUFPO2lCQUNSLENBQUMsQ0FBQzthQUNKLENBQUM7UUFDSixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxlQUFlLEVBQUUsRUFBRSxLQUFLLEVBQUcsS0FBZSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDL0UsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxpQkFBaUIsR0FBSSxLQUFlLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0UsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSyxLQUFLLENBQUMsVUFBVSxDQUFDLE9BQXdCLEVBQUUsS0FBd0I7UUFDekUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQztRQUN4QyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQztRQUN2QyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQztRQUV6QyxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDekIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSwrQkFBK0IsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDMUQsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSwwQkFBMEIsQ0FBQyxDQUFDO1FBQzdELENBQUM7UUFFRCxvQ0FBb0M7UUFDcEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2pFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNkLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUVELE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLElBQUksUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUNqRSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ25DLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztRQUN0RCxDQUFDO1FBRUQsUUFBUSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBQzVDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFFMUQsdUJBQXVCO1FBQ3ZCLE1BQU0sSUFBSSxDQUFDLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN2RCxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLFFBQVEsSUFBSSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFMUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGVBQWUsT0FBTyxJQUFJLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFN0QsT0FBTztZQUNMLE1BQU0sRUFBRSxHQUFHO1lBQ1gsT0FBTyxFQUFFLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFO1lBQy9DLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7Z0JBQy9CLE9BQU8sRUFBRSxJQUFJO2dCQUNiLE9BQU8sRUFBRSx5QkFBeUI7YUFDbkMsQ0FBQyxDQUFDO1NBQ0osQ0FBQztJQUNKLENBQUM7SUFFRDs7O09BR0c7SUFDSyxLQUFLLENBQUMsWUFBWSxDQUFDLE9BQXdCLEVBQUUsS0FBd0I7UUFDM0UsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQztRQUN4QyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQztRQUN2QyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQztRQUV6QyxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDekIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSwrQkFBK0IsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDM0QsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSwwQkFBMEIsQ0FBQyxDQUFDO1FBQzdELENBQUM7UUFFRCxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2QsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUNsRCxDQUFDO1FBRUQsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sSUFBSSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDO1FBQ2pFLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFDbkMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1FBQ3RELENBQUM7UUFFRCxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7UUFDN0MsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztRQUUxRCx1QkFBdUI7UUFDdkIsTUFBTSxJQUFJLENBQUMsd0JBQXdCLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3ZELE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxJQUFJLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUUzRSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsaUJBQWlCLE9BQU8sSUFBSSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBRS9ELE9BQU87WUFDTCxNQUFNLEVBQUUsR0FBRztZQUNYLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRTtZQUMvQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO2dCQUMvQixPQUFPLEVBQUUsSUFBSTtnQkFDYixPQUFPLEVBQUUsMkJBQTJCO2FBQ3JDLENBQUMsQ0FBQztTQUNKLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsa0JBQWtCLENBQUMsT0FBZTtRQUM5QyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2QsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUNsRCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ2pFLE9BQU8sRUFBRSxDQUFDLENBQUMsT0FBTztZQUNsQixRQUFRLEVBQUUsQ0FBQyxDQUFDLFFBQVE7WUFDcEIsVUFBVSxFQUFFLENBQUMsQ0FBQyxhQUFhLENBQUM7U0FDN0IsQ0FBQyxDQUFDLENBQUM7UUFFSixNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBRWpFLE9BQU87WUFDTCxNQUFNLEVBQUUsR0FBRztZQUNYLE9BQU8sRUFBRTtnQkFDUCxjQUFjLEVBQUUsa0JBQWtCO2dCQUNsQyxlQUFlLEVBQUUscUJBQXFCO2FBQ3ZDO1lBQ0QsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztTQUM1QyxDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGtCQUFrQixDQUFDLFNBQWlCO1FBQ2hELE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDNUQsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV6QixLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQy9CLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNqRSxJQUFJLFFBQVEsRUFBRSxDQUFDO2dCQUNiLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztvQkFDakUsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO29CQUNsQixRQUFRLEVBQUUsQ0FBQyxDQUFDLFFBQVE7b0JBQ3BCLFlBQVksRUFBRSxDQUFDLENBQUMsWUFBWSxJQUFJLEVBQUU7aUJBQ25DLENBQUMsQ0FBQyxDQUFDO2dCQUNKLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ2hDLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTFELE9BQU87WUFDTCxNQUFNLEVBQUUsR0FBRztZQUNYLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRTtZQUMvQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1NBQzVDLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsd0JBQXdCLENBQ3BDLE9BQWUsRUFDZixRQUEyQjtRQUUzQixNQUFNLE9BQU8sR0FBNkIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO2FBQ3ZFLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLGdDQUFnQzthQUN2RCxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ1QsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO1lBQ2xCLFFBQVEsRUFBRSxDQUFDLENBQUMsUUFBUTtZQUNwQixZQUFZLEVBQUUsQ0FBQyxDQUFDLFlBQVksSUFBSSxFQUFFO1lBQ2xDLFlBQVksRUFBRSxDQUFDLENBQUMsWUFBWSxJQUFJLEVBQUU7WUFDbEMsUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRO1NBQ3JCLENBQUMsQ0FBQyxDQUFDO1FBRU4sTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLHdCQUF3QixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzFELE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxrQkFBa0IsQ0FDOUIsT0FBZSxFQUNmLE9BQWUsRUFDZixRQUFnQixFQUNoQixNQUFlO1FBRWYsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUNsRSxJQUFJLENBQUMsZ0JBQWdCO1lBQUUsT0FBTztRQUU5QiwrQkFBK0I7UUFDL0IsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdEUsTUFBTSxZQUFZLEdBQUcsTUFBTSxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRTdELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQywwQkFBMEIsQ0FDaEQsZ0JBQWdCLEVBQ2hCLE9BQU8sRUFDUCxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLEVBQ3pFLFlBQVksQ0FDYixDQUFDO1FBRUYsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxlQUFlLENBQUMsT0FBZTtRQUMzQyxNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM1RCxJQUFJLENBQUMsYUFBYTtZQUFFLE9BQU87UUFFM0IsTUFBTSxLQUFLLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssS0FBSyxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUM3QixLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3BCLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNiLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNqRCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0MsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLGFBQWEsQ0FBQyxNQUFjLEVBQUUsT0FBZTtRQUNuRCxNQUFNLEtBQUssR0FBbUIsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDbEQsT0FBTztZQUNMLE1BQU07WUFDTixPQUFPLEVBQUUsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUU7WUFDL0MsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUN6QyxDQUFDO0lBQ0osQ0FBQztDQUNGIn0=
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Helper functions for RubyGems registry
3
+ * Compact Index generation, dependency formatting, etc.
4
+ */
5
+ import type { IRubyGemsDependency, ICompactIndexVersionsEntry, ICompactIndexInfoEntry } from './interfaces.rubygems.js';
6
+ /**
7
+ * Generate Compact Index versions file
8
+ * Format: GEMNAME [-]VERSION_PLATFORM[,VERSION_PLATFORM,...] MD5
9
+ * @param entries - Version entries for all gems
10
+ * @returns Compact Index versions file content
11
+ */
12
+ export declare function generateCompactIndexVersions(entries: ICompactIndexVersionsEntry[]): string;
13
+ /**
14
+ * Generate Compact Index info file for a gem
15
+ * Format: VERSION[-PLATFORM] [DEP[,DEP,...]]|REQ[,REQ,...]
16
+ * @param entries - Info entries for gem versions
17
+ * @returns Compact Index info file content
18
+ */
19
+ export declare function generateCompactIndexInfo(entries: ICompactIndexInfoEntry[]): string;
20
+ /**
21
+ * Format a dependency for Compact Index
22
+ * Format: GEM:CONSTRAINT[&CONSTRAINT]
23
+ * @param dep - Dependency object
24
+ * @returns Formatted dependency string
25
+ */
26
+ export declare function formatDependency(dep: IRubyGemsDependency): string;
27
+ /**
28
+ * Parse dependency string from Compact Index
29
+ * @param depStr - Dependency string
30
+ * @returns Dependency object
31
+ */
32
+ export declare function parseDependency(depStr: string): IRubyGemsDependency;
33
+ /**
34
+ * Generate names file (newline-separated gem names)
35
+ * @param names - List of gem names
36
+ * @returns Names file content
37
+ */
38
+ export declare function generateNamesFile(names: string[]): string;
39
+ /**
40
+ * Calculate MD5 hash for Compact Index checksum
41
+ * @param content - Content to hash
42
+ * @returns MD5 hash (hex)
43
+ */
44
+ export declare function calculateMD5(content: string): Promise<string>;
45
+ /**
46
+ * Calculate SHA256 hash for gem files
47
+ * @param data - Data to hash
48
+ * @returns SHA256 hash (hex)
49
+ */
50
+ export declare function calculateSHA256(data: Buffer): Promise<string>;
51
+ /**
52
+ * Parse gem filename to extract name, version, and platform
53
+ * @param filename - Gem filename (e.g., "rails-7.0.0-x86_64-linux.gem")
54
+ * @returns Parsed info or null
55
+ */
56
+ export declare function parseGemFilename(filename: string): {
57
+ name: string;
58
+ version: string;
59
+ platform?: string;
60
+ } | null;
61
+ /**
62
+ * Validate gem name
63
+ * Must contain only ASCII letters, numbers, _, and -
64
+ * @param name - Gem name
65
+ * @returns true if valid
66
+ */
67
+ export declare function isValidGemName(name: string): boolean;
68
+ /**
69
+ * Validate version string
70
+ * Basic semantic versioning check
71
+ * @param version - Version string
72
+ * @returns true if valid
73
+ */
74
+ export declare function isValidVersion(version: string): boolean;
75
+ /**
76
+ * Build version list entry for Compact Index
77
+ * @param versions - Version info
78
+ * @returns Version list string
79
+ */
80
+ export declare function buildVersionList(versions: Array<{
81
+ version: string;
82
+ platform?: string;
83
+ yanked: boolean;
84
+ }>): string;
85
+ /**
86
+ * Parse version list from Compact Index
87
+ * @param versionStr - Version list string
88
+ * @returns Parsed versions
89
+ */
90
+ export declare function parseVersionList(versionStr: string): Array<{
91
+ version: string;
92
+ platform?: string;
93
+ yanked: boolean;
94
+ }>;
95
+ /**
96
+ * Generate JSON response for /api/v1/versions/{gem}.json
97
+ * @param gemName - Gem name
98
+ * @param versions - Version list
99
+ * @returns JSON response object
100
+ */
101
+ export declare function generateVersionsJson(gemName: string, versions: Array<{
102
+ version: string;
103
+ platform?: string;
104
+ uploadTime?: string;
105
+ }>): any;
106
+ /**
107
+ * Generate JSON response for /api/v1/dependencies
108
+ * @param gems - Map of gem names to version dependencies
109
+ * @returns JSON response array
110
+ */
111
+ export declare function generateDependenciesJson(gems: Map<string, Array<{
112
+ version: string;
113
+ platform?: string;
114
+ dependencies: IRubyGemsDependency[];
115
+ }>>): any;
116
+ /**
117
+ * Update Compact Index versions file with new gem version
118
+ * Handles append-only semantics for the current month
119
+ * @param existingContent - Current versions file content
120
+ * @param gemName - Gem name
121
+ * @param newVersion - New version info
122
+ * @param infoChecksum - MD5 of info file
123
+ * @returns Updated versions file content
124
+ */
125
+ export declare function updateCompactIndexVersions(existingContent: string, gemName: string, newVersion: {
126
+ version: string;
127
+ platform?: string;
128
+ yanked: boolean;
129
+ }, infoChecksum: string): string;
130
+ /**
131
+ * Update Compact Index info file with new version
132
+ * @param existingContent - Current info file content
133
+ * @param newEntry - New version entry
134
+ * @returns Updated info file content
135
+ */
136
+ export declare function updateCompactIndexInfo(existingContent: string, newEntry: ICompactIndexInfoEntry): string;
137
+ /**
138
+ * Extract gem specification from .gem file
139
+ * Note: This is a simplified version. Full implementation would use tar + gzip + Marshal
140
+ * @param gemData - Gem file data
141
+ * @returns Extracted spec or null
142
+ */
143
+ export declare function extractGemSpec(gemData: Buffer): Promise<any | null>;