@danielsimonjr/memoryjs 2.7.0 → 2.8.1

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/index.cjs CHANGED
@@ -3444,6 +3444,10 @@ var init_WorkerTaskManager = __esm({
3444
3444
  touchedPoolIds = /* @__PURE__ */ new Set();
3445
3445
  cancelledTaskIds = /* @__PURE__ */ new Set();
3446
3446
  handleStatus = /* @__PURE__ */ new Map();
3447
+ // Live `pool.exec(...)` promises by task id. Populated when a task starts
3448
+ // running so the handle can call `.cancel()` on the workerpool promise
3449
+ // for mid-execution cancellation. Cleared on completion / failure.
3450
+ liveExecPromises = /* @__PURE__ */ new Map();
3447
3451
  constructor(opts = {}) {
3448
3452
  this.queue = new TaskQueue({
3449
3453
  concurrency: opts.concurrency,
@@ -3483,7 +3487,13 @@ var init_WorkerTaskManager = __esm({
3483
3487
  this.handleStatus.set(id, "running" /* RUNNING */);
3484
3488
  const pool = this.poolManager.getPool(input.workerType, opts.poolConfig);
3485
3489
  this.touchedPoolIds.add(input.workerType);
3486
- return await pool.exec(input.methodName, input.args);
3490
+ const execPromise = pool.exec(input.methodName, input.args);
3491
+ this.liveExecPromises.set(id, execPromise);
3492
+ try {
3493
+ return await execPromise;
3494
+ } finally {
3495
+ this.liveExecPromises.delete(id);
3496
+ }
3487
3497
  }
3488
3498
  };
3489
3499
  const result = this.queue.enqueue(task).then(async (res) => {
@@ -3505,8 +3515,21 @@ var init_WorkerTaskManager = __esm({
3505
3515
  const evicted = this.queue.cancel(id);
3506
3516
  if (evicted) {
3507
3517
  this.handleStatus.set(id, "cancelled" /* CANCELLED */);
3518
+ return true;
3508
3519
  }
3509
- return evicted;
3520
+ const live = this.liveExecPromises.get(id);
3521
+ if (live && typeof live.cancel === "function") {
3522
+ if (live.pending === false) return false;
3523
+ try {
3524
+ live.cancel();
3525
+ this.handleStatus.set(id, "cancelled" /* CANCELLED */);
3526
+ return true;
3527
+ } catch (e) {
3528
+ logger.warn(`[WorkerTaskManager] cancel() on live exec for ${id} threw:`, e);
3529
+ return false;
3530
+ }
3531
+ }
3532
+ return false;
3510
3533
  },
3511
3534
  status: () => this.handleStatus.get(id) ?? "pending" /* PENDING */
3512
3535
  };
@@ -21256,6 +21279,27 @@ CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
21256
21279
  CREATE INDEX IF NOT EXISTS idx_entities_content_hash ON entities(content_hash);
21257
21280
  CREATE INDEX IF NOT EXISTS idx_entities_tags_gin ON entities USING GIN(tags);
21258
21281
 
21282
+ -- v2.8.0 \u2014 generated tsvector column for full-text search.
21283
+ -- name is weighted A (highest), observations B, tags C. PostgreSQL 12+
21284
+ -- supports GENERATED ALWAYS AS ... STORED; the DO block makes the column
21285
+ -- add idempotent on older + newer servers without requiring CREATE OR
21286
+ -- REPLACE semantics on ALTER.
21287
+ DO $$ BEGIN
21288
+ IF NOT EXISTS (
21289
+ SELECT 1 FROM information_schema.columns
21290
+ WHERE table_name = 'entities' AND column_name = 'fts_vector'
21291
+ ) THEN
21292
+ ALTER TABLE entities ADD COLUMN fts_vector tsvector
21293
+ GENERATED ALWAYS AS (
21294
+ setweight(to_tsvector('english', name), 'A') ||
21295
+ setweight(to_tsvector('english', coalesce(array_to_string(observations, ' '), '')), 'B') ||
21296
+ setweight(to_tsvector('english', coalesce(array_to_string(tags, ' '), '')), 'C')
21297
+ ) STORED;
21298
+ END IF;
21299
+ END $$;
21300
+
21301
+ CREATE INDEX IF NOT EXISTS idx_entities_fts_gin ON entities USING GIN(fts_vector);
21302
+
21259
21303
  CREATE TABLE IF NOT EXISTS relations (
21260
21304
  from_name TEXT NOT NULL,
21261
21305
  to_name TEXT NOT NULL,
@@ -21575,6 +21619,38 @@ var PostgreSQLStorage = class {
21575
21619
  hasRelations(entityName) {
21576
21620
  return (this.outgoingRelations.get(entityName)?.length ?? 0) + (this.incomingRelations.get(entityName)?.length ?? 0) > 0;
21577
21621
  }
21622
+ // ==================== Full-text search ====================
21623
+ /**
21624
+ * tsvector-backed full-text search. Uses `plainto_tsquery` so the input is
21625
+ * free-form (no boolean operator syntax required); ranks via `ts_rank` on
21626
+ * the weighted `fts_vector` column (name × A, observations × B, tags × C).
21627
+ *
21628
+ * Results are ordered by descending score and capped at `options.limit`
21629
+ * (default 50). Empty / whitespace-only queries return `[]` without
21630
+ * issuing a SQL call.
21631
+ *
21632
+ * @example
21633
+ * ```typescript
21634
+ * const matches = await storage.fullTextSearch('authentication flow', { limit: 10 });
21635
+ * // → [{ name: 'AuthService', score: 0.62 }, ...]
21636
+ * ```
21637
+ */
21638
+ async fullTextSearch(query, options = {}) {
21639
+ const trimmed = query.trim();
21640
+ if (trimmed.length === 0) return [];
21641
+ await this.initSchema();
21642
+ const pool = await this.getPool();
21643
+ const limit = options.limit ?? 50;
21644
+ const res = await pool.query(
21645
+ `SELECT name, ts_rank(fts_vector, plainto_tsquery('english', $1))::float AS score
21646
+ FROM entities
21647
+ WHERE fts_vector @@ plainto_tsquery('english', $1)
21648
+ ORDER BY score DESC
21649
+ LIMIT $2`,
21650
+ [trimmed, limit]
21651
+ );
21652
+ return res.rows.map((r) => ({ name: r.name, score: Number(r.score) }));
21653
+ }
21578
21654
  // ==================== Utility ====================
21579
21655
  getFilePath() {
21580
21656
  return this.connectionString;