@danielsimonjr/memoryjs 2.4.0 → 2.6.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 +1048 -1152
- package/dist/cli/index.js +434 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +429 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +429 -1
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/dist/index.d.cts
CHANGED
|
@@ -1557,9 +1557,9 @@ interface LowercaseData {
|
|
|
1557
1557
|
* Storage configuration options.
|
|
1558
1558
|
*/
|
|
1559
1559
|
interface StorageConfig {
|
|
1560
|
-
/** Storage type: 'jsonl' or '
|
|
1561
|
-
type: 'jsonl' | 'sqlite';
|
|
1562
|
-
/** Path to storage file */
|
|
1560
|
+
/** Storage type: 'jsonl', 'sqlite', or 'postgres' / 'postgresql' */
|
|
1561
|
+
type: 'jsonl' | 'sqlite' | 'postgres' | 'postgresql';
|
|
1562
|
+
/** Path to storage file (or Postgres connection string when type === 'postgres') */
|
|
1563
1563
|
path: string;
|
|
1564
1564
|
}
|
|
1565
1565
|
/**
|
|
@@ -26409,6 +26409,8 @@ declare class ManagerContext {
|
|
|
26409
26409
|
* Supported storage types:
|
|
26410
26410
|
* - 'jsonl': JSONL file-based storage (default) - simple, human-readable
|
|
26411
26411
|
* - 'sqlite': SQLite database storage (better-sqlite3 native) - indexed, ACID transactions, FTS5
|
|
26412
|
+
* - 'postgres' / 'postgresql': PostgreSQL backend - JSONB, GIN indexes,
|
|
26413
|
+
* optional peer dep on `pg`
|
|
26412
26414
|
*
|
|
26413
26415
|
* @module core/StorageFactory
|
|
26414
26416
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1557,9 +1557,9 @@ interface LowercaseData {
|
|
|
1557
1557
|
* Storage configuration options.
|
|
1558
1558
|
*/
|
|
1559
1559
|
interface StorageConfig {
|
|
1560
|
-
/** Storage type: 'jsonl' or '
|
|
1561
|
-
type: 'jsonl' | 'sqlite';
|
|
1562
|
-
/** Path to storage file */
|
|
1560
|
+
/** Storage type: 'jsonl', 'sqlite', or 'postgres' / 'postgresql' */
|
|
1561
|
+
type: 'jsonl' | 'sqlite' | 'postgres' | 'postgresql';
|
|
1562
|
+
/** Path to storage file (or Postgres connection string when type === 'postgres') */
|
|
1563
1563
|
path: string;
|
|
1564
1564
|
}
|
|
1565
1565
|
/**
|
|
@@ -26409,6 +26409,8 @@ declare class ManagerContext {
|
|
|
26409
26409
|
* Supported storage types:
|
|
26410
26410
|
* - 'jsonl': JSONL file-based storage (default) - simple, human-readable
|
|
26411
26411
|
* - 'sqlite': SQLite database storage (better-sqlite3 native) - indexed, ACID transactions, FTS5
|
|
26412
|
+
* - 'postgres' / 'postgresql': PostgreSQL backend - JSONB, GIN indexes,
|
|
26413
|
+
* optional peer dep on `pg`
|
|
26412
26414
|
*
|
|
26413
26415
|
* @module core/StorageFactory
|
|
26414
26416
|
*/
|
package/dist/index.js
CHANGED
|
@@ -20623,6 +20623,431 @@ var ObservationStore = class _ObservationStore {
|
|
|
20623
20623
|
|
|
20624
20624
|
// src/core/StorageFactory.ts
|
|
20625
20625
|
init_esm_shims();
|
|
20626
|
+
|
|
20627
|
+
// src/core/PostgreSQLStorage.ts
|
|
20628
|
+
init_esm_shims();
|
|
20629
|
+
init_logger();
|
|
20630
|
+
var ENTITY_COLUMNS = [
|
|
20631
|
+
"name",
|
|
20632
|
+
"entity_type",
|
|
20633
|
+
"observations",
|
|
20634
|
+
"parent_id",
|
|
20635
|
+
"tags",
|
|
20636
|
+
"importance",
|
|
20637
|
+
"created_at",
|
|
20638
|
+
"last_modified",
|
|
20639
|
+
"ttl",
|
|
20640
|
+
"confidence",
|
|
20641
|
+
"project_id",
|
|
20642
|
+
"version",
|
|
20643
|
+
"parent_entity_name",
|
|
20644
|
+
"root_entity_name",
|
|
20645
|
+
"is_latest",
|
|
20646
|
+
"superseded_by",
|
|
20647
|
+
"content_hash",
|
|
20648
|
+
"valid_from",
|
|
20649
|
+
"valid_until",
|
|
20650
|
+
"observation_meta",
|
|
20651
|
+
"lifecycle_status",
|
|
20652
|
+
"extra"
|
|
20653
|
+
];
|
|
20654
|
+
var FIRST_CLASS_ENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
20655
|
+
"name",
|
|
20656
|
+
"entityType",
|
|
20657
|
+
"observations",
|
|
20658
|
+
"parentId",
|
|
20659
|
+
"tags",
|
|
20660
|
+
"importance",
|
|
20661
|
+
"createdAt",
|
|
20662
|
+
"lastModified",
|
|
20663
|
+
"ttl",
|
|
20664
|
+
"confidence",
|
|
20665
|
+
"projectId",
|
|
20666
|
+
"version",
|
|
20667
|
+
"parentEntityName",
|
|
20668
|
+
"rootEntityName",
|
|
20669
|
+
"isLatest",
|
|
20670
|
+
"supersededBy",
|
|
20671
|
+
"contentHash",
|
|
20672
|
+
"validFrom",
|
|
20673
|
+
"validUntil",
|
|
20674
|
+
"observationMeta",
|
|
20675
|
+
"lifecycleStatus"
|
|
20676
|
+
]);
|
|
20677
|
+
var SCHEMA_DDL = `
|
|
20678
|
+
CREATE TABLE IF NOT EXISTS entities (
|
|
20679
|
+
name TEXT PRIMARY KEY,
|
|
20680
|
+
entity_type TEXT NOT NULL,
|
|
20681
|
+
observations TEXT[] NOT NULL DEFAULT '{}',
|
|
20682
|
+
parent_id TEXT,
|
|
20683
|
+
tags TEXT[],
|
|
20684
|
+
importance NUMERIC(4, 2),
|
|
20685
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
20686
|
+
last_modified TIMESTAMPTZ DEFAULT NOW(),
|
|
20687
|
+
ttl INTEGER,
|
|
20688
|
+
confidence NUMERIC(4, 3),
|
|
20689
|
+
project_id TEXT,
|
|
20690
|
+
version INTEGER,
|
|
20691
|
+
parent_entity_name TEXT,
|
|
20692
|
+
root_entity_name TEXT,
|
|
20693
|
+
is_latest BOOLEAN,
|
|
20694
|
+
superseded_by TEXT,
|
|
20695
|
+
content_hash TEXT,
|
|
20696
|
+
valid_from TIMESTAMPTZ,
|
|
20697
|
+
valid_until TIMESTAMPTZ,
|
|
20698
|
+
observation_meta JSONB,
|
|
20699
|
+
lifecycle_status TEXT,
|
|
20700
|
+
extra JSONB
|
|
20701
|
+
);
|
|
20702
|
+
|
|
20703
|
+
CREATE INDEX IF NOT EXISTS idx_entities_entity_type ON entities(entity_type);
|
|
20704
|
+
CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
|
|
20705
|
+
CREATE INDEX IF NOT EXISTS idx_entities_content_hash ON entities(content_hash);
|
|
20706
|
+
CREATE INDEX IF NOT EXISTS idx_entities_tags_gin ON entities USING GIN(tags);
|
|
20707
|
+
|
|
20708
|
+
CREATE TABLE IF NOT EXISTS relations (
|
|
20709
|
+
from_name TEXT NOT NULL,
|
|
20710
|
+
to_name TEXT NOT NULL,
|
|
20711
|
+
relation_type TEXT NOT NULL,
|
|
20712
|
+
metadata JSONB,
|
|
20713
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
20714
|
+
valid_from TIMESTAMPTZ,
|
|
20715
|
+
valid_until TIMESTAMPTZ,
|
|
20716
|
+
PRIMARY KEY (from_name, to_name, relation_type)
|
|
20717
|
+
);
|
|
20718
|
+
|
|
20719
|
+
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_name);
|
|
20720
|
+
CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(relation_type);
|
|
20721
|
+
`;
|
|
20722
|
+
var PostgreSQLStorage = class {
|
|
20723
|
+
connectionString;
|
|
20724
|
+
pool = null;
|
|
20725
|
+
cache = null;
|
|
20726
|
+
nameIndex = /* @__PURE__ */ new Map();
|
|
20727
|
+
outgoingRelations = /* @__PURE__ */ new Map();
|
|
20728
|
+
incomingRelations = /* @__PURE__ */ new Map();
|
|
20729
|
+
pendingAppends = 0;
|
|
20730
|
+
schemaInitPromise = null;
|
|
20731
|
+
constructor(connectionString) {
|
|
20732
|
+
this.connectionString = connectionString;
|
|
20733
|
+
}
|
|
20734
|
+
// ==================== Connection management ====================
|
|
20735
|
+
/**
|
|
20736
|
+
* Get or lazily-construct the `pg.Pool`. Throws a friendly error if
|
|
20737
|
+
* the `pg` package isn't installed.
|
|
20738
|
+
*/
|
|
20739
|
+
async getPool() {
|
|
20740
|
+
if (this.pool) return this.pool;
|
|
20741
|
+
let pgModule;
|
|
20742
|
+
try {
|
|
20743
|
+
pgModule = await import("pg");
|
|
20744
|
+
} catch {
|
|
20745
|
+
throw new Error(
|
|
20746
|
+
"The 'pg' package is required for the PostgreSQL backend but is not installed. Run: npm install pg @types/pg"
|
|
20747
|
+
);
|
|
20748
|
+
}
|
|
20749
|
+
this.pool = new pgModule.Pool({ connectionString: this.connectionString });
|
|
20750
|
+
return this.pool;
|
|
20751
|
+
}
|
|
20752
|
+
/**
|
|
20753
|
+
* Run schema DDL idempotently. Called from `ensureLoaded`.
|
|
20754
|
+
*/
|
|
20755
|
+
async initSchema() {
|
|
20756
|
+
if (this.schemaInitPromise) return this.schemaInitPromise;
|
|
20757
|
+
this.schemaInitPromise = (async () => {
|
|
20758
|
+
const pool = await this.getPool();
|
|
20759
|
+
await pool.query(SCHEMA_DDL);
|
|
20760
|
+
})();
|
|
20761
|
+
return this.schemaInitPromise;
|
|
20762
|
+
}
|
|
20763
|
+
// ==================== Row ⇄ Entity / Relation mapping ====================
|
|
20764
|
+
entityToRow(entity) {
|
|
20765
|
+
const extra = {};
|
|
20766
|
+
for (const key of Object.keys(entity)) {
|
|
20767
|
+
if (!FIRST_CLASS_ENTITY_KEYS.has(key)) {
|
|
20768
|
+
extra[key] = entity[key];
|
|
20769
|
+
}
|
|
20770
|
+
}
|
|
20771
|
+
return {
|
|
20772
|
+
name: entity.name,
|
|
20773
|
+
entity_type: entity.entityType,
|
|
20774
|
+
observations: entity.observations,
|
|
20775
|
+
parent_id: entity.parentId ?? null,
|
|
20776
|
+
tags: entity.tags ?? null,
|
|
20777
|
+
importance: entity.importance ?? null,
|
|
20778
|
+
created_at: entity.createdAt ?? null,
|
|
20779
|
+
last_modified: entity.lastModified ?? null,
|
|
20780
|
+
ttl: entity.ttl ?? null,
|
|
20781
|
+
confidence: entity.confidence ?? null,
|
|
20782
|
+
project_id: entity.projectId ?? null,
|
|
20783
|
+
version: entity.version ?? null,
|
|
20784
|
+
parent_entity_name: entity.parentEntityName ?? null,
|
|
20785
|
+
root_entity_name: entity.rootEntityName ?? null,
|
|
20786
|
+
is_latest: entity.isLatest ?? null,
|
|
20787
|
+
superseded_by: entity.supersededBy ?? null,
|
|
20788
|
+
content_hash: entity.contentHash ?? null,
|
|
20789
|
+
valid_from: entity.validFrom ?? null,
|
|
20790
|
+
valid_until: entity.validUntil ?? null,
|
|
20791
|
+
observation_meta: entity.observationMeta ?? null,
|
|
20792
|
+
lifecycle_status: entity.lifecycleStatus ?? null,
|
|
20793
|
+
extra: Object.keys(extra).length > 0 ? extra : null
|
|
20794
|
+
};
|
|
20795
|
+
}
|
|
20796
|
+
rowToEntity(row) {
|
|
20797
|
+
const entity = {
|
|
20798
|
+
name: String(row.name),
|
|
20799
|
+
entityType: String(row.entity_type),
|
|
20800
|
+
observations: Array.isArray(row.observations) ? row.observations : []
|
|
20801
|
+
};
|
|
20802
|
+
if (row.parent_id != null) entity.parentId = String(row.parent_id);
|
|
20803
|
+
if (Array.isArray(row.tags)) entity.tags = row.tags;
|
|
20804
|
+
if (row.importance != null) entity.importance = Number(row.importance);
|
|
20805
|
+
if (row.created_at != null) entity.createdAt = new Date(String(row.created_at)).toISOString();
|
|
20806
|
+
if (row.last_modified != null) entity.lastModified = new Date(String(row.last_modified)).toISOString();
|
|
20807
|
+
if (row.ttl != null) entity.ttl = Number(row.ttl);
|
|
20808
|
+
if (row.confidence != null) entity.confidence = Number(row.confidence);
|
|
20809
|
+
if (row.project_id != null) entity.projectId = String(row.project_id);
|
|
20810
|
+
if (row.version != null) entity.version = Number(row.version);
|
|
20811
|
+
if (row.parent_entity_name != null) entity.parentEntityName = String(row.parent_entity_name);
|
|
20812
|
+
if (row.root_entity_name != null) entity.rootEntityName = String(row.root_entity_name);
|
|
20813
|
+
if (row.is_latest != null) entity.isLatest = Boolean(row.is_latest);
|
|
20814
|
+
if (row.superseded_by != null) entity.supersededBy = String(row.superseded_by);
|
|
20815
|
+
if (row.content_hash != null) entity.contentHash = String(row.content_hash);
|
|
20816
|
+
if (row.valid_from != null) entity.validFrom = new Date(String(row.valid_from)).toISOString();
|
|
20817
|
+
if (row.valid_until != null) entity.validUntil = new Date(String(row.valid_until)).toISOString();
|
|
20818
|
+
if (row.observation_meta != null) {
|
|
20819
|
+
entity.observationMeta = row.observation_meta;
|
|
20820
|
+
}
|
|
20821
|
+
if (row.lifecycle_status != null) {
|
|
20822
|
+
entity.lifecycleStatus = String(row.lifecycle_status);
|
|
20823
|
+
}
|
|
20824
|
+
if (row.extra != null && typeof row.extra === "object") {
|
|
20825
|
+
Object.assign(entity, row.extra);
|
|
20826
|
+
}
|
|
20827
|
+
return entity;
|
|
20828
|
+
}
|
|
20829
|
+
rowToRelation(row) {
|
|
20830
|
+
const relation = {
|
|
20831
|
+
from: String(row.from_name),
|
|
20832
|
+
to: String(row.to_name),
|
|
20833
|
+
relationType: String(row.relation_type)
|
|
20834
|
+
};
|
|
20835
|
+
return relation;
|
|
20836
|
+
}
|
|
20837
|
+
// ==================== Cache management ====================
|
|
20838
|
+
rebuildIndexes(graph) {
|
|
20839
|
+
this.nameIndex.clear();
|
|
20840
|
+
this.outgoingRelations.clear();
|
|
20841
|
+
this.incomingRelations.clear();
|
|
20842
|
+
for (const e of graph.entities) this.nameIndex.set(e.name, e);
|
|
20843
|
+
for (const r of graph.relations) {
|
|
20844
|
+
const outArr = this.outgoingRelations.get(r.from) ?? [];
|
|
20845
|
+
outArr.push(r);
|
|
20846
|
+
this.outgoingRelations.set(r.from, outArr);
|
|
20847
|
+
const inArr = this.incomingRelations.get(r.to) ?? [];
|
|
20848
|
+
inArr.push(r);
|
|
20849
|
+
this.incomingRelations.set(r.to, inArr);
|
|
20850
|
+
}
|
|
20851
|
+
}
|
|
20852
|
+
// ==================== IGraphStorage — read ====================
|
|
20853
|
+
async loadGraph() {
|
|
20854
|
+
await this.ensureLoaded();
|
|
20855
|
+
return this.cache;
|
|
20856
|
+
}
|
|
20857
|
+
async getGraphForMutation() {
|
|
20858
|
+
await this.ensureLoaded();
|
|
20859
|
+
const cache = this.cache;
|
|
20860
|
+
return {
|
|
20861
|
+
entities: cache.entities.map((e) => ({
|
|
20862
|
+
...e,
|
|
20863
|
+
observations: [...e.observations],
|
|
20864
|
+
tags: e.tags ? [...e.tags] : void 0
|
|
20865
|
+
})),
|
|
20866
|
+
relations: cache.relations.map((r) => ({ ...r }))
|
|
20867
|
+
};
|
|
20868
|
+
}
|
|
20869
|
+
async ensureLoaded() {
|
|
20870
|
+
if (this.cache) return;
|
|
20871
|
+
await this.initSchema();
|
|
20872
|
+
const pool = await this.getPool();
|
|
20873
|
+
const [eRes, rRes] = await Promise.all([
|
|
20874
|
+
pool.query("SELECT * FROM entities"),
|
|
20875
|
+
pool.query("SELECT * FROM relations")
|
|
20876
|
+
]);
|
|
20877
|
+
const graph = {
|
|
20878
|
+
entities: eRes.rows.map((r) => this.rowToEntity(r)),
|
|
20879
|
+
relations: rRes.rows.map((r) => this.rowToRelation(r))
|
|
20880
|
+
};
|
|
20881
|
+
this.cache = graph;
|
|
20882
|
+
this.rebuildIndexes(graph);
|
|
20883
|
+
}
|
|
20884
|
+
get cachedGraph() {
|
|
20885
|
+
return this.cache;
|
|
20886
|
+
}
|
|
20887
|
+
// ==================== IGraphStorage — write ====================
|
|
20888
|
+
async saveGraph(graph) {
|
|
20889
|
+
await this.initSchema();
|
|
20890
|
+
const pool = await this.getPool();
|
|
20891
|
+
await pool.query("TRUNCATE entities, relations");
|
|
20892
|
+
for (const entity of graph.entities) {
|
|
20893
|
+
await this.insertEntityRow(pool, entity);
|
|
20894
|
+
}
|
|
20895
|
+
for (const relation of graph.relations) {
|
|
20896
|
+
await this.insertRelationRow(pool, relation);
|
|
20897
|
+
}
|
|
20898
|
+
this.cache = {
|
|
20899
|
+
entities: graph.entities.map((e) => ({ ...e })),
|
|
20900
|
+
relations: graph.relations.map((r) => ({ ...r }))
|
|
20901
|
+
};
|
|
20902
|
+
this.rebuildIndexes(this.cache);
|
|
20903
|
+
this.pendingAppends = 0;
|
|
20904
|
+
}
|
|
20905
|
+
async insertEntityRow(pool, entity) {
|
|
20906
|
+
const row = this.entityToRow(entity);
|
|
20907
|
+
const cols = ENTITY_COLUMNS.join(", ");
|
|
20908
|
+
const placeholders = ENTITY_COLUMNS.map((_, i) => `$${i + 1}`).join(", ");
|
|
20909
|
+
const values = ENTITY_COLUMNS.map((c) => row[c]);
|
|
20910
|
+
await pool.query(
|
|
20911
|
+
`INSERT INTO entities (${cols}) VALUES (${placeholders})
|
|
20912
|
+
ON CONFLICT (name) DO UPDATE SET ${ENTITY_COLUMNS.filter((c) => c !== "name").map((c) => `${c} = EXCLUDED.${c}`).join(", ")}`,
|
|
20913
|
+
values
|
|
20914
|
+
);
|
|
20915
|
+
}
|
|
20916
|
+
async insertRelationRow(pool, relation) {
|
|
20917
|
+
await pool.query(
|
|
20918
|
+
`INSERT INTO relations (from_name, to_name, relation_type)
|
|
20919
|
+
VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`,
|
|
20920
|
+
[relation.from, relation.to, relation.relationType]
|
|
20921
|
+
);
|
|
20922
|
+
}
|
|
20923
|
+
async appendEntity(entity) {
|
|
20924
|
+
await this.ensureLoaded();
|
|
20925
|
+
const pool = await this.getPool();
|
|
20926
|
+
await this.insertEntityRow(pool, entity);
|
|
20927
|
+
if (!this.cache) return;
|
|
20928
|
+
const existingIdx = this.cache.entities.findIndex((e) => e.name === entity.name);
|
|
20929
|
+
if (existingIdx >= 0) this.cache.entities[existingIdx] = entity;
|
|
20930
|
+
else this.cache.entities.push(entity);
|
|
20931
|
+
this.nameIndex.set(entity.name, entity);
|
|
20932
|
+
this.pendingAppends += 1;
|
|
20933
|
+
}
|
|
20934
|
+
async appendRelation(relation) {
|
|
20935
|
+
await this.ensureLoaded();
|
|
20936
|
+
const pool = await this.getPool();
|
|
20937
|
+
await this.insertRelationRow(pool, relation);
|
|
20938
|
+
if (!this.cache) return;
|
|
20939
|
+
const duplicate = this.cache.relations.some(
|
|
20940
|
+
(r) => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType
|
|
20941
|
+
);
|
|
20942
|
+
if (!duplicate) {
|
|
20943
|
+
this.cache.relations.push(relation);
|
|
20944
|
+
const outArr = this.outgoingRelations.get(relation.from) ?? [];
|
|
20945
|
+
outArr.push(relation);
|
|
20946
|
+
this.outgoingRelations.set(relation.from, outArr);
|
|
20947
|
+
const inArr = this.incomingRelations.get(relation.to) ?? [];
|
|
20948
|
+
inArr.push(relation);
|
|
20949
|
+
this.incomingRelations.set(relation.to, inArr);
|
|
20950
|
+
}
|
|
20951
|
+
this.pendingAppends += 1;
|
|
20952
|
+
}
|
|
20953
|
+
async updateEntity(entityName, updates) {
|
|
20954
|
+
await this.ensureLoaded();
|
|
20955
|
+
const existing = this.nameIndex.get(entityName);
|
|
20956
|
+
if (!existing) return false;
|
|
20957
|
+
const merged = {
|
|
20958
|
+
...existing,
|
|
20959
|
+
...updates,
|
|
20960
|
+
name: entityName,
|
|
20961
|
+
lastModified: updates.lastModified ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
20962
|
+
};
|
|
20963
|
+
const pool = await this.getPool();
|
|
20964
|
+
await this.insertEntityRow(pool, merged);
|
|
20965
|
+
if (this.cache) {
|
|
20966
|
+
const idx = this.cache.entities.findIndex((e) => e.name === entityName);
|
|
20967
|
+
if (idx >= 0) this.cache.entities[idx] = merged;
|
|
20968
|
+
}
|
|
20969
|
+
this.nameIndex.set(entityName, merged);
|
|
20970
|
+
this.pendingAppends += 1;
|
|
20971
|
+
return true;
|
|
20972
|
+
}
|
|
20973
|
+
async compact() {
|
|
20974
|
+
if (this.cache) await this.saveGraph(this.cache);
|
|
20975
|
+
}
|
|
20976
|
+
clearCache() {
|
|
20977
|
+
this.cache = null;
|
|
20978
|
+
this.nameIndex.clear();
|
|
20979
|
+
this.outgoingRelations.clear();
|
|
20980
|
+
this.incomingRelations.clear();
|
|
20981
|
+
this.pendingAppends = 0;
|
|
20982
|
+
}
|
|
20983
|
+
// ==================== IGraphStorage — sync getters ====================
|
|
20984
|
+
getEntityByName(name) {
|
|
20985
|
+
return this.nameIndex.get(name);
|
|
20986
|
+
}
|
|
20987
|
+
hasEntity(name) {
|
|
20988
|
+
return this.nameIndex.has(name);
|
|
20989
|
+
}
|
|
20990
|
+
getEntitiesByType(entityType) {
|
|
20991
|
+
const result = [];
|
|
20992
|
+
for (const entity of this.nameIndex.values()) {
|
|
20993
|
+
if (entity.entityType === entityType) result.push(entity);
|
|
20994
|
+
}
|
|
20995
|
+
return result;
|
|
20996
|
+
}
|
|
20997
|
+
getEntityTypes() {
|
|
20998
|
+
const types = /* @__PURE__ */ new Set();
|
|
20999
|
+
for (const entity of this.nameIndex.values()) types.add(entity.entityType);
|
|
21000
|
+
return Array.from(types);
|
|
21001
|
+
}
|
|
21002
|
+
getLowercased(entityName) {
|
|
21003
|
+
const e = this.nameIndex.get(entityName);
|
|
21004
|
+
if (!e) return void 0;
|
|
21005
|
+
return {
|
|
21006
|
+
name: e.name.toLowerCase(),
|
|
21007
|
+
entityType: e.entityType.toLowerCase(),
|
|
21008
|
+
observations: e.observations.map((o) => o.toLowerCase()),
|
|
21009
|
+
tags: e.tags?.map((t) => t.toLowerCase()) ?? []
|
|
21010
|
+
};
|
|
21011
|
+
}
|
|
21012
|
+
getRelationsFrom(entityName) {
|
|
21013
|
+
return this.outgoingRelations.get(entityName) ?? [];
|
|
21014
|
+
}
|
|
21015
|
+
getRelationsTo(entityName) {
|
|
21016
|
+
return this.incomingRelations.get(entityName) ?? [];
|
|
21017
|
+
}
|
|
21018
|
+
getRelationsFor(entityName) {
|
|
21019
|
+
return [
|
|
21020
|
+
...this.outgoingRelations.get(entityName) ?? [],
|
|
21021
|
+
...this.incomingRelations.get(entityName) ?? []
|
|
21022
|
+
];
|
|
21023
|
+
}
|
|
21024
|
+
hasRelations(entityName) {
|
|
21025
|
+
return (this.outgoingRelations.get(entityName)?.length ?? 0) + (this.incomingRelations.get(entityName)?.length ?? 0) > 0;
|
|
21026
|
+
}
|
|
21027
|
+
// ==================== Utility ====================
|
|
21028
|
+
getFilePath() {
|
|
21029
|
+
return this.connectionString;
|
|
21030
|
+
}
|
|
21031
|
+
getPendingAppends() {
|
|
21032
|
+
return this.pendingAppends;
|
|
21033
|
+
}
|
|
21034
|
+
/**
|
|
21035
|
+
* Close the underlying `pg.Pool`. Call from process-shutdown handlers to
|
|
21036
|
+
* avoid keeping the event loop alive on idle connections.
|
|
21037
|
+
*/
|
|
21038
|
+
async close() {
|
|
21039
|
+
if (this.pool) {
|
|
21040
|
+
try {
|
|
21041
|
+
await this.pool.end();
|
|
21042
|
+
} catch (e) {
|
|
21043
|
+
logger.warn("PostgreSQLStorage.close: pool.end failed:", e);
|
|
21044
|
+
}
|
|
21045
|
+
this.pool = null;
|
|
21046
|
+
}
|
|
21047
|
+
}
|
|
21048
|
+
};
|
|
21049
|
+
|
|
21050
|
+
// src/core/StorageFactory.ts
|
|
20626
21051
|
var DEFAULT_STORAGE_TYPE = "jsonl";
|
|
20627
21052
|
function createStorage(config) {
|
|
20628
21053
|
const storageType = process.env.MEMORY_STORAGE_TYPE || config.type || DEFAULT_STORAGE_TYPE;
|
|
@@ -20631,9 +21056,12 @@ function createStorage(config) {
|
|
|
20631
21056
|
return new GraphStorage(config.path);
|
|
20632
21057
|
case "sqlite":
|
|
20633
21058
|
return new SQLiteStorage(config.path);
|
|
21059
|
+
case "postgres":
|
|
21060
|
+
case "postgresql":
|
|
21061
|
+
return new PostgreSQLStorage(config.path);
|
|
20634
21062
|
default:
|
|
20635
21063
|
throw new Error(
|
|
20636
|
-
`Unknown storage type: ${storageType}. Supported types: jsonl, sqlite`
|
|
21064
|
+
`Unknown storage type: ${storageType}. Supported types: jsonl, sqlite, postgres`
|
|
20637
21065
|
);
|
|
20638
21066
|
}
|
|
20639
21067
|
}
|