UNPKG

mcard-js

Version:

A JavaScript implementation of MCard - A data model for persistently storing content with cryptographic hashing and timestamping

205 lines (184 loc) 7.13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.MCardFromData = exports.MCard = void 0; var _bufferPolyfill = require("../utils/bufferPolyfill.js"); var _cryptoPolyfill = require("../utils/cryptoPolyfill.js"); var _g_time = _interopRequireDefault(require("./g_time.js")); var _config_constants = require("../config/config_constants.js"); var _validator = _interopRequireDefault(require("./hash/validator.js")); var _content_type_detector = _interopRequireDefault(require("../utils/content_type_detector.js")); // Corrected path // Corrected path (assuming content_type_detector.js is in src/utils/) // Create a mock HashValidator if the real one fails to load let hashValidator; try { hashValidator = new _validator.default(_bufferPolyfill.SafeBuffer.from(''), 'sha256'); } catch (error) { console.warn('Failed to initialize HashValidator, using mock implementation'); hashValidator = { computeHash: (data, algorithm) => { // Simple mock implementation for testing const hash = (0, _cryptoPolyfill.createHash)(algorithm || 'sha256'); return hash.update(data).digest('hex'); }, validate: () => Promise.resolve(true) }; } class MCard { constructor(content, hashFunction = _config_constants.HashAlgorithm.DEFAULT, options = {}) { // Validate input if (content === null) { throw new Error('Content cannot be None'); } if (hashFunction === null) { throw new Error('hash_function cannot be None'); } // Track the original content type this._originalContentType = typeof content; // Convert input content to Buffer and store as this._content if (_bufferPolyfill.SafeBuffer.isBuffer(content)) { this._content = content; } else if (typeof content === 'string') { this._content = _bufferPolyfill.SafeBuffer.from(content, 'utf-8'); } else if (typeof content === 'object' && content !== null) { try { // Ensure that plain objects are not empty before stringifying if (Object.keys(content).length === 0 && content.constructor === Object) { throw new Error('Object content cannot be empty.'); } this._content = _bufferPolyfill.SafeBuffer.from(JSON.stringify(content), 'utf-8'); } catch (e) { throw new Error(`Failed to stringify object content for MCard: ${e.message}`); } } else { if (content === undefined) { throw new Error('Content cannot be undefined.'); } throw new Error(`Unsupported content type for MCard: ${typeof content}`); } // Validate content (which is now this._content, a Buffer) is not empty if (this._content.length === 0) { throw new Error('Content cannot be empty after conversion to Buffer.'); } // Compute hash using this._content const forcedHashAlgorithm = options.forceHashAlgorithm || hashFunction; try { this.hash = hashValidator.computeHash(this._content, (0, _config_constants.HashAlgorithm)(forcedHashAlgorithm)); this.hash_algorithm = (0, _config_constants.HashAlgorithm)(forcedHashAlgorithm); } catch (error) { console.error('Error computing hash:', error); // Fallback to a simple hash if the validator fails const hash = (0, _cryptoPolyfill.createHash)(forcedHashAlgorithm || 'sha256'); this.hash = hash.update(this._content).digest('hex'); this.hash_algorithm = forcedHashAlgorithm || 'sha256'; } // Generate timestamp this.g_time = _g_time.default.stamp_now(this.hash_algorithm); } // Getter methods get_content() { // If the content is a Uint8Array and was created from a string, convert it back to a string if (this._content instanceof Uint8Array && this._originalContentType === 'string') { return new TextDecoder('utf-8').decode(this._content); } return this._content; } // Getter for content property (for backward compatibility with tests) get content() { // Ensure we return a Buffer if (_bufferPolyfill.SafeBuffer.isBuffer(this._content)) { return this._content; } // If it's a Uint8Array, convert to Buffer if (this._content instanceof Uint8Array) { return new _bufferPolyfill.SafeBuffer(this._content); } // For any other type, convert to Buffer return new _bufferPolyfill.SafeBuffer(String(this._content)); } // Setter for content property (for backward compatibility with tests) set content(value) { this._content = value; } // Convert to dictionary representation to_dict() { // Ensure content is a Buffer let content = this._content; if (!Buffer.isBuffer(content)) { content = Buffer.from(content); } return { content: content, hash: this.hash, g_time: this.g_time, hash_algorithm: this.hash_algorithm }; } get_hash() { return this.hash; } get_g_time() { return this.g_time; } // Utility methods equals(other) { return this.hash === other.hash; } } exports.MCard = MCard; class MCardFromData extends MCard { constructor(content, hash_value, g_time_str) { // Validate input parameters if (!content) { throw new Error("Content cannot be null or empty"); } // Ensure content is a Buffer if (!_bufferPolyfill.SafeBuffer.isBuffer(content)) { throw new Error("Content must be a Buffer when initializing from existing data."); } if (!hash_value) { throw new Error("Hash value cannot be None or empty"); } if (!g_time_str) { throw new Error("g_time string cannot be None or empty"); } // Call parent constructor with content and hash function super(content, _g_time.default.get_hash_function(g_time_str)); // Override the hash generated by parent constructor this.hash = hash_value; this.g_time = g_time_str; // Directly assign the provided g_time string this.hash_function = _g_time.default.get_hash_function(this.g_time); // Detect content type (for metadata only, don't modify the content) const interpreter = new _content_type_detector.default(); let contentForType = content; // For type detection, convert to Buffer if needed if (!(content instanceof Buffer) && typeof content !== 'string') { contentForType = _bufferPolyfill.SafeBuffer.from(JSON.stringify(content), 'utf-8'); } else if (typeof content === 'string') { contentForType = _bufferPolyfill.SafeBuffer.from(content, 'utf-8'); } this._content_type = interpreter.detectContentType(contentForType); } // Getter method for content type get_content_type() { return this._content_type; } // Async method for content type (for compatibility) async getContentType() { return this._content_type; } // Update to_dict to include content type to_dict() { return { content: this.content, hash: this.hash, g_time: this.g_time, content_type: this._content_type }; } } exports.MCardFromData = MCardFromData; //# sourceMappingURL=mcard.js.map