mcard-js
Version:
MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers
177 lines • 7.24 kB
JavaScript
/**
* Handle builtins for CLM execution.
*
* Implements handle_version and handle_prune operations.
*/
/**
* Execute handle version operation.
*/
export async function executeHandleVersion(ctx) {
const examples = ctx.examples || [];
if (examples.length) {
return runVersionExamples(examples);
}
// Single execution mode
const inputData = ctx.input || ctx;
const handle = inputData.handle || ctx.handle || '';
const versions = inputData.versions || ctx.versions || [];
if (!handle) {
return { success: false, error: 'handle is required' };
}
if (!versions.length) {
return { success: false, error: 'versions list is required' };
}
return executeVersionSingle(handle, versions);
}
/**
* Execute handle prune operation.
*/
export async function executeHandlePrune(ctx) {
const examples = ctx.examples || [];
if (examples.length) {
return runPruneExamples(examples);
}
// Single execution mode
const inputData = ctx.input || ctx;
const handle = inputData.handle || ctx.handle || '';
const versions = inputData.versions || ctx.versions || [];
const pruneType = inputData.prune_type || ctx.prune_type || 'all';
const olderThanSeconds = inputData.older_than_seconds || ctx.older_than_seconds;
if (!handle) {
return { success: false, error: 'handle is required' };
}
return executePruneSingle(handle, versions, pruneType, olderThanSeconds);
}
// ─── Private Helpers ─────────────────────────────────────────────────────────
async function runVersionExamples(examples) {
const results = [];
for (const example of examples) {
const inputData = example.input || example;
const handle = inputData.handle || '';
const versions = inputData.versions || [];
if (!handle || !versions.length) {
results.push({
example_name: example.name || 'unnamed',
success: false,
error: 'handle and versions required'
});
continue;
}
const result = await executeVersionSingle(handle, versions);
result.example_name = example.name || 'unnamed';
// Check expected if provided
const expected = example.expected_output || {};
result.passed = expected.history_length !== undefined
? result.history_length === expected.history_length
: result.success || false;
results.push(result);
}
return { success: results.every(r => r.passed), results };
}
async function executeVersionSingle(handle, versions) {
const { SqliteNodeEngine } = await import('../../../../storage/SqliteNodeEngine.js');
const { CardCollection } = await import('../../../../model/CardCollection.js');
const { MCard } = await import('../../../../model/MCard.js');
const engine = new SqliteNodeEngine(':memory:');
const collection = new CardCollection(engine);
try {
// Add first version
const firstContent = versions[0].content || versions[0];
const card = await MCard.create(firstContent);
await collection.addWithHandle(card, handle);
// Apply subsequent updates
for (let i = 1; i < versions.length; i++) {
const content = versions[i].content || versions[i];
const newCard = await MCard.create(content);
await collection.updateHandle(handle, newCard);
}
// Get final state
const finalCard = await collection.getByHandle(handle);
const history = await collection.getHandleHistory(handle);
const finalContent = finalCard ? new TextDecoder().decode(finalCard.content) : null;
return {
success: true,
handle,
final_hash: finalCard ? finalCard.hash : null,
final_content: finalContent,
history_length: history.length,
history
};
}
catch (e) {
return { success: false, error: e.message };
}
}
async function runPruneExamples(examples) {
const results = [];
for (const example of examples) {
const inputData = example.input || example;
const handle = inputData.handle || '';
const versions = inputData.versions || [];
const pruneType = inputData.prune_type || 'all';
const olderThanSeconds = inputData.older_than_seconds;
if (!handle) {
results.push({
example_name: example.name || 'unnamed',
success: false,
error: 'handle is required'
});
continue;
}
const result = await executePruneSingle(handle, versions, pruneType, olderThanSeconds);
result.example_name = example.name || 'unnamed';
// Check expected if provided
const expected = example.expected_output || {};
if (expected.history_after !== undefined) {
result.passed = result.history_after === expected.history_after;
}
else if (expected.deleted !== undefined) {
result.passed = result.deleted === expected.deleted;
}
else {
result.passed = result.success || false;
}
results.push(result);
}
return { success: results.every(r => r.passed), results };
}
async function executePruneSingle(handle, versions, pruneType, olderThanSeconds) {
const { SqliteNodeEngine } = await import('../../../../storage/SqliteNodeEngine.js');
const { CardCollection } = await import('../../../../model/CardCollection.js');
const { MCard } = await import('../../../../model/MCard.js');
const engine = new SqliteNodeEngine(':memory:');
const collection = new CardCollection(engine);
try {
// Setup handle with versions if provided
if (versions.length) {
const firstContent = versions[0].content || versions[0];
const card = await MCard.create(firstContent);
await collection.addWithHandle(card, handle);
for (let i = 1; i < versions.length; i++) {
const content = versions[i].content || versions[i];
const newCard = await MCard.create(content);
await collection.updateHandle(handle, newCard);
}
}
const historyBefore = (await collection.getHandleHistory(handle)).length;
let deleted = 0;
if (pruneType === 'all') {
deleted = await collection.pruneHandleHistory(handle, { deleteAll: true });
}
else if (pruneType === 'older_than' && olderThanSeconds !== undefined) {
deleted = await collection.pruneHandleHistory(handle, { olderThan: `${olderThanSeconds}s` });
}
const historyAfter = (await collection.getHandleHistory(handle)).length;
return {
success: true,
handle,
history_before: historyBefore,
deleted,
history_after: historyAfter
};
}
catch (e) {
return { success: false, error: e.message };
}
}
//# sourceMappingURL=handle.js.map