@plures/pluresdb 1.5.3 → 1.6.10

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.
@@ -0,0 +1,315 @@
1
+ /**
2
+ * PluresDB Plugin System
3
+ *
4
+ * Provides extension points for customizing PluresDB behavior:
5
+ * - Custom embedding providers
6
+ * - Custom UI panels
7
+ * - Query transformers
8
+ * - Data validators
9
+ */
10
+
11
+ export interface EmbeddingProvider {
12
+ name: string;
13
+ /**
14
+ * Generate embeddings for text content
15
+ * @param text - Text to embed
16
+ * @returns Promise resolving to embedding vector
17
+ */
18
+ embed(text: string): Promise<number[]>;
19
+ /**
20
+ * Dimensionality of embeddings produced by this provider
21
+ */
22
+ dimensions: number;
23
+ }
24
+
25
+ export interface UIPanel {
26
+ /**
27
+ * Unique identifier for the panel
28
+ */
29
+ id: string;
30
+ /**
31
+ * Display name for the panel tab
32
+ */
33
+ name: string;
34
+ /**
35
+ * Icon name or emoji
36
+ */
37
+ icon?: string;
38
+ /**
39
+ * Svelte component to render
40
+ * Note: In production, use a proper Svelte component type
41
+ */
42
+ component: unknown;
43
+ /**
44
+ * Position in tab list (lower numbers appear first)
45
+ */
46
+ order?: number;
47
+ }
48
+
49
+ export interface QueryTransformer<TQuery = Record<string, unknown>> {
50
+ /**
51
+ * Unique identifier for the transformer
52
+ */
53
+ id: string;
54
+ /**
55
+ * Transform a query before execution
56
+ * @param query - Original query
57
+ * @returns Transformed query
58
+ */
59
+ transform(query: TQuery): Promise<TQuery>;
60
+ }
61
+
62
+ export interface DataValidator {
63
+ /**
64
+ * Unique identifier for the validator
65
+ */
66
+ id: string;
67
+ /**
68
+ * Validate data before storage
69
+ * @param data - Data to validate
70
+ * @returns Validation result with errors if any
71
+ */
72
+ validate(data: Record<string, unknown>): Promise<{
73
+ valid: boolean;
74
+ errors?: string[];
75
+ }>;
76
+ }
77
+
78
+ export interface Plugin {
79
+ /**
80
+ * Unique identifier for the plugin
81
+ */
82
+ id: string;
83
+ /**
84
+ * Plugin name
85
+ */
86
+ name: string;
87
+ /**
88
+ * Plugin version
89
+ */
90
+ version: string;
91
+ /**
92
+ * Plugin description
93
+ */
94
+ description?: string;
95
+ /**
96
+ * Embedding providers contributed by this plugin
97
+ */
98
+ embeddingProviders?: EmbeddingProvider[];
99
+ /**
100
+ * UI panels contributed by this plugin
101
+ */
102
+ uiPanels?: UIPanel[];
103
+ /**
104
+ * Query transformers contributed by this plugin
105
+ */
106
+ queryTransformers?: QueryTransformer[];
107
+ /**
108
+ * Data validators contributed by this plugin
109
+ */
110
+ dataValidators?: DataValidator[];
111
+ /**
112
+ * Initialize the plugin
113
+ */
114
+ init?(): Promise<void>;
115
+ /**
116
+ * Cleanup the plugin
117
+ */
118
+ destroy?(): Promise<void>;
119
+ }
120
+
121
+ class PluginManager {
122
+ private plugins: Map<string, Plugin> = new Map();
123
+ private embeddingProviders: Map<string, EmbeddingProvider> = new Map();
124
+ private uiPanels: Map<string, UIPanel> = new Map();
125
+ private queryTransformers: Map<string, QueryTransformer> = new Map();
126
+ private dataValidators: Map<string, DataValidator> = new Map();
127
+
128
+ /**
129
+ * Register a plugin
130
+ */
131
+ async register(plugin: Plugin): Promise<void> {
132
+ if (this.plugins.has(plugin.id)) {
133
+ throw new Error(`Plugin ${plugin.id} is already registered`);
134
+ }
135
+
136
+ // Register embedding providers
137
+ if (plugin.embeddingProviders) {
138
+ for (const provider of plugin.embeddingProviders) {
139
+ this.embeddingProviders.set(provider.name, provider);
140
+ }
141
+ }
142
+
143
+ // Register UI panels
144
+ if (plugin.uiPanels) {
145
+ for (const panel of plugin.uiPanels) {
146
+ this.uiPanels.set(panel.id, panel);
147
+ }
148
+ }
149
+
150
+ // Register query transformers
151
+ if (plugin.queryTransformers) {
152
+ for (const transformer of plugin.queryTransformers) {
153
+ this.queryTransformers.set(transformer.id, transformer);
154
+ }
155
+ }
156
+
157
+ // Register data validators
158
+ if (plugin.dataValidators) {
159
+ for (const validator of plugin.dataValidators) {
160
+ this.dataValidators.set(validator.id, validator);
161
+ }
162
+ }
163
+
164
+ // Initialize plugin
165
+ if (plugin.init) {
166
+ await plugin.init();
167
+ }
168
+
169
+ this.plugins.set(plugin.id, plugin);
170
+ }
171
+
172
+ /**
173
+ * Unregister a plugin
174
+ */
175
+ async unregister(pluginId: string): Promise<void> {
176
+ const plugin = this.plugins.get(pluginId);
177
+ if (!plugin) {
178
+ return;
179
+ }
180
+
181
+ // Cleanup plugin
182
+ if (plugin.destroy) {
183
+ await plugin.destroy();
184
+ }
185
+
186
+ // Remove embedding providers
187
+ if (plugin.embeddingProviders) {
188
+ for (const provider of plugin.embeddingProviders) {
189
+ this.embeddingProviders.delete(provider.name);
190
+ }
191
+ }
192
+
193
+ // Remove UI panels
194
+ if (plugin.uiPanels) {
195
+ for (const panel of plugin.uiPanels) {
196
+ this.uiPanels.delete(panel.id);
197
+ }
198
+ }
199
+
200
+ // Remove query transformers
201
+ if (plugin.queryTransformers) {
202
+ for (const transformer of plugin.queryTransformers) {
203
+ this.queryTransformers.delete(transformer.id);
204
+ }
205
+ }
206
+
207
+ // Remove data validators
208
+ if (plugin.dataValidators) {
209
+ for (const validator of plugin.dataValidators) {
210
+ this.dataValidators.delete(validator.id);
211
+ }
212
+ }
213
+
214
+ this.plugins.delete(pluginId);
215
+ }
216
+
217
+ /**
218
+ * Get all registered plugins
219
+ */
220
+ getPlugins(): Plugin[] {
221
+ return Array.from(this.plugins.values());
222
+ }
223
+
224
+ /**
225
+ * Get embedding provider by name
226
+ */
227
+ getEmbeddingProvider(name: string): EmbeddingProvider | undefined {
228
+ return this.embeddingProviders.get(name);
229
+ }
230
+
231
+ /**
232
+ * Get all embedding providers
233
+ */
234
+ getEmbeddingProviders(): EmbeddingProvider[] {
235
+ return Array.from(this.embeddingProviders.values());
236
+ }
237
+
238
+ /**
239
+ * Get UI panel by ID
240
+ */
241
+ getUIPanel(id: string): UIPanel | undefined {
242
+ return this.uiPanels.get(id);
243
+ }
244
+
245
+ /**
246
+ * Get all UI panels sorted by order
247
+ */
248
+ getUIPanels(): UIPanel[] {
249
+ return Array.from(this.uiPanels.values()).sort(
250
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
251
+ );
252
+ }
253
+
254
+ /**
255
+ * Get query transformer by ID
256
+ */
257
+ getQueryTransformer(id: string): QueryTransformer | undefined {
258
+ return this.queryTransformers.get(id);
259
+ }
260
+
261
+ /**
262
+ * Get all query transformers
263
+ */
264
+ getQueryTransformers(): QueryTransformer[] {
265
+ return Array.from(this.queryTransformers.values());
266
+ }
267
+
268
+ /**
269
+ * Get data validator by ID
270
+ */
271
+ getDataValidator(id: string): DataValidator | undefined {
272
+ return this.dataValidators.get(id);
273
+ }
274
+
275
+ /**
276
+ * Get all data validators
277
+ */
278
+ getDataValidators(): DataValidator[] {
279
+ return Array.from(this.dataValidators.values());
280
+ }
281
+
282
+ /**
283
+ * Apply all query transformers to a query
284
+ */
285
+ async transformQuery<T = Record<string, unknown>>(query: T): Promise<T> {
286
+ let transformed = query;
287
+ for (const transformer of this.queryTransformers.values()) {
288
+ transformed = await transformer.transform(transformed as any) as T;
289
+ }
290
+ return transformed;
291
+ }
292
+
293
+ /**
294
+ * Validate data using all validators
295
+ */
296
+ async validateData(data: Record<string, unknown>): Promise<{
297
+ valid: boolean;
298
+ errors: string[];
299
+ }> {
300
+ const errors: string[] = [];
301
+ for (const validator of this.dataValidators.values()) {
302
+ const result = await validator.validate(data);
303
+ if (!result.valid && result.errors) {
304
+ errors.push(...result.errors);
305
+ }
306
+ }
307
+ return {
308
+ valid: errors.length === 0,
309
+ errors,
310
+ };
311
+ }
312
+ }
313
+
314
+ // Global plugin manager instance
315
+ export const pluginManager = new PluginManager();
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Tests for Local-First Unified API
3
+ */
4
+
5
+ import { assertEquals, assertExists } from "jsr:@std/assert@1.0.14";
6
+ import { PluresDBLocalFirst } from "../../local-first/unified-api.ts";
7
+
8
+ Deno.test("PluresDBLocalFirst - Runtime detection", () => {
9
+ // Should detect "network" mode in Deno test environment
10
+ const db = new PluresDBLocalFirst({ mode: "auto" });
11
+ const mode = db.getMode();
12
+
13
+ assertEquals(mode, "network", "Should default to network mode in Deno");
14
+ });
15
+
16
+ Deno.test("PluresDBLocalFirst - Manual mode selection", () => {
17
+ // Should allow manual mode selection
18
+ const db = new PluresDBLocalFirst({ mode: "network", port: 34567 });
19
+ const mode = db.getMode();
20
+
21
+ assertEquals(mode, "network", "Should use network mode when explicitly set");
22
+ });
23
+
24
+ Deno.test("PluresDBLocalFirst - WASM mode throws not implemented", async () => {
25
+ const db = new PluresDBLocalFirst({ mode: "wasm", dbName: "test-db" });
26
+
27
+ try {
28
+ await db.put("test:1", { value: "test" });
29
+ throw new Error("Should have thrown not implemented error");
30
+ } catch (error) {
31
+ assertEquals(
32
+ error instanceof Error && error.message.includes("not yet implemented"),
33
+ true,
34
+ "Should throw not implemented error for WASM mode"
35
+ );
36
+ }
37
+ });
38
+
39
+ Deno.test("PluresDBLocalFirst - IPC mode throws not implemented", async () => {
40
+ const db = new PluresDBLocalFirst({ mode: "ipc", channelName: "test-channel" });
41
+
42
+ try {
43
+ await db.put("test:1", { value: "test" });
44
+ throw new Error("Should have thrown not implemented error");
45
+ } catch (error) {
46
+ assertEquals(
47
+ error instanceof Error && error.message.includes("not yet implemented"),
48
+ true,
49
+ "Should throw not implemented error for IPC mode"
50
+ );
51
+ }
52
+ });
53
+
54
+ Deno.test("PluresDBLocalFirst - Network mode API surface", () => {
55
+ const db = new PluresDBLocalFirst({ mode: "network", port: 34567 });
56
+
57
+ // Check that all required methods exist
58
+ assertExists(db.put, "Should have put method");
59
+ assertExists(db.get, "Should have get method");
60
+ assertExists(db.delete, "Should have delete method");
61
+ assertExists(db.list, "Should have list method");
62
+ assertExists(db.vectorSearch, "Should have vectorSearch method");
63
+ assertExists(db.close, "Should have close method");
64
+ assertExists(db.getMode, "Should have getMode method");
65
+ });