@danielsimonjr/memoryjs 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.js +263 -73
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MemoryJS
|
|
2
2
|
|
|
3
|
-
[](https://github.com/danielsimonjr/memoryjs)
|
|
4
4
|
[](https://www.npmjs.com/package/@danielsimonjr/memoryjs)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
package/dist/cli/index.js
CHANGED
|
@@ -959,12 +959,34 @@ var init_indexes = __esm({
|
|
|
959
959
|
});
|
|
960
960
|
|
|
961
961
|
// src/utils/searchCache.ts
|
|
962
|
+
var searchCache_exports = {};
|
|
963
|
+
__export(searchCache_exports, {
|
|
964
|
+
SearchCache: () => SearchCache,
|
|
965
|
+
cleanupAllCaches: () => cleanupAllCaches,
|
|
966
|
+
clearAllSearchCaches: () => clearAllSearchCaches,
|
|
967
|
+
getAllCacheStats: () => getAllCacheStats,
|
|
968
|
+
searchCaches: () => searchCaches
|
|
969
|
+
});
|
|
962
970
|
function clearAllSearchCaches() {
|
|
963
971
|
searchCaches.basic.clear();
|
|
964
972
|
searchCaches.ranked.clear();
|
|
965
973
|
searchCaches.boolean.clear();
|
|
966
974
|
searchCaches.fuzzy.clear();
|
|
967
975
|
}
|
|
976
|
+
function getAllCacheStats() {
|
|
977
|
+
return {
|
|
978
|
+
basic: searchCaches.basic.getStats(),
|
|
979
|
+
ranked: searchCaches.ranked.getStats(),
|
|
980
|
+
boolean: searchCaches.boolean.getStats(),
|
|
981
|
+
fuzzy: searchCaches.fuzzy.getStats()
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function cleanupAllCaches() {
|
|
985
|
+
searchCaches.basic.cleanupExpired();
|
|
986
|
+
searchCaches.ranked.cleanupExpired();
|
|
987
|
+
searchCaches.boolean.cleanupExpired();
|
|
988
|
+
searchCaches.fuzzy.cleanupExpired();
|
|
989
|
+
}
|
|
968
990
|
var SearchCache, searchCaches;
|
|
969
991
|
var init_searchCache = __esm({
|
|
970
992
|
"src/utils/searchCache.ts"() {
|
|
@@ -35763,6 +35785,105 @@ var init_inspect = __esm({
|
|
|
35763
35785
|
}
|
|
35764
35786
|
});
|
|
35765
35787
|
|
|
35788
|
+
// src/cli/commands/check.ts
|
|
35789
|
+
var check_exports = {};
|
|
35790
|
+
__export(check_exports, {
|
|
35791
|
+
applyFixes: () => applyFixes,
|
|
35792
|
+
detectIssues: () => detectIssues,
|
|
35793
|
+
registerCheckCommand: () => registerCheckCommand
|
|
35794
|
+
});
|
|
35795
|
+
async function detectIssues(ctx) {
|
|
35796
|
+
const graph = await ctx.storage.loadGraph();
|
|
35797
|
+
const names = new Set(graph.entities.map((e) => e.name));
|
|
35798
|
+
const orphans = [];
|
|
35799
|
+
for (const r of graph.relations) {
|
|
35800
|
+
const fromMissing = !names.has(r.from);
|
|
35801
|
+
const toMissing = !names.has(r.to);
|
|
35802
|
+
if (fromMissing || toMissing) {
|
|
35803
|
+
orphans.push({
|
|
35804
|
+
from: r.from,
|
|
35805
|
+
to: r.to,
|
|
35806
|
+
relationType: r.relationType,
|
|
35807
|
+
reason: fromMissing && toMissing ? "both-missing" : fromMissing ? "from-missing" : "to-missing"
|
|
35808
|
+
});
|
|
35809
|
+
}
|
|
35810
|
+
}
|
|
35811
|
+
const missing = [];
|
|
35812
|
+
const cycles = [];
|
|
35813
|
+
const byName = /* @__PURE__ */ new Map();
|
|
35814
|
+
for (const e of graph.entities) byName.set(e.name, { name: e.name, parentId: e.parentId });
|
|
35815
|
+
for (const e of graph.entities) {
|
|
35816
|
+
if (!e.parentId) continue;
|
|
35817
|
+
if (!byName.has(e.parentId)) {
|
|
35818
|
+
missing.push({ entity: e.name, parentId: e.parentId });
|
|
35819
|
+
continue;
|
|
35820
|
+
}
|
|
35821
|
+
const visited = /* @__PURE__ */ new Set([e.name]);
|
|
35822
|
+
let cur = byName.get(e.parentId);
|
|
35823
|
+
while (cur && cur.parentId) {
|
|
35824
|
+
if (visited.has(cur.name)) {
|
|
35825
|
+
cycles.push({ entityInCycle: e.name, cycleThrough: cur.name });
|
|
35826
|
+
break;
|
|
35827
|
+
}
|
|
35828
|
+
visited.add(cur.name);
|
|
35829
|
+
cur = byName.get(cur.parentId);
|
|
35830
|
+
}
|
|
35831
|
+
}
|
|
35832
|
+
return { orphans, missing, cycles };
|
|
35833
|
+
}
|
|
35834
|
+
async function applyFixes(ctx, orphans, missing) {
|
|
35835
|
+
let deleted = 0;
|
|
35836
|
+
let cleared = 0;
|
|
35837
|
+
if (orphans.length > 0) {
|
|
35838
|
+
await ctx.relationManager.deleteRelations(
|
|
35839
|
+
orphans.map((o) => ({ from: o.from, to: o.to, relationType: o.relationType }))
|
|
35840
|
+
);
|
|
35841
|
+
deleted = orphans.length;
|
|
35842
|
+
}
|
|
35843
|
+
for (const m of missing) {
|
|
35844
|
+
try {
|
|
35845
|
+
await ctx.hierarchyManager.setEntityParent(m.entity, null);
|
|
35846
|
+
cleared += 1;
|
|
35847
|
+
} catch {
|
|
35848
|
+
}
|
|
35849
|
+
}
|
|
35850
|
+
return { orphanRelationsDeleted: deleted, missingParentsCleared: cleared };
|
|
35851
|
+
}
|
|
35852
|
+
function registerCheckCommand(program2) {
|
|
35853
|
+
program2.command("check").description("Detect orphan relations + missing parents + hierarchy cycles. Dry-run by default.").option("--apply", "Actually delete orphan relations + clear missing parentIds (cycles always left for human review)").action(async (opts) => {
|
|
35854
|
+
const options = getOptions(program2);
|
|
35855
|
+
const logger2 = createLogger(options);
|
|
35856
|
+
const ctx = createContext(options);
|
|
35857
|
+
try {
|
|
35858
|
+
const { orphans, missing, cycles } = await detectIssues(ctx);
|
|
35859
|
+
const ok = orphans.length === 0 && missing.length === 0 && cycles.length === 0;
|
|
35860
|
+
const report = {
|
|
35861
|
+
ok,
|
|
35862
|
+
applied: Boolean(opts.apply),
|
|
35863
|
+
orphanRelations: orphans,
|
|
35864
|
+
missingParents: missing,
|
|
35865
|
+
hierarchyCycles: cycles
|
|
35866
|
+
};
|
|
35867
|
+
if (opts.apply && (orphans.length > 0 || missing.length > 0)) {
|
|
35868
|
+
report.actions = await applyFixes(ctx, orphans, missing);
|
|
35869
|
+
}
|
|
35870
|
+
console.log(JSON.stringify(report, null, 2));
|
|
35871
|
+
if (!ok && !opts.apply) process.exit(1);
|
|
35872
|
+
} catch (error) {
|
|
35873
|
+
logger2.error(formatError(error.message));
|
|
35874
|
+
process.exit(1);
|
|
35875
|
+
}
|
|
35876
|
+
});
|
|
35877
|
+
}
|
|
35878
|
+
var init_check = __esm({
|
|
35879
|
+
"src/cli/commands/check.ts"() {
|
|
35880
|
+
"use strict";
|
|
35881
|
+
init_esm_shims();
|
|
35882
|
+
init_helpers();
|
|
35883
|
+
init_formatters2();
|
|
35884
|
+
}
|
|
35885
|
+
});
|
|
35886
|
+
|
|
35766
35887
|
// src/cli/interactive.ts
|
|
35767
35888
|
var interactive_exports = {};
|
|
35768
35889
|
__export(interactive_exports, {
|
|
@@ -36071,6 +36192,57 @@ Path (${pathResult.length} hops): ${pathResult.path.join(" -> ")}`);
|
|
|
36071
36192
|
console.log(JSON.stringify(report, null, 2));
|
|
36072
36193
|
break;
|
|
36073
36194
|
}
|
|
36195
|
+
case "check": {
|
|
36196
|
+
const apply = args[0] === "--apply";
|
|
36197
|
+
const { detectIssues: detectIssues2, applyFixes: applyFixes2 } = await Promise.resolve().then(() => (init_check(), check_exports));
|
|
36198
|
+
const { orphans, missing, cycles } = await detectIssues2(ctx);
|
|
36199
|
+
const ok = orphans.length === 0 && missing.length === 0 && cycles.length === 0;
|
|
36200
|
+
const result = {
|
|
36201
|
+
ok,
|
|
36202
|
+
applied: apply,
|
|
36203
|
+
orphanRelations: orphans,
|
|
36204
|
+
missingParents: missing,
|
|
36205
|
+
hierarchyCycles: cycles
|
|
36206
|
+
};
|
|
36207
|
+
if (apply && (orphans.length > 0 || missing.length > 0)) {
|
|
36208
|
+
result.actions = await applyFixes2(ctx, orphans, missing);
|
|
36209
|
+
}
|
|
36210
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36211
|
+
break;
|
|
36212
|
+
}
|
|
36213
|
+
case "heuristics": {
|
|
36214
|
+
const heuristics = await ctx.heuristicManager.list();
|
|
36215
|
+
console.log(JSON.stringify({ heuristics, count: heuristics.length }, null, 2));
|
|
36216
|
+
break;
|
|
36217
|
+
}
|
|
36218
|
+
case "spell": {
|
|
36219
|
+
const query = args.join(" ");
|
|
36220
|
+
if (!query) {
|
|
36221
|
+
console.log(chalk2.yellow("Usage: spell <query>"));
|
|
36222
|
+
break;
|
|
36223
|
+
}
|
|
36224
|
+
const suggestions = await ctx.spellChecker.suggest(query);
|
|
36225
|
+
console.log(JSON.stringify({ query, suggestions, count: suggestions.length }, null, 2));
|
|
36226
|
+
break;
|
|
36227
|
+
}
|
|
36228
|
+
case "cache": {
|
|
36229
|
+
const sub = args[0];
|
|
36230
|
+
const { getAllCacheStats: getAllCacheStats2, clearAllSearchCaches: clearAllSearchCaches2 } = await Promise.resolve().then(() => (init_searchCache(), searchCache_exports));
|
|
36231
|
+
if (sub === "clear") {
|
|
36232
|
+
clearAllSearchCaches2();
|
|
36233
|
+
console.log(JSON.stringify({ cleared: true }, null, 2));
|
|
36234
|
+
} else {
|
|
36235
|
+
console.log(JSON.stringify({ stats: getAllCacheStats2() }, null, 2));
|
|
36236
|
+
}
|
|
36237
|
+
break;
|
|
36238
|
+
}
|
|
36239
|
+
case "reindex": {
|
|
36240
|
+
const t = Date.now();
|
|
36241
|
+
await ctx.rankedSearch.buildIndex();
|
|
36242
|
+
await ctx.spellChecker.rebuild();
|
|
36243
|
+
console.log(JSON.stringify({ ok: true, ranked: "rebuilt", spell: "rebuilt", durationMs: Date.now() - t }, null, 2));
|
|
36244
|
+
break;
|
|
36245
|
+
}
|
|
36074
36246
|
default:
|
|
36075
36247
|
console.log(chalk2.yellow(`Unknown command: ${command}. Type "help" for available commands.`));
|
|
36076
36248
|
}
|
|
@@ -36094,6 +36266,11 @@ ${chalk2.green("Available Commands:")}
|
|
|
36094
36266
|
${chalk2.cyan("neighbors <name>")} Incoming + outgoing relations + degree counts
|
|
36095
36267
|
${chalk2.cyan("diag")} / ${chalk2.cyan("health")} Quick integrity summary
|
|
36096
36268
|
${chalk2.cyan("size")} Graph + storage footprint
|
|
36269
|
+
${chalk2.cyan("check [--apply]")} Orphan + missing-parent + cycle detect/repair
|
|
36270
|
+
${chalk2.cyan("heuristics")} List all heuristics
|
|
36271
|
+
${chalk2.cyan("spell <query>")} Spell suggestions for a query
|
|
36272
|
+
${chalk2.cyan("cache [clear]")} Search-cache stats; "cache clear" to bust
|
|
36273
|
+
${chalk2.cyan("reindex")} Rebuild ranked-search + spell vocabulary
|
|
36097
36274
|
${chalk2.cyan("history")} Show command history
|
|
36098
36275
|
${chalk2.cyan("clear")} Clear screen
|
|
36099
36276
|
${chalk2.cyan("help")} Show this help
|
|
@@ -38036,89 +38213,100 @@ function registerSpellCommands(program2) {
|
|
|
38036
38213
|
});
|
|
38037
38214
|
}
|
|
38038
38215
|
|
|
38039
|
-
// src/cli/commands/
|
|
38216
|
+
// src/cli/commands/index.ts
|
|
38217
|
+
init_check();
|
|
38218
|
+
|
|
38219
|
+
// src/cli/commands/cache.ts
|
|
38040
38220
|
init_esm_shims();
|
|
38221
|
+
init_searchCache();
|
|
38041
38222
|
init_helpers();
|
|
38042
38223
|
init_formatters2();
|
|
38043
|
-
|
|
38044
|
-
|
|
38045
|
-
|
|
38046
|
-
|
|
38047
|
-
|
|
38048
|
-
|
|
38049
|
-
|
|
38050
|
-
|
|
38051
|
-
|
|
38052
|
-
|
|
38053
|
-
|
|
38054
|
-
|
|
38055
|
-
|
|
38056
|
-
});
|
|
38057
|
-
}
|
|
38058
|
-
}
|
|
38059
|
-
const missing = [];
|
|
38060
|
-
const cycles = [];
|
|
38061
|
-
const byName = /* @__PURE__ */ new Map();
|
|
38062
|
-
for (const e of graph.entities) byName.set(e.name, { name: e.name, parentId: e.parentId });
|
|
38063
|
-
for (const e of graph.entities) {
|
|
38064
|
-
if (!e.parentId) continue;
|
|
38065
|
-
if (!byName.has(e.parentId)) {
|
|
38066
|
-
missing.push({ entity: e.name, parentId: e.parentId });
|
|
38067
|
-
continue;
|
|
38224
|
+
function emitJson4(payload) {
|
|
38225
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
38226
|
+
}
|
|
38227
|
+
function registerCacheCommands(program2) {
|
|
38228
|
+
const cache = program2.command("cache").description("Inspect or bust the per-tier search caches (basic / ranked / boolean / fuzzy)");
|
|
38229
|
+
cache.command("stats").description("Per-cache hits/misses/size/hitRate snapshot. Stats are process-local \u2014 fresh CLI invocations start at zero.").action(() => {
|
|
38230
|
+
try {
|
|
38231
|
+
const stats = getAllCacheStats();
|
|
38232
|
+
emitJson4({ stats });
|
|
38233
|
+
} catch (error) {
|
|
38234
|
+
const logger2 = createLogger(getOptions(program2));
|
|
38235
|
+
logger2.error(formatError(error.message));
|
|
38236
|
+
process.exit(1);
|
|
38068
38237
|
}
|
|
38069
|
-
|
|
38070
|
-
|
|
38071
|
-
|
|
38072
|
-
|
|
38073
|
-
|
|
38074
|
-
|
|
38075
|
-
|
|
38076
|
-
|
|
38077
|
-
|
|
38238
|
+
});
|
|
38239
|
+
cache.command("clear").description("Clear all four search caches. Idempotent; safe to call any time.").action(() => {
|
|
38240
|
+
try {
|
|
38241
|
+
clearAllSearchCaches();
|
|
38242
|
+
emitJson4({ cleared: true, caches: ["basic", "ranked", "boolean", "fuzzy"] });
|
|
38243
|
+
} catch (error) {
|
|
38244
|
+
const logger2 = createLogger(getOptions(program2));
|
|
38245
|
+
logger2.error(formatError(error.message));
|
|
38246
|
+
process.exit(1);
|
|
38078
38247
|
}
|
|
38079
|
-
}
|
|
38080
|
-
|
|
38081
|
-
}
|
|
38082
|
-
async function applyFixes(ctx, orphans, missing) {
|
|
38083
|
-
let deleted = 0;
|
|
38084
|
-
let cleared = 0;
|
|
38085
|
-
if (orphans.length > 0) {
|
|
38086
|
-
await ctx.relationManager.deleteRelations(
|
|
38087
|
-
orphans.map((o) => ({ from: o.from, to: o.to, relationType: o.relationType }))
|
|
38088
|
-
);
|
|
38089
|
-
deleted = orphans.length;
|
|
38090
|
-
}
|
|
38091
|
-
for (const m of missing) {
|
|
38248
|
+
});
|
|
38249
|
+
cache.command("cleanup").description("Sweep expired entries (TTL) across all caches without dropping live entries.").action(() => {
|
|
38092
38250
|
try {
|
|
38093
|
-
|
|
38094
|
-
|
|
38095
|
-
} catch {
|
|
38251
|
+
cleanupAllCaches();
|
|
38252
|
+
emitJson4({ cleaned: true, caches: ["basic", "ranked", "boolean", "fuzzy"] });
|
|
38253
|
+
} catch (error) {
|
|
38254
|
+
const logger2 = createLogger(getOptions(program2));
|
|
38255
|
+
logger2.error(formatError(error.message));
|
|
38256
|
+
process.exit(1);
|
|
38096
38257
|
}
|
|
38097
|
-
}
|
|
38098
|
-
return { orphanRelationsDeleted: deleted, missingParentsCleared: cleared };
|
|
38258
|
+
});
|
|
38099
38259
|
}
|
|
38100
|
-
|
|
38101
|
-
|
|
38260
|
+
|
|
38261
|
+
// src/cli/commands/reindex.ts
|
|
38262
|
+
init_esm_shims();
|
|
38263
|
+
init_RankedSearch();
|
|
38264
|
+
init_helpers();
|
|
38265
|
+
init_formatters2();
|
|
38266
|
+
import { dirname as dirname10 } from "path";
|
|
38267
|
+
import { performance as performance3 } from "perf_hooks";
|
|
38268
|
+
function emitJson5(payload) {
|
|
38269
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
38270
|
+
}
|
|
38271
|
+
function registerReindexCommand(program2) {
|
|
38272
|
+
program2.command("reindex").description("Rebuild search-side indexes (TF-IDF/BM25 ranked + spell vocabulary).").option("--ranked", "Rebuild only the ranked-search (TF-IDF/BM25) index").option("--spell", "Rebuild only the spell-checker vocabulary").action(async (opts) => {
|
|
38102
38273
|
const options = getOptions(program2);
|
|
38103
38274
|
const logger2 = createLogger(options);
|
|
38104
38275
|
const ctx = createContext(options);
|
|
38105
|
-
|
|
38106
|
-
|
|
38107
|
-
|
|
38108
|
-
const
|
|
38109
|
-
|
|
38110
|
-
|
|
38111
|
-
|
|
38112
|
-
|
|
38113
|
-
|
|
38114
|
-
}
|
|
38115
|
-
|
|
38116
|
-
|
|
38276
|
+
const targets = opts.ranked === true || opts.spell === true ? { ranked: Boolean(opts.ranked), spell: Boolean(opts.spell) } : { ranked: true, spell: true };
|
|
38277
|
+
const result = {};
|
|
38278
|
+
if (targets.ranked) {
|
|
38279
|
+
const t = performance3.now();
|
|
38280
|
+
try {
|
|
38281
|
+
const storageDir = dirname10(options.storage);
|
|
38282
|
+
const ranked = new RankedSearch(ctx.storage, storageDir);
|
|
38283
|
+
await ranked.buildIndex();
|
|
38284
|
+
result.ranked = { ok: true, durationMs: performance3.now() - t };
|
|
38285
|
+
} catch (e) {
|
|
38286
|
+
result.ranked = {
|
|
38287
|
+
ok: false,
|
|
38288
|
+
durationMs: performance3.now() - t,
|
|
38289
|
+
detail: e instanceof Error ? e.message : String(e)
|
|
38290
|
+
};
|
|
38117
38291
|
}
|
|
38118
|
-
|
|
38119
|
-
|
|
38120
|
-
|
|
38121
|
-
|
|
38292
|
+
}
|
|
38293
|
+
if (targets.spell) {
|
|
38294
|
+
const t = performance3.now();
|
|
38295
|
+
try {
|
|
38296
|
+
await ctx.spellChecker.rebuild();
|
|
38297
|
+
result.spell = { ok: true, durationMs: performance3.now() - t };
|
|
38298
|
+
} catch (e) {
|
|
38299
|
+
result.spell = {
|
|
38300
|
+
ok: false,
|
|
38301
|
+
durationMs: performance3.now() - t,
|
|
38302
|
+
detail: e instanceof Error ? e.message : String(e)
|
|
38303
|
+
};
|
|
38304
|
+
}
|
|
38305
|
+
}
|
|
38306
|
+
const failed = Object.values(result).filter((r) => !r.ok).length;
|
|
38307
|
+
emitJson5({ ok: failed === 0, failed, result });
|
|
38308
|
+
if (failed > 0) {
|
|
38309
|
+
logger2.error(formatError(`${failed} index(es) failed to rebuild`));
|
|
38122
38310
|
process.exit(1);
|
|
38123
38311
|
}
|
|
38124
38312
|
});
|
|
@@ -38146,6 +38334,8 @@ function registerCommands(program2) {
|
|
|
38146
38334
|
registerObservationDedupCommands(program2);
|
|
38147
38335
|
registerSpellCommands(program2);
|
|
38148
38336
|
registerCheckCommand(program2);
|
|
38337
|
+
registerCacheCommands(program2);
|
|
38338
|
+
registerReindexCommand(program2);
|
|
38149
38339
|
}
|
|
38150
38340
|
|
|
38151
38341
|
// src/cli/index.ts
|
|
@@ -38153,7 +38343,7 @@ init_logger();
|
|
|
38153
38343
|
import { readFileSync as readFileSync5 } from "fs";
|
|
38154
38344
|
import { createInterface as createInterface2 } from "readline";
|
|
38155
38345
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
38156
|
-
import { dirname as
|
|
38346
|
+
import { dirname as dirname11, join as join10 } from "path";
|
|
38157
38347
|
process.on("unhandledRejection", (reason) => {
|
|
38158
38348
|
logger.error("Unhandled promise rejection:", reason);
|
|
38159
38349
|
});
|
|
@@ -38163,7 +38353,7 @@ process.on("uncaughtException", (err) => {
|
|
|
38163
38353
|
function getVersion() {
|
|
38164
38354
|
try {
|
|
38165
38355
|
const __filename2 = fileURLToPath4(import.meta.url);
|
|
38166
|
-
const __dirname2 =
|
|
38356
|
+
const __dirname2 = dirname11(__filename2);
|
|
38167
38357
|
const pkgPath = join10(__dirname2, "..", "..", "package.json");
|
|
38168
38358
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
38169
38359
|
return pkg.version;
|