mcard-js
Version:
MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers
271 lines (267 loc) • 6.99 kB
JavaScript
import {
MCard
} from "./chunk-O2UMNZGF.js";
// src/monads/Maybe.ts
var Maybe = class _Maybe {
constructor(_value, _isNothing) {
this._value = _value;
this._isNothing = _isNothing;
}
/**
* Create a Just (has value)
*/
static just(value) {
return new _Maybe(value, false);
}
/**
* Create a Nothing (no value)
*/
static nothing() {
return new _Maybe(null, true);
}
/**
* Check if this is Nothing
*/
get isNothing() {
return this._isNothing;
}
/**
* Check if this is Just
*/
get isJust() {
return !this._isNothing;
}
/**
* Get the value (throws if Nothing)
*/
get value() {
if (this._isNothing) {
throw new Error("Cannot get value from Nothing");
}
return this._value;
}
/**
* Monadic bind - chain operations
* Short-circuits on Nothing
*/
bind(fn) {
if (this._isNothing) {
return _Maybe.nothing();
}
return fn(this._value);
}
/**
* Map a function over the value
*/
map(fn) {
if (this._isNothing) {
return _Maybe.nothing();
}
return _Maybe.just(fn(this._value));
}
/**
* Get value or default
*/
getOrElse(defaultValue) {
return this._isNothing ? defaultValue : this._value;
}
};
// src/model/CardCollection.ts
var CardCollection = class {
engine;
constructor(engine) {
this.engine = engine;
}
// =========== Standard Operations ===========
/**
* Add a card to the collection
* Handles duplicates (same content, same hash) and collisions (diff content, same hash)
*/
async add(card) {
const existingCard = await this.engine.get(card.hash);
if (existingCard) {
const isDuplicate = this.areContentsEqual(existingCard.content, card.content);
if (isDuplicate) {
const { generateDuplicationEvent } = await import("./EventProducer-MW6QF4IO.js");
const eventStr = generateDuplicationEvent(card);
const eventCard = await MCard.create(eventStr);
await this.engine.add(eventCard);
return card.hash;
} else {
const { generateCollisionEvent } = await import("./EventProducer-MW6QF4IO.js");
const eventStr = await generateCollisionEvent(card);
const eventCard = await MCard.create(eventStr);
await this.engine.add(eventCard);
const eventObj = JSON.parse(eventStr);
const nextAlgo = eventObj.upgraded_function;
if (!nextAlgo) {
throw new Error("Failed to determine next hash algorithm for collision");
}
const upgradedCard = await MCard.create(card.content, nextAlgo);
return this.engine.add(upgradedCard);
}
}
return this.engine.add(card);
}
areContentsEqual(a, b) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
/**
* Get a card by hash
*/
async get(hash) {
return this.engine.get(hash);
}
/**
* Delete a card by hash
*/
async delete(hash) {
return this.engine.delete(hash);
}
/**
* Get a page of cards
*/
async getPage(pageNumber = 1, pageSize = 10) {
return this.engine.getPage(pageNumber, pageSize);
}
/**
* Count total cards
*/
async count() {
return this.engine.count();
}
// =========== Handle Operations ===========
/**
* Add a card and register a handle for it
*/
async addWithHandle(card, handle) {
const hash = await this.add(card);
await this.engine.registerHandle(handle, hash);
return hash;
}
/**
* Get card by handle
*/
async getByHandle(handle) {
return this.engine.getByHandle(handle);
}
/**
* Resolve handle to hash
*/
async resolveHandle(handle) {
return this.engine.resolveHandle(handle);
}
/**
* Update handle to point to new card
*/
async updateHandle(handle, newCard) {
const hash = await this.add(newCard);
await this.engine.updateHandle(handle, hash);
return hash;
}
/**
* Get version history for a handle
*/
async getHandleHistory(handle) {
return this.engine.getHandleHistory(handle);
}
// =========== Monadic Operations ===========
/**
* Monadic get - returns Maybe<MCard>
*/
async getM(hash) {
const card = await this.get(hash);
return card ? Maybe.just(card) : Maybe.nothing();
}
/**
* Monadic getByHandle - returns Maybe<MCard>
*/
async getByHandleM(handle) {
const card = await this.getByHandle(handle);
return card ? Maybe.just(card) : Maybe.nothing();
}
/**
* Monadic resolveHandle - returns Maybe<string>
*/
async resolveHandleM(handle) {
const hash = await this.resolveHandle(handle);
return hash ? Maybe.just(hash) : Maybe.nothing();
}
/**
* Resolve handle and get card in one monadic operation
*/
async resolveAndGetM(handle) {
const maybeHash = await this.resolveHandleM(handle);
if (maybeHash.isNothing) return Maybe.nothing();
return this.getM(maybeHash.value);
}
/**
* Prune version history for a handle.
* @param handle The handle string.
* @param options Options for pruning (olderThan date, or deleteAll).
* @returns Number of deleted entries.
*/
async pruneHandleHistory(handle, options = {}) {
if (this.engine.pruneHandleHistory) {
return this.engine.pruneHandleHistory(handle, options);
}
return 0;
}
// =========== Search & Bulk Operations ===========
async clear() {
return this.engine.clear();
}
async searchByString(query, pageNumber = 1, pageSize = 10) {
return this.engine.search(query, pageNumber, pageSize);
}
async searchByContent(query, pageNumber = 1, pageSize = 10) {
return this.engine.search(query, pageNumber, pageSize);
}
async searchByHash(hashPrefix) {
return this.engine.searchByHash(hashPrefix);
}
async getAllMCardsRaw() {
return this.engine.getAll();
}
async getAllCards(pageSize = 10, processCallback) {
const cards = [];
let pageNumber = 1;
let total = 0;
while (true) {
const page = await this.getPage(pageNumber, pageSize);
if (!page.items || page.items.length === 0) break;
for (const card of page.items) {
if (processCallback) {
processCallback(card);
}
cards.push(card);
}
total = page.totalItems;
if (!page.hasNext) break;
pageNumber++;
}
return { cards, total };
}
async printAllCards() {
const cards = await this.getAllMCardsRaw();
cards.forEach((card) => {
console.log(`Hash: ${card.hash}`);
try {
const text = new TextDecoder().decode(card.content);
const preview = text.slice(0, 100).replace(/\n/g, " ");
console.log(`Content: ${preview}${text.length > 100 ? "..." : ""}`);
} catch {
console.log(`Content (binary): ${card.content.length} bytes`);
}
console.log("---");
});
}
};
export {
Maybe,
CardCollection
};