loccon
Version:
A simple local context storage and management tool with CLI and web interfaces. Store, search, and organize code snippets, notes, and development contexts with sharded JSON storage.
195 lines • 7.79 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StorageManager = void 0;
const shardManager_1 = require("./shardManager");
const indexManager_1 = require("./indexManager");
const config_1 = require("./config");
const filelock_1 = require("../utils/filelock");
const validation_1 = require("../utils/validation");
const fuse_js_1 = __importDefault(require("fuse.js"));
class StorageManager {
constructor(storagePath) {
this.storagePath = storagePath;
this.shardManager = new shardManager_1.ShardManager(storagePath);
this.indexManager = new indexManager_1.IndexManager(storagePath);
this.lock = new filelock_1.FileLock(storagePath);
}
static async create(customStoragePath) {
const storagePath = config_1.ConfigManager.getStoragePath(customStoragePath);
await filelock_1.FileLock.cleanupStaleLocks(storagePath);
await config_1.ConfigManager.initializeStorage(storagePath);
return new StorageManager(storagePath);
}
async addContext(tag, content, categories = []) {
validation_1.Validator.validateTag(tag);
validation_1.Validator.validateContent(content);
validation_1.Validator.validateCategories(categories);
await this.lock.acquire();
try {
// Check if tag already exists
if (await this.indexManager.tagExists(tag)) {
throw new Error(`Tag '${tag}' already exists`);
}
// Get current shard or create new one
let currentShardId = await this.shardManager.getCurrentShard();
// Check if current shard has capacity
const entry = {
content: validation_1.Validator.sanitizeInput(content),
metadata: {
created: new Date().toISOString(),
modified: new Date().toISOString(),
categories: categories.map(cat => validation_1.Validator.sanitizeInput(cat))
}
};
if (!(await this.shardManager.checkShardCapacity(currentShardId, JSON.stringify(entry)))) {
currentShardId = await this.shardManager.createNewShard();
}
// Load shard, add entry, and save
const shardData = await this.shardManager.loadShard(currentShardId);
shardData.contexts[tag] = entry;
await this.shardManager.saveShard(shardData);
// Update index
await this.indexManager.setTagShard(tag, currentShardId);
// Update last accessed time
await config_1.ConfigManager.updateLastAccessed(this.storagePath);
}
finally {
await this.lock.release();
}
}
async readContext(tag) {
validation_1.Validator.validateTag(tag);
const shardId = await this.indexManager.getShardForTag(tag);
if (!shardId) {
return null;
}
const shardData = await this.shardManager.loadShard(shardId);
return shardData.contexts[tag] || null;
}
async updateContext(tag, content, categories = []) {
validation_1.Validator.validateTag(tag);
validation_1.Validator.validateContent(content);
validation_1.Validator.validateCategories(categories);
await this.lock.acquire();
try {
const shardId = await this.indexManager.getShardForTag(tag);
if (!shardId) {
throw new Error(`Tag '${tag}' not found`);
}
const shardData = await this.shardManager.loadShard(shardId);
const existingEntry = shardData.contexts[tag];
if (!existingEntry) {
throw new Error(`Tag '${tag}' not found in shard`);
}
// Update the entry
shardData.contexts[tag] = {
content: validation_1.Validator.sanitizeInput(content),
metadata: {
created: existingEntry.metadata.created,
modified: new Date().toISOString(),
categories: categories.map(cat => validation_1.Validator.sanitizeInput(cat))
}
};
await this.shardManager.saveShard(shardData);
await config_1.ConfigManager.updateLastAccessed(this.storagePath);
}
finally {
await this.lock.release();
}
}
async removeContext(tag) {
validation_1.Validator.validateTag(tag);
await this.lock.acquire();
try {
const shardId = await this.indexManager.getShardForTag(tag);
if (!shardId) {
return false;
}
const shardData = await this.shardManager.loadShard(shardId);
if (!(tag in shardData.contexts)) {
return false;
}
delete shardData.contexts[tag];
await this.shardManager.saveShard(shardData);
await this.indexManager.removeTag(tag);
await config_1.ConfigManager.updateLastAccessed(this.storagePath);
return true;
}
finally {
await this.lock.release();
}
}
async listAllTags() {
return await this.indexManager.getAllTags();
}
async getAllContexts() {
const allTags = await this.indexManager.getAllTags();
const contexts = {};
for (const tag of allTags) {
const context = await this.readContext(tag);
if (context) {
contexts[tag] = context;
}
}
return contexts;
}
async searchContexts(query, useFuzzy = false) {
const allShardIds = await this.shardManager.getAllShardIds();
const allEntries = [];
// Collect all entries from all shards
for (const shardId of allShardIds) {
const shardData = await this.shardManager.loadShard(shardId);
for (const [tag, entry] of Object.entries(shardData.contexts)) {
allEntries.push({ tag, entry });
}
}
if (useFuzzy) {
// Use Fuse.js for fuzzy search
const options = {
includeScore: true,
keys: [
{ name: 'tag', weight: 0.4 },
{ name: 'entry.content', weight: 0.4 },
{ name: 'entry.metadata.categories', weight: 0.2 }
],
threshold: 0.6
};
const fuse = new fuse_js_1.default(allEntries, options);
const fuseResults = fuse.search(query);
return fuseResults.map((result) => ({
tag: result.item.tag,
entry: result.item.entry,
score: result.score
}));
}
else {
// Simple text search
const results = [];
const queryLower = query.toLowerCase();
for (const { tag, entry } of allEntries) {
const searchText = `${tag} ${entry.content} ${entry.metadata.categories.join(' ')}`.toLowerCase();
if (searchText.includes(queryLower)) {
results.push({ tag, entry });
}
}
return results;
}
}
async rebuildIndex() {
await this.lock.acquire();
try {
await this.indexManager.rebuildIndex();
}
finally {
await this.lock.release();
}
}
getStoragePath() {
return this.storagePath;
}
}
exports.StorageManager = StorageManager;
//# sourceMappingURL=storage.js.map