pluresdb 1.0.1 → 1.3.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.
Files changed (79) hide show
  1. package/README.md +100 -5
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/better-sqlite3-shared.d.ts +12 -0
  4. package/dist/better-sqlite3-shared.d.ts.map +1 -0
  5. package/dist/better-sqlite3-shared.js +143 -0
  6. package/dist/better-sqlite3-shared.js.map +1 -0
  7. package/dist/better-sqlite3.d.ts +4 -0
  8. package/dist/better-sqlite3.d.ts.map +1 -0
  9. package/dist/better-sqlite3.js +8 -0
  10. package/dist/better-sqlite3.js.map +1 -0
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +21 -16
  13. package/dist/cli.js.map +1 -1
  14. package/dist/node-index.d.ts +98 -2
  15. package/dist/node-index.d.ts.map +1 -1
  16. package/dist/node-index.js +312 -6
  17. package/dist/node-index.js.map +1 -1
  18. package/dist/node-wrapper.d.ts.map +1 -1
  19. package/dist/node-wrapper.js +5 -3
  20. package/dist/node-wrapper.js.map +1 -1
  21. package/dist/types/index.d.ts.map +1 -1
  22. package/dist/types/index.js.map +1 -1
  23. package/dist/types/node-types.d.ts +12 -0
  24. package/dist/types/node-types.d.ts.map +1 -1
  25. package/dist/types/node-types.js.map +1 -1
  26. package/dist/vscode/extension.d.ts.map +1 -1
  27. package/dist/vscode/extension.js +4 -4
  28. package/dist/vscode/extension.js.map +1 -1
  29. package/examples/basic-usage.d.ts +1 -1
  30. package/examples/vscode-extension-example/src/extension.ts +15 -6
  31. package/examples/vscode-extension-integration.d.ts +24 -17
  32. package/examples/vscode-extension-integration.js +140 -106
  33. package/examples/vscode-extension-integration.ts +1 -1
  34. package/{src → legacy}/benchmarks/memory-benchmarks.ts +85 -51
  35. package/{src → legacy}/benchmarks/run-benchmarks.ts +32 -10
  36. package/legacy/better-sqlite3-shared.ts +157 -0
  37. package/legacy/better-sqlite3.ts +4 -0
  38. package/{src → legacy}/cli.ts +14 -4
  39. package/{src → legacy}/config.ts +2 -1
  40. package/{src → legacy}/core/crdt.ts +4 -1
  41. package/{src → legacy}/core/database.ts +57 -22
  42. package/{src → legacy}/healthcheck.ts +11 -5
  43. package/{src → legacy}/http/api-server.ts +125 -21
  44. package/{src → legacy}/index.ts +2 -2
  45. package/{src → legacy}/logic/rules.ts +3 -1
  46. package/{src → legacy}/main.ts +11 -4
  47. package/legacy/node-index.ts +823 -0
  48. package/{src → legacy}/node-wrapper.ts +18 -9
  49. package/{src → legacy}/sqlite-compat.ts +63 -16
  50. package/{src → legacy}/sqlite3-compat.ts +2 -2
  51. package/{src → legacy}/storage/kv-storage.ts +3 -1
  52. package/{src → legacy}/tests/core.test.ts +37 -13
  53. package/{src → legacy}/tests/fixtures/test-data.json +6 -1
  54. package/{src → legacy}/tests/integration/api-server.test.ts +110 -8
  55. package/{src → legacy}/tests/integration/mesh-network.test.ts +8 -2
  56. package/{src → legacy}/tests/logic.test.ts +6 -2
  57. package/{src → legacy}/tests/performance/load.test.ts +4 -2
  58. package/{src → legacy}/tests/security/input-validation.test.ts +5 -1
  59. package/{src → legacy}/tests/unit/core.test.ts +13 -3
  60. package/{src → legacy}/tests/unit/subscriptions.test.ts +1 -1
  61. package/{src → legacy}/tests/vscode_extension_test.ts +39 -11
  62. package/{src → legacy}/types/node-types.ts +14 -0
  63. package/{src → legacy}/vscode/extension.ts +37 -14
  64. package/package.json +19 -9
  65. package/scripts/compiled-crud-verify.ts +3 -1
  66. package/scripts/dogfood.ts +55 -16
  67. package/scripts/postinstall.js +4 -3
  68. package/scripts/release-check.js +190 -0
  69. package/scripts/run-tests.ts +5 -2
  70. package/scripts/update-changelog.js +214 -0
  71. package/web/svelte/package.json +5 -5
  72. package/src/node-index.ts +0 -385
  73. /package/{src → legacy}/main.rs +0 -0
  74. /package/{src → legacy}/network/websocket-server.ts +0 -0
  75. /package/{src → legacy}/tests/fixtures/performance-data.json +0 -0
  76. /package/{src → legacy}/tests/unit/vector-search.test.ts +0 -0
  77. /package/{src → legacy}/types/index.ts +0 -0
  78. /package/{src → legacy}/util/debug.ts +0 -0
  79. /package/{src → legacy}/vector/index.ts +0 -0
