@danielsimonjr/memoryjs 2.6.0 → 2.8.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/dist/cli/index.js +65 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +198 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +162 -1
- package/dist/index.d.ts +162 -1
- package/dist/index.js +195 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3413,6 +3413,144 @@ var init_WorkerPoolManager = __esm({
|
|
|
3413
3413
|
}
|
|
3414
3414
|
});
|
|
3415
3415
|
|
|
3416
|
+
// src/utils/WorkerTaskManager.ts
|
|
3417
|
+
function getWorkerTaskManager() {
|
|
3418
|
+
if (!DEFAULT_INSTANCE) DEFAULT_INSTANCE = new WorkerTaskManager();
|
|
3419
|
+
return DEFAULT_INSTANCE;
|
|
3420
|
+
}
|
|
3421
|
+
async function batchProcessViaWorkers(items, workerType, methodName, mapArgs, opts = {}) {
|
|
3422
|
+
if (items.length === 0) return [];
|
|
3423
|
+
const wtm = getWorkerTaskManager();
|
|
3424
|
+
const promises = items.map(
|
|
3425
|
+
(item, i) => wtm.submit(workerType, methodName, mapArgs(item, i), opts)
|
|
3426
|
+
);
|
|
3427
|
+
return Promise.all(promises);
|
|
3428
|
+
}
|
|
3429
|
+
var SUBMISSION_COUNTER, WorkerTaskManager, DEFAULT_INSTANCE;
|
|
3430
|
+
var init_WorkerTaskManager = __esm({
|
|
3431
|
+
"src/utils/WorkerTaskManager.ts"() {
|
|
3432
|
+
"use strict";
|
|
3433
|
+
init_esm_shims();
|
|
3434
|
+
init_taskScheduler();
|
|
3435
|
+
init_WorkerPoolManager();
|
|
3436
|
+
init_logger();
|
|
3437
|
+
SUBMISSION_COUNTER = 0;
|
|
3438
|
+
WorkerTaskManager = class {
|
|
3439
|
+
queue;
|
|
3440
|
+
poolManager;
|
|
3441
|
+
touchedPoolIds = /* @__PURE__ */ new Set();
|
|
3442
|
+
cancelledTaskIds = /* @__PURE__ */ new Set();
|
|
3443
|
+
handleStatus = /* @__PURE__ */ new Map();
|
|
3444
|
+
constructor(opts = {}) {
|
|
3445
|
+
this.queue = new TaskQueue({
|
|
3446
|
+
concurrency: opts.concurrency,
|
|
3447
|
+
timeout: opts.defaultTimeout,
|
|
3448
|
+
// We dispatch to WorkerPoolManager ourselves — the queue's
|
|
3449
|
+
// internal-worker-pool path would double-up.
|
|
3450
|
+
useWorkerPool: false
|
|
3451
|
+
});
|
|
3452
|
+
this.poolManager = getWorkerPoolManager();
|
|
3453
|
+
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Submit a task and await its result. Throws on failure / cancellation.
|
|
3456
|
+
*
|
|
3457
|
+
* @param workerType — routing key. Maps to a named `WorkerPoolManager` pool.
|
|
3458
|
+
* @param methodName — function name exposed by the worker module.
|
|
3459
|
+
* @param args — positional arguments passed to the worker function.
|
|
3460
|
+
*/
|
|
3461
|
+
async submit(workerType, methodName, args, opts = {}) {
|
|
3462
|
+
const handle = this.submitWithHandle(workerType, methodName, args, opts);
|
|
3463
|
+
return handle.result;
|
|
3464
|
+
}
|
|
3465
|
+
/**
|
|
3466
|
+
* Submit a task and return a handle for cancellation + status polling.
|
|
3467
|
+
*/
|
|
3468
|
+
submitWithHandle(workerType, methodName, args, opts = {}) {
|
|
3469
|
+
const id = `wtm-${Date.now()}-${++SUBMISSION_COUNTER}`;
|
|
3470
|
+
this.handleStatus.set(id, "pending" /* PENDING */);
|
|
3471
|
+
const task = {
|
|
3472
|
+
id,
|
|
3473
|
+
priority: opts.priority ?? 1 /* NORMAL */,
|
|
3474
|
+
timeout: opts.timeout,
|
|
3475
|
+
input: { workerType, methodName, args },
|
|
3476
|
+
fn: async (input) => {
|
|
3477
|
+
if (this.cancelledTaskIds.has(id)) {
|
|
3478
|
+
throw new Error(`Task ${id} cancelled before dispatch`);
|
|
3479
|
+
}
|
|
3480
|
+
this.handleStatus.set(id, "running" /* RUNNING */);
|
|
3481
|
+
const pool = this.poolManager.getPool(input.workerType, opts.poolConfig);
|
|
3482
|
+
this.touchedPoolIds.add(input.workerType);
|
|
3483
|
+
return await pool.exec(input.methodName, input.args);
|
|
3484
|
+
}
|
|
3485
|
+
};
|
|
3486
|
+
const result = this.queue.enqueue(task).then(async (res) => {
|
|
3487
|
+
this.handleStatus.set(id, res.status);
|
|
3488
|
+
if (res.status === "completed" /* COMPLETED */) {
|
|
3489
|
+
return res.result;
|
|
3490
|
+
}
|
|
3491
|
+
if (res.status === "cancelled" /* CANCELLED */ || this.cancelledTaskIds.has(id)) {
|
|
3492
|
+
throw new Error(`Task ${id} cancelled`);
|
|
3493
|
+
}
|
|
3494
|
+
const err2 = res.error ?? new Error(`Task ${id} failed: ${res.status}`);
|
|
3495
|
+
throw err2;
|
|
3496
|
+
});
|
|
3497
|
+
return {
|
|
3498
|
+
id,
|
|
3499
|
+
result,
|
|
3500
|
+
cancel: () => {
|
|
3501
|
+
this.cancelledTaskIds.add(id);
|
|
3502
|
+
const evicted = this.queue.cancel(id);
|
|
3503
|
+
if (evicted) {
|
|
3504
|
+
this.handleStatus.set(id, "cancelled" /* CANCELLED */);
|
|
3505
|
+
}
|
|
3506
|
+
return evicted;
|
|
3507
|
+
},
|
|
3508
|
+
status: () => this.handleStatus.get(id) ?? "pending" /* PENDING */
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
/**
|
|
3512
|
+
* Aggregated stats across the queue + touched pools.
|
|
3513
|
+
*/
|
|
3514
|
+
getStats() {
|
|
3515
|
+
const pools = [];
|
|
3516
|
+
for (const poolId of this.touchedPoolIds) {
|
|
3517
|
+
const stats = this.poolManager.getPoolStats(poolId);
|
|
3518
|
+
if (stats) {
|
|
3519
|
+
pools.push({
|
|
3520
|
+
poolId,
|
|
3521
|
+
workers: stats.totalWorkers,
|
|
3522
|
+
activeTasks: stats.activeTasks,
|
|
3523
|
+
pendingTasks: stats.pendingTasks,
|
|
3524
|
+
totalTasksExecuted: stats.totalTasksExecuted
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
return { queue: this.queue.getStats(), pools };
|
|
3529
|
+
}
|
|
3530
|
+
/**
|
|
3531
|
+
* Drain the queue (wait for all pending + running tasks) and return their
|
|
3532
|
+
* results in completion order. Pool workers are NOT terminated — call
|
|
3533
|
+
* `shutdown()` for that.
|
|
3534
|
+
*/
|
|
3535
|
+
async drain() {
|
|
3536
|
+
return this.queue.drain();
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Shut down the queue + the underlying WorkerPoolManager pools touched by
|
|
3540
|
+
* this instance. Idempotent. Returns after every owned worker has exited.
|
|
3541
|
+
*/
|
|
3542
|
+
async shutdown() {
|
|
3543
|
+
try {
|
|
3544
|
+
await this.queue.shutdown();
|
|
3545
|
+
} catch (e) {
|
|
3546
|
+
logger.warn("[WorkerTaskManager.shutdown] queue.shutdown failed:", e);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
};
|
|
3550
|
+
DEFAULT_INSTANCE = null;
|
|
3551
|
+
}
|
|
3552
|
+
});
|
|
3553
|
+
|
|
3416
3554
|
// src/utils/BatchProcessor.ts
|
|
3417
3555
|
async function processBatch(items, processor, options = {}) {
|
|
3418
3556
|
const batchProcessor = new BatchProcessor(options);
|
|
@@ -11267,6 +11405,7 @@ var init_utils = __esm({
|
|
|
11267
11405
|
init_taskScheduler();
|
|
11268
11406
|
init_operationUtils();
|
|
11269
11407
|
init_WorkerPoolManager();
|
|
11408
|
+
init_WorkerTaskManager();
|
|
11270
11409
|
init_BatchProcessor();
|
|
11271
11410
|
init_MemoryMonitor();
|
|
11272
11411
|
init_relationHelpers();
|
|
@@ -20705,6 +20844,27 @@ CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
|
|
|
20705
20844
|
CREATE INDEX IF NOT EXISTS idx_entities_content_hash ON entities(content_hash);
|
|
20706
20845
|
CREATE INDEX IF NOT EXISTS idx_entities_tags_gin ON entities USING GIN(tags);
|
|
20707
20846
|
|
|
20847
|
+
-- v2.8.0 \u2014 generated tsvector column for full-text search.
|
|
20848
|
+
-- name is weighted A (highest), observations B, tags C. PostgreSQL 12+
|
|
20849
|
+
-- supports GENERATED ALWAYS AS ... STORED; the DO block makes the column
|
|
20850
|
+
-- add idempotent on older + newer servers without requiring CREATE OR
|
|
20851
|
+
-- REPLACE semantics on ALTER.
|
|
20852
|
+
DO $$ BEGIN
|
|
20853
|
+
IF NOT EXISTS (
|
|
20854
|
+
SELECT 1 FROM information_schema.columns
|
|
20855
|
+
WHERE table_name = 'entities' AND column_name = 'fts_vector'
|
|
20856
|
+
) THEN
|
|
20857
|
+
ALTER TABLE entities ADD COLUMN fts_vector tsvector
|
|
20858
|
+
GENERATED ALWAYS AS (
|
|
20859
|
+
setweight(to_tsvector('english', name), 'A') ||
|
|
20860
|
+
setweight(to_tsvector('english', coalesce(array_to_string(observations, ' '), '')), 'B') ||
|
|
20861
|
+
setweight(to_tsvector('english', coalesce(array_to_string(tags, ' '), '')), 'C')
|
|
20862
|
+
) STORED;
|
|
20863
|
+
END IF;
|
|
20864
|
+
END $$;
|
|
20865
|
+
|
|
20866
|
+
CREATE INDEX IF NOT EXISTS idx_entities_fts_gin ON entities USING GIN(fts_vector);
|
|
20867
|
+
|
|
20708
20868
|
CREATE TABLE IF NOT EXISTS relations (
|
|
20709
20869
|
from_name TEXT NOT NULL,
|
|
20710
20870
|
to_name TEXT NOT NULL,
|
|
@@ -21024,6 +21184,38 @@ var PostgreSQLStorage = class {
|
|
|
21024
21184
|
hasRelations(entityName) {
|
|
21025
21185
|
return (this.outgoingRelations.get(entityName)?.length ?? 0) + (this.incomingRelations.get(entityName)?.length ?? 0) > 0;
|
|
21026
21186
|
}
|
|
21187
|
+
// ==================== Full-text search ====================
|
|
21188
|
+
/**
|
|
21189
|
+
* tsvector-backed full-text search. Uses `plainto_tsquery` so the input is
|
|
21190
|
+
* free-form (no boolean operator syntax required); ranks via `ts_rank` on
|
|
21191
|
+
* the weighted `fts_vector` column (name × A, observations × B, tags × C).
|
|
21192
|
+
*
|
|
21193
|
+
* Results are ordered by descending score and capped at `options.limit`
|
|
21194
|
+
* (default 50). Empty / whitespace-only queries return `[]` without
|
|
21195
|
+
* issuing a SQL call.
|
|
21196
|
+
*
|
|
21197
|
+
* @example
|
|
21198
|
+
* ```typescript
|
|
21199
|
+
* const matches = await storage.fullTextSearch('authentication flow', { limit: 10 });
|
|
21200
|
+
* // → [{ name: 'AuthService', score: 0.62 }, ...]
|
|
21201
|
+
* ```
|
|
21202
|
+
*/
|
|
21203
|
+
async fullTextSearch(query, options = {}) {
|
|
21204
|
+
const trimmed = query.trim();
|
|
21205
|
+
if (trimmed.length === 0) return [];
|
|
21206
|
+
await this.initSchema();
|
|
21207
|
+
const pool = await this.getPool();
|
|
21208
|
+
const limit = options.limit ?? 50;
|
|
21209
|
+
const res = await pool.query(
|
|
21210
|
+
`SELECT name, ts_rank(fts_vector, plainto_tsquery('english', $1))::float AS score
|
|
21211
|
+
FROM entities
|
|
21212
|
+
WHERE fts_vector @@ plainto_tsquery('english', $1)
|
|
21213
|
+
ORDER BY score DESC
|
|
21214
|
+
LIMIT $2`,
|
|
21215
|
+
[trimmed, limit]
|
|
21216
|
+
);
|
|
21217
|
+
return res.rows.map((r) => ({ name: r.name, score: Number(r.score) }));
|
|
21218
|
+
}
|
|
21027
21219
|
// ==================== Utility ====================
|
|
21028
21220
|
getFilePath() {
|
|
21029
21221
|
return this.connectionString;
|
|
@@ -51761,6 +51953,7 @@ export {
|
|
|
51761
51953
|
VisibilityResolver,
|
|
51762
51954
|
WorkThreadManager,
|
|
51763
51955
|
WorkerPoolManager,
|
|
51956
|
+
WorkerTaskManager,
|
|
51764
51957
|
WorkingMemoryManager,
|
|
51765
51958
|
WorldModelManager,
|
|
51766
51959
|
WorldStateSnapshot,
|
|
@@ -51770,6 +51963,7 @@ export {
|
|
|
51770
51963
|
applyPagination,
|
|
51771
51964
|
asWarning,
|
|
51772
51965
|
batchProcess,
|
|
51966
|
+
batchProcessViaWorkers,
|
|
51773
51967
|
buildTFVector,
|
|
51774
51968
|
calculateIDF,
|
|
51775
51969
|
calculateIDFFromTokenSets,
|
|
@@ -51840,6 +52034,7 @@ export {
|
|
|
51840
52034
|
getQuickHint,
|
|
51841
52035
|
getRoleProfile,
|
|
51842
52036
|
getWorkerPoolManager,
|
|
52037
|
+
getWorkerTaskManager,
|
|
51843
52038
|
globalMemoryMonitor,
|
|
51844
52039
|
groupEntitiesByType,
|
|
51845
52040
|
hasAllTags,
|