@push.rocks/smartregistry 2.1.2 → 2.2.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.js +1 -1
- package/dist_ts/composer/classes.composerregistry.js +8 -5
- package/dist_ts/core/classes.registrystorage.d.ts +7 -2
- package/dist_ts/core/classes.registrystorage.js +17 -4
- package/dist_ts/core/helpers.buffer.d.ts +18 -0
- package/dist_ts/core/helpers.buffer.js +33 -0
- package/dist_ts/maven/classes.mavenregistry.js +3 -2
- package/dist_ts/oci/classes.ociregistry.d.ts +15 -0
- package/dist_ts/oci/classes.ociregistry.js +73 -20
- package/dist_ts/pypi/classes.pypiregistry.js +5 -3
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/composer/classes.composerregistry.ts +8 -4
- package/ts/core/classes.registrystorage.ts +17 -3
- package/ts/core/helpers.buffer.ts +34 -0
- package/ts/maven/classes.mavenregistry.ts +2 -1
- package/ts/oci/classes.ociregistry.ts +79 -19
- package/ts/pypi/classes.pypiregistry.ts +4 -2
|
@@ -198,7 +198,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
198
198
|
const digest = query.digest;
|
|
199
199
|
if (digest && body) {
|
|
200
200
|
// Monolithic upload: complete upload in single POST
|
|
201
|
-
const blobData =
|
|
201
|
+
const blobData = this.toBuffer(body);
|
|
202
202
|
|
|
203
203
|
// Verify digest
|
|
204
204
|
const calculatedDigest = await this.calculateDigest(blobData);
|
|
@@ -320,10 +320,17 @@ export class OciRegistry extends BaseRegistry {
|
|
|
320
320
|
};
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
// Get stored content type, falling back to detecting from manifest content
|
|
324
|
+
let contentType = await this.storage.getOciManifestContentType(repository, digest);
|
|
325
|
+
if (!contentType) {
|
|
326
|
+
// Fallback: detect content type from manifest content
|
|
327
|
+
contentType = this.detectManifestContentType(manifestData);
|
|
328
|
+
}
|
|
329
|
+
|
|
323
330
|
return {
|
|
324
331
|
status: 200,
|
|
325
332
|
headers: {
|
|
326
|
-
'Content-Type':
|
|
333
|
+
'Content-Type': contentType,
|
|
327
334
|
'Docker-Content-Digest': digest,
|
|
328
335
|
},
|
|
329
336
|
body: manifestData,
|
|
@@ -356,10 +363,18 @@ export class OciRegistry extends BaseRegistry {
|
|
|
356
363
|
|
|
357
364
|
const manifestData = await this.storage.getOciManifest(repository, digest);
|
|
358
365
|
|
|
366
|
+
// Get stored content type, falling back to detecting from manifest content
|
|
367
|
+
let contentType = await this.storage.getOciManifestContentType(repository, digest);
|
|
368
|
+
if (!contentType && manifestData) {
|
|
369
|
+
// Fallback: detect content type from manifest content
|
|
370
|
+
contentType = this.detectManifestContentType(manifestData);
|
|
371
|
+
}
|
|
372
|
+
contentType = contentType || 'application/vnd.oci.image.manifest.v1+json';
|
|
373
|
+
|
|
359
374
|
return {
|
|
360
375
|
status: 200,
|
|
361
376
|
headers: {
|
|
362
|
-
'Content-Type':
|
|
377
|
+
'Content-Type': contentType,
|
|
363
378
|
'Docker-Content-Digest': digest,
|
|
364
379
|
'Content-Length': manifestData ? manifestData.length.toString() : '0',
|
|
365
380
|
},
|
|
@@ -388,16 +403,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
388
403
|
|
|
389
404
|
// Preserve raw bytes for accurate digest calculation
|
|
390
405
|
// Per OCI spec, digest must match the exact bytes sent by client
|
|
391
|
-
|
|
392
|
-
if (Buffer.isBuffer(body)) {
|
|
393
|
-
manifestData = body;
|
|
394
|
-
} else if (typeof body === 'string') {
|
|
395
|
-
// String body - convert directly without JSON transformation
|
|
396
|
-
manifestData = Buffer.from(body, 'utf-8');
|
|
397
|
-
} else {
|
|
398
|
-
// Body was already parsed as JSON object - re-serialize as fallback
|
|
399
|
-
manifestData = Buffer.from(JSON.stringify(body));
|
|
400
|
-
}
|
|
406
|
+
const manifestData = this.toBuffer(body);
|
|
401
407
|
const contentType = headers?.['content-type'] || headers?.['Content-Type'] || 'application/vnd.oci.image.manifest.v1+json';
|
|
402
408
|
|
|
403
409
|
// Calculate manifest digest
|
|
@@ -525,7 +531,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
525
531
|
|
|
526
532
|
private async uploadChunk(
|
|
527
533
|
uploadId: string,
|
|
528
|
-
data: Buffer,
|
|
534
|
+
data: Buffer | Uint8Array | unknown,
|
|
529
535
|
contentRange: string
|
|
530
536
|
): Promise<IResponse> {
|
|
531
537
|
const session = this.uploadSessions.get(uploadId);
|
|
@@ -537,8 +543,9 @@ export class OciRegistry extends BaseRegistry {
|
|
|
537
543
|
};
|
|
538
544
|
}
|
|
539
545
|
|
|
540
|
-
|
|
541
|
-
session.
|
|
546
|
+
const chunkData = this.toBuffer(data);
|
|
547
|
+
session.chunks.push(chunkData);
|
|
548
|
+
session.totalSize += chunkData.length;
|
|
542
549
|
session.lastActivity = new Date();
|
|
543
550
|
|
|
544
551
|
return {
|
|
@@ -555,7 +562,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
555
562
|
private async completeUpload(
|
|
556
563
|
uploadId: string,
|
|
557
564
|
digest: string,
|
|
558
|
-
finalData?: Buffer
|
|
565
|
+
finalData?: Buffer | Uint8Array | unknown
|
|
559
566
|
): Promise<IResponse> {
|
|
560
567
|
const session = this.uploadSessions.get(uploadId);
|
|
561
568
|
if (!session) {
|
|
@@ -567,7 +574,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
567
574
|
}
|
|
568
575
|
|
|
569
576
|
const chunks = [...session.chunks];
|
|
570
|
-
if (finalData) chunks.push(finalData);
|
|
577
|
+
if (finalData) chunks.push(this.toBuffer(finalData));
|
|
571
578
|
const blobData = Buffer.concat(chunks);
|
|
572
579
|
|
|
573
580
|
// Verify digest
|
|
@@ -665,6 +672,59 @@ export class OciRegistry extends BaseRegistry {
|
|
|
665
672
|
// HELPER METHODS
|
|
666
673
|
// ========================================================================
|
|
667
674
|
|
|
675
|
+
/**
|
|
676
|
+
* Detect manifest content type from manifest content.
|
|
677
|
+
* OCI Image Index has "manifests" array, OCI Image Manifest has "config" object.
|
|
678
|
+
* Also checks the mediaType field if present.
|
|
679
|
+
*/
|
|
680
|
+
private detectManifestContentType(manifestData: Buffer): string {
|
|
681
|
+
try {
|
|
682
|
+
const manifest = JSON.parse(manifestData.toString('utf-8'));
|
|
683
|
+
|
|
684
|
+
// First check if manifest has explicit mediaType field
|
|
685
|
+
if (manifest.mediaType) {
|
|
686
|
+
return manifest.mediaType;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Otherwise detect from structure
|
|
690
|
+
if (Array.isArray(manifest.manifests)) {
|
|
691
|
+
// OCI Image Index (multi-arch manifest list)
|
|
692
|
+
return 'application/vnd.oci.image.index.v1+json';
|
|
693
|
+
} else if (manifest.config) {
|
|
694
|
+
// OCI Image Manifest
|
|
695
|
+
return 'application/vnd.oci.image.manifest.v1+json';
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Fallback to standard manifest type
|
|
699
|
+
return 'application/vnd.oci.image.manifest.v1+json';
|
|
700
|
+
} catch (e) {
|
|
701
|
+
// If parsing fails, return default
|
|
702
|
+
return 'application/vnd.oci.image.manifest.v1+json';
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Convert any binary-like data to Buffer.
|
|
708
|
+
* Handles Buffer, Uint8Array (modern cross-platform), string, and objects.
|
|
709
|
+
*
|
|
710
|
+
* Note: Buffer.isBuffer(Uint8Array) returns false even though Buffer extends Uint8Array.
|
|
711
|
+
* This is because Uint8Array is the modern, cross-platform standard while Buffer is Node.js-specific.
|
|
712
|
+
* Many HTTP frameworks pass request bodies as Uint8Array for better compatibility.
|
|
713
|
+
*/
|
|
714
|
+
private toBuffer(data: unknown): Buffer {
|
|
715
|
+
if (Buffer.isBuffer(data)) {
|
|
716
|
+
return data;
|
|
717
|
+
}
|
|
718
|
+
if (data instanceof Uint8Array) {
|
|
719
|
+
return Buffer.from(data);
|
|
720
|
+
}
|
|
721
|
+
if (typeof data === 'string') {
|
|
722
|
+
return Buffer.from(data, 'utf-8');
|
|
723
|
+
}
|
|
724
|
+
// Fallback: serialize object to JSON (may cause digest mismatch for manifests)
|
|
725
|
+
return Buffer.from(JSON.stringify(data));
|
|
726
|
+
}
|
|
727
|
+
|
|
668
728
|
private async getTagsData(repository: string): Promise<Record<string, string>> {
|
|
669
729
|
const path = `oci/tags/${repository}/tags.json`;
|
|
670
730
|
const data = await this.storage.getObject(path);
|
|
@@ -678,7 +738,7 @@ export class OciRegistry extends BaseRegistry {
|
|
|
678
738
|
}
|
|
679
739
|
|
|
680
740
|
private generateUploadId(): string {
|
|
681
|
-
return `${Date.now()}-${Math.random().toString(36).
|
|
741
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
682
742
|
}
|
|
683
743
|
|
|
684
744
|
private async calculateDigest(data: Buffer): Promise<string> {
|
|
@@ -3,6 +3,7 @@ import { BaseRegistry } from '../core/classes.baseregistry.js';
|
|
|
3
3
|
import { RegistryStorage } from '../core/classes.registrystorage.js';
|
|
4
4
|
import { AuthManager } from '../core/classes.authmanager.js';
|
|
5
5
|
import type { IRequestContext, IResponse, IAuthToken } from '../core/interfaces.core.js';
|
|
6
|
+
import { isBinaryData, toBuffer } from '../core/helpers.buffer.js';
|
|
6
7
|
import type {
|
|
7
8
|
IPypiPackageMetadata,
|
|
8
9
|
IPypiFile,
|
|
@@ -328,8 +329,9 @@ export class PypiRegistry extends BaseRegistry {
|
|
|
328
329
|
const version = formData.version;
|
|
329
330
|
// Support both: formData.content.filename (multipart parsed) and formData.filename (flat)
|
|
330
331
|
const filename = formData.content?.filename || formData.filename;
|
|
331
|
-
// Support both: formData.content.data (multipart parsed) and formData.content (Buffer directly)
|
|
332
|
-
const
|
|
332
|
+
// Support both: formData.content.data (multipart parsed) and formData.content (Buffer/Uint8Array directly)
|
|
333
|
+
const rawContent = formData.content?.data || (isBinaryData(formData.content) ? formData.content : null);
|
|
334
|
+
const fileData = rawContent ? toBuffer(rawContent) : null;
|
|
333
335
|
const filetype = formData.filetype; // 'bdist_wheel' or 'sdist'
|
|
334
336
|
const pyversion = formData.pyversion;
|
|
335
337
|
|