@@ -58,7 +58,10 @@ function createHarness(storageDir: string): TestHarness {
58
58
  return inputQueue.shift();
59
59
  },
60
60
  async showTextDocument(doc: unknown) {
61
- if (typeof doc === "object" && doc && "content" in doc && "language" in doc) {
61
+ if (
62
+ typeof doc === "object" && doc && "content" in doc &&
63
+ "language" in doc
64
+ ) {
62
65
  const record = doc as RecordedDocument;
63
66
  docs.push({ content: record.content, language: record.language });
64
67
  }
@@ -72,7 +75,9 @@ function createHarness(storageDir: string): TestHarness {
72
75
  },
73
76
  env: {
74
77
  async openExternal(target) {
75
- shownTargets.push(typeof target === "string" ? target : target.toString());
78
+ shownTargets.push(
79
+ typeof target === "string" ? target : target.toString(),
80
+ );
76
81
  },
77
82
  },
78
83
  Uri: {
@@ -107,7 +112,11 @@ async function getFreePort(): Promise<number> {
107
112
  return port;
108
113
  }
109
114
 
110
- async function waitFor(predicate: () => Promise<boolean>, timeout = 10_000, interval = 200) {
115
+ async function waitFor(
116
+ predicate: () => Promise<boolean>,
117
+ timeout = 10_000,
118
+ interval = 200,
119
+ ) {
111
120
  const deadline = Date.now() + timeout;
112
121
  while (Date.now() < deadline) {
113
122
  if (await predicate()) return;
@@ -126,7 +135,8 @@ async function removeDirWithRetry(target: string) {
126
135
  await Deno.remove(target, { recursive: true });
127
136
  return;
128
137
  } catch (error) {
129
- const retryable = error instanceof Error && /used by another process/i.test(error.message);
138
+ const retryable = error instanceof Error &&
139
+ /used by another process/i.test(error.message);
130
140
  if (!retryable || i === delays.length - 1) {
131
141
  throw error;
132
142
  }
@@ -198,17 +208,28 @@ Deno.test("VSCode integration dogfood workflow", async () => {
198
208
  if (!schemaDoc) {
199
209
  throw new Error("Expected schema document to open");
200
210
  }
201
- const schemaRows = JSON.parse(schemaDoc.content) as Array<Record<string, unknown>>;
211
+ const schemaRows = JSON.parse(schemaDoc.content) as Array<
212
+ Record<string, unknown>
213
+ >;
202
214
  const columnNames = schemaRows.map((row) => String(row.name));
203
- assert(columnNames.includes("key"), "Settings table should expose key column");
215
+ assert(
216
+ columnNames.includes("key"),
217
+ "Settings table should expose key column",
218
+ );
204
219
 
205
- harness.queueInputs("user:alpha", '{"name":"Ada","vector":[0.2,0.1,0.3,0.4],"role":"builder"}');
220
+ harness.queueInputs(
221
+ "user:alpha",
222
+ '{"name":"Ada","vector":[0.2,0.1,0.3,0.4],"role":"builder"}',
223
+ );
206
224
  const storeCommand = harness.commands.get("pluresdb.storeData");
207
225
  if (!storeCommand) {
208
226
  throw new Error("storeData command should be registered");
209
227
  }
210
228
  await storeCommand();
211
- assertMatch(harness.messages.at(-1)?.text ?? "", /Stored data for key: user:alpha/);
229
+ assertMatch(
230
+ harness.messages.at(-1)?.text ?? "",
231
+ /Stored data for key: user:alpha/,
232
+ );
212
233
 
213
234
  harness.queueInputs("user:alpha");
214
235
  const retrieveCommand = harness.commands.get("pluresdb.retrieveData");
@@ -233,8 +254,13 @@ Deno.test("VSCode integration dogfood workflow", async () => {
233
254
  if (!vectorDoc) {
234
255
  throw new Error("Expected vector search document to open");
235
256
  }
236
- const vectorResults = JSON.parse(vectorDoc.content) as Array<Record<string, unknown>>;
237
- assert(vectorResults.length > 0, "Vector search should return at least one result");
257
+ const vectorResults = JSON.parse(vectorDoc.content) as Array<
258
+ Record<string, unknown>
259
+ >;
260
+ assert(
261
+ vectorResults.length > 0,
262
+ "Vector search should return at least one result",
263
+ );
238
264
 
239
265
  const settingsRows = await extension.executeSQL(
240
266
  "SELECT name FROM pragma_table_info('documents')",
@@ -246,7 +272,9 @@ Deno.test("VSCode integration dogfood workflow", async () => {
246
272
  await removeDirWithRetry(storageDir);
247
273
  } catch (error) {
248
274
  console.warn(
249
- `⚠️ Failed to remove temp dir: ${error instanceof Error ? error.message : String(error)}`,
275
+ `⚠️ Failed to remove temp dir: ${
276
+ error instanceof Error ? error.message : String(error)
277
+ }`,
250
278
  );
251
279
  }
252
280
  }
@@ -23,6 +23,20 @@ export interface QueryResult {
23
23
  lastInsertRowId: number;
24
24
  }
25
25
 
26
+ export interface BetterSQLite3Options extends PluresDBOptions {
27
+ filename?: string;
28
+ memory?: boolean;
29
+ readonly?: boolean;
30
+ fileMustExist?: boolean;
31
+ verbose?: (...args: unknown[]) => void;
32
+ }
33
+
34
+ export interface BetterSQLite3RunResult {
35
+ changes: number;
36
+ lastInsertRowid: number | null;
37
+ columns?: string[];
38
+ }
39
+
26
40
  export interface VectorSearchResult {
27
41
  id: string;
28
42
  content: string;
@@ -24,7 +24,10 @@ export type VSCodeWindow = {
24
24
  };
25
25
 
26
26
  export type VSCodeCommands = {
27
- registerCommand(command: string, callback: (...args: unknown[]) => unknown): DisposableLike;
27
+ registerCommand(
28
+ command: string,
29
+ callback: (...args: unknown[]) => unknown,
30
+ ): DisposableLike;
28
31
  };
29
32
 
30
33
  export type VSCodeWorkspace = {
@@ -77,7 +80,11 @@ export class PluresVSCodeExtension {
77
80
  private readonly disposables: DisposableLike[] = [];
78
81
  private activated = false;
79
82
 
80
- constructor(vscodeApi: VSCodeAPI, context: ExtensionContextLike, options: ExtensionOptions = {}) {
83
+ constructor(
84
+ vscodeApi: VSCodeAPI,
85
+ context: ExtensionContextLike,
86
+ options: ExtensionOptions = {},
87
+ ) {
81
88
  this.vscode = vscodeApi;
82
89
  this.context = context;
83
90
  this.commandPrefix = options.commandPrefix ?? "pluresdb";
@@ -88,10 +95,10 @@ export class PluresVSCodeExtension {
88
95
  ...options.config,
89
96
  };
90
97
 
91
- this.plures =
92
- options.pluresInstance ?? new PluresNode({ config: mergedConfig, autoStart: false });
93
- this.sqlite =
94
- options.sqliteInstance ?? new SQLiteCompatibleAPI({ config: mergedConfig, autoStart: false });
98
+ this.plures = options.pluresInstance ??
99
+ new PluresNode({ config: mergedConfig, autoStart: false });
100
+ this.sqlite = options.sqliteInstance ??
101
+ new SQLiteCompatibleAPI({ config: mergedConfig, autoStart: false });
95
102
 
96
103
  this.setupEventHandlers();
97
104
  }
@@ -109,7 +116,9 @@ export class PluresVSCodeExtension {
109
116
  this.activated = true;
110
117
  await this.safeInfo("PluresDB extension activated");
111
118
  } catch (error) {
112
- await this.safeError(`Failed to activate PluresDB: ${this.errorMessage(error)}`);
119
+ await this.safeError(
120
+ `Failed to activate PluresDB: ${this.errorMessage(error)}`,
121
+ );
113
122
  throw error;
114
123
  }
115
124
  }
@@ -140,7 +149,12 @@ export class PluresVSCodeExtension {
140
149
  return this.sqlite.getValue(`settings:${key}`);
141
150
  }
142
151
 
143
- async storeDocument(id: string, content: string, language: string, filePath: string) {
152
+ async storeDocument(
153
+ id: string,
154
+ content: string,
155
+ language: string,
156
+ filePath: string,
157
+ ) {
144
158
  return this.sqlite.put(`documents:${id}`, {
145
159
  content,
146
160
  language,
@@ -187,8 +201,9 @@ export class PluresVSCodeExtension {
187
201
 
188
202
  private registerCommands() {
189
203
  const register = (name: string, factory: CommandFactory) => {
190
- const disposable = this.vscode.commands.registerCommand(`${this.commandPrefix}.${name}`, () =>
191
- factory(),
204
+ const disposable = this.vscode.commands.registerCommand(
205
+ `${this.commandPrefix}.${name}`,
206
+ () => factory(),
192
207
  );
193
208
  this.context.subscriptions.push(disposable);
194
209
  this.disposables.push(disposable);
@@ -235,7 +250,9 @@ export class PluresVSCodeExtension {
235
250
  });
236
251
  await this.vscode.window.showTextDocument(doc);
237
252
  } catch (error) {
238
- await this.safeError(`Vector search failed: ${this.errorMessage(error)}`);
253
+ await this.safeError(
254
+ `Vector search failed: ${this.errorMessage(error)}`,
255
+ );
239
256
  }
240
257
  });
241
258
 
@@ -259,7 +276,9 @@ export class PluresVSCodeExtension {
259
276
  await this.sqlite.put(key, value);
260
277
  await this.safeInfo(`Stored data for key: ${key}`);
261
278
  } catch (error) {
262
- await this.safeError(`Failed to store data: ${this.errorMessage(error)}`);
279
+ await this.safeError(
280
+ `Failed to store data: ${this.errorMessage(error)}`,
281
+ );
263
282
  }
264
283
  });
265
284
 
@@ -283,7 +302,9 @@ export class PluresVSCodeExtension {
283
302
  await this.safeInfo("Key not found");
284
303
  }
285
304
  } catch (error) {
286
- await this.safeError(`Failed to retrieve data: ${this.errorMessage(error)}`);
305
+ await this.safeError(
306
+ `Failed to retrieve data: ${this.errorMessage(error)}`,
307
+ );
287
308
  }
288
309
  });
289
310
  }
@@ -316,7 +337,9 @@ export class PluresVSCodeExtension {
316
337
  try {
317
338
  await this.sqlite.exec(sql);
318
339
  } catch (error) {
319
- await this.safeError(`Failed to initialize database: ${this.errorMessage(error)}`);
340
+ await this.safeError(
341
+ `Failed to initialize database: ${this.errorMessage(error)}`,
342
+ );
320
343
  }
321
344
  }
322
345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pluresdb",
3
- "version": "1.0.1",
3
+ "version": "1.3.0",
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",
@@ -15,6 +15,11 @@
15
15
  "require": "./dist/node-index.js",
16
16
  "default": "./dist/node-index.js"
17
17
  },
18
+ "./better-sqlite3": {
19
+ "types": "./dist/better-sqlite3.d.ts",
20
+ "require": "./dist/better-sqlite3.js",
21
+ "default": "./dist/better-sqlite3.js"
22
+ },
18
23
  "./vscode": {
19
24
  "types": "./dist/vscode/extension.d.ts",
20
25
  "require": "./dist/vscode/extension.js",
@@ -42,10 +47,15 @@
42
47
  "dev": "deno run -A --unstable-kv --watch src/main.ts serve --port 34567",
43
48
  "start": "node dist/cli.js serve",
44
49
  "test": "deno test -A --unstable-kv",
50
+ "lint": "eslint . --ext .js,.ts,.tsx",
51
+ "fmt:check": "prettier --check .",
52
+ "fmt": "prettier --write .",
45
53
  "verify": "npm run build:lib && npm test",
46
54
  "prepare": "npm run build:lib",
47
55
  "prepublishOnly": "npm run verify && npm run build:web",
48
- "postinstall": "node scripts/postinstall.js"
56
+ "postinstall": "node scripts/postinstall.js",
57
+ "release-check": "node scripts/release-check.js",
58
+ "update-changelog": "node scripts/update-changelog.js"
49
59
  },
50
60
  "keywords": [
51
61
  "database",
@@ -81,7 +91,7 @@
81
91
  "files": [
82
92
  "dist/",
83
93
  "web/svelte/dist/",
84
- "src/",
94
+ "legacy/",
85
95
  "examples/",
86
96
  "scripts/",
87
97
  "README.md",
@@ -90,18 +100,18 @@
90
100
  ],
91
101
  "dependencies": {
92
102
  "cors": "^2.8.5",
93
- "express": "^4.21.2",
103
+ "express": "^5.1.0",
94
104
  "ws": "^8.18.3"
95
105
  },
96
106
  "devDependencies": {
97
107
  "@types/cors": "^2.8.19",
98
- "@types/express": "^4.17.23",
99
- "@types/node": "^20.19.19",
108
+ "@types/express": "^5.0.0",
109
+ "@types/node": "^22.10.0",
100
110
  "@types/vscode": "^1.104.0",
101
111
  "@types/ws": "^8.18.1",
102
- "@typescript-eslint/eslint-plugin": "^8.45.0",
103
- "@typescript-eslint/parser": "^8.45.0",
104
- "eslint": "^9.36.0",
112
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
113
+ "@typescript-eslint/parser": "^8.46.4",
114
+ "eslint": "^9.39.1",
105
115
  "eslint-config-prettier": "^10.1.8",
106
116
  "eslint-plugin-import": "^2.32.0",
107
117
  "prettier": "^3.6.2",
@@ -16,7 +16,9 @@ clientB.connect(serverUrl);
16
16
 
17
17
  const id = `bin:crud:${crypto.randomUUID()}`;
18
18
 
19
- const receivedOnB = new Promise<void>((resolve) => clientB.on(id, (n) => n && resolve()));
19
+ const receivedOnB = new Promise<void>((resolve) =>
20
+ clientB.on(id, (n) => n && resolve())
21
+ );
20
22
 
21
23
  await clientA.put(id, { text: "compiled works" } as Record<string, unknown>);
22
24
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S deno run -A --unstable-kv
2
2
 
3
- import { GunDB } from "../src/core/database.ts";
4
- import { startApiServer } from "../src/http/api-server.ts";
3
+ import { GunDB } from "../legacy/core/database.ts";
4
+ import { startApiServer } from "../legacy/http/api-server.ts";
5
5
 
6
6
  declare const Deno: any;
7
7
 
@@ -100,7 +100,9 @@ async function main() {
100
100
  assert(putRes.ok, `API put failed (${putRes.status})`);
101
101
  record("API put", true);
102
102
 
103
- const getRes = await fetch(`${apiUrl}/api/get?id=${encodeURIComponent(nodeId)}`);
103
+ const getRes = await fetch(
104
+ `${apiUrl}/api/get?id=${encodeURIComponent(nodeId)}`,
105
+ );
104
106
  assert(getRes.ok, "API get failed");
105
107
  const getJson = await getRes.json();
106
108
  assert(getJson.text === initialPayload.text, "Unexpected API get payload");
@@ -120,7 +122,10 @@ async function main() {
120
122
  );
121
123
  assert(searchRes.ok, "API search failed");
122
124
  const searchJson = await searchRes.json();
123
- assert(Array.isArray(searchJson) && searchJson.length > 0, "Vector search returned no results");
125
+ assert(
126
+ Array.isArray(searchJson) && searchJson.length > 0,
127
+ "Vector search returned no results",
128
+ );
124
129
  record("API vector search", true);
125
130
 
126
131
  const updatePayload = {
@@ -135,17 +140,27 @@ async function main() {
135
140
  assert(putUpdate.ok, "API put update failed");
136
141
  record("API update", true);
137
142
 
138
- const historyRes = await fetch(`${apiUrl}/api/history?id=${encodeURIComponent(nodeId)}`);
143
+ const historyRes = await fetch(
144
+ `${apiUrl}/api/history?id=${encodeURIComponent(nodeId)}`,
145
+ );
139
146
  assert(historyRes.ok, "API history failed");
140
147
  const historyJson = await historyRes.json();
141
- assert(Array.isArray(historyJson) && historyJson.length >= 2, "API history missing versions");
148
+ assert(
149
+ Array.isArray(historyJson) && historyJson.length >= 2,
150
+ "API history missing versions",
151
+ );
142
152
  record("API history", true, `${historyJson.length} versions`);
143
153
 
144
- const restoreTimestamp =
145
- historyJson.at(-1)?.timestamp ?? historyJson[historyJson.length - 1]?.timestamp;
146
- assert(typeof restoreTimestamp === "number", "Failed to locate restore timestamp");
154
+ const restoreTimestamp = historyJson.at(-1)?.timestamp ??
155
+ historyJson[historyJson.length - 1]?.timestamp;
156
+ assert(
157
+ typeof restoreTimestamp === "number",
158
+ "Failed to locate restore timestamp",
159
+ );
147
160
  const restoreRes = await fetch(
148
- `${apiUrl}/api/restore?id=${encodeURIComponent(nodeId)}&timestamp=${restoreTimestamp}`,
161
+ `${apiUrl}/api/restore?id=${
162
+ encodeURIComponent(nodeId)
163
+ }&timestamp=${restoreTimestamp}`,
149
164
  );
150
165
  assert(restoreRes.ok, "API restore failed");
151
166
  record("API restore", true);
@@ -153,7 +168,10 @@ async function main() {
153
168
  const restored = await (
154
169
  await fetch(`${apiUrl}/api/get?id=${encodeURIComponent(nodeId)}`)
155
170
  ).json();
156
- assert(restored.text === initialPayload.text, "Restore did not revert payload");
171
+ assert(
172
+ restored.text === initialPayload.text,
173
+ "Restore did not revert payload",
174
+ );
157
175
  record("API post-restore verification", true);
158
176
 
159
177
  const instancesRes = await fetch(
@@ -162,7 +180,8 @@ async function main() {
162
180
  assert(instancesRes.ok, "API instances failed");
163
181
  const instancesJson = await instancesRes.json();
164
182
  assert(
165
- Array.isArray(instancesJson) && instancesJson.some((n: any) => n.id === nodeId),
183
+ Array.isArray(instancesJson) &&
184
+ instancesJson.some((n: any) => n.id === nodeId),
166
185
  "Instances endpoint missing node",
167
186
  );
168
187
  record("API type instances", true);
@@ -192,10 +211,19 @@ async function main() {
192
211
  const cliList = await runCli(["list", "--kv", kvPath]);
193
212
  assert(cliList.code === 0, `CLI list failed: ${cliList.stderr}`);
194
213
  const cliListJson = JSON.parse(cliList.stdout.trim() || "[]");
195
- assert(Array.isArray(cliListJson) && cliListJson.length >= 2, "CLI list missing entries");
214
+ assert(
215
+ Array.isArray(cliListJson) && cliListJson.length >= 2,
216
+ "CLI list missing entries",
217
+ );
196
218
  record("CLI list", true);
197
219
 
198
- const cliSearch = await runCli(["vsearch", "dogfooding", "5", "--kv", kvPath]);
220
+ const cliSearch = await runCli([
221
+ "vsearch",
222
+ "dogfooding",
223
+ "5",
224
+ "--kv",
225
+ kvPath,
226
+ ]);
199
227
  assert(cliSearch.code === 0, `CLI vsearch failed: ${cliSearch.stderr}`);
200
228
  const cliSearchJson = JSON.parse(cliSearch.stdout.trim() || "[]");
201
229
  assert(
@@ -206,7 +234,11 @@ async function main() {
206
234
 
207
235
  console.log("\n🎉 Dogfooding run succeeded!");
208
236
  } catch (error) {
209
- record("Dogfooding run", false, error instanceof Error ? error.message : String(error));
237
+ record(
238
+ "Dogfooding run",
239
+ false,
240
+ error instanceof Error ? error.message : String(error),
241
+ );
210
242
  await finalize();
211
243
  console.log("\n❌ Dogfooding run failed");
212
244
  console.log(error);
@@ -218,7 +250,14 @@ async function main() {
218
250
 
219
251
  async function runCli(args: string[]) {
220
252
  const command = new Deno.Command("deno", {
221
- args: ["run", "-A", "--unstable-kv", "--no-lock", "src/main.ts", ...args],
253
+ args: [
254
+ "run",
255
+ "-A",
256
+ "--unstable-kv",
257
+ "--no-lock",
258
+ "legacy/main.ts",
259
+ ...args,
260
+ ],
222
261
  stdout: "piped",
223
262
  stderr: "piped",
224
263
  });
@@ -37,7 +37,8 @@ function installDeno() {
37
37
 
38
38
  if (platform === "win32") {
39
39
  // Windows - use PowerShell
40
- installCommand = `powershell -c "iwr https://deno.land/install.ps1 -useb | iex"`;
40
+ installCommand =
41
+ `powershell -c "iwr https://deno.land/install.ps1 -useb | iex"`;
41
42
  } else if (platform === "darwin") {
42
43
  // macOS - use Homebrew or curl
43
44
  installCommand = "curl -fsSL https://deno.land/install.sh | sh";
@@ -115,7 +116,7 @@ async function main() {
115
116
  log("Deno not found, attempting to install...");
116
117
  try {
117
118
  await installDeno();
118
- } catch (error) {
119
+ } catch {
119
120
  logError("Failed to install Deno automatically");
120
121
  logError("Please install Deno manually:");
121
122
  logError(" Windows: iwr https://deno.land/install.ps1 -useb | iex");
@@ -147,7 +148,7 @@ async function main() {
147
148
  log(" node node_modules/pluresdb/dist/cli.js serve");
148
149
  } catch (error) {
149
150
  logError(`Setup failed: ${error.message}`);
150
- process.exit(1);
151
+ Deno.exit(1);
151
152
  }
152
153
  }
153
154
 
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Pre-release validation checks
5
+ * Ensures the repository is ready for a release
6
+ *
7
+ * Usage: node scripts/release-check.js
8
+ */
9
+
10
+ const { execSync } = require('child_process');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ function run(cmd, ignoreError = false) {
15
+ try {
16
+ return execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim();
17
+ } catch (e) {
18
+ if (ignoreError) return null;
19
+ throw e;
20
+ }
21
+ }
22
+
23
+ function checkGitStatus() {
24
+ console.log('🔍 Checking git status...');
25
+ const status = run('git status --porcelain', true);
26
+
27
+ if (status && status.length > 0) {
28
+ console.warn('⚠️ Warning: Working directory has uncommitted changes');
29
+ console.log(status);
30
+ } else {
31
+ console.log('✅ Working directory is clean');
32
+ }
33
+ }
34
+
35
+ function checkPackageVersions() {
36
+ console.log('\n🔍 Checking package versions...');
37
+
38
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
39
+ const cargoToml = fs.readFileSync('Cargo.toml', 'utf8');
40
+
41
+ const packageVersion = packageJson.version;
42
+ const cargoMatch = cargoToml.match(/^version\s*=\s*"([^"]+)"/m);
43
+ const cargoVersion = cargoMatch ? cargoMatch[1] : null;
44
+
45
+ console.log(` package.json: ${packageVersion}`);
46
+ console.log(` Cargo.toml: ${cargoVersion || 'not found'}`);
47
+
48
+ if (cargoVersion && packageVersion !== cargoVersion) {
49
+ console.error('❌ Version mismatch between package.json and Cargo.toml');
50
+ return false;
51
+ }
52
+
53
+ console.log('✅ Package versions are consistent');
54
+ return true;
55
+ }
56
+
57
+ function checkChangelog() {
58
+ console.log('\n🔍 Checking CHANGELOG.md...');
59
+
60
+ if (!fs.existsSync('CHANGELOG.md')) {
61
+ console.error('❌ CHANGELOG.md not found');
62
+ return false;
63
+ }
64
+
65
+ const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
66
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
67
+ const currentVersion = packageJson.version;
68
+
69
+ if (!changelog.includes(`## [${currentVersion}]`)) {
70
+ console.warn(`⚠️ Warning: Current version ${currentVersion} not found in CHANGELOG.md`);
71
+ } else {
72
+ console.log(`✅ CHANGELOG.md includes current version ${currentVersion}`);
73
+ }
74
+
75
+ return true;
76
+ }
77
+
78
+ function checkCommitMessages() {
79
+ console.log('\n🔍 Checking recent commit messages...');
80
+
81
+ const lastTag = run('git describe --tags --abbrev=0 2>/dev/null', true);
82
+ const range = lastTag ? `${lastTag}..HEAD` : '--all';
83
+
84
+ const commits = run(`git log --format="%s" ${range}`, true);
85
+
86
+ if (!commits) {
87
+ console.log('ℹ️ No commits to check');
88
+ return true;
89
+ }
90
+
91
+ const commitLines = commits.split('\n').filter(Boolean);
92
+ const conventionalTypes = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert'];
93
+
94
+ let conventionalCount = 0;
95
+ commitLines.forEach(commit => {
96
+ const match = commit.match(/^(\w+)(\(.+\))?:/);
97
+ if (match && conventionalTypes.includes(match[1])) {
98
+ conventionalCount++;
99
+ }
100
+ });
101
+
102
+ const percentage = (conventionalCount / commitLines.length * 100).toFixed(0);
103
+ console.log(` ${conventionalCount}/${commitLines.length} commits follow conventional format (${percentage}%)`);
104
+
105
+ if (conventionalCount === 0) {
106
+ console.warn('⚠️ Warning: No conventional commits found');
107
+ } else {
108
+ console.log('✅ Found conventional commits');
109
+ }
110
+
111
+ return true;
112
+ }
113
+
114
+ function checkBuildStatus() {
115
+ console.log('\n🔍 Checking if project builds...');
116
+
117
+ try {
118
+ // Check if npm build works
119
+ console.log(' Running npm run build:lib...');
120
+ run('npm run build:lib');
121
+ console.log('✅ Build successful');
122
+ return true;
123
+ } catch (e) {
124
+ console.error('❌ Build failed');
125
+ console.error(e.message);
126
+ return false;
127
+ }
128
+ }
129
+
130
+ function checkTests() {
131
+ console.log('\n🔍 Checking tests...');
132
+
133
+ try {
134
+ console.log(' Running tests...');
135
+ run('npm test');
136
+ console.log('✅ Tests passed');
137
+ return true;
138
+ } catch (e) {
139
+ console.error('❌ Tests failed');
140
+ console.error(e.message);
141
+ return false;
142
+ }
143
+ }
144
+
145
+ function main() {
146
+ console.log('🚀 Running pre-release checks for pluresdb\n');
147
+
148
+ let allPassed = true;
149
+
150
+ try {
151
+ checkGitStatus();
152
+
153
+ if (!checkPackageVersions()) {
154
+ allPassed = false;
155
+ }
156
+
157
+ if (!checkChangelog()) {
158
+ allPassed = false;
159
+ }
160
+
161
+ checkCommitMessages();
162
+
163
+ // Skip build and test checks in CI for now
164
+ if (process.env.CI !== 'true') {
165
+ if (!checkBuildStatus()) {
166
+ allPassed = false;
167
+ }
168
+
169
+ if (!checkTests()) {
170
+ allPassed = false;
171
+ }
172
+ } else {
173
+ console.log('\nℹ️ Skipping build and test checks in CI');
174
+ }
175
+
176
+ console.log('\n' + '='.repeat(50));
177
+ if (allPassed) {
178
+ console.log('✅ All pre-release checks passed!');
179
+ process.exit(0);
180
+ } else {
181
+ console.log('❌ Some pre-release checks failed');
182
+ process.exit(1);
183
+ }
184
+ } catch (e) {
185
+ console.error('\n❌ Pre-release check error:', e.message);
186
+ process.exit(1);
187
+ }
188
+ }
189
+
190
+ main();