@utaba/deep-memory 0.1.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/LICENSE +190 -0
- package/README.md +224 -0
- package/dist/StorageProvider-CJjz8uBY.d.ts +56 -0
- package/dist/StorageProvider-CkFjdboX.d.cts +56 -0
- package/dist/index.cjs +4054 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +408 -0
- package/dist/index.d.ts +408 -0
- package/dist/index.js +4004 -0
- package/dist/index.js.map +1 -0
- package/dist/portability-DdlNYXGX.d.cts +897 -0
- package/dist/portability-DdlNYXGX.d.ts +897 -0
- package/dist/providers/index.cjs +19 -0
- package/dist/providers/index.cjs.map +1 -0
- package/dist/providers/index.d.cts +75 -0
- package/dist/providers/index.d.ts +75 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/testing/conformance.cjs +357 -0
- package/dist/testing/conformance.cjs.map +1 -0
- package/dist/testing/conformance.d.cts +12 -0
- package/dist/testing/conformance.d.ts +12 -0
- package/dist/testing/conformance.js +332 -0
- package/dist/testing/conformance.js.map +1 -0
- package/dist/types/index.cjs +19 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +121 -0
- package/dist/types/index.d.ts +121 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/providers/index.ts
|
|
17
|
+
var providers_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(providers_exports);
|
|
19
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/providers/index.ts"],"sourcesContent":["// Provider interface re-exports — @utaba/deep-memory/providers\n\nexport type { StorageProvider } from './StorageProvider.js';\nexport type { EmbeddingProvider } from './EmbeddingProvider.js';\nexport type {\n SearchProvider,\n SearchableEntity,\n} from './SearchProvider.js';\nexport type {\n LockProvider,\n LockOptions,\n LockHandle,\n} from './LockProvider.js';\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export { S as StorageProvider } from '../StorageProvider-CkFjdboX.cjs';
|
|
2
|
+
import { a5 as SearchOptions, P as PaginatedResult, a6 as SearchHit } from '../portability-DdlNYXGX.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EmbeddingProvider — enables semantic search and vocabulary deduplication.
|
|
6
|
+
*
|
|
7
|
+
* Optional. When not provided:
|
|
8
|
+
* - `searchByConcept` throws EmbeddingProviderRequiredError
|
|
9
|
+
* - Vocabulary deduplication falls back to string similarity (Jaro-Winkler)
|
|
10
|
+
* - Entity creation skips embedding generation
|
|
11
|
+
*/
|
|
12
|
+
interface EmbeddingProvider {
|
|
13
|
+
/** Generate a single embedding vector */
|
|
14
|
+
embed(text: string): Promise<number[]>;
|
|
15
|
+
/** Generate embeddings in batch (more efficient for bulk operations) */
|
|
16
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
17
|
+
/** The dimensionality of the embedding vectors (e.g., 1536, 768) */
|
|
18
|
+
dimensions(): number;
|
|
19
|
+
/** Identifier for the model used — stored with embeddings for compatibility tracking */
|
|
20
|
+
modelId(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Compute cosine similarity between two vectors.
|
|
23
|
+
* Default implementation provided by the library — override for optimised hardware paths.
|
|
24
|
+
*/
|
|
25
|
+
similarity?(a: number[], b: number[]): number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Entity data indexed for full-text search */
|
|
29
|
+
interface SearchableEntity {
|
|
30
|
+
entityId: string;
|
|
31
|
+
entityType: string;
|
|
32
|
+
label: string;
|
|
33
|
+
summary?: string;
|
|
34
|
+
properties?: Record<string, unknown>;
|
|
35
|
+
data?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SearchProvider — enhances entity finding with full-text search.
|
|
39
|
+
*
|
|
40
|
+
* Optional. When not provided, `findEntities` falls back to the
|
|
41
|
+
* StorageProvider's basic label/property matching.
|
|
42
|
+
*/
|
|
43
|
+
interface SearchProvider {
|
|
44
|
+
/** Index an entity for full-text search */
|
|
45
|
+
indexEntity(repositoryId: string, entity: SearchableEntity): Promise<void>;
|
|
46
|
+
/** Remove an entity from the search index */
|
|
47
|
+
removeEntity(repositoryId: string, entityId: string): Promise<void>;
|
|
48
|
+
/** Full-text search across entity labels, summaries, properties, and data */
|
|
49
|
+
search(repositoryId: string, query: string, options?: SearchOptions): Promise<PaginatedResult<SearchHit>>;
|
|
50
|
+
/** Re-index an entire repository (for rebuild/migration) */
|
|
51
|
+
reindexRepository?(repositoryId: string, entities: AsyncIterable<SearchableEntity>): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* LockProvider — for multi-process or distributed deployments.
|
|
56
|
+
*
|
|
57
|
+
* Optional. When not provided, the core uses a no-op lock internally.
|
|
58
|
+
* Used for vocabulary modifications and bulk import operations.
|
|
59
|
+
*/
|
|
60
|
+
interface LockProvider {
|
|
61
|
+
/** Acquire a named lock. Returns a handle to release it. Throws if not acquired within timeout. */
|
|
62
|
+
acquire(lockName: string, options?: LockOptions): Promise<LockHandle>;
|
|
63
|
+
}
|
|
64
|
+
interface LockOptions {
|
|
65
|
+
/** Max wait to acquire in milliseconds (default 5000) */
|
|
66
|
+
timeoutMs?: number;
|
|
67
|
+
/** Auto-release after TTL in milliseconds (default 30000) */
|
|
68
|
+
ttlMs?: number;
|
|
69
|
+
}
|
|
70
|
+
interface LockHandle {
|
|
71
|
+
release(): Promise<void>;
|
|
72
|
+
extend(additionalMs: number): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type { EmbeddingProvider, LockHandle, LockOptions, LockProvider, SearchProvider, SearchableEntity };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export { S as StorageProvider } from '../StorageProvider-CJjz8uBY.js';
|
|
2
|
+
import { a5 as SearchOptions, P as PaginatedResult, a6 as SearchHit } from '../portability-DdlNYXGX.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EmbeddingProvider — enables semantic search and vocabulary deduplication.
|
|
6
|
+
*
|
|
7
|
+
* Optional. When not provided:
|
|
8
|
+
* - `searchByConcept` throws EmbeddingProviderRequiredError
|
|
9
|
+
* - Vocabulary deduplication falls back to string similarity (Jaro-Winkler)
|
|
10
|
+
* - Entity creation skips embedding generation
|
|
11
|
+
*/
|
|
12
|
+
interface EmbeddingProvider {
|
|
13
|
+
/** Generate a single embedding vector */
|
|
14
|
+
embed(text: string): Promise<number[]>;
|
|
15
|
+
/** Generate embeddings in batch (more efficient for bulk operations) */
|
|
16
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
17
|
+
/** The dimensionality of the embedding vectors (e.g., 1536, 768) */
|
|
18
|
+
dimensions(): number;
|
|
19
|
+
/** Identifier for the model used — stored with embeddings for compatibility tracking */
|
|
20
|
+
modelId(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Compute cosine similarity between two vectors.
|
|
23
|
+
* Default implementation provided by the library — override for optimised hardware paths.
|
|
24
|
+
*/
|
|
25
|
+
similarity?(a: number[], b: number[]): number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Entity data indexed for full-text search */
|
|
29
|
+
interface SearchableEntity {
|
|
30
|
+
entityId: string;
|
|
31
|
+
entityType: string;
|
|
32
|
+
label: string;
|
|
33
|
+
summary?: string;
|
|
34
|
+
properties?: Record<string, unknown>;
|
|
35
|
+
data?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SearchProvider — enhances entity finding with full-text search.
|
|
39
|
+
*
|
|
40
|
+
* Optional. When not provided, `findEntities` falls back to the
|
|
41
|
+
* StorageProvider's basic label/property matching.
|
|
42
|
+
*/
|
|
43
|
+
interface SearchProvider {
|
|
44
|
+
/** Index an entity for full-text search */
|
|
45
|
+
indexEntity(repositoryId: string, entity: SearchableEntity): Promise<void>;
|
|
46
|
+
/** Remove an entity from the search index */
|
|
47
|
+
removeEntity(repositoryId: string, entityId: string): Promise<void>;
|
|
48
|
+
/** Full-text search across entity labels, summaries, properties, and data */
|
|
49
|
+
search(repositoryId: string, query: string, options?: SearchOptions): Promise<PaginatedResult<SearchHit>>;
|
|
50
|
+
/** Re-index an entire repository (for rebuild/migration) */
|
|
51
|
+
reindexRepository?(repositoryId: string, entities: AsyncIterable<SearchableEntity>): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* LockProvider — for multi-process or distributed deployments.
|
|
56
|
+
*
|
|
57
|
+
* Optional. When not provided, the core uses a no-op lock internally.
|
|
58
|
+
* Used for vocabulary modifications and bulk import operations.
|
|
59
|
+
*/
|
|
60
|
+
interface LockProvider {
|
|
61
|
+
/** Acquire a named lock. Returns a handle to release it. Throws if not acquired within timeout. */
|
|
62
|
+
acquire(lockName: string, options?: LockOptions): Promise<LockHandle>;
|
|
63
|
+
}
|
|
64
|
+
interface LockOptions {
|
|
65
|
+
/** Max wait to acquire in milliseconds (default 5000) */
|
|
66
|
+
timeoutMs?: number;
|
|
67
|
+
/** Auto-release after TTL in milliseconds (default 30000) */
|
|
68
|
+
ttlMs?: number;
|
|
69
|
+
}
|
|
70
|
+
interface LockHandle {
|
|
71
|
+
release(): Promise<void>;
|
|
72
|
+
extend(additionalMs: number): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type { EmbeddingProvider, LockHandle, LockOptions, LockProvider, SearchProvider, SearchableEntity };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/providers-builtin/conformance.ts
|
|
21
|
+
var conformance_exports = {};
|
|
22
|
+
__export(conformance_exports, {
|
|
23
|
+
runStorageProviderConformanceTests: () => runStorageProviderConformanceTests
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(conformance_exports);
|
|
26
|
+
var import_vitest = require("vitest");
|
|
27
|
+
function makeProvenance() {
|
|
28
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29
|
+
return {
|
|
30
|
+
createdBy: "conformance-test",
|
|
31
|
+
createdByType: "agent",
|
|
32
|
+
createdAt: now,
|
|
33
|
+
modifiedBy: "conformance-test",
|
|
34
|
+
modifiedByType: "agent",
|
|
35
|
+
modifiedAt: now
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function makeEntity(id, type = "test-type", label) {
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, "-")}`,
|
|
42
|
+
entityType: type,
|
|
43
|
+
label: label ?? id,
|
|
44
|
+
summary: `Summary for ${id}`,
|
|
45
|
+
properties: { key: "value" },
|
|
46
|
+
provenance: makeProvenance()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function makeRelationship(id, type, sourceId, targetId, bidirectional = false) {
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
relationshipType: type,
|
|
53
|
+
sourceEntityId: sourceId,
|
|
54
|
+
targetEntityId: targetId,
|
|
55
|
+
properties: {},
|
|
56
|
+
bidirectional,
|
|
57
|
+
provenance: makeProvenance()
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function runStorageProviderConformanceTests(factory) {
|
|
61
|
+
const repoId = "40000000-0000-4000-a000-000000000001";
|
|
62
|
+
let provider;
|
|
63
|
+
async function setup() {
|
|
64
|
+
provider = await factory();
|
|
65
|
+
if (provider.initialise) await provider.initialise();
|
|
66
|
+
await provider.createRepository({
|
|
67
|
+
repositoryId: repoId,
|
|
68
|
+
label: "Conformance Test",
|
|
69
|
+
governanceConfig: { mode: "open" },
|
|
70
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
71
|
+
createdBy: "conformance-test"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
(0, import_vitest.describe)("StorageProvider Conformance Tests", () => {
|
|
75
|
+
(0, import_vitest.beforeEach)(async () => {
|
|
76
|
+
await setup();
|
|
77
|
+
});
|
|
78
|
+
(0, import_vitest.describe)("repository operations", () => {
|
|
79
|
+
(0, import_vitest.it)("creates a repository", async () => {
|
|
80
|
+
const repo = await provider.getRepository(repoId);
|
|
81
|
+
(0, import_vitest.expect)(repo).not.toBeNull();
|
|
82
|
+
(0, import_vitest.expect)(repo.repositoryId).toBe(repoId);
|
|
83
|
+
(0, import_vitest.expect)(repo.label).toBe("Conformance Test");
|
|
84
|
+
});
|
|
85
|
+
(0, import_vitest.it)("returns null for non-existent repository", async () => {
|
|
86
|
+
const repo = await provider.getRepository("ffffffff-ffff-4fff-afff-ffffffffffff");
|
|
87
|
+
(0, import_vitest.expect)(repo).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
(0, import_vitest.it)("lists repositories", async () => {
|
|
90
|
+
const list = await provider.listRepositories();
|
|
91
|
+
(0, import_vitest.expect)(list.items.length).toBeGreaterThanOrEqual(1);
|
|
92
|
+
(0, import_vitest.expect)(list.items.some((r) => r.repositoryId === repoId)).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
(0, import_vitest.it)("updates a repository", async () => {
|
|
95
|
+
const updated = await provider.updateRepository(repoId, {
|
|
96
|
+
label: "Updated Label",
|
|
97
|
+
description: "Updated description",
|
|
98
|
+
governanceConfig: { mode: "open", defaultSimilarityThreshold: 0.4 }
|
|
99
|
+
});
|
|
100
|
+
(0, import_vitest.expect)(updated.label).toBe("Updated Label");
|
|
101
|
+
(0, import_vitest.expect)(updated.description).toBe("Updated description");
|
|
102
|
+
(0, import_vitest.expect)(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);
|
|
103
|
+
const fetched = await provider.getRepository(repoId);
|
|
104
|
+
(0, import_vitest.expect)(fetched.label).toBe("Updated Label");
|
|
105
|
+
(0, import_vitest.expect)(fetched.governanceConfig.defaultSimilarityThreshold).toBe(0.4);
|
|
106
|
+
});
|
|
107
|
+
(0, import_vitest.it)("deletes a repository", async () => {
|
|
108
|
+
await provider.deleteRepository(repoId);
|
|
109
|
+
(0, import_vitest.expect)(await provider.getRepository(repoId)).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
(0, import_vitest.it)("returns repository stats", async () => {
|
|
112
|
+
const stats = await provider.getRepositoryStats(repoId);
|
|
113
|
+
(0, import_vitest.expect)(stats.entityCount).toBe(0);
|
|
114
|
+
(0, import_vitest.expect)(stats.relationshipCount).toBe(0);
|
|
115
|
+
(0, import_vitest.expect)(typeof stats.vocabularyVersion).toBe("string");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
(0, import_vitest.describe)("vocabulary operations", () => {
|
|
119
|
+
(0, import_vitest.it)("gets and saves vocabulary", async () => {
|
|
120
|
+
const vocab = await provider.getVocabulary(repoId);
|
|
121
|
+
(0, import_vitest.expect)(vocab).toBeDefined();
|
|
122
|
+
(0, import_vitest.expect)(typeof vocab.version).toBe("string");
|
|
123
|
+
const updated = { ...vocab, version: "1.0.0" };
|
|
124
|
+
await provider.saveVocabulary(repoId, updated);
|
|
125
|
+
const fetched = await provider.getVocabulary(repoId);
|
|
126
|
+
(0, import_vitest.expect)(fetched.version).toBe("1.0.0");
|
|
127
|
+
});
|
|
128
|
+
(0, import_vitest.it)("returns vocabulary change log", async () => {
|
|
129
|
+
const log = await provider.getVocabularyChangeLog(repoId);
|
|
130
|
+
(0, import_vitest.expect)(Array.isArray(log.items)).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
(0, import_vitest.describe)("entity operations", () => {
|
|
134
|
+
(0, import_vitest.it)("creates and retrieves an entity", async () => {
|
|
135
|
+
const entity = makeEntity("e1");
|
|
136
|
+
await provider.createEntity(repoId, entity);
|
|
137
|
+
const retrieved = await provider.getEntity(repoId, "e1");
|
|
138
|
+
(0, import_vitest.expect)(retrieved).not.toBeNull();
|
|
139
|
+
(0, import_vitest.expect)(retrieved.id).toBe("e1");
|
|
140
|
+
(0, import_vitest.expect)(retrieved.label).toBe("e1");
|
|
141
|
+
});
|
|
142
|
+
(0, import_vitest.it)("retrieves an entity by slug", async () => {
|
|
143
|
+
const entity = makeEntity("e1", "test-type", "Alpha");
|
|
144
|
+
await provider.createEntity(repoId, entity);
|
|
145
|
+
const retrieved = await provider.getEntityBySlug(repoId, entity.slug);
|
|
146
|
+
(0, import_vitest.expect)(retrieved).not.toBeNull();
|
|
147
|
+
(0, import_vitest.expect)(retrieved.id).toBe("e1");
|
|
148
|
+
(0, import_vitest.expect)(retrieved.slug).toBe(entity.slug);
|
|
149
|
+
});
|
|
150
|
+
(0, import_vitest.it)("returns null for non-existent entity", async () => {
|
|
151
|
+
const result = await provider.getEntity(repoId, "nonexistent");
|
|
152
|
+
(0, import_vitest.expect)(result).toBeNull();
|
|
153
|
+
});
|
|
154
|
+
(0, import_vitest.it)("returns null for non-existent slug", async () => {
|
|
155
|
+
const result = await provider.getEntityBySlug(repoId, "nonexistent:slug");
|
|
156
|
+
(0, import_vitest.expect)(result).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
(0, import_vitest.it)("batch retrieves entities", async () => {
|
|
159
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
160
|
+
await provider.createEntity(repoId, makeEntity("e2"));
|
|
161
|
+
const map = await provider.getEntities(repoId, ["e1", "e2", "missing"]);
|
|
162
|
+
(0, import_vitest.expect)(map.size).toBe(2);
|
|
163
|
+
(0, import_vitest.expect)(map.has("e1")).toBe(true);
|
|
164
|
+
(0, import_vitest.expect)(map.has("e2")).toBe(true);
|
|
165
|
+
(0, import_vitest.expect)(map.has("missing")).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
(0, import_vitest.it)("updates an entity", async () => {
|
|
168
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
169
|
+
const updated = await provider.updateEntity(repoId, "e1", {
|
|
170
|
+
label: "Updated Label",
|
|
171
|
+
provenance: makeProvenance()
|
|
172
|
+
});
|
|
173
|
+
(0, import_vitest.expect)(updated.label).toBe("Updated Label");
|
|
174
|
+
const fetched = await provider.getEntity(repoId, "e1");
|
|
175
|
+
(0, import_vitest.expect)(fetched.label).toBe("Updated Label");
|
|
176
|
+
});
|
|
177
|
+
(0, import_vitest.it)("deletes an entity", async () => {
|
|
178
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
179
|
+
await provider.deleteEntity(repoId, "e1");
|
|
180
|
+
(0, import_vitest.expect)(await provider.getEntity(repoId, "e1")).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
(0, import_vitest.it)("finds entities by search term", async () => {
|
|
183
|
+
await provider.createEntity(repoId, makeEntity("e1", "test-type", "Alpha"));
|
|
184
|
+
await provider.createEntity(repoId, makeEntity("e2", "test-type", "Beta"));
|
|
185
|
+
const result = await provider.findEntities(repoId, {
|
|
186
|
+
searchTerm: "alpha",
|
|
187
|
+
limit: 10,
|
|
188
|
+
offset: 0
|
|
189
|
+
});
|
|
190
|
+
(0, import_vitest.expect)(result.items).toHaveLength(1);
|
|
191
|
+
(0, import_vitest.expect)(result.items[0].label).toBe("Alpha");
|
|
192
|
+
});
|
|
193
|
+
(0, import_vitest.it)("finds entities by type filter", async () => {
|
|
194
|
+
await provider.createEntity(repoId, makeEntity("e1", "type-a", "A"));
|
|
195
|
+
await provider.createEntity(repoId, makeEntity("e2", "type-b", "B"));
|
|
196
|
+
const result = await provider.findEntities(repoId, {
|
|
197
|
+
entityTypes: ["type-a"],
|
|
198
|
+
limit: 10,
|
|
199
|
+
offset: 0
|
|
200
|
+
});
|
|
201
|
+
(0, import_vitest.expect)(result.items).toHaveLength(1);
|
|
202
|
+
(0, import_vitest.expect)(result.items[0].entityType).toBe("type-a");
|
|
203
|
+
});
|
|
204
|
+
(0, import_vitest.it)("paginates find results", async () => {
|
|
205
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
206
|
+
await provider.createEntity(repoId, makeEntity("e2"));
|
|
207
|
+
await provider.createEntity(repoId, makeEntity("e3"));
|
|
208
|
+
const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });
|
|
209
|
+
(0, import_vitest.expect)(page1.items).toHaveLength(2);
|
|
210
|
+
(0, import_vitest.expect)(page1.hasMore).toBe(true);
|
|
211
|
+
const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });
|
|
212
|
+
(0, import_vitest.expect)(page2.items).toHaveLength(1);
|
|
213
|
+
(0, import_vitest.expect)(page2.hasMore).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
(0, import_vitest.describe)("relationship operations", () => {
|
|
217
|
+
(0, import_vitest.beforeEach)(async () => {
|
|
218
|
+
await provider.createEntity(repoId, makeEntity("a"));
|
|
219
|
+
await provider.createEntity(repoId, makeEntity("b"));
|
|
220
|
+
await provider.createEntity(repoId, makeEntity("c"));
|
|
221
|
+
});
|
|
222
|
+
(0, import_vitest.it)("creates and retrieves a relationship", async () => {
|
|
223
|
+
const rel = makeRelationship("r1", "connects", "a", "b");
|
|
224
|
+
await provider.createRelationship(repoId, rel);
|
|
225
|
+
const retrieved = await provider.getRelationship(repoId, "r1");
|
|
226
|
+
(0, import_vitest.expect)(retrieved).not.toBeNull();
|
|
227
|
+
(0, import_vitest.expect)(retrieved.sourceEntityId).toBe("a");
|
|
228
|
+
(0, import_vitest.expect)(retrieved.targetEntityId).toBe("b");
|
|
229
|
+
});
|
|
230
|
+
(0, import_vitest.it)("returns null for non-existent relationship", async () => {
|
|
231
|
+
(0, import_vitest.expect)(await provider.getRelationship(repoId, "nonexistent")).toBeNull();
|
|
232
|
+
});
|
|
233
|
+
(0, import_vitest.it)("gets entity relationships", async () => {
|
|
234
|
+
await provider.createRelationship(repoId, makeRelationship("r1", "connects", "a", "b"));
|
|
235
|
+
await provider.createRelationship(repoId, makeRelationship("r2", "connects", "c", "a"));
|
|
236
|
+
const result = await provider.getEntityRelationships(repoId, "a");
|
|
237
|
+
(0, import_vitest.expect)(result.items).toHaveLength(2);
|
|
238
|
+
});
|
|
239
|
+
(0, import_vitest.it)("filters relationships by direction", async () => {
|
|
240
|
+
await provider.createRelationship(repoId, makeRelationship("r1", "connects", "a", "b"));
|
|
241
|
+
await provider.createRelationship(repoId, makeRelationship("r2", "connects", "c", "a"));
|
|
242
|
+
const outbound = await provider.getEntityRelationships(repoId, "a", { direction: "outbound" });
|
|
243
|
+
(0, import_vitest.expect)(outbound.items).toHaveLength(1);
|
|
244
|
+
(0, import_vitest.expect)(outbound.items[0].targetEntityId).toBe("b");
|
|
245
|
+
const inbound = await provider.getEntityRelationships(repoId, "a", { direction: "inbound" });
|
|
246
|
+
(0, import_vitest.expect)(inbound.items).toHaveLength(1);
|
|
247
|
+
(0, import_vitest.expect)(inbound.items[0].sourceEntityId).toBe("c");
|
|
248
|
+
});
|
|
249
|
+
(0, import_vitest.it)("deletes a relationship", async () => {
|
|
250
|
+
await provider.createRelationship(repoId, makeRelationship("r1", "connects", "a", "b"));
|
|
251
|
+
await provider.deleteRelationship(repoId, "r1");
|
|
252
|
+
(0, import_vitest.expect)(await provider.getRelationship(repoId, "r1")).toBeNull();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
(0, import_vitest.describe)("graph traversal", () => {
|
|
256
|
+
(0, import_vitest.beforeEach)(async () => {
|
|
257
|
+
await provider.createEntity(repoId, makeEntity("a", "node", "A"));
|
|
258
|
+
await provider.createEntity(repoId, makeEntity("b", "node", "B"));
|
|
259
|
+
await provider.createEntity(repoId, makeEntity("c", "node", "C"));
|
|
260
|
+
await provider.createRelationship(repoId, makeRelationship("r1", "links", "a", "b"));
|
|
261
|
+
await provider.createRelationship(repoId, makeRelationship("r2", "links", "b", "c"));
|
|
262
|
+
});
|
|
263
|
+
(0, import_vitest.it)("explores neighbourhood at depth 1", async () => {
|
|
264
|
+
const result = await provider.exploreNeighbourhood(repoId, "a", {
|
|
265
|
+
depth: 1,
|
|
266
|
+
direction: "both",
|
|
267
|
+
limitPerType: 10,
|
|
268
|
+
offsetPerType: 0
|
|
269
|
+
});
|
|
270
|
+
(0, import_vitest.expect)(result.centreId).toBe("a");
|
|
271
|
+
(0, import_vitest.expect)(result.layers).toHaveLength(1);
|
|
272
|
+
});
|
|
273
|
+
(0, import_vitest.it)("finds paths between connected entities", async () => {
|
|
274
|
+
const result = await provider.findPaths(repoId, "a", "c", {
|
|
275
|
+
maxDepth: 3,
|
|
276
|
+
limit: 5,
|
|
277
|
+
offset: 0
|
|
278
|
+
});
|
|
279
|
+
(0, import_vitest.expect)(result.paths.length).toBeGreaterThanOrEqual(1);
|
|
280
|
+
const firstPath = result.paths[0];
|
|
281
|
+
(0, import_vitest.expect)(firstPath.entityIds[0]).toBe("a");
|
|
282
|
+
(0, import_vitest.expect)(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe("c");
|
|
283
|
+
});
|
|
284
|
+
(0, import_vitest.it)("returns empty paths when no connection", async () => {
|
|
285
|
+
await provider.createEntity(repoId, makeEntity("isolated", "node", "Isolated"));
|
|
286
|
+
const result = await provider.findPaths(repoId, "a", "isolated", {
|
|
287
|
+
maxDepth: 3,
|
|
288
|
+
limit: 5,
|
|
289
|
+
offset: 0
|
|
290
|
+
});
|
|
291
|
+
(0, import_vitest.expect)(result.paths).toHaveLength(0);
|
|
292
|
+
});
|
|
293
|
+
(0, import_vitest.it)("finds paths through non-bidirectional inbound edges", async () => {
|
|
294
|
+
await provider.createEntity(repoId, makeEntity("d", "node", "D"));
|
|
295
|
+
await provider.createRelationship(repoId, makeRelationship("r3", "links", "d", "b"));
|
|
296
|
+
const result = await provider.findPaths(repoId, "a", "d", {
|
|
297
|
+
maxDepth: 3,
|
|
298
|
+
limit: 5,
|
|
299
|
+
offset: 0
|
|
300
|
+
});
|
|
301
|
+
(0, import_vitest.expect)(result.paths.length).toBeGreaterThanOrEqual(1);
|
|
302
|
+
const firstPath = result.paths[0];
|
|
303
|
+
(0, import_vitest.expect)(firstPath.entityIds[0]).toBe("a");
|
|
304
|
+
(0, import_vitest.expect)(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe("d");
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
(0, import_vitest.describe)("timeline", () => {
|
|
308
|
+
(0, import_vitest.it)("returns timeline events", async () => {
|
|
309
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
310
|
+
const result = await provider.getTimeline(repoId, "e1", {
|
|
311
|
+
limit: 10,
|
|
312
|
+
offset: 0
|
|
313
|
+
});
|
|
314
|
+
(0, import_vitest.expect)(result.events.length).toBeGreaterThanOrEqual(1);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
(0, import_vitest.describe)("bulk operations", () => {
|
|
318
|
+
(0, import_vitest.it)("exports data", async () => {
|
|
319
|
+
await provider.createEntity(repoId, makeEntity("e1"));
|
|
320
|
+
const chunks = [];
|
|
321
|
+
for await (const chunk of provider.exportAll(repoId)) {
|
|
322
|
+
chunks.push(chunk);
|
|
323
|
+
}
|
|
324
|
+
(0, import_vitest.expect)(chunks.length).toBeGreaterThanOrEqual(1);
|
|
325
|
+
});
|
|
326
|
+
(0, import_vitest.it)("imports data", async () => {
|
|
327
|
+
const result = await provider.importBulk(repoId, [
|
|
328
|
+
{ entities: [makeEntity("imported-1"), makeEntity("imported-2")] },
|
|
329
|
+
{ relationships: [makeRelationship("ir1", "links", "imported-1", "imported-2")] }
|
|
330
|
+
]);
|
|
331
|
+
(0, import_vitest.expect)(result.entitiesImported).toBe(2);
|
|
332
|
+
(0, import_vitest.expect)(result.relationshipsImported).toBe(1);
|
|
333
|
+
const e = await provider.getEntity(repoId, "imported-1");
|
|
334
|
+
(0, import_vitest.expect)(e).not.toBeNull();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
(0, import_vitest.describe)("stats reflect data", () => {
|
|
338
|
+
(0, import_vitest.it)("counts entities and relationships", async () => {
|
|
339
|
+
await provider.createEntity(repoId, makeEntity("e1", "alpha"));
|
|
340
|
+
await provider.createEntity(repoId, makeEntity("e2", "alpha"));
|
|
341
|
+
await provider.createEntity(repoId, makeEntity("e3", "beta"));
|
|
342
|
+
await provider.createRelationship(repoId, makeRelationship("r1", "links", "e1", "e2"));
|
|
343
|
+
const stats = await provider.getRepositoryStats(repoId);
|
|
344
|
+
(0, import_vitest.expect)(stats.entityCount).toBe(3);
|
|
345
|
+
(0, import_vitest.expect)(stats.relationshipCount).toBe(1);
|
|
346
|
+
(0, import_vitest.expect)(stats.entityTypeBreakdown["alpha"]).toBe(2);
|
|
347
|
+
(0, import_vitest.expect)(stats.entityTypeBreakdown["beta"]).toBe(1);
|
|
348
|
+
(0, import_vitest.expect)(stats.relationshipTypeBreakdown["links"]).toBe(1);
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
354
|
+
0 && (module.exports = {
|
|
355
|
+
runStorageProviderConformanceTests
|
|
356
|
+
});
|
|
357
|
+
//# sourceMappingURL=conformance.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/providers-builtin/conformance.ts"],"sourcesContent":["// Provider Conformance Test Suite\n// Any StorageProvider implementer can import and run these tests to verify conformance.\n//\n// Usage:\n// import { runStorageProviderConformanceTests } from '@utaba/deep-memory';\n// runStorageProviderConformanceTests(() => new MyStorageProvider());\n\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport type { StorageProvider } from '../providers/StorageProvider.js';\nimport type { StoredEntity } from '../types/entities.js';\nimport type { StoredRelationship } from '../types/relationships.js';\nimport type { Provenance } from '../types/provenance.js';\n\nfunction makeProvenance(): Provenance {\n const now = new Date().toISOString();\n return {\n createdBy: 'conformance-test',\n createdByType: 'agent',\n createdAt: now,\n modifiedBy: 'conformance-test',\n modifiedByType: 'agent',\n modifiedAt: now,\n };\n}\n\nfunction makeEntity(id: string, type = 'test-type', label?: string): StoredEntity {\n return {\n id,\n slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,\n entityType: type,\n label: label ?? id,\n summary: `Summary for ${id}`,\n properties: { key: 'value' },\n provenance: makeProvenance(),\n };\n}\n\nfunction makeRelationship(\n id: string,\n type: string,\n sourceId: string,\n targetId: string,\n bidirectional = false,\n): StoredRelationship {\n return {\n id,\n relationshipType: type,\n sourceEntityId: sourceId,\n targetEntityId: targetId,\n properties: {},\n bidirectional,\n provenance: makeProvenance(),\n };\n}\n\n/**\n * Run the full StorageProvider conformance test suite.\n *\n * @param factory - A function that creates a fresh, empty StorageProvider instance.\n * Called before each test to ensure isolation.\n */\nexport function runStorageProviderConformanceTests(\n factory: () => StorageProvider | Promise<StorageProvider>,\n): void {\n // Use a stable GUID so external cleanup scripts can target it\n const repoId = '40000000-0000-4000-a000-000000000001';\n\n let provider: StorageProvider;\n\n async function setup(): Promise<void> {\n provider = await factory();\n if (provider.initialise) await provider.initialise();\n\n await provider.createRepository({\n repositoryId: repoId,\n label: 'Conformance Test',\n governanceConfig: { mode: 'open' },\n createdAt: new Date().toISOString(),\n createdBy: 'conformance-test',\n });\n }\n\n describe('StorageProvider Conformance Tests', () => {\n beforeEach(async () => {\n await setup();\n });\n\n // ─── Repository ─────────────────────────────────────────\n\n describe('repository operations', () => {\n it('creates a repository', async () => {\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n expect(repo!.repositoryId).toBe(repoId);\n expect(repo!.label).toBe('Conformance Test');\n });\n\n it('returns null for non-existent repository', async () => {\n const repo = await provider.getRepository('ffffffff-ffff-4fff-afff-ffffffffffff');\n expect(repo).toBeNull();\n });\n\n it('lists repositories', async () => {\n const list = await provider.listRepositories();\n expect(list.items.length).toBeGreaterThanOrEqual(1);\n expect(list.items.some((r) => r.repositoryId === repoId)).toBe(true);\n });\n\n it('updates a repository', async () => {\n const updated = await provider.updateRepository(repoId, {\n label: 'Updated Label',\n description: 'Updated description',\n governanceConfig: { mode: 'open', defaultSimilarityThreshold: 0.4 },\n });\n expect(updated.label).toBe('Updated Label');\n expect(updated.description).toBe('Updated description');\n expect(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n\n // Verify persistence\n const fetched = await provider.getRepository(repoId);\n expect(fetched!.label).toBe('Updated Label');\n expect(fetched!.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n });\n\n it('deletes a repository', async () => {\n await provider.deleteRepository(repoId);\n expect(await provider.getRepository(repoId)).toBeNull();\n });\n\n it('returns repository stats', async () => {\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n expect(typeof stats.vocabularyVersion).toBe('string');\n });\n });\n\n // ─── Vocabulary ─────────────────────────────────────────\n\n describe('vocabulary operations', () => {\n it('gets and saves vocabulary', async () => {\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n expect(typeof vocab.version).toBe('string');\n\n const updated = { ...vocab, version: '1.0.0' };\n await provider.saveVocabulary(repoId, updated);\n\n const fetched = await provider.getVocabulary(repoId);\n expect(fetched.version).toBe('1.0.0');\n });\n\n it('returns vocabulary change log', async () => {\n const log = await provider.getVocabularyChangeLog(repoId);\n expect(Array.isArray(log.items)).toBe(true);\n });\n });\n\n // ─── Entities ───────────────────────────────────────────\n\n describe('entity operations', () => {\n it('creates and retrieves an entity', async () => {\n const entity = makeEntity('e1');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntity(repoId, 'e1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.label).toBe('e1');\n });\n\n it('retrieves an entity by slug', async () => {\n const entity = makeEntity('e1', 'test-type', 'Alpha');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntityBySlug(repoId, entity.slug);\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.slug).toBe(entity.slug);\n });\n\n it('returns null for non-existent entity', async () => {\n const result = await provider.getEntity(repoId, 'nonexistent');\n expect(result).toBeNull();\n });\n\n it('returns null for non-existent slug', async () => {\n const result = await provider.getEntityBySlug(repoId, 'nonexistent:slug');\n expect(result).toBeNull();\n });\n\n it('batch retrieves entities', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n\n const map = await provider.getEntities(repoId, ['e1', 'e2', 'missing']);\n expect(map.size).toBe(2);\n expect(map.has('e1')).toBe(true);\n expect(map.has('e2')).toBe(true);\n expect(map.has('missing')).toBe(false);\n });\n\n it('updates an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const updated = await provider.updateEntity(repoId, 'e1', {\n label: 'Updated Label',\n provenance: makeProvenance(),\n });\n expect(updated.label).toBe('Updated Label');\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.label).toBe('Updated Label');\n });\n\n it('deletes an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.deleteEntity(repoId, 'e1');\n expect(await provider.getEntity(repoId, 'e1')).toBeNull();\n });\n\n it('finds entities by search term', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'test-type', 'Beta'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'alpha',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by type filter', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'type-a', 'A'));\n await provider.createEntity(repoId, makeEntity('e2', 'type-b', 'B'));\n\n const result = await provider.findEntities(repoId, {\n entityTypes: ['type-a'],\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.entityType).toBe('type-a');\n });\n\n it('paginates find results', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n await provider.createEntity(repoId, makeEntity('e3'));\n\n const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });\n expect(page1.items).toHaveLength(2);\n expect(page1.hasMore).toBe(true);\n\n const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });\n expect(page2.items).toHaveLength(1);\n expect(page2.hasMore).toBe(false);\n });\n });\n\n // ─── Relationships ──────────────────────────────────────\n\n describe('relationship operations', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a'));\n await provider.createEntity(repoId, makeEntity('b'));\n await provider.createEntity(repoId, makeEntity('c'));\n });\n\n it('creates and retrieves a relationship', async () => {\n const rel = makeRelationship('r1', 'connects', 'a', 'b');\n await provider.createRelationship(repoId, rel);\n\n const retrieved = await provider.getRelationship(repoId, 'r1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.sourceEntityId).toBe('a');\n expect(retrieved!.targetEntityId).toBe('b');\n });\n\n it('returns null for non-existent relationship', async () => {\n expect(await provider.getRelationship(repoId, 'nonexistent')).toBeNull();\n });\n\n it('gets entity relationships', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const result = await provider.getEntityRelationships(repoId, 'a');\n expect(result.items).toHaveLength(2);\n });\n\n it('filters relationships by direction', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const outbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'outbound' });\n expect(outbound.items).toHaveLength(1);\n expect(outbound.items[0]!.targetEntityId).toBe('b');\n\n const inbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'inbound' });\n expect(inbound.items).toHaveLength(1);\n expect(inbound.items[0]!.sourceEntityId).toBe('c');\n });\n\n it('deletes a relationship', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.deleteRelationship(repoId, 'r1');\n expect(await provider.getRelationship(repoId, 'r1')).toBeNull();\n });\n });\n\n // ─── Graph Traversal ────────────────────────────────────\n\n describe('graph traversal', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a', 'node', 'A'));\n await provider.createEntity(repoId, makeEntity('b', 'node', 'B'));\n await provider.createEntity(repoId, makeEntity('c', 'node', 'C'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'links', 'b', 'c'));\n });\n\n it('explores neighbourhood at depth 1', async () => {\n const result = await provider.exploreNeighbourhood(repoId, 'a', {\n depth: 1,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n expect(result.centreId).toBe('a');\n expect(result.layers).toHaveLength(1);\n });\n\n it('finds paths between connected entities', async () => {\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('c');\n });\n\n it('returns empty paths when no connection', async () => {\n await provider.createEntity(repoId, makeEntity('isolated', 'node', 'Isolated'));\n const result = await provider.findPaths(repoId, 'a', 'isolated', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths).toHaveLength(0);\n });\n\n it('finds paths through non-bidirectional inbound edges', async () => {\n // Graph: a → b ← d (both edges are non-bidirectional)\n // Path from a to d should traverse: a →(outbound) b ←(inbound) d\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'd', 'b'));\n const result = await provider.findPaths(repoId, 'a', 'd', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('d');\n });\n });\n\n // ─── Timeline ───────────────────────────────────────────\n\n describe('timeline', () => {\n it('returns timeline events', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const result = await provider.getTimeline(repoId, 'e1', {\n limit: 10,\n offset: 0,\n });\n expect(result.events.length).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ─── Bulk Operations ────────────────────────────────────\n\n describe('bulk operations', () => {\n it('exports data', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n\n const chunks = [];\n for await (const chunk of provider.exportAll(repoId)) {\n chunks.push(chunk);\n }\n expect(chunks.length).toBeGreaterThanOrEqual(1);\n });\n\n it('imports data', async () => {\n const result = await provider.importBulk(repoId, [\n { entities: [makeEntity('imported-1'), makeEntity('imported-2')] },\n { relationships: [makeRelationship('ir1', 'links', 'imported-1', 'imported-2')] },\n ]);\n expect(result.entitiesImported).toBe(2);\n expect(result.relationshipsImported).toBe(1);\n\n // Verify imported data is accessible\n const e = await provider.getEntity(repoId, 'imported-1');\n expect(e).not.toBeNull();\n });\n });\n\n // ─── Stats after data ───────────────────────────────────\n\n describe('stats reflect data', () => {\n it('counts entities and relationships', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e3', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(3);\n expect(stats.relationshipCount).toBe(1);\n expect(stats.entityTypeBreakdown['alpha']).toBe(2);\n expect(stats.entityTypeBreakdown['beta']).toBe(1);\n expect(stats.relationshipTypeBreakdown['links']).toBe(1);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { S as StorageProvider } from '../StorageProvider-CkFjdboX.cjs';
|
|
2
|
+
import '../portability-DdlNYXGX.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Run the full StorageProvider conformance test suite.
|
|
6
|
+
*
|
|
7
|
+
* @param factory - A function that creates a fresh, empty StorageProvider instance.
|
|
8
|
+
* Called before each test to ensure isolation.
|
|
9
|
+
*/
|
|
10
|
+
declare function runStorageProviderConformanceTests(factory: () => StorageProvider | Promise<StorageProvider>): void;
|
|
11
|
+
|
|
12
|
+
export { runStorageProviderConformanceTests };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { S as StorageProvider } from '../StorageProvider-CJjz8uBY.js';
|
|
2
|
+
import '../portability-DdlNYXGX.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Run the full StorageProvider conformance test suite.
|
|
6
|
+
*
|
|
7
|
+
* @param factory - A function that creates a fresh, empty StorageProvider instance.
|
|
8
|
+
* Called before each test to ensure isolation.
|
|
9
|
+
*/
|
|
10
|
+
declare function runStorageProviderConformanceTests(factory: () => StorageProvider | Promise<StorageProvider>): void;
|
|
11
|
+
|
|
12
|
+
export { runStorageProviderConformanceTests };
|