@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,388 @@
1
+ // @ts-nocheck
2
+ import {
3
+ assertEquals,
4
+ assertExists,
5
+ assertRejects,
6
+ assert,
7
+ } from "jsr:@std/assert@1.0.14";
8
+ import {
9
+ pluginManager,
10
+ type Plugin,
11
+ type EmbeddingProvider,
12
+ type UIPanel,
13
+ type QueryTransformer,
14
+ type DataValidator,
15
+ } from "../../plugins/plugin-system.ts";
16
+
17
+ // Test helpers
18
+ class TestEmbeddingProvider implements EmbeddingProvider {
19
+ name = "test-embeddings";
20
+ dimensions = 128;
21
+
22
+ async embed(text: string): Promise<number[]> {
23
+ return new Array(this.dimensions).fill(0.1);
24
+ }
25
+ }
26
+
27
+ class TestQueryTransformer implements QueryTransformer {
28
+ id = "test-transformer";
29
+
30
+ async transform(query: any): Promise<any> {
31
+ return { ...query, transformed: true };
32
+ }
33
+ }
34
+
35
+ class TestDataValidator implements DataValidator {
36
+ id = "test-validator";
37
+
38
+ async validate(data: Record<string, unknown>): Promise<{
39
+ valid: boolean;
40
+ errors?: string[];
41
+ }> {
42
+ if (!data.required) {
43
+ return { valid: false, errors: ["Missing required field"] };
44
+ }
45
+ return { valid: true };
46
+ }
47
+ }
48
+
49
+ Deno.test("Plugin System - Plugin Registration", async () => {
50
+ const plugin: Plugin = {
51
+ id: "test-plugin-1",
52
+ name: "Test Plugin",
53
+ version: "1.0.0",
54
+ description: "A test plugin",
55
+ };
56
+
57
+ await pluginManager.register(plugin);
58
+
59
+ const plugins = pluginManager.getPlugins();
60
+ const registered = plugins.find((p) => p.id === "test-plugin-1");
61
+
62
+ assertExists(registered);
63
+ assertEquals(registered.name, "Test Plugin");
64
+ assertEquals(registered.version, "1.0.0");
65
+
66
+ // Cleanup
67
+ await pluginManager.unregister("test-plugin-1");
68
+ });
69
+
70
+ Deno.test("Plugin System - Prevent Duplicate Plugin IDs", async () => {
71
+ const plugin1: Plugin = {
72
+ id: "duplicate-test",
73
+ name: "First Plugin",
74
+ version: "1.0.0",
75
+ };
76
+
77
+ const plugin2: Plugin = {
78
+ id: "duplicate-test",
79
+ name: "Second Plugin",
80
+ version: "2.0.0",
81
+ };
82
+
83
+ await pluginManager.register(plugin1);
84
+
85
+ // Attempting to register a plugin with duplicate ID should throw
86
+ await assertRejects(
87
+ async () => {
88
+ await pluginManager.register(plugin2);
89
+ },
90
+ Error,
91
+ "already registered",
92
+ );
93
+
94
+ // Cleanup
95
+ await pluginManager.unregister("duplicate-test");
96
+ });
97
+
98
+ Deno.test("Plugin System - Embedding Provider Registration", async () => {
99
+ const provider = new TestEmbeddingProvider();
100
+ const plugin: Plugin = {
101
+ id: "embedding-test",
102
+ name: "Embedding Test",
103
+ version: "1.0.0",
104
+ embeddingProviders: [provider],
105
+ };
106
+
107
+ await pluginManager.register(plugin);
108
+
109
+ const registeredProvider = pluginManager.getEmbeddingProvider(
110
+ "test-embeddings",
111
+ );
112
+ assertExists(registeredProvider);
113
+ assertEquals(registeredProvider.dimensions, 128);
114
+
115
+ const embedding = await registeredProvider.embed("test");
116
+ assertEquals(embedding.length, 128);
117
+ assertEquals(embedding[0], 0.1);
118
+
119
+ // Cleanup
120
+ await pluginManager.unregister("embedding-test");
121
+ });
122
+
123
+ Deno.test("Plugin System - Query Transformer Registration", async () => {
124
+ const transformer = new TestQueryTransformer();
125
+ const plugin: Plugin = {
126
+ id: "transformer-test",
127
+ name: "Transformer Test",
128
+ version: "1.0.0",
129
+ queryTransformers: [transformer],
130
+ };
131
+
132
+ await pluginManager.register(plugin);
133
+
134
+ const registeredTransformer = pluginManager.getQueryTransformer(
135
+ "test-transformer",
136
+ );
137
+ assertExists(registeredTransformer);
138
+
139
+ const query = { field: "value" };
140
+ const transformed = await registeredTransformer.transform(query);
141
+ assertEquals(transformed.field, "value");
142
+ assertEquals(transformed.transformed, true);
143
+
144
+ // Cleanup
145
+ await pluginManager.unregister("transformer-test");
146
+ });
147
+
148
+ Deno.test("Plugin System - Data Validator Registration", async () => {
149
+ const validator = new TestDataValidator();
150
+ const plugin: Plugin = {
151
+ id: "validator-test",
152
+ name: "Validator Test",
153
+ version: "1.0.0",
154
+ dataValidators: [validator],
155
+ };
156
+
157
+ await pluginManager.register(plugin);
158
+
159
+ const registeredValidator = pluginManager.getDataValidator("test-validator");
160
+ assertExists(registeredValidator);
161
+
162
+ // Test valid data
163
+ const validResult = await registeredValidator.validate({ required: true });
164
+ assertEquals(validResult.valid, true);
165
+
166
+ // Test invalid data
167
+ const invalidResult = await registeredValidator.validate({});
168
+ assertEquals(invalidResult.valid, false);
169
+ assertExists(invalidResult.errors);
170
+ assertEquals(invalidResult.errors.length, 1);
171
+
172
+ // Cleanup
173
+ await pluginManager.unregister("validator-test");
174
+ });
175
+
176
+ Deno.test("Plugin System - Plugin Lifecycle (init/destroy)", async () => {
177
+ let initCalled = false;
178
+ let destroyCalled = false;
179
+
180
+ const plugin: Plugin = {
181
+ id: "lifecycle-test",
182
+ name: "Lifecycle Test",
183
+ version: "1.0.0",
184
+ async init() {
185
+ initCalled = true;
186
+ },
187
+ async destroy() {
188
+ destroyCalled = true;
189
+ },
190
+ };
191
+
192
+ await pluginManager.register(plugin);
193
+ assertEquals(initCalled, true);
194
+
195
+ await pluginManager.unregister("lifecycle-test");
196
+ assertEquals(destroyCalled, true);
197
+ });
198
+
199
+ Deno.test("Plugin System - Transform Query with Multiple Transformers", async () => {
200
+ const transformer1: QueryTransformer = {
201
+ id: "transformer-1",
202
+ async transform(query: any) {
203
+ return { ...query, step1: true };
204
+ },
205
+ };
206
+
207
+ const transformer2: QueryTransformer = {
208
+ id: "transformer-2",
209
+ async transform(query: any) {
210
+ return { ...query, step2: true };
211
+ },
212
+ };
213
+
214
+ const plugin: Plugin = {
215
+ id: "multi-transformer-test",
216
+ name: "Multi Transformer Test",
217
+ version: "1.0.0",
218
+ queryTransformers: [transformer1, transformer2],
219
+ };
220
+
221
+ await pluginManager.register(plugin);
222
+
223
+ const query = { original: true };
224
+ const transformed = await pluginManager.transformQuery(query);
225
+
226
+ assertEquals(transformed.original, true);
227
+ assertEquals(transformed.step1, true);
228
+ assertEquals(transformed.step2, true);
229
+
230
+ // Cleanup
231
+ await pluginManager.unregister("multi-transformer-test");
232
+ });
233
+
234
+ Deno.test("Plugin System - Validate Data with Multiple Validators", async () => {
235
+ const validator1: DataValidator = {
236
+ id: "validator-1",
237
+ async validate(data: Record<string, unknown>) {
238
+ if (!data.field1) {
239
+ return { valid: false, errors: ["Missing field1"] };
240
+ }
241
+ return { valid: true };
242
+ },
243
+ };
244
+
245
+ const validator2: DataValidator = {
246
+ id: "validator-2",
247
+ async validate(data: Record<string, unknown>) {
248
+ if (!data.field2) {
249
+ return { valid: false, errors: ["Missing field2"] };
250
+ }
251
+ return { valid: true };
252
+ },
253
+ };
254
+
255
+ const plugin: Plugin = {
256
+ id: "multi-validator-test",
257
+ name: "Multi Validator Test",
258
+ version: "1.0.0",
259
+ dataValidators: [validator1, validator2],
260
+ };
261
+
262
+ await pluginManager.register(plugin);
263
+
264
+ // Test with all fields valid
265
+ const validResult = await pluginManager.validateData({
266
+ field1: true,
267
+ field2: true,
268
+ });
269
+ assertEquals(validResult.valid, true);
270
+ assertEquals(validResult.errors.length, 0);
271
+
272
+ // Test with missing fields
273
+ const invalidResult = await pluginManager.validateData({});
274
+ assertEquals(invalidResult.valid, false);
275
+ assertEquals(invalidResult.errors.length, 2);
276
+ assert(invalidResult.errors.includes("Missing field1"));
277
+ assert(invalidResult.errors.includes("Missing field2"));
278
+
279
+ // Cleanup
280
+ await pluginManager.unregister("multi-validator-test");
281
+ });
282
+
283
+ Deno.test("Plugin System - Unregister Non-Existent Plugin", async () => {
284
+ // Should not throw error when unregistering non-existent plugin
285
+ await pluginManager.unregister("non-existent-plugin");
286
+ // If we get here without error, test passes
287
+ assert(true);
288
+ });
289
+
290
+ Deno.test("Plugin System - Get All Providers After Registration", async () => {
291
+ const provider1 = new TestEmbeddingProvider();
292
+ provider1.name = "provider-1";
293
+
294
+ const provider2 = new TestEmbeddingProvider();
295
+ provider2.name = "provider-2";
296
+
297
+ const plugin: Plugin = {
298
+ id: "multi-provider-test",
299
+ name: "Multi Provider Test",
300
+ version: "1.0.0",
301
+ embeddingProviders: [provider1, provider2],
302
+ };
303
+
304
+ await pluginManager.register(plugin);
305
+
306
+ const providers = pluginManager.getEmbeddingProviders();
307
+ const testProviders = providers.filter((p) =>
308
+ p.name === "provider-1" || p.name === "provider-2"
309
+ );
310
+
311
+ assertEquals(testProviders.length, 2);
312
+
313
+ // Cleanup
314
+ await pluginManager.unregister("multi-provider-test");
315
+ });
316
+
317
+ Deno.test("Plugin System - UI Panel Registration", async () => {
318
+ const panel: UIPanel = {
319
+ id: "test-panel",
320
+ name: "Test Panel",
321
+ icon: "๐Ÿงช",
322
+ component: {} as any, // Mock component
323
+ order: 100,
324
+ };
325
+
326
+ const plugin: Plugin = {
327
+ id: "panel-test",
328
+ name: "Panel Test",
329
+ version: "1.0.0",
330
+ uiPanels: [panel],
331
+ };
332
+
333
+ await pluginManager.register(plugin);
334
+
335
+ const registeredPanel = pluginManager.getUIPanel("test-panel");
336
+ assertExists(registeredPanel);
337
+ assertEquals(registeredPanel.name, "Test Panel");
338
+ assertEquals(registeredPanel.icon, "๐Ÿงช");
339
+ assertEquals(registeredPanel.order, 100);
340
+
341
+ // Cleanup
342
+ await pluginManager.unregister("panel-test");
343
+ });
344
+
345
+ Deno.test("Plugin System - UI Panels Sorted by Order", async () => {
346
+ const panel1: UIPanel = {
347
+ id: "panel-1",
348
+ name: "Panel 1",
349
+ component: {} as any,
350
+ order: 30,
351
+ };
352
+
353
+ const panel2: UIPanel = {
354
+ id: "panel-2",
355
+ name: "Panel 2",
356
+ component: {} as any,
357
+ order: 10,
358
+ };
359
+
360
+ const panel3: UIPanel = {
361
+ id: "panel-3",
362
+ name: "Panel 3",
363
+ component: {} as any,
364
+ order: 20,
365
+ };
366
+
367
+ const plugin: Plugin = {
368
+ id: "sorted-panels-test",
369
+ name: "Sorted Panels Test",
370
+ version: "1.0.0",
371
+ uiPanels: [panel1, panel2, panel3],
372
+ };
373
+
374
+ await pluginManager.register(plugin);
375
+
376
+ const panels = pluginManager.getUIPanels();
377
+ const testPanels = panels.filter((p) =>
378
+ ["panel-1", "panel-2", "panel-3"].includes(p.id)
379
+ );
380
+
381
+ // Should be sorted by order: panel-2 (10), panel-3 (20), panel-1 (30)
382
+ assertEquals(testPanels[0].id, "panel-2");
383
+ assertEquals(testPanels[1].id, "panel-3");
384
+ assertEquals(testPanels[2].id, "panel-1");
385
+
386
+ // Cleanup
387
+ await pluginManager.unregister("sorted-panels-test");
388
+ });
@@ -1,6 +1,19 @@
1
1
  export const DEBUG_ENABLED: boolean = (() => {
2
2
  try {
3
- const v = Deno.env.get("PLURESDB_DEBUG") ?? "";
3
+ let v = "";
4
+ // Check for Deno environment
5
+ if (typeof (globalThis as any).Deno !== "undefined") {
6
+ const Deno = (globalThis as any).Deno;
7
+ if (Deno.env && Deno.env.get) {
8
+ v = Deno.env.get("PLURESDB_DEBUG") ?? "";
9
+ }
10
+ } else {
11
+ // Check for Node.js environment
12
+ const globalProcess = (globalThis as any).process;
13
+ if (typeof globalProcess !== "undefined" && globalProcess?.env) {
14
+ v = globalProcess.env.PLURESDB_DEBUG ?? "";
15
+ }
16
+ }
4
17
  return v === "1" || v.toLowerCase() === "true";
5
18
  } catch {
6
19
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plures/pluresdb",
3
- "version": "1.5.3",
3
+ "version": "1.6.10",
4
4
  "description": "P2P Graph Database with SQLite Compatibility - Local-first, offline-first database for modern applications",
5
5
  "main": "dist/node-index.js",
6
6
  "types": "dist/node-index.d.ts",
@@ -35,6 +35,11 @@
35
35
  "require": "./dist/types/node-types.js",
36
36
  "default": "./dist/types/node-types.js"
37
37
  },
38
+ "./local-first": {
39
+ "types": "./dist/local-first/unified-api.d.ts",
40
+ "require": "./dist/local-first/unified-api.js",
41
+ "default": "./dist/local-first/unified-api.js"
42
+ },
38
43
  "./package.json": "./package.json"
39
44
  },
40
45
  "bin": {
@@ -46,7 +51,7 @@
46
51
  "build:web": "cd web/svelte && npm install && npm run build",
47
52
  "dev": "deno run -A --unstable-kv --watch src/main.ts serve --port 34567",
48
53
  "start": "node dist/cli.js serve",
49
- "test": "deno test -A --unstable-kv",
54
+ "test": "deno test -A --unstable-kv --sloppy-imports",
50
55
  "test:azure:relay": "deno test --allow-net --allow-env azure/tests/relay-tests.ts",
51
56
  "test:azure:full": "npm run test:azure:relay",
52
57
  "lint": "eslint . --ext .js,.ts,.tsx",
@@ -55,6 +60,7 @@
55
60
  "verify": "npm run build:lib && npm test",
56
61
  "prepare": "npm run build:lib",
57
62
  "prepublishOnly": "npm run verify && npm run build:web",
63
+ "validate-publish": "node scripts/validate-npm-publish.js",
58
64
  "postinstall": "node scripts/postinstall.js",
59
65
  "release-check": "node scripts/release-check.js",
60
66
  "update-changelog": "node scripts/update-changelog.js"
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Pre-publish Validation Script for NPM
5
+ *
6
+ * This script validates that the package is ready to be published to npm.
7
+ * It checks:
8
+ * 1. TypeScript compilation succeeds
9
+ * 2. Deno type checking passes
10
+ * 3. All tests pass
11
+ * 4. Required files exist in dist/
12
+ * 5. package.json is valid
13
+ */
14
+
15
+ const { execSync } = require("node:child_process");
16
+ const fs = require("node:fs");
17
+ const path = require("node:path");
18
+
19
+ const RED = "\x1b[31m";
20
+ const GREEN = "\x1b[32m";
21
+ const YELLOW = "\x1b[33m";
22
+ const RESET = "\x1b[0m";
23
+ const BOLD = "\x1b[1m";
24
+
25
+ // Configuration
26
+ const MAX_PACKAGE_SIZE_MB = 10;
27
+
28
+ function log(message, color = RESET) {
29
+ console.log(`${color}${message}${RESET}`);
30
+ }
31
+
32
+ function error(message) {
33
+ log(`โœ— ${message}`, RED);
34
+ }
35
+
36
+ function success(message) {
37
+ log(`โœ“ ${message}`, GREEN);
38
+ }
39
+
40
+ function info(message) {
41
+ log(`โ„น ${message}`, YELLOW);
42
+ }
43
+
44
+ function title(message) {
45
+ log(`\n${BOLD}${message}${RESET}`);
46
+ }
47
+
48
+ function runCommand(command, description) {
49
+ try {
50
+ info(`Running: ${description}...`);
51
+ execSync(command, { stdio: "inherit", cwd: process.cwd() });
52
+ success(description);
53
+ return true;
54
+ } catch (err) {
55
+ error(`${description} failed`);
56
+ return false;
57
+ }
58
+ }
59
+
60
+ function checkFileExists(filePath, description) {
61
+ const fullPath = path.join(process.cwd(), filePath);
62
+ if (fs.existsSync(fullPath)) {
63
+ success(`${description}: ${filePath}`);
64
+ return true;
65
+ } else {
66
+ error(`${description} missing: ${filePath}`);
67
+ return false;
68
+ }
69
+ }
70
+
71
+ async function main() {
72
+ title("๐Ÿš€ NPM Publish Validation");
73
+
74
+ let allChecksPassed = true;
75
+
76
+ // 1. Check package.json is valid
77
+ title("๐Ÿ“ฆ Validating package.json...");
78
+ try {
79
+ const packageJson = JSON.parse(
80
+ fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8"),
81
+ );
82
+ if (!packageJson.name || !packageJson.version) {
83
+ error("package.json missing required fields (name or version)");
84
+ allChecksPassed = false;
85
+ } else {
86
+ success(
87
+ `Package: ${packageJson.name}@${packageJson.version}`,
88
+ );
89
+ }
90
+ } catch (err) {
91
+ error(`Invalid package.json: ${err.message}`);
92
+ allChecksPassed = false;
93
+ }
94
+
95
+ // 2. TypeScript compilation
96
+ title("๐Ÿ”จ Building TypeScript...");
97
+ if (!runCommand("npm run build:lib", "TypeScript compilation")) {
98
+ allChecksPassed = false;
99
+ }
100
+
101
+ // 3. Check required dist files exist
102
+ title("๐Ÿ“ Checking required files...");
103
+ const requiredFiles = [
104
+ "dist/node-index.js",
105
+ "dist/node-index.d.ts",
106
+ "dist/better-sqlite3.js",
107
+ "dist/better-sqlite3.d.ts",
108
+ "dist/cli.js",
109
+ "dist/cli.d.ts",
110
+ "dist/local-first/unified-api.js",
111
+ "dist/local-first/unified-api.d.ts",
112
+ "dist/vscode/extension.js",
113
+ "dist/vscode/extension.d.ts",
114
+ "dist/types/node-types.js",
115
+ "dist/types/node-types.d.ts",
116
+ ];
117
+
118
+ for (const file of requiredFiles) {
119
+ if (!checkFileExists(file, "Required file")) {
120
+ allChecksPassed = false;
121
+ }
122
+ }
123
+
124
+ // 4. Deno type checking
125
+ title("๐Ÿฆ• Deno type checking...");
126
+ const denoPath = process.env.DENO_PATH || "deno";
127
+ const denoCheckFiles = [
128
+ "legacy/local-first/unified-api.ts",
129
+ "legacy/node-index.ts",
130
+ "legacy/better-sqlite3.ts",
131
+ "legacy/cli.ts",
132
+ "legacy/vscode/extension.ts",
133
+ ];
134
+
135
+ // Check if Deno is available
136
+ let denoAvailable = false;
137
+ try {
138
+ execSync(`${denoPath} --version`, { stdio: "pipe" });
139
+ denoAvailable = true;
140
+ } catch (err) {
141
+ info("Deno not available - skipping Deno type checks");
142
+ }
143
+
144
+ if (denoAvailable) {
145
+ let denoChecksFailed = false;
146
+ for (const file of denoCheckFiles) {
147
+ if (
148
+ !runCommand(
149
+ `${denoPath} check --sloppy-imports ${file}`,
150
+ `Deno type check: ${file}`,
151
+ )
152
+ ) {
153
+ error(`Deno type check failed for ${file}`);
154
+ denoChecksFailed = true;
155
+ allChecksPassed = false;
156
+ // Continue checking other files to show all failures
157
+ }
158
+ }
159
+ if (!denoChecksFailed) {
160
+ success("All Deno type checks passed");
161
+ }
162
+ }
163
+
164
+ // 5. Run tests (if Deno is available)
165
+ title("๐Ÿงช Running tests...");
166
+ if (denoAvailable) {
167
+ // Set DENO_PATH environment variable so npm test can find deno
168
+ const testEnv = { ...process.env };
169
+ const denoPathEnv = process.env.DENO_PATH;
170
+ if (denoPathEnv && denoPathEnv.includes(path.sep)) {
171
+ // If DENO_PATH was provided as a path, make sure its directory is in PATH for npm test
172
+ const denoBinDir = path.dirname(denoPathEnv);
173
+ // Use path.delimiter for cross-platform compatibility (: on Unix, ; on Windows)
174
+ testEnv.PATH = `${denoBinDir}${path.delimiter}${process.env.PATH}`;
175
+ }
176
+
177
+ try {
178
+ execSync("npm test", { stdio: "inherit", cwd: process.cwd(), env: testEnv });
179
+ success("Deno tests");
180
+ } catch (err) {
181
+ error("Tests failed");
182
+ allChecksPassed = false;
183
+ }
184
+ } else {
185
+ info("Deno tests skipped (Deno not available)");
186
+ }
187
+
188
+ // 6. Check package size
189
+ title("๐Ÿ“Š Package size check...");
190
+ try {
191
+ const output = execSync("npm pack --dry-run 2>&1", { encoding: "utf-8" });
192
+ const sizeMatch = output.match(/package size:\s+(\d+\.?\d*)\s*(\w+)/i);
193
+ if (sizeMatch) {
194
+ const size = parseFloat(sizeMatch[1]);
195
+ const unit = sizeMatch[2];
196
+ success(`Package size: ${size} ${unit}`);
197
+
198
+ // Warn if package is larger than configured threshold
199
+ if (unit.toLowerCase() === "mb" && size > MAX_PACKAGE_SIZE_MB) {
200
+ info(
201
+ `Warning: Package size is quite large (${size} ${unit}). Consider excluding unnecessary files.`,
202
+ );
203
+ }
204
+ }
205
+ } catch (err) {
206
+ info("Could not determine package size");
207
+ }
208
+
209
+ // Summary
210
+ title("๐Ÿ“‹ Validation Summary");
211
+ if (allChecksPassed) {
212
+ success("All critical checks passed! โœจ");
213
+ log(
214
+ "\nThe package is ready to be published to npm.",
215
+ GREEN,
216
+ );
217
+ process.exit(0);
218
+ } else {
219
+ error("Some checks failed. Please fix the issues before publishing.");
220
+ process.exit(1);
221
+ }
222
+ }
223
+
224
+ main().catch((err) => {
225
+ error(`Validation failed with error: ${err.message}`);
226
+ console.error(err);
227
+ process.exit(1);
228
+ });