fix(core): Normalize binary data handling across registries and add buffer helpers
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartregistry',
|
||||
version: '2.2.0',
|
||||
version: '2.2.1',
|
||||
description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { BaseRegistry } from '../core/classes.baseregistry.js';
|
||||
import type { RegistryStorage } from '../core/classes.registrystorage.js';
|
||||
import type { AuthManager } from '../core/classes.authmanager.js';
|
||||
import type { IRequestContext, IResponse, IAuthToken } from '../core/interfaces.core.js';
|
||||
import { isBinaryData, toBuffer } from '../core/helpers.buffer.js';
|
||||
import type {
|
||||
IComposerPackage,
|
||||
IComposerPackageMetadata,
|
||||
@@ -255,7 +256,7 @@ export class ComposerRegistry extends BaseRegistry {
|
||||
};
|
||||
}
|
||||
|
||||
if (!body || !Buffer.isBuffer(body)) {
|
||||
if (!body || !isBinaryData(body)) {
|
||||
return {
|
||||
status: 400,
|
||||
headers: {},
|
||||
@@ -263,8 +264,11 @@ export class ComposerRegistry extends BaseRegistry {
|
||||
};
|
||||
}
|
||||
|
||||
// Convert to Buffer for ZIP processing
|
||||
const zipData = toBuffer(body);
|
||||
|
||||
// Extract and validate composer.json from ZIP
|
||||
const composerJson = await extractComposerJsonFromZip(body);
|
||||
const composerJson = await extractComposerJsonFromZip(zipData);
|
||||
if (!composerJson || !validateComposerJson(composerJson)) {
|
||||
return {
|
||||
status: 400,
|
||||
@@ -292,13 +296,13 @@ export class ComposerRegistry extends BaseRegistry {
|
||||
}
|
||||
|
||||
// Calculate SHA-1 hash
|
||||
const shasum = await calculateSha1(body);
|
||||
const shasum = await calculateSha1(zipData);
|
||||
|
||||
// Generate reference (use version or commit hash)
|
||||
const reference = composerJson.source?.reference || version.replace(/[^a-zA-Z0-9.-]/g, '-');
|
||||
|
||||
// Store ZIP file
|
||||
await this.storage.putComposerPackageZip(vendorPackage, reference, body);
|
||||
await this.storage.putComposerPackageZip(vendorPackage, reference, zipData);
|
||||
|
||||
// Get or create metadata
|
||||
let metadata = await this.storage.getComposerPackageMetadata(vendorPackage);
|
||||
|
||||
34
ts/core/helpers.buffer.ts
Normal file
34
ts/core/helpers.buffer.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Shared buffer utilities for consistent binary data handling across all registry types.
|
||||
*
|
||||
* This module addresses the common issue where `Buffer.isBuffer(Uint8Array)` returns `false`,
|
||||
* which can cause data handling bugs when binary data arrives as Uint8Array instead of Buffer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if value is binary data (Buffer or Uint8Array)
|
||||
*/
|
||||
export function isBinaryData(value: unknown): value is Buffer | Uint8Array {
|
||||
return Buffer.isBuffer(value) || value instanceof Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any binary-like data to Buffer.
|
||||
* Handles Buffer, Uint8Array, string, and objects.
|
||||
*
|
||||
* @param data - The data to convert to Buffer
|
||||
* @returns A Buffer containing the data
|
||||
*/
|
||||
export function toBuffer(data: unknown): Buffer {
|
||||
if (Buffer.isBuffer(data)) {
|
||||
return data;
|
||||
}
|
||||
if (data instanceof Uint8Array) {
|
||||
return Buffer.from(data);
|
||||
}
|
||||
if (typeof data === 'string') {
|
||||
return Buffer.from(data, 'utf-8');
|
||||
}
|
||||
// Fallback: serialize object to JSON
|
||||
return Buffer.from(JSON.stringify(data));
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { BaseRegistry } from '../core/classes.baseregistry.js';
|
||||
import type { RegistryStorage } from '../core/classes.registrystorage.js';
|
||||
import type { AuthManager } from '../core/classes.authmanager.js';
|
||||
import type { IRequestContext, IResponse, IAuthToken } from '../core/interfaces.core.js';
|
||||
import { toBuffer } from '../core/helpers.buffer.js';
|
||||
import type { IMavenCoordinate, IMavenMetadata, IChecksums } from './interfaces.maven.js';
|
||||
import {
|
||||
pathToGAV,
|
||||
@@ -296,7 +297,7 @@ export class MavenRegistry extends BaseRegistry {
|
||||
coordinate: IMavenCoordinate,
|
||||
body: Buffer | any
|
||||
): Promise<IResponse> {
|
||||
const data = Buffer.isBuffer(body) ? body : Buffer.from(JSON.stringify(body));
|
||||
const data = toBuffer(body);
|
||||
|
||||
// Validate POM if uploading .pom file
|
||||
if (coordinate.extension === 'pom') {
|
||||
|
||||
@@ -738,7 +738,7 @@ export class OciRegistry extends BaseRegistry {
|
||||
}
|
||||
|
||||
private generateUploadId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
||||
}
|
||||
|
||||
private async calculateDigest(data: Buffer): Promise<string> {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BaseRegistry } from '../core/classes.baseregistry.js';
|
||||
import { RegistryStorage } from '../core/classes.registrystorage.js';
|
||||
import { AuthManager } from '../core/classes.authmanager.js';
|
||||
import type { IRequestContext, IResponse, IAuthToken } from '../core/interfaces.core.js';
|
||||
import { isBinaryData, toBuffer } from '../core/helpers.buffer.js';
|
||||
import type {
|
||||
IPypiPackageMetadata,
|
||||
IPypiFile,
|
||||
@@ -328,8 +329,9 @@ export class PypiRegistry extends BaseRegistry {
|
||||
const version = formData.version;
|
||||
// Support both: formData.content.filename (multipart parsed) and formData.filename (flat)
|
||||
const filename = formData.content?.filename || formData.filename;
|
||||
// Support both: formData.content.data (multipart parsed) and formData.content (Buffer directly)
|
||||
const fileData = (formData.content?.data || (Buffer.isBuffer(formData.content) ? formData.content : null)) as Buffer;
|
||||
// Support both: formData.content.data (multipart parsed) and formData.content (Buffer/Uint8Array directly)
|
||||
const rawContent = formData.content?.data || (isBinaryData(formData.content) ? formData.content : null);
|
||||
const fileData = rawContent ? toBuffer(rawContent) : null;
|
||||
const filetype = formData.filetype; // 'bdist_wheel' or 'sdist'
|
||||
const pyversion = formData.pyversion;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user